使用 DuiLib 做过一个非常小的项目,当时因为没有研究清楚如何编译为静态库遂使用了 的动态库来做的。最近自己又有使用 DuiLib 的需求,而且希望能编译成静态库使用,所以研究了一下(在群里也有很多朋友帮忙,最终解决了问题)。网上流传的一些注释原有代码一些宏定义并声明一个 UILIB_API
的方法并不正确,因为代码里面有专门针对静态库处理的位置,比如下面的代码:
struct DUI_MSGMAP
{
#ifndef UILIB_STATIC
const DUI_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const DUI_MSGMAP* pBaseMap;
#endif
const DUI_MSGMAP_ENTRY* lpEntries;
};
如果你仅声明了一个 UILIB_API
的宏并不能让 UILIB_STATIC
下面的代码正确的编译进去。所以我们最终还是要使用 UILIB_STATIC
宏来解决问题。
添加官方 DuiLib_Static 项目
在官方的代码中有一个 DuiLib_Static.vcxproj
的工程文件,这就是 DuiLib 的静态库编译的工程。
但是细心的人可能发现了,仅有一个 .vcproj
的项目文件,没有 .filters
的文件目录树描述文件。没关系,我们直接复制一份
文件改名为 Duilib_Static.
即可,这个文件只是描述在 vs 中看到的文件目录结构,静态库和动态库都使用一样的目录树即可。修改完成后是下面这幅图的样子:
我们增加了 Duilib_Static. 的文件,此时我们把这个工程添加到 duilib 的解决方案中。
选择 Duilib_Static 项目然后点击添加。
添加完成后我们就能看到 Duilib_Static 的项目在 DuiLib 的解决方案中了,而且目录树结构完整。
此时我们选择 UnicodeDebug 的版本编译一个静态库来进行一下测试,注意如果你的测试程序是多字节的这里也要编译成多字节的版本,否则链接是会出错。
1>------ 已启动生成: 项目: DuiLib_Static, 配置: UnicodeDebug Win32 ------
1> StdAfx.cpp
1> UIWebBrowser.cpp
1> UITreeView.cpp
1> UIText.cpp
1> UISlider.cpp
1> UIScrollBar.cpp
1> UIRichEdit.cpp
1> UIProgress.cpp
1> UIOption.cpp
1> UIList.cpp
1> UILabel.cpp
1> UIGifAnim.cpp
1> UIEdit.cpp
1> UIDateTime.cpp
1> UICombo.cpp
1> UICheckBox.cpp
1> UIButton.cpp
1> UIActiveX.cpp
1> UIVerticalLayout.cpp
1> UITileLayout.cpp
1> UITabLayout.cpp
1> 正在生成代码...
1> 正在编译...
1> UIHorizontalLayout.cpp
1> UIChildLayout.cpp
1> UIRender.cpp
1> UIMarkup.cpp
1> UIManager.cpp
1> UIDlgBuilder.cpp
1> UIControl.cpp
1> UIContainer.cpp
1> UIBase.cpp
1> Utils.cpp
1> UIDelegate.cpp
1> WinImplBase.cpp
1> UIlib.cpp
1> 正在生成代码...
1> stb_image.c
1> XUnzip.cpp
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppBuild.targets(1361,5): warning MSB8012: TargetPath(d:\Documents\Code\DuiLib\DuiLib\.\Build\Debug_u\DuiLib_Static.lib) does not match the Library's OutputFile property value (d:\Documents\Code\DuiLib\DuiLib\Build\Debug_u\DuiLib_Static_ud.lib). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppBuild.targets(1363,5): warning MSB8012: TargetName(DuiLib_Static) does not match the Library's OutputFile property value (DuiLib_Static_ud). This may cause your project to build incorrectly. To correct this, please make sure that $(OutDir), $(TargetName) and $(TargetExt) property values match the value specified in
1> DuiLib_Static.vcxproj -> d:\Documents\Code\DuiLib\DuiLib\.\Build\Debug_u\DuiLib_Static.lib
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
项目成功生成了(以上操作我已经提交 PR 给 duilib 官方,是否合并要看他们了),生成的静态库在 DuiLib\Build\Debug_u 目录下,接下来我们新建一个项目来引入 DuiLib 静态库。
新建测试项目
在你新建项目的时候,也有一些注意点,我们一步一步介绍,然后重要的关键点跟大家强调一下就可以了。
首先我们新建一个 Win32 项目取名为 DuilibTest,要注意的是,Win32 项目要选择引用 ATL 库
否则编译的时候会提示如下错误:
error C2504: “VARIANT”: 未定义基类
error C2061: 语法错误: 标识符“LPOLESTR”
error C2535: “DuiLib::CVariant::CVariant(void)”: 已经定义或声明成员函数
...
如果你的工程已经建立好了,想引入 ATL,那么直接在 中添加下面三行代码就可以了,但记得要在引入 DuiLib 头文件之前。
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
#include <>
#include <>
新建好项目以后我们来清理一下无用代码,打开 文件,删除一些无用的代码,最终清理后的代码如下:
#include ""
#include ""
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
简单几行实现了一个 WinMain 函数,然后我们来引入 Duilib 的头文件,首先右键 DuilibTest 项目属性。首先选择 VC++目录->包含目录
,里面添加上 duilib 的头文件路径。如下图所示:
然后打开 常规->平台工具集,官方目前 github 上的版本是 VS2013 支持 XP,我们的测试项目也要与编译静态库时选择的这个平台工具集一样。
确定后打开 连接器->常规->附加库目录
,将 DuiLib 生成的静态库目录添加进去(这里只为做演示,真正项目自己规划这个目录的结构)
确定后再打开 链接器->输入->附加依赖库
,将 DuiLib_Static_ud.lib 添加到附加依赖库列表中。
最后再打开 C/C++->预处理器->预处理器定义,将 UILIB_STATIC
的宏添加进去,这相当于我们在代码中 #define UILIB_STATIC
至此差不多了,我们添加一些测试代码(代码来自 Alberl),如下:
#include ""
#include ""
#include ""
using namespace DuiLib;
class CDuiFrameWnd : public CWindowWnd, public INotifyUI
{
public:
virtual LPCTSTR GetWindowClassName() const { return _T("DUIMainFrame"); }
virtual void Notify(TNotifyUI& msg) {}
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
if (uMsg == WM_CREATE)
{
CControlUI *pWnd = new CButtonUI;
pWnd->SetText(_T("Hello World"));
pWnd->SetBkColor(0xFF00FF00);
m_PaintManager.Init(m_hWnd);
m_PaintManager.AttachDialog(pWnd);
return lRes;
}
if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
{
return lRes;
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
protected:
CPaintManagerUI m_PaintManager;
};
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
CPaintManagerUI::SetInstance(hInstance);
CDuiFrameWnd duiFrame;
(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
代码添加完成后我们编译一下程序,你可能会收到如下错误:
1>------ 已启动生成: 项目: DuiLibTest, 配置: Debug Win32 ------
1> stdafx.cpp
1> DuiLibTest.cpp
1>DuiLib_Static_ud.lib(UIManager.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIBase.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIControl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIButton.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(StdAfx.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(Utils.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIDelegate.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIRender.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIRichEdit.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UILabel.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIContainer.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>DuiLib_Static_ud.lib(UIScrollBar.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(DuiLibTest.obj 中)
1>LINK : warning LNK4098: 默认库“LIBCMTD”与其他库的使用冲突;请使用 /NODEFAULTLIB:library
1>d:\Documents\Visual Studio 2013\Projects\DuilibTest\Debug\DuiLibTest.exe : fatal error LNK1319: 检测到 12 个不匹配项
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这是因为我们静态库编译时选择的代码生成时运行库不一样,依次打开 C/C++->代码生成->运行库
,在 DuiLib_Static 的项目中,Unicode_Debug 版本这里的运行库选择的是 多线程调试 (/MTd)
,所以我们测试程序也要选择成一样的 多线程调试 (/MTd)
。
顺利的话,你再次编译一下程序,就可以正常编译通过了。
1>------ 已启动生成: 项目: DuiLibTest, 配置: Debug Win32 ------
1> stdafx.cpp
1> DuiLibTest.cpp
1> DuiLibTest.vcxproj -> d:\Documents\Visual Studio 2013\Projects\DuilibTest\Debug\DuiLibTest.exe
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
如果你遇到了其他错误,请一定要按上面的几个步骤仔细核对。
总结
最后我再罗列一下需要注意的点。
- 添加静态库工程的时候记得自己拷贝一份
DuiLib_Static.
。
- 新建的工程编码方式一定要与 DuiLib 的编码一致,DuiLib 中名字为
Unicode_Debug
的配置才是支持 Unicode 的版本,别搞错了。
- 新建的工程要选择 ATL 支持。
- 新建的工程平台工具集要与 DuiLib 编译的静态库保持一致。
- 新建的工程添加
链接器->输入->附加依赖项
时也要对准是不是对应的多字节/ Unicode 或 Debug/Release 版本,如果没有对准,则会提示无法链接到 DuiLib 里面的一些函数。这个一定要注意。
- 新建的工程一定要在
预处理器->预处理器定义
中增加 UILIB_STATIC
。
- 新建的工程
代码生成->运行库
也一定要与 DuiLib 保持一致。