正確編譯 DuiLib 靜態庫的方法

  使用 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 的靜態庫編譯的工程。
  
duilib_static工程文件

  但是細心的人可能發現了,僅有一個 .vcproj 的項目文件,沒有 .filters 的文件目錄樹描述文件。沒關係,我們直接複製一份 Duilib.vcxproj.filters 文件改名爲 Duilib_Static.vcxproj.filters 即可,這個文件只是描述在 vs 中看到的文件目錄結構,靜態庫和動態庫都使用一樣的目錄樹即可。修改完成後是下面這幅圖的樣子:
  
增加了 duilib_static 工程

  我們增加了 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 庫
  
引入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靜態庫路徑

  確定後再打開 鏈接器->輸入->附加依賴庫,將 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 保持一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章