10

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

// 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 );
        }
    }
}

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

桌面缩放 125%,带有 256x256 图标集 使用常规图标的桌面缩放 125%

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

编辑:在我的图标文件中,我有以下分辨率: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 问题。在为演示目的捕获图像时,我注意到音量混合器图标本身看起来很糟糕。为了比较: 音量混合器比较

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

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 );
4

3 回答 3

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函数,cxWidth并且cxHeight参数都设置为 0 并且传递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并在您的应用程序中嵌入清单。#pragma可以使用上面示例代码中显示的特定于 MSVC 的s 来完成,也可以在项目的属性中进行配置。如果您未能执行上述任何一项操作,您将在首次启动应用程序时收到链接时错误或“未找到序数”错误。

于 2013-08-02T05:37:32.490 回答
1

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

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

于 2017-06-14T13:01:49.427 回答
0

非常感谢你们。我让它在我们的 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"
于 2017-09-19T22:49:24.973 回答