Windows 10/11で特定アプリの通知を常にオンにする方法|PowerShellとSQLite(wpndatabase.db)で確実に有効化する手順【MECM/Intune/GPO代替】

「設定>システム>通知」ではオンなのに特定アプリのトーストが出ない——。Windows 10/11 の現場で頻発するこの“あるある”は、UIの裏で通知可否がレジストリとローカル SQLite データベースの二層で管理されていることが原因です。本記事では PowerShell・GPO・MECM 等の管理観点から、特定アプリの通知を常にオンに強制する実践手順と運用の落とし穴、企業展開用スクリプトまでを一挙に整理します。

目次

前提と結論:通知は「レジストリ+SQLite(wpndatabase.db)」の二層管理

Windows の「送信元ごとの通知」スイッチは、次の二か所に状態が保持されます。どちらか片方だけを書き換えても実動しないため、両者の整合が必要です。

  • レジストリHKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings\<AppKey>Enabled(DWORD)。UI 表示に強く影響。
  • SQLite DB%LOCALAPPDATA%\Microsoft\Windows\Notifications\wpndatabase.dbNotificationHandlerHandlerSettings テーブルにアプリごとの設定(SettingKey='s:toast')が格納。実際の挙動に影響。

レジストリで Enabled=1 にしてもトーストが出ないケースは、DB 側の s:toast が 0 のままだからです。よって、DB の更新 → レジストリ確認 → 通知サービス再起動の順で処理します。

対象読者とゴール

  • 企業 IT/情シス、MSP、クライアント管理者、ヘルプデスク
  • PowerShell/MECM(SCCM)/Intune/GPO で「特定アプリの通知を常にオン」にしたい
  • UI ではオンに見えるのにトーストが出ない事象を根本解決したい

通知内部の最小限の仕組み

理解に必要なテーブルだけ押さえます。スキーマ名は環境で微妙に異なる場合がありますが、概念は同じです。

テーブル主な列意味
NotificationHandlerRecordId, PrimaryId, Kind …通知の“送信元”を一意に識別。PrimaryId は UWP なら AUMID、Win32 なら実行ファイルパスや AppUserModelID に相当。
HandlerSettingsHandlerId, SettingKey, ValueHandlerIdNotificationHandler.RecordId を参照。SettingKey='s:toast' が 1 ならその送信元のトーストを許可。

最短レシピ:PowerShell + PSSQLite で DB を直接更新する

以下は powershell.exe を例に、DB の s:toast を 1 にし、レジストリも確認・補完して反映する最小コードです。モジュール PSSQLite を使用します。

# 1) 必要モジュール
# オフライン環境なら内部リポジトリやファイル配布で事前展開してください
Import-Module PSSQLite -ErrorAction Stop

# 2) DB パス(ユーザーごと)
$db = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Notifications\wpndatabase.db'

# 3) powershell.exe の HandlerId と設定値を取得(LIKE マッチ)
$select = @"
SELECT HS.HandlerId, HS.Value, NH.PrimaryId
FROM NotificationHandler NH
JOIN HandlerSettings HS ON NH.RecordId = HS.HandlerId
WHERE NH.PrimaryId LIKE '%powershell.exe'
  AND HS.SettingKey = 's:toast'
"@

$row = Invoke-SqliteQuery -DataSource $db -Query $select

if (-not $row) {
    Write-Host '対象アプリのハンドラが見つかりません。初回登録が未発生の可能性があります。' -ForegroundColor Yellow
} else {
    if ($row.Value -ne 1) {
        $update = @"
UPDATE HandlerSettings
   SET Value = 1
 WHERE HandlerId = '$($row.HandlerId)'
   AND SettingKey = 's:toast'
"@
        Invoke-SqliteQuery -DataSource $db -Query $update
        Write-Host "DB を更新しました: $($row.PrimaryId) s:toast=1"
    } else {
        Write-Host 'DB は既に s:toast=1 です。'
    }
}

# 4) レジストリも 1 に補正(例:PowerShell)
#   HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings\{1AC14E77-...}\WindowsPowerShell\v1.0\powershell.exe
$regBase = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings'
$regKey  = Join-Path $regBase '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
if (-not (Test-Path $regKey)) { New-Item -Path $regKey -Force | Out-Null }
Set-ItemProperty -Path $regKey -Name Enabled -Type DWord -Value 1

# 5) 通知サービス再起動(ユーザーごとのサービス名)
Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Restart-Service -Force
Write-Host '通知サービスを再起動しました。'

ポイント

  • DB の更新だけ/レジストリだけでは不十分。両方そろえてから WpnUserService の再起動で反映させます。
  • アプリごとに PrimaryId の表記が異なるため、LIKE 条件は対象に合わせて変更します(例:%notepad.exe%Microsoft.Todos_8wekyb3d8bbwe!)。

量産用:関数化(UWP / Win32 どちらにも対応)

企業展開向けに、対象を AUMID / 実行ファイル名の両方でマッチできる汎用関数を用意します。複数候補が見つかった場合も全て s:toast=1 に更新します。

function Set-AppToastEnabled {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$Match, # AUMID の一部 or EXE 名/パスの一部(LIKE 用)
        [switch]$StopServiceSafely, # 競合回避のため事前停止したいとき
        [switch]$RegistryProbe # 可能ならレジストリも Enabled=1 に補正
    )

    Import-Module PSSQLite -ErrorAction Stop
    $db = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Notifications\wpndatabase.db'

    if ($StopServiceSafely) {
        Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Stop-Service -Force -ErrorAction SilentlyContinue
        Start-Sleep -Milliseconds 500
    }

    $select = @"
SELECT NH.RecordId AS HandlerId, NH.PrimaryId, HS.Value
  FROM NotificationHandler NH
  LEFT JOIN HandlerSettings HS
    ON NH.RecordId = HS.HandlerId AND HS.SettingKey = 's:toast'
 WHERE NH.PrimaryId LIKE '%$Match%'
"@
    $rows = Invoke-SqliteQuery -DataSource $db -Query $select

    if (-not $rows) {
        Write-Host "PrimaryId LIKE %$Match% が見つかりません。アプリがまだ通知登録していない可能性があります。" -ForegroundColor Yellow
    } else {
        foreach ($r in $rows) {
            if ($PSCmdlet.ShouldProcess($r.PrimaryId, 'Enable toast (s:toast=1)')) {
                $exists = $null -ne $r.Value
                if ($exists -and $r.Value -eq 1) {
                    Write-Host "既に許可済み: $($r.PrimaryId)"
                } else {
                    if ($exists) {
                        $q = "UPDATE HandlerSettings SET Value = 1 WHERE HandlerId = '$($r.HandlerId)' AND SettingKey = 's:toast'"
                    } else {
                        $q = "INSERT INTO HandlerSettings (HandlerId, SettingKey, Value) VALUES ('$($r.HandlerId)', 's:toast', 1)"
                    }
                    Invoke-SqliteQuery -DataSource $db -Query $q
                    Write-Host "更新: $($r.PrimaryId) → s:toast=1"
                }
                if ($RegistryProbe) {
                    # 可能性の高いキー名を総当たりで探すだけの簡易補正
                    $regRoot = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings'
                    Get-ChildItem $regRoot -Recurse -ErrorAction SilentlyContinue |
                        Where-Object { $_.Name -like "*$Match*" -or $_.PSChildName -like "*$Match*" } |
                        ForEach-Object {
                            try {
                                New-Item -Path $_.PSPath -Force | Out-Null
                                Set-ItemProperty -Path $_.PSPath -Name Enabled -Type DWord -Value 1 -ErrorAction Stop
                                Write-Host "レジストリ補正: $($_.PSPath) Enabled=1"
                            } catch {}
                        }
                }
            }
        }
    }

    Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Start-Service -ErrorAction SilentlyContinue
    Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Restart-Service -Force
}

利用例:

# Win32 例:実行ファイル名で
Set-AppToastEnabled -Match 'powershell.exe' -RegistryProbe

# UWP 例:AUMID の一部で

Set-AppToastEnabled -Match 'Microsoft.WindowsTerminal' -RegistryProbe 

「UI はオン、でも出ない」を潰すチェックリスト

  • DB 側HandlerSettings.s:toast が 1 か。
  • レジストリ側Enabled=1 か(該当 AppKey)。
  • 通知サービスWpnUserService を再起動したか。
  • フォーカス アシスト:自動ルールや全画面最前面アプリで抑止されていないか。
  • 最初の登録:アプリが一度も通知 API を呼んでおらず、NotificationHandler が生成されていない可能性。
  • 多重プロファイル:別ユーザーの HKCU/LOCALAPPDATA を操作していないか。

MECM(SCCM/Configuration Manager)で一斉配布する

CI/CB(構成項目/基準)として「検出(準拠判定)」と「修復(強制オン)」を分けて設計します。

検出スクリプト(例:powershell.exe)

# 準拠: exit 0 / 非準拠: exit 1
Import-Module PSSQLite -ErrorAction Stop
$db = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Notifications\wpndatabase.db'
$q = @"
SELECT HS.Value AS Toast
  FROM NotificationHandler NH
  JOIN HandlerSettings HS ON NH.RecordId = HS.HandlerId
 WHERE NH.PrimaryId LIKE '%powershell.exe'
   AND HS.SettingKey='s:toast'
"@
$row = Invoke-SqliteQuery -DataSource $db -Query $q
$toastOk = ($row -and $row.Toast -eq 1)

# レジストリ側

$regOk = $false
try {
$key = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe'
$v = (Get-ItemProperty -Path $key -Name Enabled -ErrorAction Stop).Enabled
$regOk = ($v -eq 1)
} catch {}

if ($toastOk -and $regOk) { exit 0 } else { exit 1 } 

修復スクリプト(共通化関数の呼び出し)

# 事前に Set-AppToastEnabled 関数をパッケージへ同梱し、ここでは呼び出すだけ
. "$PSScriptRoot\Set-AppToastEnabled.ps1"
Set-AppToastEnabled -Match 'powershell.exe' -RegistryProbe -StopServiceSafely

展開の勘所

  • 適用タイミング:ログオン直後、あるいはビジーでない時間帯に。通知着弾と同時更新は競合しやすい。
  • ユーザー コンテキスト:HKCU と %LOCALAPPDATA% を触るため ユーザーの権限で実行(「ユーザーごと」CI)。
  • 再評価:OS アップグレードやアプリ再登録で HandlerId が変わる。定期的に検出を回す。

Intune / GPO の現状と回避策

2025 年 11 月時点では、テンプレートベースの GPO / Intune 設定に「個別アプリの通知スイッチを強制的にオン」する明示的な項目は存在しません。UWP 全体や通知センター自体の有効/無効、 Quiet Hours の制御などはありますが、アプリ単位の強制オンは UI の後ろで動く DB を更新しないかぎり再現できません。したがって、本記事のスクリプトによる DB 直接操作またはリセット手法が実質的な回避策となります。

トラブル時の“初期化リセット”手順

既にオフにして戻せない、BurntToast 等のテスト通知が出ない場合は、以下で通知 DB/キャッシュを初期化すると既定(オン)に戻せます。既存の履歴は失われます。

  1. WpnUserService* を停止
  2. レジストリの該当アプリ配下(...Notifications\Settings...PushNotifications\Backup)を削除
  3. %LOCALAPPDATA%\Microsoft\Windows\Notifications\* の DB/キャッシュを削除(wpndatabase.db 含む)
  4. サービス再起動
方法概要メリットデメリット
通知 DB/キャッシュのリセット履歴ごと初期化し既定(オン)に戻す戻らない症状の切り札。破損時にも有効通知履歴と並び順が消える。初回再学習が必要
DB+レジストリ更新(推奨)s:toast=1Enabled=1 を整合履歴を温存しながら確実に反映アプリがまだ登録されていないと更新対象が出ない
レジストリのみ編集UI 表示だけ変える実装が簡単DB が変わらないため実働しない

PrimaryId(AUMID / 実行ファイル)の見つけ方

  • UWP/Store アプリ:AUMID が PrimaryId。アプリ パッケージ名+エントリポイント(例:Microsoft.WindowsTerminal_8wekyb3d8bbwe!App)。
  • Win32(クラシック):通常は実行ファイルのフルパス(もしくはショートカットの AppUserModelID)で識別。

DB に登録がない場合は、対象アプリを一度起動して通知 API を呼ばせる(または BurntToast などでテストトーストを発生させる)と NotificationHandler が生成されます。

ハンドラを一覧するワンライナー

Import-Module PSSQLite
$db = "$env:LOCALAPPDATA\Microsoft\Windows\Notifications\wpndatabase.db"
Invoke-SqliteQuery -DataSource $db -Query @"
SELECT NH.PrimaryId,
       COALESCE(MAX(CASE WHEN HS.SettingKey='s:toast' THEN HS.Value END), -1) AS Toast
  FROM NotificationHandler NH
  LEFT JOIN HandlerSettings HS ON NH.RecordId = HS.HandlerId
 GROUP BY NH.PrimaryId
 ORDER BY NH.PrimaryId
"@ | Format-Table -AutoSize

安全対策:バックアップとロールバック

# DB のバックアップ
$src = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Notifications\wpndatabase.db'
$dst = Join-Path $env:TEMP "wpndatabase-$(Get-Date -Format yyyyMMdd-HHmmss).bak"
Copy-Item $src $dst -Force
Write-Host "バックアップ作成: $dst"

# ロールバック(バックアップから戻す)

Stop-Service -Name 'WpnUserService*' -Force -ErrorAction SilentlyContinue
Copy-Item $dst $src -Force
Start-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue 
  • ユーザープロファイル配下の DB は通常ユーザーモードで書込可能ですが、通知着弾のタイミングと重なると競合しうるため、停止→更新→起動が安全です。

現場の Tips/つまずきポイント

  • フォーカス アシスト:時間帯・全画面・ゲーム検知などの自動ルールで抑止されると、s:toast=1でも出ません。検証時は一時的に切る。
  • 省電力モード:一部のデバイス設定やバッテリー節約で通知が抑止されることがあります。
  • 多言語 UI:UI 表示文言は変わっても内部キー(SettingKey)は一定。
  • OS アップグレード:メジャーアップ後に HandlerId が変わることがあるため、動的検索→更新のスクリプト設計が安全。
  • VDI / ローミング%LOCALAPPDATA% はローミングしない。ユーザー切替や再構築時は再適用を前提に。

「常にオン」を制度化するための運用テンプレート

運用項目推奨値 / 具体例目的
準拠判定(検出)DB: s:toast=1、REG: Enabled=1 の両方UI と実動の乖離を防ぐ
修復Set-AppToastEnabled -Match "<アプリ識別子>" -RegistryProbe一括強制オン
サービス制御WpnUserService* を再起動(競合時は停止→更新→開始)確実な反映
配布チャネルMECM(ユーザーごと CI/CB) or Intune スクリプト全社への標準適用
再評価間隔OS 更新やアプリ更新に合わせて 1〜4 週HandlerId 変動への追従

サンプル:複数アプリをまとめて常時オンにする

$targets = @(
  'powershell.exe',
  'notepad.exe',
  'Microsoft.WindowsTerminal',
  'Microsoft.ToDo' # 例
)

. "$PSScriptRoot\Set-AppToastEnabled.ps1"
foreach ($t in $targets) {
Set-AppToastEnabled -Match $t -RegistryProbe -StopServiceSafely
} 

品質保証のための自動テスト(手順例)

  1. 対象アプリを一度起動(Handler の生成)。
  2. スクリプトで s:toast=01 に往復変更できるか確認。
  3. UI を開いて表示と整合しているか確認。
  4. BurntToast などでテスト通知を送出し、トーストの出現を目視確認。
  5. 再ログオン/再起動後も設定が維持されることを確認。

よくある質問(FAQ)

Q. DB を直接変更して大丈夫?
ユーザーモードで書き換え可能な設計です。とはいえ競合や破損を避けるため、バックアップ・サービス停止を併用し、更新後は必ず再起動してください。

Q. どの値を変えれば良い?
HandlerSettingsSettingKey='s:toast'1 に。存在しない場合は INSERT、存在する場合は UPDATE します。

Q. レジストリは必須?
UI と実挙動の乖離をなくすために Enabled=1 を合わせることを推奨します。キー名は UWP と Win32 で異なるため、総当たり検索や既知パスの補正が有効です。

Q. 一度も通知したことがないアプリは?
DB にハンドラが無いため更新対象が見つからないことがあります。アプリを一度起動し通知 API を呼ばせるか、テストトーストを発生させて初期登録を作らせてください。

Q. Intune でやりたい
サインイン ユーザー スコープの PowerShell スクリプトとして同様の処理を配布できます。検出スクリプトで「準拠/非準拠」をデバイス準拠に直結させる運用も可能です。

実運用の落とし穴と対策まとめ

  • 二層管理の整合:DB とレジストリがズレると UI と実挙動が不一致に。
  • 反映タイミング:サービス再起動を忘れない。
  • ハンドラ未生成:初回登録が起きるまで DB に対象行がない。
  • OS/アプリ更新HandlerId 変化に備え、LIKE 検索+動的更新のスクリプトにしておく。
  • 大規模展開:ログオン直後の実行や、停止→更新→開始で競合を最小化。

まとめ

Windows 10/11 で「特定アプリの通知を常にオン」にするには、レジストリのみでは不十分で、wpndatabase.db(SQLite)の s:toast を 1 にすることが不可欠です。PowerShell+PSSQLite による DB 更新、レジストリの補正、そして通知サービス再起動という 3点セットを標準化すれば、UI と実挙動の齟齬を解消し、MECM/Intune での企業展開も堅牢に実現できます。トラブル時は DB リセットの切り札も用意し、定期的な検出・修復で“常にオン”を継続してください。

付録:完全版ワンファイル スクリプト

以下は単体で実行可能な「バックアップ→DB/REG 更新→サービス再起動→結果表示」までを含む一体型です。企業配布時はコード署名の上で利用してください。

#requires -Version 5.1
param(
    [Parameter(Mandatory)][string]$Match, # AUMID/EXE の一部
    [switch]$VerboseLog
)

function Write-Info($msg){ Write-Host "[INFO] $msg" -ForegroundColor Cyan }
function Write-Warn($msg){ Write-Host "[WARN] $msg" -ForegroundColor Yellow }
function Write-Ok($msg){ Write-Host "[ OK ] $msg" -ForegroundColor Green }

try {
Import-Module PSSQLite -ErrorAction Stop
} catch {
throw "PSSQLite が見つかりません。事前に配布してください。"
}

# 1) パス類

$db = Join-Path $env:LOCALAPPDATA 'Microsoft\Windows\Notifications\wpndatabase.db'
if (-not (Test-Path $db)) { throw "DB が見つかりません: $db" }

# 2) バックアップ

$bak = Join-Path $env:TEMP "wpndb-$(Get-Date -Format yyyyMMdd-HHmmss).bak"
Copy-Item $db $bak -Force
Write-Info "DB バックアップ: $bak"

# 3) サービス停止

Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Stop-Service -Force -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 500

# 4) 検索&更新

$select = @"
SELECT NH.RecordId AS HandlerId, NH.PrimaryId,
COALESCE(MAX(CASE WHEN HS.SettingKey='s:toast' THEN HS.Value END), NULL) AS Toast
FROM NotificationHandler NH
LEFT JOIN HandlerSettings HS ON NH.RecordId = HS.HandlerId
WHERE NH.PrimaryId LIKE '%$Match%'
GROUP BY NH.RecordId, NH.PrimaryId
"@
$rows = Invoke-SqliteQuery -DataSource $db -Query $select

if (-not $rows) {
Write-Warn "対象が見つかりませんでした。初回登録未発生の可能性。"
} else {
foreach ($r in $rows) {
if ($null -eq $r.Toast) {
$q = "INSERT INTO HandlerSettings (HandlerId, SettingKey, Value) VALUES ('$($r.HandlerId)','s:toast',1)"
Invoke-SqliteQuery -DataSource $db -Query $q
Write-Ok  "INSERT: $($r.PrimaryId) s:toast=1"
} elseif ($r.Toast -ne 1) {
$q = "UPDATE HandlerSettings SET Value=1 WHERE HandlerId='$($r.HandlerId)' AND SettingKey='s:toast'"
Invoke-SqliteQuery -DataSource $db -Query $q
Write-Ok  "UPDATE: $($r.PrimaryId) s:toast=1"
} else {
Write-Info "既に許可済み: $($r.PrimaryId)"
}
}
}

# 5) レジストリ補正(ヒューリスティック検索)

$regRoot = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Notifications\Settings'
if (Test-Path $regRoot) {
Get-ChildItem $regRoot -Recurse -ErrorAction SilentlyContinue |
Where-Object { $*.Name -like "*$Match*" -or $*.PSChildName -like "*$Match*" } |
ForEach-Object {
try {
if (-not (Test-Path $*.PSPath)) { New-Item -Path $*.PSPath -Force | Out-Null }
Set-ItemProperty -Path $*.PSPath -Name Enabled -Type DWord -Value 1 -ErrorAction Stop
if ($VerboseLog) { Write-Ok "REG: $($*.PSPath) Enabled=1" }
} catch {
if ($VerboseLog) { Write-Warn "REG失敗: $($*.PSPath) $*" }
}
}
} else {
Write-Warn "通知レジストリ ルートが見つかりませんでした。"
}

# 6) サービス起動&再起動

Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Start-Service -ErrorAction SilentlyContinue
Get-Service -Name 'WpnUserService*' -ErrorAction SilentlyContinue | Restart-Service -Force

# 7) 検証出力

$verify = Invoke-SqliteQuery -DataSource $db -Query $select
$verify | ForEach-Object {
$status = (($*.Toast -eq 1) ? 'Enabled' : 'Disabled/NA')
Write-Ok "結果: $($*.PrimaryId) -> $status"
} 

実例:PowerShell / Windows Terminal / メモ帳

アプリPrimaryId の例呼び出し例
Windows PowerShell...\WindowsPowerShell\v1.0\powershell.exeSet-AppToastEnabled -Match 'powershell.exe'
Windows TerminalMicrosoft.WindowsTerminal_8wekyb3d8bbwe!AppSet-AppToastEnabled -Match 'Microsoft.WindowsTerminal'
メモ帳...\notepad.exeSet-AppToastEnabled -Match 'notepad.exe'

これで“常にオン”を制度化できる

UI に頼らず PowerShell だけで「送信元ごとの通知」を確実にオンにできれば、重要アラート(EDR/監視クライアント、会議/通話アプリ、内製ツールなど)の見逃しリスクを大幅に低減できます。MECM/Intune/GPO の限界を補い、DB とレジストリの二層を制御することで、Windows 10/11 の通知を“運用に耐える”状態へ持ち上げましょう。

この記事を書いた人

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

コメント

コメントする

目次