Windowsでサウンド コントロール パネルの「無効」と同等にオーディオデバイスを制御する方法(IPolicyConfig::SetEndpointVisibility解説)

Windowsのサウンド コントロール パネルで右クリック「無効」にすると、デバイス マネージャー側は無効化されないのに、アプリからはそのデバイスが突然使えなくなります。本記事では、この挙動をC++のCore Audio APIと非公開のIPolicyConfig::SetEndpointVisibilityで再現する具体的な手順と注意点を解説します。

目次

サウンド コントロール パネルの「無効」は何をしているのか

まず整理したいのは、「サウンド(mmsys.cpl)」の右クリック → 無効 と、デバイス マネージャーでの「デバイスの無効化」はまったく別物だという点です。

サウンド パネルが操作しているのは、「オーディオ エンドポイント」と呼ばれる、アプリから見える論理デバイスです。一方、デバイス マネージャーが操作するのは「ハードウェア デバイス(Mediaクラス)」です。

場所対象ユーザーから見える挙動実際に起きていること
サウンド パネル
(mmsys.cpl)
オーディオ エンドポイント一覧からグレー表示 or 非表示になり、アプリから使えなくなるエンドポイントの可視性フラグが切り替わる
デバイス マネージャー
音声入出力
AudioEndpoint クラス「音声入出力」の項目が無効化されるが、サウンドには残っていることがあるAudioEndpoint デバイスが無効化されるが、エンドポイントの可視性とは別
デバイス マネージャー
サウンド、ビデオ、およびゲーム コントローラー
Media クラスハードウェア自体が無効化され、サウンド パネルからも消えるドライバー単位で無効化される

「サウンド パネルの無効」と同じことをやりたい場合、ターゲットにすべきなのはエンドポイントの可視性であり、ハードウェア デバイスではありません。

WMI・devcon・pnputil・SetupAPIでは満たせない理由

多くの人が最初に試すのは、以下のどれかでしょう。

  • WMI 経由でデバイスを無効化
  • devcon や pnputil で無効/有効
  • CM_Disable_DevNode など SetupAPI

しかし、これらはすべて「デバイス マネージャー寄り」の API であり、挙動としては次のようになります。

手法対象クラスサウンド パネルへの影響アプリからの見え方
AudioEndpoint デバイスを無効化音声入出力デバイス マネージャーでは無効になるが、サウンド上は変化しないケースが多い多くのアプリではそのまま使えてしまう
Media デバイス(サウンド、ビデオ、およびゲーム コントローラー)を無効化Mediaサウンドからも消える(ハードウェアごと見えなくなる)ドライバーごと止まるので、関連するすべてのエンドポイントが消える
pnputil / devconMedia / AudioEndpoint基本的にデバイス マネージャーと同じ扱い「アプリからだけ見えなくする」ような細かい制御はできない

つまり、

  • 「エンドポイントだけ非表示にして、アプリからだけ使えなくする」
  • 「デバイス マネージャー側のハードウェア状態はなるべく触らない」

という要求は、WMI や SetupAPI ではそもそもターゲットが違うため満たせません。

答えは Core Audio のポリシー API:IPolicyConfig::SetEndpointVisibility

サウンド パネルの挙動に最も近いのが、Core Audio の非公開インターフェイス IPolicyConfig です。この中にある SetEndpointVisibility メソッドを使うと、まさに「サウンドの無効」と同等の動きをさせることができます。

SetEndpointVisibility の基本

SetEndpointVisibility のシグネチャは概念的には次のような形です。

HRESULT SetEndpointVisibility(
    LPCWSTR pszEndpointId, // IMMDevice::GetId() で取得したエンドポイント ID
    BOOL    bVisible       // TRUE=表示, FALSE=非表示
);

使い方と挙動の対応関係は次の通りです。

呼び出しサウンド パネルの表示アプリからの利用デバイス マネージャーへの影響
SetEndpointVisibility(id, FALSE)無効(非表示またはグレー表示)即座に使えなくなる。
既に開いているストリームはエラーになる
Media クラスなどの状態は変わらない
SetEndpointVisibility(id, TRUE)有効(通常表示)アプリから再び選択・使用できるようになるハードウェア状態は元々のまま

ポイントは、ハードウェアは一切触らず、エンドポイントの可視性だけを切り替えるという点です。これがサウンド パネルの挙動と一致します。

エンドポイント ID とデバイス インスタンス ID を混同しない

SetEndpointVisibility が受け取るのは、SetupAPI のデバイス インスタンス ID ではなく、IMMDevice::GetId() で取得するエンドポイント ID 文字列です。

ID の種類取得方法例SetEndpointVisibility で使用可否
エンドポイント IDIMMDevice::GetId(){0.0.0.00000000}.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}◯ 必須
デバイス インスタンス IDSetupAPI(SetupDiGetDeviceInstanceId)HDAUDIO\FUNC_01&VEN_10EC&DEV_1234...× 渡すと ERROR_NOT_SUPPORTED になりやすい

SetEndpointVisibility を呼び出して 0x00000032 (ERROR_NOT_SUPPORTED) が返ってくる典型的な原因は、ここで ID を取り違えているケースです。

C++/Win32 での実装フロー

ここからは C++ で実装する手順を、できるだけ具体的に整理します。

手順の全体像

  1. COM を初期化する
  2. Core Audio のデバイス列挙で目的のエンドポイントを探す
  3. エンドポイント ID を取得する
  4. IPolicyConfig を生成する
  5. SetEndpointVisibility で可視性を切り替える
  6. COM を後始末する

COM の初期化

マルチスレッド アパートメントで十分な場合は次のようにします。

#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>

int wmain()
{
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr)) {
        wprintf(L"CoInitializeEx failed: 0x%08X\n", hr);
        return 1;
    }

    // ここにメイン処理

    CoUninitialize();
    return 0;
}

IMMDeviceEnumerator でエンドポイントを列挙する

再生デバイス(スピーカーなど)のアクティブなエンドポイントから、フレンドリ名で絞り込む例です。

CComPtr<IMMDeviceEnumerator> enumerator;
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
                      CLSCTX_ALL, IID_PPV_ARGS(&enumerator));
if (FAILED(hr)) {
    wprintf(L"MMDeviceEnumerator 作成に失敗: 0x%08X\n", hr);
    return 1;
}

CComPtr<IMMDeviceCollection> collection;
hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection);
if (FAILED(hr)) {
    wprintf(L"EnumAudioEndpoints に失敗: 0x%08X\n", hr);
    return 1;
}

UINT count = 0;
collection->GetCount(&count);

for (UINT i = 0; i < count; ++i) {
    CComPtr<IMMDevice> device;
    if (FAILED(collection->Item(i, &device))) continue;

    CComPtr<IPropertyStore> props;
    if (FAILED(device->OpenPropertyStore(STGM_READ, &props))) continue;

    PROPVARIANT friendlyName;
    PropVariantInit(&friendlyName);
    if (SUCCEEDED(props->GetValue(PKEY_Device_FriendlyName, &friendlyName))) {
        wprintf(L"[%u] %s\n", i, friendlyName.pwszVal);
    }
    PropVariantClear(&friendlyName);
}

このログ出力を見て、ユーザーに「どのデバイスを無効化するか」を選ばせる UI を作る、あるいは名前に含まれるキーワードで自動選択する、といった実装が現実的です。

エンドポイント ID の取得

目的の IMMDevice が決まったら、GetId でエンドポイント ID を取得します。

CComPtr<IMMDevice> targetDevice = /* さきほど選んだデバイス */;

LPWSTR endpointId = nullptr;
hr = targetDevice->GetId(&endpointId);
if (FAILED(hr)) {
    wprintf(L"GetId に失敗: 0x%08X\n", hr);
    return 1;
}

wprintf(L"EndpointId = %s\n", endpointId);

// 使い終わったら必ず解放
CoTaskMemFree(endpointId);

この endpointId をそのまま SetEndpointVisibility に渡します。SetupAPI で取得した文字列などを「それっぽいから」と流用しないよう注意してください。

IPolicyConfig のインターフェイス定義

IPolicyConfig は Windows SDK に公式には公開されていないインターフェイスです。そのため、プロジェクト側で独自にインターフェイス宣言とクラシッド(CLSID_PolicyConfigClient 等)を定義する必要があります。

実務では、次のような方針がおすすめです。

  • ネット上のサンプル(「IPolicyConfig SetEndpointVisibility」など)からヘッダー定義を入手
  • 必要なメソッドだけ(SetEndpointVisibility など)を残し、コメントで「未使用メソッドは省略」と明示
  • Windows のバージョンごとに GUID が変わる可能性もゼロではないため、テスト環境で十分に確認

記事内で全定義を掲載すると長くなるため、ここでは概念だけ示します。

// GUID は環境に応じた正しい値を定義すること
// 例: CLSID_PolicyConfigClient, IID_IPolicyConfig など

MIDL_INTERFACE("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
IPolicyConfig : public IUnknown
{
public:
    // ... 実際には複数メソッドがあるが省略 ...
    virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
        LPCWSTR pszEndpointId,
        BOOL    bVisible) = 0;
};

ポイントは、GUID とメソッド順序は正しく定義する必要があるということです。ここは公式サポート外なので、自己責任での利用になります。

PolicyConfigClient の生成と可視性切り替え

インターフェイス定義と GUID を用意できたら、あとは通常の COM オブジェクトと同じように生成して呼び出すだけです。

CComPtr<IPolicyConfig> policy;

hr = CoCreateInstance(CLSID_PolicyConfigClient, nullptr,
                      CLSCTX_ALL, IID_PPV_ARGS(&policy));
if (FAILED(hr)) {
    wprintf(L"PolicyConfigClient の生成に失敗: 0x%08X\n", hr);
    return 1;
}

LPCWSTR endpointId = /* IMMDevice::GetId() で得た ID */;

// 無効化(サウンド パネルの「無効」と同等)
hr = policy->SetEndpointVisibility(endpointId, FALSE);
if (FAILED(hr)) {
    wprintf(L"SetEndpointVisibility(false) に失敗: 0x%08X\n", hr);
}

// 有効化(サウンド パネルで再度「有効」にするのと同等)
// hr = policy->SetEndpointVisibility(endpointId, TRUE);

呼び出しはほぼ即時に反映され、IMMNotificationClient を購読しているアプリには、エンドポイントの状態変化が通知されます。

PowerShell やスクリプトから使いたい場合の現実解

残念ながら、Windows 純正の PowerShell コマンドレットには、サウンド パネルの「無効」をそのまま再現するものはありません。Core Audio / COM / 非公開インターフェイスという構成上、スクリプト言語だけで完結させるのはかなりしんどいのが実情です。

そこで実務では、次のようなパターンがよく使われます。

  1. C++ で小さな CLI ツールを作る
    • 例: EndpointVisibility.exe --id "<endpointId>" --visible false
    • 内部では本記事で説明した COM 呼び出しを行うだけ
  2. PowerShell やバッチからその CLI を呼び出す

PowerShell から呼び出す例を示します。

# 例: FriendlyName でエンドポイント ID を取得して無効化するイメージ

$tool = "C:\Tools\EndpointVisibility.exe"
$endpointId = "エンドポイント ID を何らかの方法で取得しておく"

# 無効化
&amp; $tool --id "$endpointId" --visible false

# 有効化
# &amp; $tool --id "$endpointId" --visible true

「エンドポイント ID をどう取得するか」は別途課題になりますが、

  • 初回だけ C++ ツールで一覧を吐いてログに保存する
  • フレンドリ名から ID を検索するサブコマンドを CLI 側に用意する

などの運用が現実的です。

よくあるつまずきと対処法

ERROR_NOT_SUPPORTED (0x00000032) が返る

もっとも多いのは、エンドポイント ID ではなくデバイス インスタンス ID を渡しているケースです。必ず IMMDevice::GetId() の戻り値をそのまま渡しているか確認しましょう。

  • SetupAPI で取得した文字列を流用していないか?
  • 文字列の変換で UTF-8 / UTF-16 を間違えていないか?
  • 末尾のヌル終端が落ちていないか?

ビルド構成と実行環境

  • 可能であれば x64 版をビルドして使う方が安全です。
  • サウンド関連は 32bit / 64bit の混在環境で微妙な差異が出ることもあるため、ターゲット環境に合わせてテストすることをおすすめします。

管理者権限は必要?

  • 基本的には管理者権限は不要です(通常ユーザーでもサウンド パネルの「無効」は使えるため)。
  • ただし、企業環境などでポリシーが強く制限されている場合、呼び出しが失敗する可能性があります。
  • その場合は、IT 管理者に「GUI ではできる操作を自動化したい」という観点で相談するのが現実的です。

非公開 API を使うリスク

IPolicyConfig は SDK に公式に載っていないため、「いつか挙動が変わる」可能性をゼロにはできません。製品や業務ツールで使うなら、次のような備えをしておくと安心です。

  • Windows のメジャーバージョンが変わるたびにテストする
  • 万が一 API が使えない環境では
    • ユーザーにサウンド パネルを開いて手動で切り替えてもらう
    • もしくは機能を無効化する
  • ログに「どの API 呼び出しが失敗したか」を詳細に記録する

カメラ(Web カメラ)にも同じことはできる?

よく聞かれる関連質問として、「オーディオみたいにカメラだけ非表示にしたい」というものがあります。しかし、現時点では、オーディオの SetEndpointVisibility に相当する公開 API はカメラにはありません。

カメラを使えなくしたい場合の選択肢は、概ね次のどれかになります。

  • デバイス マネージャーや devcon / pnputil でカメラ デバイスそのものを無効化する
  • グループポリシーや MDM(プライバシー設定)でカメラの利用を禁止する
  • アプリ側でカメラ アクセスを制御する(独自の権限管理)

つまり、オーディオのように「エンドポイントだけ非表示にしてアプリから使えなくする」という細かい制御は、カメラには提供されていないということになります。

運用設計のポイント:ユーザー体験と安全性のバランス

IPolicyConfig を使うと技術的には簡単に「見せない・使わせない」制御ができますが、ユーザー体験や運用を考えると、もう一歩踏み込んだ設計が必要です。

  • ユーザーへのフィードバック
    • なぜデバイスが突然使えなくなったのか、ユーザーは気づきにくい
    • アプリ内で「ポリシーによりこのデバイスは無効化されています」といった説明を出すと親切
  • 復旧手段の確保
    • 誤操作で無効化したまま復旧できず、サポート行き…は避けたい
    • 「すべてのデバイスを有効化する」ボタンを用意するなど、ワンクリックで戻せる仕組みがあると安心
  • ログと監査
    • いつ、どのエンドポイントを、誰が無効化したか
    • 特に企業環境では、トラブルシュートや監査の観点でログを残しておく価値が高い

まとめ

  • サウンド コントロール パネルの「無効」は、オーディオ エンドポイントの可視性を切り替える動作であり、ハードウェア デバイスの無効化とは別物です。
  • 同等の挙動をプログラムから実現したい場合は、Core Audio のポリシー API に相当する IPolicyConfig::SetEndpointVisibility を使うのが近道です。
  • SetEndpointVisibility(endpointId, FALSE) で「サウンド パネルの無効」とほぼ同じ挙動を再現でき、TRUE で元に戻せます。
  • ここで使う endpointId は、必ず IMMDevice::GetId() で取得するエンドポイント ID を渡してください。SetupAPI のデバイス インスタンス ID を渡すと ERROR_NOT_SUPPORTED になりやすいです。
  • WMI / pnputil / devcon / SetupAPI の API は、基本的にハードウェア デバイスを無効化するためのもので、「アプリからだけエンドポイントを隠す」用途には向きません。
  • PowerShell から直接これを行うことは難しく、小さなネイティブ CLI(C++製)を用意し、それをスクリプトから呼び出す構成が現実的です。
  • IPolicyConfig は非公開 API であるため、将来の互換性は自己責任です。OS 更新時のテストやフォールバック手段を用意しておくと安全です。

「サウンド パネルの無効と同じことを自動化したい」というニーズは多い一方で、情報が断片的になりがちなテーマです。本記事をベースに、ご自身の環境に合わせて小さなツールやスクリプトを整備していけば、運用の手間を大きく減らせるはずです。

この記事を書いた人

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

コメント

コメントする

目次