SetWindowCompositionAttributeで透明化したウィンドウ/タスクバーを元に戻す方法(ACCENT_DISABLEDで黒くなる対策・Windows 11対応)

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\Personalize
  • EnableTransparency(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 の差異にも耐えやすくなります。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次