简介:Codejock Xtreme Toolkit Pro v15.3.1 完整源码包,已针对 VS2017 完成 32/64 位工程属性适配,开发者可直接打开 .sln 编译,也可直接引用包内已编译好的 debug/release 动态库与静态库(如 ToolkitPro1531vc150.lib、ToolkitPro1531vc150.dll、ToolkitPro1531vc150S.lib、ToolkitPro1531vc150SD.lib 等),省去自行编译和手工配置的繁琐。面向需要为 MFC/WTL 程序添加专业界面组件的 Windows 开发者,尤其适合对 CommandBars、皮肤引擎、Docking Pane、主题换肤等模块有定制需求的中高级用户。包内共 2000 个文件,包含 675 个 h 头文件、573 个 cpp 源文件、410 个 rc 资源脚本,以及大量 png/bmp 位图素材;其中 h/cpp 构成可读的完整工程源码,rc 与位图资源便于界面换肤和图标替换,另有 41 个 sln 解决方案与 24 个 vcxproj 工程文件辅助分模块编译,压缩包约 80MB。已编译好的库可直接用于快速集成,完整源码则可满足对内部机制的研究需求,从底层绘制到高级组件一应俱全。整体文件类型与工程结构清晰,目录划分便于查找模块,既能学习 Xtreme Toolkit 的内部实现,也能快速集成到既有 MFC 项目。已有 461 人学习下载。
1. Codejock Xtreme Toolkit Pro v15.3.1 是什么:给 VS2017 的 MFC 老项目补上现代界面
还在用 VS2017 维护 MFC 桌面程序的团队,多半经历过同一个尴尬:业务逻辑稳如老狗,界面却被客户截图挂出来当反面教材。Codejock Xtreme Toolkit Pro v15.3.1 就是这类项目最常见的答案——一套商业级 MFC 界面组件集,Ribbon、DockingPane、PropertyGrid、SkinFramework 等组件齐全,装上之后 MFC 窗口直接长出 Office 式外观,业务层基本不用动。
v15.3.1 对应 VS2017 的 vc141 工具集,安装包自带匹配的库和 DLL,省去拿源码自己重编。适合谁:维护 VS2017 + MFC 老产品、不想推倒重写、又要给客户一个说得过去的交付观感的团队。下面一条线讲完从配置到排查。
2. 在 VS2017 里配通 Codejock 环境:安装、vc141 库与最小工程
2.1 安装前先看清版本线:为什么认准 vc141
VS2017 默认的 C++ 工具集是 v141,Codejock 的 v15.3.1 正是按这条工具集发布的。老项目如果是从 VS2013/2015 升上来的,要确认工程属性里「平台工具集」选的是 v141,否则后面链接时导入库的 CRT 版本和 MFC 对不上,编译期会出现大量 C4819 之类的警告,看着烦,实际也埋着坑。
安装包默认装在C:\Program Files (x86)\Codejock Software\Xtreme Toolkit Pro v15.3.1\,目录里重点认三个子目录:Sources是所有头文件和源码,Libs是按工具集分层的库文件,Redist是允许随程序分发的 DLL。装的时候建议把 Samples 勾上,里面每个示例工程都是对应组件的最小用法,后面我带你看的每一种组件,都能在 Samples 里找到原型。
如果你的开发机没网,VS2017 要用离线安装包装,这时记得勾上「VC++ 2017 工具集」和「适用于 v141 的 MFC(x86 与 x64)」两个负载。很多人只勾了「使用 C++ 的桌面开发」,MFC 类库没装,Codejock 的头文件能过编译,一链接就报cannot open file mfc140u.lib,属于白折腾半小时的典型。
提示:安装时组件选少了,重跑安装程序勾选变更即可,不用卸载重来。
另外别忽略 x86/x64 的选择。v15.3.1 的 Libs 下 vc141 会拆出 x86 和 x64 两套,64 位系统上跑 32 位程序照样用 x86 库,但把 x86 库配给 x64 目标工程,链接器直接报平台不匹配。新建工程时先定死目标平台,把属性管理器里用不到的平台配置删掉,省得后面误选。
2.2 工程配置四件套:include、lib、预处理宏,一个不能少
用过 WPF 那边 HandyControl 的人,第一次配 Codejock 会很不习惯:MFC 没有 NuGet,也没有 Qt VS Tools 那种自动填路径的插件。include 目录、库目录、预处理宏、运行库四项全靠手配。这个套路和 Windows 下配置 PCL 一模一样——头文件目录、库文件目录、依赖库、运行库四件事配齐,少一件都是链接报错。
标准做法是建一个属性表(.props)而不是改单个工程的 .vcxproj,团队所有成员引用同一份:
<!-- Codejock.props:静态链接版。动态链接时删掉 _XTP_STATICLINK --> <PropertyGroup Label="Codejock"> <CodejockRoot>C:\Program Files (x86)\Codejock Software\Xtreme Toolkit Pro v15.3.1</CodejockRoot> </PropertyGroup> <ItemDefinitionGroup> <ClCompile> <AdditionalIncludeDirectories>$(CodejockRoot)\Sources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions>_XTP_STATICLINK;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <AdditionalLibraryDirectories>$(CodejockRoot)\Libs\vc141\x64\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> </Link> </ItemDefinitionGroup>这里的_XTP_STATICLINK只在静态链接 Codejock 时才定义,动态链接(默认方式)千万别加,否则链接器会找一堆不存在的符号。$(Configuration)会自动带出 Debug 或 Release 目录,前提是 Libs 里对应的子目录名按这个规律存在。库文件命名也有规律:名字里一定带vc141,Debug 版带D,静态库带Static,按这三个词在 Libs 目录里过滤,比死记文件名可靠。另外链接器输入里还要加具体的库文件名,把 Libs 目录下对应配置的 .lib 逐个加进「附加依赖项」,名字以你安装包里的实际文件为准。
2.3 最小工程:先跑通 Codejock 的接入骨架
路径配好,先别急着堆组件,跑通一个最小工程最重要。以一个对话框程序为例:
// App.cpp —— Codejock 接入的最少骨架 #include "XTPToolkit.h" // 总入口,含 XTPApplicationInitializer 声明 #include "XTPCommandBars.h" // CommandBars / Ribbon 组件 class CMyApp : public CWinApp { public: BOOL InitInstance() override; XTPApplicationInitializer m_xtpApp; // 随 CWinApp 构造完成 XTP 全局初始化 }; CMyApp theApp; BOOL CMyApp::InitInstance() { if (!CXTPCommandBars::Initialize()) // 注册组件内部窗口类 return FALSE; CMyDlg dlg; m_pMainWnd = &dlg; dlg.DoModal(); return FALSE; }这段代码有两个容易抄错的地方。第一,XTPApplicationInitializer必须是 CWinApp 派生类的成员,而且和 theApp 全局对象放在同一个编译单元,保证它在 WinMain 入口之前完成构造;漏掉它,程序启动后所有 XTP 窗口的创建都会断言失败。第二,CXTPCommandBars::Initialize()放在 InitInstance 最前面,负责注册组件内部窗口类,失败直接 return FALSE,不要硬着头皮往下走。
工程字符集建议保持 UNICODE。v15 时代的多字节字符集支持已经明显弱化,示例工程全走 UNICODE,硬切多字节会冒出一堆字符集转换的编译报错,不值得。把 CMyDlg 换成 CFrameWnd 派生框架,就能进下一节真正加 Ribbon。
3. 上手核心组件:Ribbon、DockingPane 与 PropertyGrid 的参数要点
3.1 Ribbon 的三级结构:Page、Group、Button
Codejock 的 Ribbon 严格按「页面(Page)→ 组(Group)→ 按钮(Button)」三级组织。在框架窗口的 OnCreate 里创建:
// MainFrame::OnCreate,this 是 CFrameWnd 派生类 CXTPRibbonBar* pRibbon = CXTPRibbonBar::CreateRibbonBar(this); pRibbon->SetTheme(xtpRibbonThemeOffice2013); // 扁平化主题,v15 内置 pRibbon->EnableTooltips(TRUE); CXTPRibbonPage* pHome = pRibbon->AddPage(_T("主页")); CXTPRibbonGroup* pFile = pHome->AddGroup(_T("文件")); pFile->AddButton(ID_FILE_OPEN); // 按钮用命令 ID 关联 pFile->AddButton(ID_FILE_SAVE); pRibbon->AttachToFrame(this); // 接管框架的非客户区绘制参数上最值得说的是两点。一是SetTheme的枚举:v15.3.1 里 Office2007、Office2010、Office2013 三套都内置,Office2013 是扁平无高光风格,机器配置一般的工业上位机建议用这套,渲染开销最小;想要旧式立体感再切回 Office2007。二是AddButton传的是命令 ID 而不是字符串,Codejock 会自己去资源里找标题、图标和状态栏提示——所以 String Table 里必须给ID_FILE_OPEN配好中文标题,否则按钮上显示的是数字 ID。图标建议准备 16x16 和 32x32 两套,CommandBars 会按上下文自动切换,只给一套在大图标模式下会拉伸模糊。
另一个高频疑问是「原来的菜单栏怎么办」。AttachToFrame之后旧菜单由 Ribbon 接管绘制,如果 OnCreate 里还有 LoadMenu 逻辑,界面就会出现菜单和 Ribbon 重叠,需要去掉载入菜单那几行,把菜单资源只留给快捷键映射用。个别小版本的挂载函数名有出入,以你安装包头文件里的声明为准,思路是一样的。
3.2 DockingPane:停靠布局与注册表状态保存
DockingPane 是 Codejock 里和 Ribbon 搭配最多的组件,先装管理器,再注册面板:
// 同一个框架窗口,管理器只安装一次 CXTPDockingPaneManager* pPanes = GetDockingPaneManager(); pPanes->InstallDockingPanes(this, xtpPaneThemeOffice2013); // 创建右侧停靠的属性面板,初始宽度 320 CXTPDockingPane* pPropPane = pPanes->CreatePane( ID_PANE_PROPERTIES, CRect(0, 0, 320, 480), xtpPaneDockRight); pPropPane->SetCaption(_T("属性")); // 布局持久化:SaveState 在窗口销毁前调用,LoadState 在主窗口创建后调用 pPanes->SaveState(_T("Software\\MyCompany\\MyApp\\Layout"));CreatePane的第三个参数控制初始停靠位置,可选左、右、上、下、浮动五种,用户手动拖动后布局会变,代码里不要再去写死位置,交给用户操作即可。浮动面板用xtpPaneDockFloat,创建后用户可以拖到任意显示器上;程序退出前记得 SaveState,下次启动 LoadState 恢复,用户不用每天重新排一遍窗口。
SaveState/LoadState 的注册表路径要写完整,带公司名和产品名,避免和别的软件共用 HKCU 下的通用键名互相覆盖。有个隐蔽点:LoadState 必须在主窗口已创建、Ribbon 已 AttachToFrame 之后调用,否则面板位置用的是旧窗口矩形,恢复出来的布局会整体偏移一截,排查起来非常像代码算错坐标。
3.3 PropertyGrid:属性面板的常用参数表
属性网格直接嵌到 DockingPane 面板里:
// 以面板窗口为父窗口创建属性网格 CXTPPropertyGrid* pGrid = new CXTPPropertyGrid(); pGrid->Create(WS_CHILD | WS_VISIBLE, CRect(0, 0, 320, 480), pPropPane->GetPaneWindow(), ID_PROP_GRID); CXTPPropertyGridCategory* pCat = pGrid->AddCategory(_T("通信参数")); pCat->AddItem(new CXTPPropertyGridItem(_T("端口号"), _T("8080"))); pCat->AddItem(new CXTPPropertyGridItemBool(_T("自动重连"), TRUE));PropertyGrid 在 v15 里已经很成熟,日常就用到这几个参数:
| 属性/方法 | 作用 | 注意点 |
|---|---|---|
| ShowDescription(TRUE) | 显示/隐藏底部描述栏 | 默认不显示,交付前建议打开 |
| SetReadOnly(TRUE) | 整表只读 | 只读态不触发变更通知 |
| AddCategory / AddItem | 组织分类和条目 | 分类名支持运行时修改 |
| CXTPPropertyGridItemBool | 布尔型条目 | 显示为复选框,取值 TRUE/FALSE |
条目值变更时,网格会向父窗口发送属性变更通知消息,具体消息名在头文件里搜一下就有,在消息映射里处理通知比用 Timer 轮询拿值干净得多。参数不熟时最有效的方法是打开 Samples 里的 PropertyGrid 示例工程,把示例网格原样搬过来改条目,比对着文档猜枚举值快得多。
4. 授权、DLL 分发与皮肤文件:交付阶段绕不开的三个坑
把程序从开发机搬到客户机器,才是 Codejock 项目真正开始考验人的地方。下面三个是交付阶段最高频的坑。
4.1 动态链接还是静态链接:先选好再动手
Codejock 两种链接方式都支持,选择直接影响交付物形态:
| 链接方式 | 预处理宏 | 交付物 | 适用场景 |
|---|---|---|---|
| 动态(默认) | 不加 | 主程序 + Redist 里对应 DLL | 多数商业软件,DLL 可单独升级 |
| 静态 | _XTP_STATICLINK | 单 exe,无 XTP 相关 DLL | 绿色小工具、明确规定不带第三方 DLL |
选静态链接时,必须把工程属性里「MFC 使用」同步切到「在静态库中使用 MFC」,两者必须同静态或同动态,混着用会在链接期报一堆LNK2038运行时库冲突。我的习惯是:客户机环境不可控的场景用动态,exe 小、升级灵活;要求单文件交付的用静态,但体积明显变大,Debug 版尤其夸张,发布前记得切 Release。
动态链接还要考虑一层:Codejock 的 DLL 内部引用 MFC 和 VC 运行库,精简版 Windows 上可能缺 vc141 运行库。VS2017 的 Redist 目录里有现成的合并包,或者用 NuGet 的 Microsoft.VC141.CRT 包,安装包方式最省事,别假设客户机器一定有。
4.2 Redist 目录与授权常识:别和 VS2017 产品密匙混为一谈
Codejock 的 DLL 不装进系统目录,而是从安装包Redist拷贝到 exe 同目录。分发时对照 Redist 清单把用到的组件 DLL 全带上,常见的是 ToolkitPro 主 DLL 和皮肤相关 DLL;只拷主 DLL、漏掉皮肤 DLL,本机正常,换台机器启动就闪退。Debug 版 DLL 不要发给客户,一是性能差,二是客户机器基本没有 Debug 运行库。
授权这块和 VS2017 产品密匙是两码事。很多人搜「vs2017产品密匙」时顺手以为 Codejock 也有类似的激活码,其实 Xtreme Toolkit Pro 没有运行时联网激活:正式购买后注册码在邮件里,安装或升级时填入,程序内不会弹评估提示;没填注册码跑的是评估模式,界面会有水印。不要用网上流传的注册机类工具——这个库的授权是随购买记录走的,后续升级、续费、技术咨询全靠它,省这一笔后面全是麻烦。
如果团队计划未来迁到更高版本的 VS,要提前评估对应工具集的版本线支持情况,别在 v15 上死等官方补丁。老库的维护节奏是跟着工具集走的,VS2017 一旦不是主力开发环境,配套组件的更新也会慢下来。
4.3 皮肤文件两种交付方式:外置 xpr 还是编进资源
SkinFramework 的皮肤是.xpr文件,最简单的加载方式是外置路径:
#include "XTPResourceImages.h" // SkinFramework 头文件 // InitInstance 中、主窗口创建之后加载 CXTPSkinManager* pSkin = CXTPSkinManager::GetInstance(); pSkin->SetApplyTheme(TRUE); pSkin->LoadSkin(_T(".\\Skins\\Office2016.xpr"), _T("Office2016"));外置 xpr 有个实际交付问题:用户只拷走 exe、没拷 Skins 目录,程序能启动但界面回退成灰色经典风格,客户会觉得交付的东西缺了一块。常见做法是把 xpr 作为自定义资源编进 exe,运行时用资源句柄加载,交付物只有单个 exe 或一个安装包。资源方式的代码在最后一章给出。SetApplyTheme(TRUE)要在窗口创建之前调用,皮肤才对全局生效;中途换主题,已经创建的窗口需要重建才能刷出新外观。需要做多套皮肤切换时,把皮肤名列表写进注册表或配置文件,启动时读取加载,避免硬编码在代码里。
5. 避坑手册:VS2017 下 Xtreme Toolkit Pro 的 5 个高频翻车现场
5.1 LNK2038 运行时库冲突:Debug 和 Release 的库别混链
现象:链接期刷一屏LNK2038: mismatch detected for 'RuntimeLibrary',指针指向某个 Codejock 库文件。
原因:Libs 目录下 Debug/Release 子目录名很像,手选路径时点错,或者属性表里写了固定 Release 路径,工程切到 Debug 配置时还在链 Release 库。MFC 和 Codejock 的库必须同为 MD 系列或同为 MT 系列,混了就报这个错。
解决:属性表里库目录用$(Configuration)自动切换;同时检查「MFC 使用」和「运行库」两项在 Debug/Release 配置下是否对称。改完把整个解决方案重新编译一遍,不要只增量编译碰运气的部分。
5.2 一启动就断言失败:XTP 全局初始化被漏了
现象:程序启动即弹断言,调用栈停在某个窗口创建或AssertValid的位置,Release 下则直接内存访问违例。
原因:CWinApp 派生类里没有XTPApplicationInitializer成员,或者初始化顺序不对,XTP 内部的核心单例还没就绪就创建窗口。
解决:按 2.3 的骨架,把初始化成员加进 App 类声明,和 theApp 全局对象放同一编译单元。检查时在整个工程里搜XTPApplicationInitializer,只出现一次就对了;出现零次说明漏了,出现多次说明有人重复声明了。
5.3 静态链接后一片 unresolved external:漏了 _XTP_STATICLINK
现象:明明把静态库加进了链接器输入,链接器还是报几百个unresolved external symbol,符号全是 XTP 类的方法。
原因:预处理宏里没加_XTP_STATICLINK,头文件里的导入导出宏按动态库方式展开,链接器按 DLL 导入库去找符号,自然找不到。
解决:预处理定义里补上_XTP_STATICLINK,同时把 MFC 切到静态库模式,然后全量重新编译。这个宏会影响头文件里大量_XTP_EXT_CLASS的展开,只重编改动的文件会留下缓存的目标文件,报错依旧。
5.4 Ribbon 图标全空白:图片资源没进 ImageManager
现象:程序跑起来按钮文字正常,图标位置全是空白,或者显示成小黑块。
原因:Codejock 的图标不是直接绑按钮的。AddButton 传的命令 ID 对应的位图或 PNG,需要先注册到全局 ImageManager;工程里没编入 Codejock 的资源文件,图片就找不到。
解决:把 Codejock 安装包 Samples 里的XTPResource.rc(或类似的资源文件)并入工程一起编译,图标按命令 ID 自动匹配;自定义图标用XTPImageManager()->SetIcon(...)这类接口显式注册。排查方法:资源视图里搜XTPID开头的资源是否存在,不存在就是资源文件没编进来。
5.5 客户机启动闪退:xpr 路径和 DLL 清单双重问题
现象:本机一切正常,装到客户 Windows 7/10 上,双击图标转圈后进程消失。
原因:多数情况是两个问题叠加——皮肤外置路径不存在(见 4.3),且 Redist 里的 DLL 没带全。事件查看器里通常能看到对应 XTP DLL 加载失败的记录。
解决:先把 xpr 编进资源消除路径依赖;再用 Process Monitor 或任意依赖检查工具查缺失的 DLL,对照 Redist 目录逐个核对。经验值是:安装包 Redist 里出现过一次的 DLL,就进分发清单,不要自作聪明删掉自认为没用的。
6. 进阶:把皮肤编进 exe,并用属性表把整套接入沉淀下来
最后一节给两个我每次接手 Codejock 项目都会做的事,都是花小力气省大时间的活。
6.1 用资源句柄加载皮肤,交付物只剩一个文件
最省心的皮肤交付方式是把 xpr 加进资源文件,改一次,以后分发再不会少文件:
// Resource.h 里声明资源 ID;RC 文件中: // IDR_SKIN_OFFICE2016 XPR "res\\Office2016.xpr" HRSRC hRes = FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_SKIN_OFFICE2016), _T("XPR")); HGLOBAL hGlobal = LoadResource(AfxGetResourceHandle(), hRes); CXTPSkinManager* pSkin = CXTPSkinManager::GetInstance(); pSkin->SetApplyTheme(TRUE); pSkin->LoadSkin(hGlobal); // 资源句柄重载,不依赖外置文件加载皮肤的时机仍在主窗口创建之后、显示之前,做在 InitInstance 里最稳。换皮肤时重新走一遍这段,旧皮肤才能被干净替换。
6.2 验证版本与升级决策:两条习惯
拿到手的 v15.3.1 到底是不是匹配 VS2017 的版本,最直接的办法是看 Redist 里主 DLL 的版本信息:右键属性看详细信息,版本号对得上 15.3.1,编译工具链标注也没问题,就能放心用。程序里也可以在启动时打印版本号,防止测试机装了别的小版本。
另一个习惯是把 Codejock 的 props 属性表连同安装包路径、版本号写进工程根目录的 README,提交版本库。以后升级小版本或评估更高版本线,只改 props 里的根路径和库名,重编一次,能过就升,过不了一分钟退回,不用翻文档从头配。
我自己的惯例是:接到这类老库项目,先把最小 Ribbon 工程编译通过,然后立刻把 props 提交进版本库,再开始动业务代码。这个顺序能省掉后面至少三次因为环境不统一引发的扯皮,撑住了版本基线,后面换皮肤、加面板、调主题才敢放手做。希望帮到你。
本文还有配套的精品资源,点击获取