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

时间:2010-11-04 15:10:18

标签: visual-studio-2008 winapi visual-c++ mfc

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

我在Visual Studio 2008中创建了一个MFC应用程序,它可以生成一个托盘图标和一些通知。我已经阅读过,通过设置结构的NOTIFYICONDATA属性来初始化它,我可以为Windows Vista使用不同的cbSize结构而不是Windows XP。我还读过,我可以在Windows Vista中使用LoadIconMetric加载我的通知图标,而在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

我正在Visual Studio 2008中的Windows 7 x64计算机上编译和链接Windows 7 SDK。我对Windows Vista或更高版本的测试看起来像这样(直接来自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);
}

现在这是有趣的部分。我使用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
}

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

3 个答案:

答案 0 :(得分:10)

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

Common Control Versions

我通过在包含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='*'\"")

它源自Microsoft示例项目:

Notification Icon Sample - NotificationIcon.zip

答案 1 :(得分:7)

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

解决您的问题:

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

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

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

答案 2 :(得分:3)

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