Windows是否在应用程序中预加载ComCtrl32.dll?

Mik*_*ron 4 winapi mfc visual-studio-2008 visual-c++

我遇到过一种我知道代码的情况

我在Visual Studio 2008中创建了一个MFC应用程序,它生成一个托盘图标和一些通知.我已经阅读过,我可以使用与NOTIFYICONDATAWindows XP 不同的结构,通过设置cbSize结构的属性来初始化它.我还读过,我可以LoadIconMetric在Windows Vista中使用加载我的通知图标,而在Windows XP中,我无法使用该功能,我必须使用LoadIcon.

在我的应用程序中,我设置了以下内容:

#ifndef WINVER
#define WINVER 0x0600 // Vista
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 // Vista
#end#if

#ifndef _WIN32_WINDOWS
#define _WIN32_WINDOWS 0x0600 // Vista
#endif

#ifndef _WIN32_IE
#define _WIN32_IE 0x0700
#endif
Run Code Online (Sandbox Code Playgroud)

我正在Visual Studio 2008中的Windows 7 x64计算机上编译和链接Windows 7 SDK.我对WindowsVista或更高版本的测试看起来像这样(直接来自MSDN):

static BOOL IsWinVistaOrLater()
{
    // Initialize the OSVERSIONINFOEX structure.
    OSVERSIONINFOEX osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    osvi.dwMajorVersion = 6;
    osvi.dwMinorVersion = 1;

    // Initialize the condition mask.
    DWORDLONG dwlConditionMask = 0;
    VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
    VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);

    // Perform the test.
    return VerifyVersionInfo(&osvi, 
            VER_MAJORVERSION | VER_MINORVERSION,
            dwlConditionMask);
}
Run Code Online (Sandbox Code Playgroud)

现在这是有趣的部分.我使用IsWinVistaOrLater来确定是否应该使用LoadIconMetric或LoadIcon:

if (IsWinVistaOrLater())
{
    tnd_Happy.dwInfoFlags = NIIF_LARGE_ICON | tnd_Happy.dwInfoFlags;
    LoadIconMetric(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDI_ICON_HAPPY), LIM_SMALL, &(tnd_Happy.hIcon));
} else {
    tnd_Happy.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE (IDI_ICON_HAPPY)); // ICON RESOURCE ID
}
Run Code Online (Sandbox Code Playgroud)

在XP上崩溃与"在ComCtrl32.dll中找不到Ordinal 380".如果我对这个电话进行评论,那么LoadIconMetric事情就会很开心(即使将所有目标版本设置为Vista).无论运行时代码路径如何,Windows都会尝试导入所有函数调用吗?

小智 10

只是为了帮助那些可能遇到非常类似问题的人.即使在使用Visual Studio C++ 2010 Express WinApi模板应用程序的Windows 7下,我也无法使用LoadIconMetric.将Comctl32.lib添加到dependecies并包括CommCtrl.h之后,它继续说"在ComCtrl32.dll中找不到Ordinal 380".我不太清楚我错过了什么,因为我完全不熟悉这些组件的切换版本.经过相当长的一段时间后,我发现了这一点,这里是这种情况的宝贵来源:

通用控件版本

我通过在包含CommCtrl.h之前添加此行来解决了这个问题(请注意,我的应用程序仅针对Windows Vista及更高版本):

// Need CommCtrl v6 for LoadIconMetric()
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
Run Code Online (Sandbox Code Playgroud)

它源自Microsoft示例项目:

通知图标示例 - NotificationIcon.zip


Chr*_*cke 7

如果您为Vista设置了头文件,那么它将允许您构建无法在早期版本的Windows上加载的应用程序.这就是为什么正确设置该文件非常重要的原因.

要解决您的问题:

  • 应该可以在comctl32.dll上使用/ DELAYLOAD开关.没有/ DELAYLOAD Windows将尝试预先导入所有功能(无论代码路径如何)

  • 将目标版本头文件重置为0x0501(XP).如果您尝试使用任何高级功能,Windows现在会自动发出警告.使用LoadLibrary("comctl32.dll")&& GetProcAddress("LoadIconMetric")访问新功能.这种方法的缺点是你可以通过GetProcAddress进行新的调用,但结构(如果它们已经改变)将是旧的结构.

最后,很多comctl32.dll功能都是一个技巧:除非您具有特定的启用视觉样式,否则您的comctl32.dll版本应该始终是版本5版本.(如果在VS2005或VS2008中创建了MFC项目,则应该已自动完成).这意味着可以将WINVER设置为Vista,但随后ComCtl32调用开始失败,因为版本5 ComCtl32无法理解新的更大的结构.


She*_* 蒋晟 3

您可以编写一个仅适用于Vista 的延迟加载链接 DLL,其中包含所有仅适用于 Vista 的代码,而您的主项目仍以 XP 为目标,而不是 Chris 所建议的LoadLibraryGetProcAddress然后,您不需要加载仅 Vista 的代码,除非执行通过了 Vista 版本检查并调用从 Vista 代码 DLL 导出的函数。