【问题标题】:Windows Volume Mixer icon size is too largeWindows 音量混合器图标太大
【发布时间】:2013-07-30 18:10:42
【问题描述】:

在 Windows 音量混合器中,当您的应用程序播放声音时,它会添加您的应用程序图标和一个自定义音量滑块来调整特定于该应用程序的音量...太好了!但是,当您为应用程序使用大尺寸图标时(在高 DPI 中尤其重要,当 Windows 缩放任务栏图标等时),音量混合器中的图标无法正确缩放。具体来说,以下代码是我用来设置应用程序图标的:

// set icons the normal way
cWnd.SetIcon( theApp.LoadIcon( res_id ), FALSE );
cWnd.SetIcon( theApp.LoadIcon( res_id ), TRUE );

// set hi-res if available
OSVERSIONINFO osv;
osv.dwOSVersionInfoSize = sizeof( osv );
if ( GetVersionEx( &osv ) ) {
    // if we're Vista or more recent, use hi-def icons
    if ( osv.dwMajorVersion >= 6 ) {
        HICON hIcon = (HICON)::LoadImage( theApp.m_hInstance, MAKEINTRESOURCE( res_id ), IMAGE_ICON, 256, 256, LR_SHARED );
        if ( hIcon ) {
            cWnd.SetIcon( hIcon, TRUE );
        }
    }
}

罪魁祸首是“高分辨率(如果有)”部分。如果我包括在内,任务栏图标看起来很棒,但音量混合器没有缩放并且看起来很糟糕。如果我排除它,任务栏图标看起来很糟糕(可怕的缩放),但音量混合器至少是正确的大小:

有没有人找到一个解决方案,让这两个图标看起来都很好?

编辑:在我的图标文件中,我有以下分辨率:256x256、48x48、32x32、24x24 和 16x16,均为 32 位。 256x256 的一个是 PNG 压缩的,其他的是原始的。所有尺寸在文件中的分辨率下看起来都很棒(我试图将 ICO 放在此处或 imgur 中,但显然两者都不允许使用图标)。此外,我尝试过添加一些 8 位图像,但这似乎并没有改变。

编辑:我使用GetDeviceCaps( hdc, LOGPIXELSX )(和Y)来确定桌面缩放比例。通常桌面缩放为 100%,我得到正常的 96 结果。但我越来越多地看到计算机默认为 125%。这可以通过右键单击桌面、个性化、其他来更改:显示...那里有一个滑块(需要注销/登录才能更改)。

编辑:我还想指出,托盘图标在高 DPI 模式下(即使用Shell_NotifyIcon 时)会遇到类似的缩放问题。但是,在这种情况下,我可以使用GetDeviceCaps( hdc, LOGPIXELSX ) 来确定 Windows 想要什么。如果我有尺寸,请直接提供,否则提供 256x256 的尺寸,Windows 确实正确缩放它.

编辑:悲伤随之而来。此问题可能是 Windows 问题。在为演示目的捕获图像时,我注意到音量混合器图标本身看起来很差。为了比较:

最终编辑:如下所述,该问题的解决方法是缩放图标。因此,最终的工作代码是从Comctl32.dll(未显示)加载指向LoadIconWithScaleDown 函数的指针,并在可用时使用它,或者退回到“常规/旧”方式:

HICON hIcon = 0;
if ( FAILED( comctl32Loader.LoadIconWithScaleDown( theApp.m_hInstance, MAKEINTRESOURCE( res_id ), GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), &hIcon ) ) ) {
    hIcon = theApp.LoadIcon( res_id );
}
cWnd.SetIcon( hIcon, FALSE );
if ( FAILED( comctl32Loader.LoadIconWithScaleDown( theApp.m_hInstance, MAKEINTRESOURCE( res_id ), GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), &hIcon ) ) ) {
    hIcon = theApp.LoadIcon( res_id );
}
cWnd.SetIcon( hIcon, TRUE );

【问题讨论】:

    标签: windows winapi mfc icons


    【解决方案1】:

    非常感谢你们。我让它在我们的 Wx 应用程序中工作,如果有人想要一些预烘焙的代码放入他们的 Wx 应用程序,下面是我的:

    #ifdef __WXMSW__
    #include <Windows.h>
    #include <CommCtrl.h>
    #include <wx/msw/private.h>
    typedef int (WINAPI *func_LoadIconWithScaleDown)(HINSTANCE, LPCWSTR, int, int, HICON*);
    #endif
    
    void MainFrame::BindAppIcon() {
    #ifdef __WXMSW__
        wxDynamicLibrary comctl32("comctl32", wxDL_DEFAULT | wxDL_QUIET);
        func_LoadIconWithScaleDown load_icon_scaled = reinterpret_cast<func_LoadIconWithScaleDown>(comctl32.GetSymbol("LoadIconWithScaleDown"));
        int icon_set_count = 0;
    
        HICON hIconLg;
        if (load_icon_scaled && SUCCEEDED(load_icon_scaled(wxGetInstance(), _T("AAAAA_MAINICON"), ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), &hIconLg))) {
            ::SendMessage(GetHandle(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIconLg));
            ++icon_set_count;
        }
        HICON hIconSm;
        if (load_icon_scaled && SUCCEEDED(load_icon_scaled(wxGetInstance(), _T("AAAAA_MAINICON"), ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), &hIconSm))) {
            ::SendMessage(GetHandle(), WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIconSm));
            ++icon_set_count;
        }
    
        if (icon_set_count == 2) return;
        // otherwise fall back to Wx method of setting icon
    #endif
        wxIcon icon = wxXmlResource::Get()->LoadIcon(wxT("MainIcon"));
    
        if (!icon.IsOk()) {
            wxLogInfo(_("Main icon not found"));
            icon = wxICON(wxvbam);
        }
    
        SetIcon(icon);
    }
    

    您的 app.rc 中的第一行将是这样的:

    AAAAA_MAINICON ICON "icons/app.ico"
    

    【讨论】:

      【解决方案2】:

      我在 C#/WPF 程序中遇到过类似问题。

      在我的情况下,问题似乎是由 ico 文件中缺少大小引起的。我的应用程序 ico 文件的最小尺寸是 64x64。一旦我在该文件中添加了较小的尺寸,问题就解决了。

      【讨论】:

        【解决方案3】:

        是的,当我使用与问题中所示相同的代码时,我可以重现您描述的问题。正如我们已经确定的那样,该问题仅在高 DPI 设置下才会出现。在 100% 缩放 (~96 dpi) 时,即使在 Windows Vista 及更高版本上,您也只需为窗口使用“大”版本的图标(SM_CXICONSM_CYICON;通常为 32x32 像素),而不是 256x256像素版。这就是与 Windows 捆绑在一起的应用程序所做的事情,包括您正在测试的 Volume Mixer 小程序。

        当您使用高 DPI 设置时会出现问题,这会使“大”尺寸增大:

        ╔════════════╦═════════════════╦═════════════════╗
        ║    DPI     ║ Large Icon Size ║ Small Icon Size ║
        ║            ║   (SM_C?ICON)   ║  (SM_C?SMICON)  ║
        ╠════════════╬═════════════════╬═════════════════╣
        ║  96 (100%) ║     32x32       ║     16x16       ║
        ║ 120 (125%) ║     40x40       ║     20x20       ║
        ║ 144 (150%) ║     48x48       ║     24x24       ║
        ║ 192 (200%) ║     64x64       ║     32x32       ║
        ╚════════════╩═════════════════╩═════════════════╝
        

        加载 256x256 像素图标时,无论 DPI 如何,一切正常,因为 Windows 会自动将其缩小到所需大小。与尝试放大 32x32 像素图标相比,这会生成质量更好的图标(没有锯齿和其他瑕疵)。所以你的猜测是正确的,问题确实与缩放有关。

        我将假设您在使用 256x256 像素图标时在 Volume Mixer 小程序中看到的内容是一个错误 - 它应该将该大图标缩小到它预期的大小,这是一个“大" 图标 (SM_C?ICON)。据推测,它正在调用DrawIconEx 函数,其中cxWidthcxHeight 参数都设置为0 并且not 传递DI_DEFAULTSIZE 标志。这导致图标使用其实际大小进行绘制 - 巨大。

        您必须通过自己缩放图标来手动解决该问题。幸运的是,Windows Vista 引入了许多专门为此目的而设计的功能。在这种情况下最容易使用的是LoadIconWithScaleDown。顾名思义,它的工作原理类似于旧的LoadIcon/LoadImage 函数,但它不是放大一个太小的图标,而是缩小一个更大的图标——非常适合当你拥有一个巨大的、高质量的图标时ICO 文件中的 256x256 像素图标。

        很遗憾,这些功能在旧版本的 Windows 上不可用,在更高 DPI 设置下使用时可能会出现同样的问题。您需要在那里找到替代方案,或者只是满足于这些旧操作系统上的锯齿状、缩放图标。

        示例代码:

        #include <CommCtrl.h>                  // include Common Controls header
        #pragma comment(lib, "comctl32.lib")   // link to Common Controls library
        
        // Embed a standard manifest to use Common Controls v6
        #pragma comment(linker, "/manifestdependency:\"type='win32' "                         \
                                "name='Microsoft.Windows.Common-Controls' version='6.0.0.0' " \
                                "processorArchitecture='*' "                                  \
                                "publicKeyToken='6595b64144ccf1df' "                          \
                                "language='*'\"")
        
        // Load and set "large" icon (typically 32x32 pixels, but not necessarily)
        HICON hIconLg;
        if (SUCCEEDED(LoadIconWithScaleDown(g_hInstance,
                                            MAKEINTRESOURCE(IDI_ICON),
                                            GetSystemMetrics(SM_CXICON),
                                            GetSystemMetrics(SM_CYICON),
                                            &hIconLg)))
        {
           SendMessage(hWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIconLg));
        }
        
        // Load and set "small" icon (typically 16x16 pixels, but not necessarily)
        HICON hIconSm;
        if (SUCCEEDED(LoadIconWithScaleDown(g_hInstance,
                                            MAKEINTRESOURCE(IDI_ICON),
                                            GetSystemMetrics(SM_CXSMICON),
                                            GetSystemMetrics(SM_CYSMICON),
                                            &hIconSm)))
        {
           SendMessage(hWnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIconSm));
        }
        

        请注意,要使用这些新功能,您需要链接到通用控件库的版本 6。这要求您指示编译器链接 comctl32.lib 并将清单嵌入到您的应用程序中。可以使用上面示例代码中显示的特定于 MSVC 的 #pragmas 来完成,也可以在项目的属性中进行配置。如果您未能执行上述任何一项操作,您将在首次启动应用程序时收到链接时错误或“未找到序数”错误。

        【讨论】:

        • 哇,感谢您提供的所有详细信息和分析。在我的 ICO 文件中,所有分辨率看起来都与我提供的分辨率一样好(参见上面的编辑)。我的底部示例“锯齿状”图像确实由 Windows 缩放(我的 ICO 文件中没有类似的 ICO)。 Windows 在更高 DPI 模式下执行此操作。就您而言,当我在正常桌面缩放(100%)中执行 GetDeviceCaps 时,我得到 96... 但如果我将桌面设置为 125% 缩放或 200% 缩放(右键单击桌面、个性化、其他:显示),我得到了不同的 DPI,Windows 需要更大的图像开始以这种“糟糕”的方式缩放 ICON。
        • Net-net of above:如果我“让 Windows 决定”在高 DPI 模式下使用什么 ICO(即没有 LoadImage 或通过 GetSystemMetrics(SM_CXSMICON)),那么它会选择低分辨率图标并放大(而且很糟糕)。
        • 只是为了确保我没有失去理智,我用你的代码代替了我的......它看起来 just fine 在 100% 桌面缩放。我将我的桌面设置为 125% 缩放,并且 音量混合器和任务栏图标看起来都很糟糕(锯齿状缩放伪影)。
        • @mark Nuts,我完全错过了在高 DPI 设置下的测试。我应该从您的屏幕截图中注意到您使用的不是 96 dpi。抱歉,我正在更新我的答案。
        • LoadIconWithScaleDown 函数中找到了很好的发现。我的应用程序被编译为与 XP 兼容,但由于我正在检测 Vista,我只是组合了一个类来从 Comctl32.dll... 查询函数指针...非常感谢您的工作!仅供参考,我验证这适用于 100%、125%、150% 和 138% 的缩放比例(只是为了尝试自定义)。
        猜你喜欢
        • 2016-03-17
        • 2018-07-01
        • 1970-01-01
        • 2018-01-06
        • 2017-01-19
        • 1970-01-01
        • 1970-01-01
        • 2022-06-11
        • 2022-07-30
        相关资源
        最近更新 更多