SetWindowCompositionAttribute(ACCENT_POLICY)でウィンドウやタスクバーをアクリル風(ぼかし/透明化)にしたあと、元の見た目に戻らない/ACCENT_DISABLED にすると黒くなる…という現象は珍しくありません。ポイントは「効果を無効化する操作」と「シェル(特にタスクバー)が見た目を再評価するタイミング」が別物だという点です。Windows 10/11 で復帰させる考え方と現実的な対処をまとめます。
SetWindowCompositionAttribute(ACCENT_POLICY)で「戻らない/黒くなる」が起きる理由
SetWindowCompositionAttribute は、ウィンドウに対してアクセント(ぼかし、半透明グラデーション、Acrylic 風など)を適用するために使われることが多い API です。ところが、この API は挙動が OS バージョンや実装(Explorer/シェル側)に強く依存し、特にタスクバー(Shell_TrayWnd)のような OS コンポーネントに対しては、期待どおりに「元へ戻る」保証がありません。
「ACCENT_DISABLED を指定したのに黒いまま」「戻したのに色が復帰しない」場合、よくある構図は次のとおりです。
| 起きていること | ありがちな原因 | 対処の方向性 |
|---|---|---|
| 自作ウィンドウが戻らない | 無効化の呼び出しが不完全(色/フラグが残る)、別の透明化手段(Opacity/Layered)が残っている | ACCENT_DISABLED + パラメータ初期化、Layered/Opacity を解除 |
| ACCENT_DISABLED にすると黒っぽい | シェル(特にタスクバー)が「通常のテーマ描画」へ再評価されていない | Explorer/タスクバーの再描画・設定反映を促す |
| Windows 10 では戻ったが Windows 11 で戻らない | タスクバー実装や描画経路が変わり、同じメッセージや更新トリガーが効かない | 複数の再評価手段を用意、最終手段として Explorer 再起動 |
まずは基本:同じ SetWindowCompositionAttribute で ACCENT_DISABLED に戻す
透明化/ぼかし(ACCENT_ENABLE_BLURBEHIND や ACCENT_ENABLE_ACRYLICBLURBEHIND)を適用したなら、解除も同じ API 呼び出しで ACCENT_DISABLED を指定するのが基本です。
ここで重要なのが、AccentState だけ戻して終わりにしないことです。黒く残るケースの一部は、AccentFlags や GradientColor が残った状態で無効化してしまい、シェル側が「中途半端な値」を拾ってしまうことがあります。解除時は構造体全体をゼロクリアするつもりで戻すのが安全です。
ACCENT_POLICY(代表的な AccentState)
一般に知られている(実装で見かける)状態は次のようなものです。OS ビルドによって利用可否や見た目は変わります。
| AccentState | 用途イメージ | 備考 |
|---|---|---|
| ACCENT_DISABLED (0) | 効果なし(通常表示へ戻す) | 「戻す」基本。黒残り時は再評価が別途必要になりやすい |
| ACCENT_ENABLE_GRADIENT (1) | 不透明/半透明のグラデーション | GradientColor の影響が出やすい |
| ACCENT_ENABLE_TRANSPARENTGRADIENT (2) | 透明寄りグラデーション | 環境により見た目が大きく変わる |
| ACCENT_ENABLE_BLURBEHIND (3) | ぼかし(Blur Behind) | Windows 10 でよく使われた |
| ACCENT_ENABLE_ACRYLICBLURBEHIND (4) | Acrylic 風 | Alpha 値や色が絡む。無効化時に値が残ると違和感が出やすい |
| ACCENT_ENABLE_HOSTBACKDROP (5) | ホストバックドロップ系 | OS 側の実装依存が強い |
C#(P/Invoke)で「適用」と「解除」をセットで用意する
Windows フォームや WPF でも、ハンドル(HWND)さえ取れれば基本は同じです。解除時は AccentState を ACCENT_DISABLED にして、フラグや色を 0 に戻す形にします。
using System;
using System.Runtime.InteropServices;
public static class AccentHelper
{
private enum AccentState
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
ACCENT_ENABLE_HOSTBACKDROP = 5
}
[StructLayout(LayoutKind.Sequential)]
private struct ACCENT_POLICY
{
public AccentState AccentState;
public int AccentFlags;
public int GradientColor; // AABBGGRR(実装でよく見かける並び。環境差あり)
public int AnimationId;
}
private enum WindowCompositionAttribute
{
WCA_ACCENT_POLICY = 19
}
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWCOMPOSITIONATTRIBDATA
{
public WindowCompositionAttribute Attribute;
public IntPtr Data;
public int SizeOfData;
}
[DllImport("user32.dll")]
private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WINDOWCOMPOSITIONATTRIBDATA data);
public static void EnableBlur(IntPtr hwnd)
{
var policy = new ACCENT_POLICY
{
AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND,
AccentFlags = 0,
GradientColor = 0,
AnimationId = 0
};
Apply(hwnd, policy);
}
public static void DisableAccent(IntPtr hwnd)
{
// 解除は「値を残さない」方針で安全側に倒す
var policy = new ACCENT_POLICY
{
AccentState = AccentState.ACCENT_DISABLED,
AccentFlags = 0,
GradientColor = 0,
AnimationId = 0
};
Apply(hwnd, policy);
}
private static void Apply(IntPtr hwnd, ACCENT_POLICY policy)
{
int size = Marshal.SizeOf<ACCENT_POLICY>();
IntPtr pPolicy = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(policy, pPolicy, false);
var data = new WINDOWCOMPOSITIONATTRIBDATA
{
Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
Data = pPolicy,
SizeOfData = size
};
SetWindowCompositionAttribute(hwnd, ref data);
}
finally
{
Marshal.FreeHGlobal(pPolicy);
}
}
}
この「DisableAccent」を呼んだのに戻らない場合は、次の章のチェック項目を先に潰すのが近道です。
自作ウィンドウが戻らないときのチェックリスト
SetWindowCompositionAttribute の解除が効いていないように見えても、実際には別の透明化が残っているケースが多いです。特に「フォーム全体の透明度」や「Layered Window」を使っていると、文字やボタンまで透ける(後述)原因にもなります。
| チェック項目 | 確認ポイント | 対処例 |
|---|---|---|
| Opacity / AllowsTransparency を使っていないか | WinForms の Opacity、WPF の AllowsTransparency が有効だと全体がアルファ合成される | Opacity を 1.0 に戻す/AllowsTransparency を避ける |
| WS_EX_LAYERED が残っていないか | 拡張スタイルで Layered 化していると、DWM とは別に透ける | スタイルを戻す/SetLayeredWindowAttributes を解除 |
| GradientColor/Flags を解除時に 0 にしているか | 無効化時に値が残ると黒っぽく見えることがある | ACCENT_DISABLED + 構造体をゼロに戻す |
| 適用先の HWND が変わっていないか | 再作成されたハンドルに古い HWND で解除している | HandleCreated 後に適用/解除も現 HWND に対して実施 |
| 再描画が止まっていないか | カスタム描画で背景更新が抑制されている | Invalidate/Update を適宜呼ぶ、WM_NCPAINT 周りを見直す |
タスクバー(Shell_TrayWnd)だけ「黒いまま」になりやすい理由
タスクバーは Explorer(シェル)が管理する特別なウィンドウです。ここに SetWindowCompositionAttribute を当てると、見た目は変わっても、解除後に「シェルが本来のテーマ描画へ戻す」タイミングが来ないことがあります。結果として、ACCENT_DISABLED にしても黒っぽく残ったり、アクセント色やアクリルが復帰しないように見えます。
また Windows 11 はタスクバーの実装が大きく変わった影響で、Windows 10 で効いていた「更新メッセージ」が効かなかったり、別の挙動になる可能性があります。ここは「単発の魔法のメッセージ」よりも、再評価を促す手段を複数持つのが実務的です。
対処A:タスクバーへ再描画・再評価を促す(メッセージ送信+再描画)
まずは副作用が比較的小さい「再描画の促進」から試すのが定石です。タスクバーの HWND は一般に FindWindow("Shell_TrayWnd", null) で取れます。
メッセージ送信で重要なのは、Explorer が応答しない状況に巻き込まれないように SendMessageTimeout を使うことです(固まった Explorer に同期 SendMessage すると、呼び出し側が固まるリスクがあります)。
再評価に使われやすいメッセージ例
環境差が大きいので「どれか 1 つで必ず直る」とは言い切れませんが、テーマ/配色/DWM 関連の変更を通知する目的で、次のようなものが候補になります。
| 目的 | メッセージ例 | 狙い | Windows 11 での注意 |
|---|---|---|---|
| テーマ変更の通知 | WM_THEMECHANGED | テーマ再読み込み・描画更新 | 効かない場合もある(無視されることがある) |
| DWM 状態変更 | WM_DWMCOMPOSITIONCHANGED | 合成(コンポジション)関連の更新 | Explorer 側の内部更新に依存 |
| 設定変更の通知 | WM_SETTINGCHANGE(WM_WININICHANGE) | ユーザー設定反映トリガー | lParam(文字列)によっては効果が変わる |
| システム色変更 | WM_SYSCOLORCHANGE | 色の再取得を促す | 即効性は環境次第 |
| 単純な再描画 | RedrawWindow / InvalidateRect | 強制再描画 | 「再評価」ではなく「塗り直し」だけで終わる場合も |
C# 例:タスクバーへ DisableAccent → メッセージ送信 → Redraw
using System;
using System.Runtime.InteropServices;
public static class TaskbarRefresh
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(string lpClassName, string? lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
uint fuFlags, uint uTimeout, out IntPtr lpdwResult);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);
private const uint SMTO_ABORTIFHUNG = 0x0002;
private const uint WM_THEMECHANGED = 0x031A;
private const uint WM_DWMCOMPOSITIONCHANGED = 0x031E;
private const uint WM_SETTINGCHANGE = 0x001A;
private const uint WM_SYSCOLORCHANGE = 0x0015;
private const uint RDW_INVALIDATE = 0x0001;
private const uint RDW_UPDATENOW = 0x0100;
private const uint RDW_ALLCHILDREN = 0x0080;
private const uint RDW_FRAME = 0x0400;
public static void RestoreTaskbarLook()
{
IntPtr tray = FindWindow("Shell_TrayWnd", null);
if (tray == IntPtr.Zero) return;
// 1) まずは ACCENT_DISABLED で無効化
AccentHelper.DisableAccent(tray);
// 2) 再評価を促す(固まり対策で SendMessageTimeout)
Send(tray, WM_THEMECHANGED);
Send(tray, WM_DWMCOMPOSITIONCHANGED);
Send(tray, WM_SYSCOLORCHANGE);
Send(tray, WM_SETTINGCHANGE);
// 3) 再描画を強制
RedrawWindow(tray, IntPtr.Zero, IntPtr.Zero,
RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN | RDW_UPDATENOW);
}
private static void Send(IntPtr hwnd, uint msg)
{
SendMessageTimeout(hwnd, msg, IntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 300, out _);
}
}
この方法は「見た目の再評価」を誘発できることがありますが、Windows 11 では期待どおりに効かない場合もあります。効かないときは次の対処Bへ進みます。
対処B:透明効果設定を一度トグルして、シェルに“設定反映”を強制する
Windows 10 で「タスクバーの黒残り」を戻せた実例として多いのが、透明効果設定(EnableTransparency)を一瞬だけ切り替えて戻す方法です。これは「設定アプリで透明効果を OFF→ON と切り替える」と同じ効果を狙います。
対象は一般に次のキーです。
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\PersonalizeEnableTransparency(DWORD)
注意:この方法はユーザー設定を一時的に変更します。必ず元の値に戻してください。また、環境によっては反映にタイムラグやちらつきが出ます。UX が重要なアプリで常用するのはおすすめしません(あくまで「復帰のための最後の一手」寄りです)。
PowerShell 例:EnableTransparency を 0↔1 トグルして元に戻す
$path = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"
$name = "EnableTransparency"
$current = (Get-ItemProperty -Path $path -Name $name -ErrorAction SilentlyContinue).$name
if ($null -eq $current) { $current = 1 }
$toggle = if ($current -eq 1) { 0 } else { 1 }
Set-ItemProperty -Path $path -Name $name -Type DWord -Value $toggle
Start-Sleep -Milliseconds 200
Set-ItemProperty -Path $path -Name $name -Type DWord -Value $current
プログラムから実行する場合は、このトグルの前後で WM_SETTINGCHANGE をブロードキャスト(全ウィンドウへ通知)し、シェル側の再評価を促すのが定番です。ブロードキャストは影響範囲が広いので、多用は避けてください。
対処C:最終手段として Explorer を再起動する
どうしてもタスクバーの見た目が戻らない場合、最終手段は Explorer(explorer.exe)の再起動です。タスクバーの所有プロセス自体が描画状態を抱え込んでいると、再描画やメッセージでは戻らないことがあります。
- 手動:タスクマネージャーで「Windows エクスプローラー」を選び「再起動」
- 運用:カスタマイズツール側で「復帰できない場合のリカバリ手順」として案内
アプリ側で強制的に Explorer を落とすのは、作業中のユーザー体験を壊しやすいので慎重に扱ってください。
色の指定はできる?「黒じゃないと透明にできない?」への現実的な答え
ACCENT_POLICY には GradientColor があり、ARGB を詰めて色付き半透明(ティント)に寄せられることがあります。ただし、ここは次の制約を理解しておく必要があります。
- ウィンドウ種別で効き方が違う:自作ウィンドウでは効果が出ても、タスクバーのような OS コンポーネントでは無視されたり別解釈されることがある。
- OS バージョン差が大きい:Windows 10 と Windows 11 で同じ値でも同じ見た目にならない。
- “黒くなる”のは色指定の問題ではないことが多い:解除後に再評価が走らず、シェルが未初期化に近い背景で塗ってしまうイメージ。
GradientColor(ARGB)の入れ方の考え方
実装例としては、アルファ(透明度)を上位 8bit に置き、残りに RGB(ただし BGR 並びで扱われる例も多い)を詰めるパターンが見られます。環境差があるため、まずは自作ウィンドウで検証するのが安全です。
| やりたいこと | 推奨アプローチ | 理由 |
|---|---|---|
| 自作アプリのウィンドウをアクリル風にしたい | ACCENT_POLICY の色指定を試す(ただし検証前提) | 自作 HWND なら制御できる範囲が広い |
| タスクバーを緑の透明にしたい | 基本は非推奨(OS 依存が強い)。やるなら「戻す導線」を最優先で設計 | Windows 11 は特に実装が変わりやすい |
| 安定して“それっぽい”見た目にしたい | (自作ウィンドウなら)DwmSetWindowAttribute のシステムバックドロップ系を検討 | ドキュメント化された経路のほうが壊れにくい |
Windows 11 で「タスクバー更新が効かない」時に意識したい設計
Windows 11 のタスクバーは、Windows 10 時代の「Explorer の 1 ウィンドウに対する小細工」が効きにくい傾向があります。実装が刷新され、内部状態の持ち方や再描画のトリガーが変わったと考えるのが安全です。
その前提で、カスタマイズツール/社内ユーティリティを作るなら、次の設計が現実的です。
- “必ず戻せる”操作を最優先:適用したときのパラメータと、解除(ACCENT_DISABLED+ゼロクリア)を必ずペアで持つ。
- 復帰手段を複数用意:メッセージ送信/再描画/設定トグル/(最後に)Explorer 再起動を段階的に。
- OS コンポーネントへの適用は最小限:可能なら自作ウィンドウに限定し、タスクバーは触らない。触るなら「復帰ボタン」と「セーフモード(何もしない起動)」を用意。
- クラッシュ時の後始末を想定:プロセスが落ちると解除処理が走らない。次回起動時に「復帰を試す」ルーチンを持つ。
補足:ぼかし適用でボタン文字まで透ける(読みにくい)問題の整理
「フォームをぼかしたら、上のコントロール文字も薄く/透明っぽくなった」という場合、原因は DWM のぼかしではなく、ウィンドウ全体をアルファ合成しているケースが多いです。たとえば次のような設定は、背景だけでなく UI 全部に影響します。
- WinForms:
this.Opacityを 1 未満にしている - Win32:
WS_EX_LAYEREDを付けてSetLayeredWindowAttributesで透過している - WPF:
AllowsTransparency="True"を有効にしている(描画経路が変わる)
つまり、「背景だけぼかしたい」のに「ウィンドウ全体が透明」になってしまっている状態です。対策は、フォーム自体の不透明度は触らず、DWM 側の効果だけ使う方向になります。
透明化手段ごとの“文字が透ける”リスク
| 手段 | 背景だけ変更 | 文字/ボタンが透ける | コメント |
|---|---|---|---|
| SetWindowCompositionAttribute(Blur/Acrylic) | 比較的しやすい | 基本は透けない | あくまで背景効果が主。実装差はある |
| WinForms Opacity / Layered Window | 難しい | 透けやすい | ウィンドウ全体がアルファ合成される |
| WPF AllowsTransparency | 設計次第 | 透けやすい | パフォーマンスや互換性も注意 |
実務的な対策パターン
- Opacity を使わない:背景透過は DWM(SWCA や DwmSetWindowAttribute)で行い、UI は通常描画のまま。
- 文字や主要 UI を別レイヤーに分離:背景専用のウィンドウ(ぼかし担当)と、前面の UI ウィンドウ(不透明担当)を分ける。構成は面倒ですが読みやすさは安定します。
- 適用タイミングを統一:WinForms なら Handle 作成後(OnHandleCreated など)に適用し、解除も同じ HWND に対して行う。
「元に戻す」を失敗しないための運用メモ(特にタスクバーを触る場合)
SetWindowCompositionAttribute は便利ですが、戻し方まで含めて設計しないと「ユーザー環境を壊した」扱いになりがちです。最低限、次を守ると事故率が下がります。
- 適用前の状態を記録する:少なくとも「自分が何を適用したか(AccentState/Flags/Color)」は保持し、解除時にゼロクリアで戻す。
- 解除ボタン(またはコマンド)を常に用意:UI が壊れても押せる導線(ホットキー、設定ファイル、コマンドライン)を持つ。
- Windows 11 では“更新メッセージ一発”に依存しない:メッセージ送信・再描画・設定トグル・Explorer 再起動の段階設計にする。
- 社内配布なら検証環境を分ける:Windows 10 / Windows 11、テーマ(ライト/ダーク)、アクセント色、透明効果 ON/OFF の組み合わせで確認する。
最後にまとめると、復帰は「ACCENT_DISABLED に戻す」だけで完結しないことがあり、特にタスクバーは「シェルの再評価」をどう起こすかが核心になります。まずは安全な順に(無効化 → 再描画 → 設定反映 → 最終手段)を試せるように実装しておくと、Windows 10/11 の差異にも耐えやすくなります。

コメント