正确编译 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 保持一致。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章