唉,因为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 <atlbase.h> 和 #include <atlapp.h> 之间(见上文)。
让我们从基础开始,基于 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
非常好,所以我们实际上只缺少两个类函数:GetModuleInstance 和 Init。让我们也把它们拉进来。为了更好地衡量,我们还将添加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。