【问题标题】:How to shrink the size of a WTL application?如何缩小 WTL 应用程序的大小?
【发布时间】:2016-07-09 18:26:12
【问题描述】:

WTL 应用程序已经很小了。但是,使用 VS 2005 时,静态链接的应用程序与 WTL 9.10 在 Win32 配置下的重量为 136 kB(139,264 字节)。

查看可执行文件时,我注意到oleaut32.dll 的静态导入。粗略地看一下dumpbin 显示了一个通过序数导入。

OLEAUT32.dll
            4181C0 Import Address Table
            41C9B8 Import Name Table
                 0 time date stamp
                 0 Index of first forwarder reference

                  Ordinal   277

检查oleaut32.dll 发现导出名为VarUI4FromStr

用 IDA 挖掘了一下,我发现 VarUI4FromStrATL::CRegParser::AddValue 使用。从那里跟随家属,在ATL::CRegParser::RegisterSubkeys 显示两个电话。

交叉引用我的 Visual Studio 安装的 ATL 代码和结果,我发现罪魁祸首是 ATL::CAtlComModule。它做了很多我的用例根本不需要的 TypeLib 注册工作。

但是,链接器似乎将所有这些都保留了下来,因为它无法合理地决定将其丢弃。

如何摆脱这种看似多余的导入?

【问题讨论】:

    标签: c++ windows visual-c++ atl wtl


    【解决方案1】:

    唉,因为WTL::CAppModule 直接派生自ATL::CComModule,包括atlbase.h 标头而定义_ATL_NO_COMMODULE 会导致错误:

    Error   1   fatal error C1189: #error :  WTL requires that _ATL_NO_COMMODULE is not defined $(ProjectDir)\wtl\Include\atlapp.h  33  
    

    然而,最终引起ATL::CComModule 的真正罪魁祸首是ATL::CAtlComModule。所以我们的目标是摆脱它们。

    我们将尝试通过定义_ATL_NO_COMMODULE 来欺骗atlbase.h 排除所有TypeLib 注册代码,但在我们完成包含它之后立即取消定义它。这样atlapp.h 和其他 WTL 标头就不会“注意到”。

    #define _ATL_NO_COMMODULE
    #include <atlbase.h>
    #undef _ATL_NO_COMMODULE
    #include <atlapp.h>
    

    显然这会给我们带来一些麻烦:

    1>atlapp.h(1515) : error C2039: 'CComModule' : is not a member of 'ATL'
    1>atlapp.h(1515) : error C2504: 'CComModule' : base class undefined
    1>atlapp.h(1524) : error C2653: 'CComModule' : is not a class or namespace name
    1>atlapp.h(1543) : error C2653: 'CComModule' : is not a class or namespace name
    1>atlapp.h(1625) : error C3861: 'GetModuleInstance': identifier not found
    1>atlapp.h(1784) : error C2653: 'CComModule' : is not a class or namespace name
    1>atlapp.h(1806) : error C2065: 'm_nLockCnt' : undeclared identifier
    

    不过,看起来我们应该能够快速修复这几个错误。

    首先让我们检查ATL::CComModule 的来源:它在atlbase.h 中声明。我们当然可以声明我们自己的类并将其挤在 #include &lt;atlbase.h&gt;#include &lt;atlapp.h&gt; 之间(见上文)。

    让我们从基础开始,基于 atlbase.h 中可以找到的内容:

    namespace ATL
    {
        class CComModule : public CAtlModuleT<CComModule>
        {
        public:
            CComModule() {}
        };
    };
    

    现在我们得到一组不同的错误:

    1>atlapp.h(1524) : error C2039: 'Init' : is not a member of 'ATL::CComModule'
    1>        stdafx.h(31) : see declaration of 'ATL::CComModule'
    1>atlapp.h(1625) : error C3861: 'GetModuleInstance': identifier not found
    

    非常好,所以我们实际上只缺少两个类函数:GetModuleInstanceInit。让我们也把它们拉进来。为了更好地衡量,我们还将添加GetResourceInstance

    namespace ATL
    {
        class CComModule : public CAtlModuleT<CComModule>
        {
        public:
            CComModule() {}
            HINSTANCE GetModuleInstance() throw() { return _AtlBaseModule.m_hInst; }
            HINSTANCE GetResourceInstance() throw() { return _AtlBaseModule.m_hInstResource; }
            HRESULT Init(_ATL_OBJMAP_ENTRY*, HINSTANCE, const GUID*) throw() { return S_OK; }
        };
    };
    

    一个简短的测试证明这工作正常。 oleaut32.dll 导入与来自ole32.dll 的另外三个导入一起消失了。大获成功!

    因此,我们在可执行文件大小上节省了 12 kB

    你知道其他缩小 WTL 应用程序大小的绝妙技巧吗?

    重要

    请注意,根据您的代码使用的ATL::CComModule 的功能,您可能不得不回退到使用原始代码。唯一的另一种选择是扩展上述模拟版本的 ATL::CComModule 以修复您可能遇到的任何编译错误。

    注意事项

    我尚未对此进行广泛测试,如果遇到任何问题,我会报告(通过编辑此答案)。但是,从 ATL 和 WTL 代码以及在我更改之前的 IDA 数据库中查看它,我认为这是一个安全的更改。

    奖励技巧

    您还可以使用 6001.18002 独立 WDK(适用于 Vista SP1),它支持面向 Windows 2000 (SP4) 和更高版本。它包含 ATL 7.0,因此适合构建 ATL+WTL 应用程序。

    为此,您只需将以下两行添加到您的 sources 文件中:

    USE_STATIC_ATL=1
    ATL_VER=70
    

    感谢 WDK,它使用系统 DLL msvcrt.dll 作为其默认(多线程动态链接)C 运行时,然后您可以通过将动态 链接到 C 来进一步缩小可执行文件大小运行。在这种情况下,由于特定的 C 运行时是系统 DLL(从 Windows 2000 开始),因此您可以放心,它会工作。

    考虑到所有这些因素,您可以构建非常小的标准 Windows 应用程序(GUI 或控制台)和 DLL。

    这是一个示例 sources 文件,您可以将其用作模板:

    # Name of the program
    TARGETNAME=progname
    # Prefix used for the intermediate/output paths (e.g. objfre_w2k_x86)
    TARGETPATH=obj
    # A program, not a driver or DLL or static lib
    TARGETTYPE=PROGRAM
    # windows == GUI, console == Console, native == no subsystem ...
    UMTYPE=windows
    # Use Unicode ("wide char") instead of ANSI
    UNICODE=1
    # By default the WDK build treats warnings as errors - this turns it off
    BUILD_ALLOW_ALL_WARNINGS=1
    # Link dynamically to msvcrt.dll
    USE_MSVCRT=1
    # Don't link against any of the ATL DLLs
    USE_STATIC_ATL=1
    # In the 7600.16385.1 WDK you can use 70 as well, which translates to 71
    ATL_VER=70
    USE_NATIVE_EH=1
     # RTTI is required for some ATL/WTL features
    USE_RTTI=1
    
    # GUI programs require these entry points
    !IF defined(UNICODE) && $(UNICODE)
    UMENTRY=wwinmain
    C_DEFINES=$(C_DEFINES) /DUNICODE /D_UNICODE
    !ELSE
    UMENTRY=winmain
    C_DEFINES=$(C_DEFINES) /DMBCS /D_MBCS
    !ENDIF
    
    # Include folders
    INCLUDES=$(DDK_INC_PATH);$(CRT_INC_PATH);$(SDK_INC_PATH);wtl\Include
    
    # Libraries to link to, not just import libraries
    TARGETLIBS=\
                $(SDK_LIB_PATH)\kernel32.lib \
                $(SDK_LIB_PATH)\user32.lib \
                $(SDK_LIB_PATH)\Comctl32.lib \
    
    # Give source files (also resources and .mc) in the current or parent directory
    SOURCES=...
    

    7600.16385.1 独立 WDK 也适用于此。从 Windows 8 WDK 开始的 WDK 现在需要可以集成到其中的 Visual C++。这也导致二进制文件需要相应的 C 运行时版本,而不是以前 WDK 版本中的 msvcrt.dll

    【讨论】:

      猜你喜欢
      • 2020-01-28
      • 1970-01-01
      • 2023-04-04
      • 2021-01-23
      • 2020-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多