PCに接続されているマイク(オーディオデバイス)を取得し、音量設定をする(EnumAudioEndpoints)(C#/CsWin32使用版)

オーディオデバイス関連 もくじ
https://tera1707.com/entry/2022/02/06/144447#COM

やりたいこと

別の記事で、「CsWin32」というライブラリを使って、P/InvokeでWin32APIを呼ぶということをした。

で、そのCsWin32は、COMのinterfaceやインスタンスに対しても使えるらしい。試してみる。

で、以前、PCに接続されているマイク(オーディオデバイス)を取得し、音量設定をする(EnumAudioEndpoints)(C#版)という記事で、C#でそういうことをしたので、それをCsWin32使用して、同じことをやってみる。

前提

Microsoft.Windows.CsWin32 の 0.3.106
preview版ということで、現在も実装、改善が行われている様子。ぜひ進化してほしい。。。

  • .net8
  • VS2022 Version 17.11.2

やってみたコード

こちら。

https://github.com/tera1707/AudioDevice

これが、メインのコード。

using Windows.Win32.Foundation;
using Windows.Win32.Media.Audio;
using Windows.Win32.System.Com.StructuredStorage;
using Windows.Win32.UI.Shell.PropertiesSystem;
using Windows.Win32.System.Com;
using Windows.Win32.Media.Audio.Endpoints;

namespace MicJikkenCsWithCsWin32;

internal class Program
{
    static void Main(string[] args)
    {
        var pEnum = new MMDeviceEnumerator() as IMMDeviceEnumerator;

        var pNotifClient = new CMMNotificationClient();

        pEnum!.RegisterEndpointNotificationCallback(pNotifClient);

        pEnum!.EnumAudioEndpoints(EDataFlow.eCapture, DEVICE_STATE.DEVICE_STATE_ACTIVE, out var pCollection);

        pCollection!.GetCount(out var deviceCount);

        for (uint i = 0; i < deviceCount; i++)
        {
            pCollection.Item(i, out var pEndpoint);

            pEndpoint.OpenPropertyStore(STGM.STGM_READ, out var pProperties);

            unsafe
            {
                var PKEY_Device_FriendlyName = new PROPERTYKEY() { fmtid = new Guid(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), pid = 14 };
                pProperties.GetValue(&PKEY_Device_FriendlyName, out var vName);

                Console.WriteLine(vName.Anonymous.Anonymous.Anonymous.pwszVal);

                Guid IID_IAudioEndpointVolume = typeof(IAudioEndpointVolume).GUID;
                pEndpoint.Activate(&IID_IAudioEndpointVolume, 0, (PROPVARIANT_unmanaged*)0 ,out var endpointVolume);
                var masterVol = endpointVolume as IAudioEndpointVolume;

                var guid = new Guid();
                masterVol!.GetMasterVolumeLevelScalar(out var pMasterVolumeLevel);
                masterVol!.SetMasterVolumeLevelScalar(0.80f, &guid);
            }
        }

        Console.ReadLine();
    }

    public class CMMNotificationClient : IMMNotificationClient
    {
        public void OnDeviceStateChanged(PCWSTR pwstrDeviceId, DEVICE_STATE dwNewState)
        {
            Console.WriteLine(pwstrDeviceId);
        }

        public void OnDeviceAdded(PCWSTR pwstrDeviceId)
        {
            Console.WriteLine(pwstrDeviceId);
        }

        public void OnDeviceRemoved(PCWSTR pwstrDeviceId)
        {
            Console.WriteLine(pwstrDeviceId);
        }

        public void OnDefaultDeviceChanged(EDataFlow flow, ERole role, PCWSTR pwstrDefaultDeviceId)
        {
            Console.WriteLine(pwstrDefaultDeviceId);
        }

        public void OnPropertyValueChanged(PCWSTR pwstrDeviceId, PROPERTYKEY key)
        {
            Console.WriteLine(pwstrDeviceId);
        }
    }
}

コードのよこに、NativeMethods.txtというファイルを作成し、使いたいCOMのinterfaceなどを並べる。
今回は下記のようにした。

IMMDeviceEnumerator
MMDeviceEnumerator
IMMNotificationClient
IAudioEndpointVolume
PROPERTYKEY

※一つ、interfaceを書いたら、そいつが使う別のinterfaceや構造体も、自動で定義してくれる様子。便利。

注意点

  • モノによっては、unsafeな定義がされるので、プロジェクトをunsafeありにしないといけない。
    • (解決方法としては、CsWin32を使うPJだけ独立させる、というやり方があるらしいが、未検証)

CsWin32の設定

今回はやってないが、「NativeMethods.json」というファイルを作成し、そこにCsWin32の設定を記述できるらしい。

参考

CsWin32 公式

https://github.com/microsoft/CsWin32?tab=readme-ov-file#readme

PCに接続されているマイク(オーディオデバイス)を取得し、音量設定をする(EnumAudioEndpoints)(C#版)

https://tera1707.com/entry/2023/11/21/213253

CsWin32 で Win32 API や COM を使ったアプリケーション開発を効率化する

https://blog.shibayan.jp/entry/20220501/1651339430

unsafeの書き方

https://ufcpp.net/study/csharp/sp_unsafe.html

NativeMethods.jsonに書ける設定項目一覧

https://github.com/microsoft/CsWin32/blob/main/src/Microsoft.Windows.CsWin32/settings.schema.json

NativeMethods.jsonについて

https://qiita.com/radian-jp/items/a4509f9a44101fb2f30e

C# Win32API完全入門 ※pinvoke、ここだけ見ればいいのではというくらい詳しい。

https://qiita.com/nekotadon/items/f376d17de85dfb84fbd5