PowerShellでシステム音量を簡単に取得する方法を安全に進める結論は「標準PowerShellだけの短い公式cmdletはありません。既存の社内署名済みhelperまたはCore Audio APIのIAudioEndpointVolume.GetMasterVolumeLevelScalarを呼ぶ小さなwrapperで、default render endpointの0.0~1.0を読みます。Gallery moduleを無検証でinstallしません。」です。PowerShell標準にはGet-AudioDevice/Get-SystemVolume cmdletがなく、IAudioEndpointVolumeを使う承認済みwrapperが必要という前提を外すと同じ入力でも結果が変わります。まず読み取り確認で現状を固定し、その後に必要最小限の操作、検証、復元を分けて行います。 確認ポイント:取得対象は既定render endpointのmaster scalarであり、application別volumeや物理knobの値ではありません。
PowerShell標準cmdletだけでは音量値を取得できない
Windowsのシステム音量を読む処理では、現在の既定レンダーエンドポイントが存在することと、取得APIが0から1のスカラー値を返すことを確認します。結果を0から100パーセントへ変換して表示するだけに限定し、音量やミュート状態は変更しません。リモートや非対話セッションでは取得不能を0パーセントと扱いません。
- Get-Command Get-AudioDeviceで非標準moduleの有無とSourceを確認する
- default playback endpointがspeaker/headsetのどれか確認する
- wrapperの署名、source、bitness、対応OSを確認する
- 取得だけか音量変更も許可するか権限を分ける
既定render endpointと取得値の範囲を理解する
Core Audioのmaster volumeはdefault endpointごとで、application session volume、hardware knob、communication deviceとは別です。0%とmuteも別状態です。Remote DesktopやBluetooth接続でdefault endpointが切り替わるため、値だけでなくendpoint ID/nameと取得時刻を記録します。Win32_SoundDeviceのStatusはvolumeではありません。
Core Audio読取ラッパーを段階的に組み立てる
Audio系commandの有無を確認する
Get-Command '*Audio*' -CommandType Cmdlet,Function -All -ErrorAction SilentlyContinue | Select-Object Name,CommandType,Source,Version
結果に出るcommandは導入済みmodule由来か確認します。Get-AudioDeviceはWindows標準ではありません。
Win32_SoundDeviceは音量値ではない
Get-CimInstance Win32_SoundDevice | Select-Object Name,Manufacturer,Status,DeviceID
これはmaster volume値を返しません。device認識の確認だけです。
IMMDeviceEnumeratorから既定端点を選ぶ
Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
internal class MMDeviceEnumeratorComObject { }
[ComImport]
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDeviceEnumerator {
[PreserveSig] int EnumAudioEndpoints(int dataFlow, int stateMask, out IntPtr devices);
[PreserveSig] int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
[PreserveSig] int GetDevice([MarshalAs(UnmanagedType.LPWStr)] string id, out IMMDevice device);
[PreserveSig] int RegisterEndpointNotificationCallback(IntPtr client);
[PreserveSig] int UnregisterEndpointNotificationCallback(IntPtr client);
}
[ComImport]
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IMMDevice {
[PreserveSig] int Activate(ref Guid iid, int clsctx, IntPtr activationParams,
[MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer);
[PreserveSig] int OpenPropertyStore(int access, out IntPtr properties);
[PreserveSig] int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id);
[PreserveSig] int GetState(out int state);
}
[ComImport]
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAudioEndpointVolume {
[PreserveSig] int RegisterControlChangeNotify(IntPtr notify);
[PreserveSig] int UnregisterControlChangeNotify(IntPtr notify);
[PreserveSig] int GetChannelCount(out uint count);
[PreserveSig] int SetMasterVolumeLevel(float levelDb, Guid context);
[PreserveSig] int SetMasterVolumeLevelScalar(float level, Guid context);
[PreserveSig] int GetMasterVolumeLevel(out float levelDb);
[PreserveSig] int GetMasterVolumeLevelScalar(out float level);
[PreserveSig] int SetChannelVolumeLevel(uint channel, float levelDb, Guid context);
[PreserveSig] int SetChannelVolumeLevelScalar(uint channel, float level, Guid context);
[PreserveSig] int GetChannelVolumeLevel(uint channel, out float levelDb);
[PreserveSig] int GetChannelVolumeLevelScalar(uint channel, out float level);
[PreserveSig] int SetMute([MarshalAs(UnmanagedType.Bool)] bool muted, Guid context);
[PreserveSig] int GetMute([MarshalAs(UnmanagedType.Bool)] out bool muted);
[PreserveSig] int GetVolumeStepInfo(out uint step, out uint stepCount);
[PreserveSig] int VolumeStepUp(Guid context);
[PreserveSig] int VolumeStepDown(Guid context);
[PreserveSig] int QueryHardwareSupport(out uint mask);
[PreserveSig] int GetVolumeRange(out float minDb, out float maxDb, out float incrementDb);
}
public static class AudioLevelReader {
public static float GetMasterVolumeScalar() {
IMMDeviceEnumerator enumerator = null;
IMMDevice device = null;
IAudioEndpointVolume endpoint = null;
try {
enumerator = (IMMDeviceEnumerator)new MMDeviceEnumeratorComObject();
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(0, 1, out device));
Guid iid = typeof(IAudioEndpointVolume).GUID;
object rawEndpoint;
Marshal.ThrowExceptionForHR(device.Activate(ref iid, 23, IntPtr.Zero, out rawEndpoint));
endpoint = (IAudioEndpointVolume)rawEndpoint;
float level;
Marshal.ThrowExceptionForHR(endpoint.GetMasterVolumeLevelScalar(out level));
return level;
}
finally {
if (endpoint != null) Marshal.ReleaseComObject(endpoint);
if (device != null) Marshal.ReleaseComObject(device);
if (enumerator != null) Marshal.ReleaseComObject(enumerator);
}
}
}
'@
$scalar = [AudioLevelReader]::GetMasterVolumeScalar()
if ($scalar -lt 0.0 -or $scalar -gt 1.0) { throw "Core Audio returned an out-of-range value: $scalar" }
[pscustomobject]@{
Scalar = $scalar
Percent = [math]::Round($scalar * 100, 1)
}
以下の完成例はCore Audioの必要interface、HRESULT検査、COM object解放を一つのAdd-Type定義に含めます。取得methodだけを公開し、SetMasterVolumeやSetMuteは呼びません。実行sessionの既定render endpointを読む点を確認してください。
読み込んだ.NET型とmethodを確認する
[AudioLevelReader].Assembly.FullName
[AudioLevelReader].GetMethod('GetMasterVolumeScalar').ToString()
Add-Type後はAudioLevelReader typeのassemblyとGetMasterVolumeScalar methodを表示し、想定したtypeが現在sessionへ読み込まれたことを確認します。別sourceの同名typeが既にある場合は新しいsessionで内容を照合します。
0.0〜1.0を百分率へ変換する
$volume=[AudioLevelReader]::GetMasterVolumeScalar()
if($volume -lt 0.0 -or $volume -gt 1.0){ throw "Core Audio returned out-of-range value: $volume" }
[pscustomobject]@{Scalar=$volume;Percent=[math]::Round($volume*100,1);SessionId=[Diagnostics.Process]::GetCurrentProcess().SessionId}
GetMasterVolumeScalarの戻り値を0.0〜1.0で検証してからpercentへ変換します。endpointなしやCOM errorを0へ置換せず例外として扱い、0%と取得失敗を明確に分けます。
WMIのStatusを音量と誤認しない
- Get-AudioDeviceを標準cmdletと説明する
- Win32_SoundDevice Statusをvolumeとする
- endpoint切替を見落とす
- muteと0%を同一視する
- 未審査Gallery moduleをinstallする
読取専用処理で復元が不要な範囲
取得だけなら影響を抑えられます。旧記事のInstall-Module AudioDeviceCmdletsをそのまま実行せず、repository、publisher、source code、署名、versionをsecurity reviewします。Set系で夜間に自動変更すると会議・alert音を消す恐れがあるため、本記事では変更しません。変更が必要なら元endpoint、volume、muteを保存し、失敗時にその値へ戻します。
session差を含めて検証記録を残す
Windows SettingsのVolume mixerと同時刻に比較し、speaker/headset切替、mute、0/50/100%の境界をtestします。helperはHRESULT error、endpointなし、Remote Desktop、複数deviceを明示statusへし、推測値を返しません。
既定音量取得の合格基準
標準cmdletにsystem master volume取得がないため、Windows Core AudioのIMMDeviceEnumeratorで既定render/multimedia endpointを選び、IAudioEndpointVolume.GetMasterVolumeLevelScalarを呼びます。戻り値0.0〜1.0をpercentへ変換し、音量を変更するmethodは実装しません。
完成したCore Audio読取コード
本文中の完成codeが主手順です。ここでは同じcodeを重複掲載せず、次の三状態と境界値を使って結果を判定します。期待する通常状態は「既定render endpointがありscalar 0〜1を返し、0〜100%へ変換できる」です。
正常値・endpoint不在・実行失敗を分ける
- 期待どおり:既定render endpointがありscalar 0〜1を返し、0〜100%へ変換できる
- 0件・非適用:audio endpointなしはNoDefaultEndpointとして0%と区別する
- 実行error:COM activation、session、device無効、API HRESULT errorは例外として取得失敗にする
Win32_SoundDeviceのStatusはhardware状態でmaster volumeではありません。未定義のGet-SystemMasterVolumeを呼ぶだけの例を避け、Core Audio wrapperのsource、bitness、署名/内容を確認します。remote/noninteractive sessionではendpointが異なる場合があります。
無音と範囲外値を確認するfixture
$volume=[AudioLevelReader]::GetMasterVolumeScalar()
if($volume -lt 0.0 -or $volume -gt 1.0){ throw "Core Audio returned out-of-range value: $volume" }
[pscustomobject]@{Scalar=$volume;Percent=[math]::Round($volume*100,1);SessionId=[Diagnostics.Process]::GetCurrentProcess().SessionId}
volume 0/中間/100%、mute、endpointなし、RDP/local session、device切替をtestします。endpoint role、session、scalar、percent、HRESULT/exceptionを保存し、取得前後で音量が変わらないことを確認します。
PowerShellでシステム音量を簡単に取得する方法の証跡には、実行対象と取得時刻に加え、通常・0件・errorのどれへ分類したかを残します。通常系は「既定render endpointがありscalar 0〜1を返し、0〜100%へ変換できる」、停止系は「COM activation、session、device無効、API HRESULT errorは例外として取得失敗にする」を判断文としてそのまま作業票へ写し、担当者ごとの言い換えで意味が変わらないようにします。
Core Audioの音量取得を定期実行する場合は、変更APIを呼ばない読取専用processとして固定し、同時起動をlockで防ぎます。実行hostとsession種別、選択されたdefault endpoint、開始・終了時刻を一つのrun IDへ結び、COM初期化失敗とendpoint未検出を別の状態で通知します。前回値との差は傾向監視に使いますが、音量を自動補正する契機にはしません。

コメント