Visual Studio 2026 で Win32 アプリケーションを開発するで実装したアプリケーションにアイコンリソースを追加します。SVG 形式のアイコン画像から Win32 アプリケーションのリソースとして使用できる ICO 形式のアイコン画像に変換する方法も説明します。
アイコンのデザインガイドラインを概観する
Windows アプリのデザインは Microsoft の Fluent デザイン言語に準拠しています。Win32 アプリケーションには、Windows 7 のために作成されたアイコンの UX ガイドラインがあるのですが、Windows 11 で動作させることを考えると、Win32 アプリケーションでも Windows アプリアイコンのためのデザインガイドラインに準拠してアイコンをデザインするとよさそうです。
本稿では、WinUI のロゴ画像をサンプルに使用しますが、独自の Win32 アプリケーションを作成する際には、ぜひガイドラインを参考に Inkscape などを使用してアイコンをデザインしてみてください。
アイコンのバリエーションとサイズを検討する
Windows アプリのアイコンを作成するには、アイコンのバリエーションとサイズについての説明があります。アイコンのバリエーションについては、アプリのアイコン以外に、タイルやスプラッシュスクリーン、バッジロゴ、パッケージロゴがあります。
アイコンのサイズについては、Windows アプリのアイコンだけに限定しても14種類のサイズが必要です。ただ、完全に一致するものがない場合、大きいサイズをスケールダウンして表示するため問題はないようです。また、「アプリには、16×16、24×24、32×32、48×48、256×256が最低限必要です」という説明があります。一方、Win32 アプリケーションの UX ガイドラインに記載のあるアイコンのサイズ要件には、「完全なセットには、16×16、32×32、48×48、256×256が含まれます」という説明もあります。Win32 アプリケーションにおいてアイコンのサイズを何種類用意すべきかについては、Stack Overflow でも議論されているように標準的な方針がないようです。いずれにせよ、Windows アプリのガイドラインに準拠する場合、最低限の5種類から、AppList アイコンで必須の14種類までの範囲に納まると考えてよいでしょう。
私の環境で、Win32 アプリケーションのアイコンの各サイズに目印を付与して確認したところ、エクスプローラーなどで表示されるアイコンとして、16×16、24×24、32×32、48×48、256×256の5種類が確認できました。十分な検証とは言えないのですが、本稿では最低限必要な5種類のアイコンサイズ16×16、24×24、32×32、48×48、256×256を用意することにします。
アイコンリソースを追加するために ICO 形式を理解する
ICO 形式は様々なサイズのビットマップ画像を集めたファイル形式です。Win32 アプリケーションにアイコンリソースとして追加するためには ICO 形式である必要があります。歴史のあるファイル形式で1995年に作成されたアイコンの公式ドキュメントに ICO ファイルの説明があります。仕様が追加されてきた歴史もあるので、まとまった情報として Wikipedia の ICO ファイル形式のページは参考になります。
ICO ファイルに含まれるビットマップ画像は、DIB (Device Independent Bitmap) 形式か PNG 形式です。ICO ファイル内の PNG 形式は Windows Vista でサポートされたので、Windows 11 で動作させる Win32 アプリケーションであれば、すべて PNG 形式でよさそうです。実際に、現在の Visual Studio Installer の実行ファイルに含まれるアイコンは PNG 形式のみでした。
しかし、ImageMagick のような ICO ファイルを生成できるアプリケーションは、互換性の確保から、256×256のアイコンのみ PNG 形式で格納し、それ以外は DIB 形式で格納するという動作になります。これは、前述したアイコンの UX ガイドラインで「256×256ピクセルイメージの32ビットコピーのみを含める必要があり、ファイルサイズを減らすために256×256ピクセルイメージのみを圧縮する必要があります。」と説明されているためかと思います。
本稿では、ImageMagick による互換性のある ICO ファイルと、すべて PNG 形式の ICO ファイルの生成方法を説明します。
SVG ファイルから ICO ファイルを生成する
サンプルの SVG ファイルとして WinUI ロゴを使用します。CC-BY-4.0 ライセンスと MIT ライセンスで提供されているリポジトリに含まれる SVG ファイルなのでラインセンス上は問題ないかと思います。また、Microsoft の商標にも登録はなさそうです。
SVG ファイルの前処理
サンプルの SVG ファイルが正方形ではないため、テキストエディタで SVG ファイルを開き、1行目を下記のように更新し、SVG ファイルが正方形になるように修正します。
<svg width="365" height="365" viewBox="0 -16.5 365 365" fill="none" xmlns="http://www.w3.org/2000/svg">
Inkscape と ImageMagick で ICO ファイルを生成する
SVG ファイルから PNG ファイルを生成するために使用する Inkscape をインストールします。
winget install inkscape.inkscape
Inkscape コマンドのマニュアルを参考に SVG ファイルから各サイズの PNG ファイルを生成します。実行ファイルとして inkscape.com と inkscape.exe がありますが、inkscape.com を使用することに注意してください。また、アイコンに余白ができないように、アイコン周囲の透明な背景をトリミングする -D オプションを付与しています。
$inkscape = gcm 'C:\Program Files\Inkscape\bin\inkscape.com'
& $inkscape -o logo-winui.16.png -D -w 16 -h 16 logo-winui.svg
& $inkscape -o logo-winui.24.png -D -w 24 -h 24 logo-winui.svg
& $inkscape -o logo-winui.32.png -D -w 32 -h 32 logo-winui.svg
& $inkscape -o logo-winui.48.png -D -w 48 -h 48 logo-winui.svg
& $inkscape -o logo-winui.256.png -D -w 256 -h 256 logo-winui.svg
PNG ファイルから ICO ファイルを生成するために使用する ImageMagick をインストールします。
winget install imagemagick.imagemagick
PowerShell を再起動し、magick コマンドの PATH が追加された PATH 環境変数を反映させ、下記のコマンドで ICO ファイルを生成します。
magick logo-winui.256.png logo-winui.48.png logo-winui.32.png logo-winui.24.png logo-winui.16.png logo-winui.ico
生成された ICO ファイルの内容を確認すると、256×256 のアイコンだけ PNG 形式で格納されていることがわかります。
magick identify logo-winui.ico
logo-winui.ico[0] PNG 256x256 256x256+0+0 8-bit sRGB 41242B 0.001u 0:00.000
logo-winui.ico[1] ICO 48x48 48x48+0+0 8-bit sRGB 0.001u 0:00.000
logo-winui.ico[2] ICO 32x32 32x32+0+0 8-bit sRGB 0.001u 0:00.000
logo-winui.ico[3] ICO 24x24 24x24+0+0 8-bit sRGB 0.001u 0:00.001
logo-winui.ico[4] ICO 16x16 16x16+0+0 8-bit sRGB 58800B 0.001u 0:00.001
すべて PNG 形式の ICO ファイルを生成する
デファクトスタンダードとして使用されているツールを発見できなかったので、PowerShell スクリプト ConvertTo-Ico.ps1 を作成しました。スクリプトの説明は本稿では割愛しますが、生成 AI に説明してもらうと理解しやすいと思います。
param(
[Parameter(Mandatory)]
[ValidatePattern('\.svg$')]
[string]$Path,
[ValidateNotNullOrEmpty()]
[ValidateRange(1, 256)]
[int[]]$Sizes = @(16, 24, 32, 48, 256)
)
$inkscape = 'C:\Program Files\Inkscape\bin\inkscape.com'
if (-not (Test-Path -LiteralPath $inkscape)) {
throw 'Inkscape was not found. Install it with: winget install Inkscape.Inkscape'
}
$svgFile = Get-Item -LiteralPath $Path -ErrorAction Stop
$tempDirectory = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
$icoFile = $null
$icoWriter = $null
try {
New-Item -ItemType Directory -Path $tempDirectory -ErrorAction Stop | Out-Null
$images = $Sizes | Sort-Object -Descending -Unique | ForEach-Object {
$pngPath = Join-Path -Path $tempDirectory -ChildPath "$($svgFile.BaseName).$_.png"
& $inkscape -o $pngPath -D -w $_ -h $_ $svgFile
[PSCustomObject]@{
Size = if ($_ -ge 256) { 0 } else { $_ }
Bytes = [System.IO.File]::ReadAllBytes($pngPath)
}
}
$icoPath = Join-Path -Path $svgFile.DirectoryName -ChildPath "$($svgFile.BaseName).ico"
$icoFile = [System.IO.File]::Open($icoPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
$icoWriter = [System.IO.BinaryWriter]::new($icoFile)
$icoWriter.Write([UInt16]0)
$icoWriter.Write([UInt16]1)
$icoWriter.Write([UInt16]$images.Count)
$offset = 6 + (16 * $images.Count)
foreach ($image in $images) {
$icoWriter.Write([Byte]$image.Size)
$icoWriter.Write([Byte]$image.Size)
$icoWriter.Write([Byte]0)
$icoWriter.Write([Byte]0)
$icoWriter.Write([UInt16]1)
$icoWriter.Write([UInt16]32)
$icoWriter.Write([UInt32]$image.Bytes.Length)
$icoWriter.Write([UInt32]$offset)
$offset += $image.Bytes.Length
}
foreach ($image in $images) {
$icoWriter.Write($image.Bytes)
}
}
finally {
if ($icoWriter) { $icoWriter.Dispose() }
if ($icoFile) { $icoFile.Dispose() }
if (Test-Path -LiteralPath $tempDirectory) { Remove-Item -LiteralPath $tempDirectory -Recurse }
}
上記のスクリプトをファイルに保存し、下記のように実行すると SVG ファイルと同じフォルダーに、すべて PNG 形式の ICO ファイルが生成されます。
.\ConvertTo-Ico.ps1 logo-winui.svg
生成された ICO ファイルの内容を確認すると、すべて PNG 形式で格納されていることがわかります。
magick identify logo-winui.ico
logo-winui.ico[0] PNG 256x256 256x256+0+0 8-bit sRGB 41969B 0.001u 0:00.001
logo-winui.ico[1] PNG 48x48 48x48+0+0 8-bit sRGB 3265B 0.001u 0:00.001
logo-winui.ico[2] PNG 32x32 32x32+0+0 8-bit sRGB 1937B 0.001u 0:00.001
logo-winui.ico[3] PNG 24x24 24x24+0+0 8-bit sRGB 1364B 0.001u 0:00.001
logo-winui.ico[4] PNG 16x16 16x16+0+0 8-bit sRGB 49398B 0.001u 0:00.000
Win32 アプリケーションにアイコンリソースを追加する
Visual Studio 2026 で Win32 アプリケーションを開発するで実装したアプリケーションにアイコンリソースを追加してみます。まず、Win32App プロジェクトのフォルダーに Win32App.ico という名前で ICO ファイルをコピーします。
ソリューションエクスプローラーからリソースを追加します。

リソースの追加ダイアログからアイコンをインポートします。

アイコンの ID を IDI_ICON に修正します。

これで、resource.h と Win32App.rc というリソースファイルが更新されます。GUI によって更新されたままでも動作しますが、私は下記のハイライトした行のように修正しています。
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Win32App.rc
//
#define IDR_CONTEXTMENU 101
#define IDI_ICON 102
#define ID_CONTEXTMENU_EXIT 40001
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40002
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON ICON "Win32App.ico"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
IDR_CONTEXTMENU MENU
BEGIN
POPUP "ContextMenu"
BEGIN
MENUITEM "Exit", ID_CONTEXTMENU_EXIT
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
下記のコードで Win32App.cpp の AddNotifyIcon 関数を更新し、通知アイコンも同じアイコンリソースを使用するように修正します。
int AddNotifyIcon(const HWND hWnd)
{
NOTIFYICONDATA nid{
.cbSize = sizeof(NOTIFYICONDATA),
.hWnd = hWnd,
.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP | NIF_GUID | NIF_SHOWTIP,
.uCallbackMessage = WM_NOTIFYICON,
.hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_ICON)),
.szTip = L"Win32 App",
.uVersion = NOTIFYICON_VERSION_4,
.guidItem = NOTIFYICON_GUID,
}; // NOLINT(clang-diagnostic-missing-designated-field-initializers)
if (!Shell_NotifyIcon(NIM_ADD, &nid)) return ERROR_NIM_ADD;
if (!Shell_NotifyIcon(NIM_SETVERSION, &nid)) return ERROR_NIM_SETVERSION;
return 0;
}
ビルドして実行すると指定したアイコンリソースで通知アイコンが表示されます。

また、実行ファイルのアイコンも更新されます。エクスプローラーがアイコンをキャッシュするので、Windows を再起動するなどしないと反映されないこともあるかもしれません。

これで Win32 アプリケーションにアイコンリソースを追加することができました。
本稿では、ICO 形式のアイコン画像について概観し、SVG ファイルから ICO ファイルを生成する方法と Win32 アプリケーションに ICO ファイルをアイコンリソースとして追加する方法を説明しました。次回は、VERSIONINFO リソースの記事を書こうと思います。
