使用 DuiLib 做過一個非常小的項目,當時因爲沒有研究清楚如何編譯爲靜態庫遂使用了 DuiLib.dll 的動態庫來做的。最近自己又有使用 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;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
如果你僅聲明瞭一個 UILIB_API
的宏並不能讓 UILIB_STATIC
下面的代碼正確的編譯進去。所以我們最終還是要使用
UILIB_STATIC
宏來解決問題。
添加官方 DuiLib_Static 項目
在官方的代碼中有一個 DuiLib_Static.vcxproj
的工程文件,這就是 DuiLib 的靜態庫編譯的工程。
但是細心的人可能發現了,僅有一個 .vcproj
的項目文件,沒有 .filters
的文件目錄樹描述文件。沒關係,我們直接複製一份
Duilib.vcxproj.filters
文件改名爲 Duilib_Static.vcxproj.filters
即可,這個文件只是描述在 vs 中看到的文件目錄結構,靜態庫和動態庫都使用一樣的目錄樹即可。修改完成後是下面這幅圖的樣子:
我們增加了 Duilib_Static.vcxproj.filters 的文件,此時我們把這個工程添加到 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 %(Lib.OutputFile).
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 %(Lib.OutputFile).
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
項目成功生成了(以上操作我已經提交 PR 給 duilib 官方,是否合併要看他們了),生成的靜態庫在 DuiLib\Build\Debug_u 目錄下,接下來我們新建一個項目來引入 DuiLib 靜態庫。
新建測試項目
在你新建項目的時候,也有一些注意點,我們一步一步介紹,然後重要的關鍵點跟大家強調一下就可以了。
首先我們新建一個 Win32 項目取名爲 DuilibTest,要注意的是,Win32 項目要選擇引用 ATL 庫
否則編譯的時候會提示如下錯誤:
error C2504: “VARIANT”: 未定義基類
error C2061: 語法錯誤: 標識符“LPOLESTR”
error C2535: “DuiLib::CVariant::CVariant(void)”: 已經定義或聲明成員函數
...
- 1
- 2
- 3
- 4
如果你的工程已經建立好了,想引入 ATL,那麼直接在 stdafx.h 中添加下面三行代碼就可以了,但記得要在引入 DuiLib 頭文件之前。
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
#include <atlbase.h>
#include <atlstr.h>
- 1
- 2
- 3
新建好項目以後我們來清理一下無用代碼,打開 DuilibTest.cpp 文件,刪除一些無用的代碼,最終清理後的代碼如下:
// DuilibTest.cpp : 定義應用程序的入口點。
//
#include "stdafx.h"
#include "DuilibTest.h"
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
簡單幾行實現了一個 WinMain 函數,然後我們來引入 Duilib 的頭文件,首先右鍵 DuilibTest 項目屬性。首先選擇 VC++目錄->包含目錄
,裏面添加上 duilib 的頭文件路徑。如下圖所示:
然後打開 常規->平臺工具集,官方目前 github 上的版本是 VS2013 支持 XP,我們的測試項目也要與編譯靜態庫時選擇的這個平臺工具集一樣。
確定後打開 連接器->常規->附加庫目錄
,將 DuiLib 生成的靜態庫目錄添加進去(這裏只爲做演示,真正項目自己規劃這個目錄的結構)
確定後再打開 鏈接器->輸入->附加依賴庫
,將 DuiLib_Static_ud.lib 添加到附加依賴庫列表中。
最後再打開 C/C++->預處理器->預處理器定義,將 UILIB_STATIC
的宏添加進去,這相當於我們在代碼中
#define UILIB_STATIC
至此差不多了,我們添加一些測試代碼(代碼來自 Alberl),如下:
// DuilibTest.cpp : 定義應用程序的入口點。
//
#include "stdafx.h"
#include "DuilibTest.h"
#include "UIlib.h"
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;
duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
duiFrame.ShowModal();
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>------ 已啓動生成: 項目: 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
這是因爲我們靜態庫編譯時選擇的代碼生成時運行庫不一樣,依次打開 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 個 ==========
- 1
- 2
- 3
- 4
- 5
如果你遇到了其他錯誤,請一定要按上面的幾個步驟仔細覈對。
總結
最後我再羅列一下需要注意的點。
- 添加靜態庫工程的時候記得自己拷貝一份
DuiLib_Static.vcxproj.filters
。 - 新建的工程編碼方式一定要與 DuiLib 的編碼一致,DuiLib 中名字爲
Unicode_Debug
的配置纔是支持 Unicode 的版本,別搞錯了。 - 新建的工程要選擇 ATL 支持。
- 新建的工程平臺工具集要與 DuiLib 編譯的靜態庫保持一致。
- 新建的工程添加
鏈接器->輸入->附加依賴項
時也要對準是不是對應的多字節/ Unicode 或 Debug/Release 版本,如果沒有對準,則會提示無法鏈接到 DuiLib 裏面的一些函數。這個一定要注意。 - 新建的工程一定要在
預處理器->預處理器定義
中增加UILIB_STATIC
。 - 新建的工程
代碼生成->運行庫
也一定要與 DuiLib 保持一致。