【问题标题】:Does windows preload ComCtrl32.dll in an app?Windows 是否在应用程序中预加载 ComCtrl32.dll?
【发布时间】:2010-11-04 15:10:17
【问题描述】:

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

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

现在是有趣的部分。我使用 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 中找不到序数 380”而崩溃。如果我注释掉对LoadIconMetric 的调用,一切都会很愉快(即使所有这些目标版本都设置为Vista)。 Windows 是否会尝试导入所有函数调用而不考虑运行时代码路径?

【问题讨论】:

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


    【解决方案1】:

    只是为了帮助可能遇到非常类似问题的其他人。即使在 Windows 7 下使用 Visual Studio C++ 2010 Express WinApi 模板应用程序,我也无法让 LoadIconMetric 工作。将 Comctl32.lib 添加到依赖项并包括 CommCtrl.h 后,它一直说“在 ComCtrl32.dll 中找不到序数 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='*'\"")
    

    源自微软示例项目:

    Notification Icon Sample - NotificationIcon.zip

    【讨论】:

    • 你为我节省了一些时间;)
    【解决方案2】:

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

    解决您的问题:

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 这正是我所做的。效果很好。
      猜你喜欢
      • 2016-02-17
      • 1970-01-01
      • 2018-09-06
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-06
      相关资源
      最近更新 更多