もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
以前の記事で、C++で、オーディオデバイス(マイクとかスピーカーとか)が接続されたときのイベントを拾うということをした。
実験コード
COMの練習でやったように、COMのインターフェースやCoClassを取り込む。
それ以外は、やることはC++版と同じ。
using System.Runtime.InteropServices; namespace MicJikkenCs { internal class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); var pEnum = new MMDeviceEnumerator(); var pNotifClient = new CMMNotificationClient(); if (pEnum is IMMDeviceEnumerator p) { var HResult = p.RegisterEndpointNotificationCallback(pNotifClient); } Console.ReadLine(); } } } [ComImport] [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] internal class MMDeviceEnumerator { } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")] internal interface IMMDeviceEnumerator { [PreserveSig] public void dummy1(); [PreserveSig] public void dummy2(); [PreserveSig] public void dummy3(); [PreserveSig] public int RegisterEndpointNotificationCallback(IMMNotificationClient pClient); } [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("7991EEC9-7E89-4D85-8390-6C703CEC60C0")] internal interface IMMNotificationClient { [PreserveSig] int OnDeviceStateChanged(string pwstrDeviceId, uint dwNewState); [PreserveSig] int OnDeviceAdded(string pwstrDeviceId); [PreserveSig] int OnDeviceRemoved(string pwstrDeviceId); [PreserveSig] int OnDefaultDeviceChanged(EDataFlow flow, ERole role, string pwstrDefaultDeviceId); [PreserveSig] int OnPropertyValueChanged(string pwstrDeviceId, ref PROPERTYKEY key); } public enum EDataFlow { eRender = 0, eCapture = (EDataFlow.eRender + 1), eAll = (EDataFlow.eCapture + 1), EDataFlow_enum_count = (EDataFlow.eAll + 1) } public struct PROPERTYKEY { public PROPERTYKEY(Guid InputId, uint InputPid) { fmtid = InputId; pid = InputPid; } private Guid fmtid; private uint pid; } public enum ERole { eConsole = 0, eMultimedia = (ERole.eConsole + 1), eCommunications = (ERole.eMultimedia + 1), ERole_enum_count = (ERole.eCommunications + 1) } public class CMMNotificationClient : IMMNotificationClient { public int OnDeviceStateChanged(string pwstrDeviceId, uint dwNewState) { Console.WriteLine(pwstrDeviceId); Console.WriteLine(""); return 0; } // ★以下のメソッドは今回は使わないが、実装はしておかないと異常終了しちゃう public int OnDeviceAdded(string pwstrDeviceId) { return 0; } public int OnDeviceRemoved(string pwstrDeviceId) { return 0; } public int OnDefaultDeviceChanged(EDataFlow flow, ERole role, string pwstrDefaultDeviceId) { return 0; } public int OnPropertyValueChanged(string pwstrDeviceId, ref PROPERTYKEY key) { return 0; } }
ポイント
COMの練習で、COMのインターフェースを取り込むときに、「使わないメソッドは真面目に書かなくてよい」と言っていたが、今回の、抜きさしイベントを拾うためのIMMNotificationClient
は、持ってるメソッドを全部書かないとダメ。
今回の抜きさし検出に使うのはOnDeviceStateChanged()
だけだったので
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("7991EEC9-7E89-4D85-8390-6C703CEC60C0")] internal interface IMMNotificationClient { [PreserveSig] int OnDeviceStateChanged(string pwstrDeviceId, uint dwNewState); }
だけ書いて、そのインターフェースを実装するクラスの方も、
public class CMMNotificationClient : IMMNotificationClient { public int OnDeviceStateChanged(string pwstrDeviceId, uint dwNewState) { Console.WriteLine(pwstrDeviceId); Console.WriteLine(""); return 0; } }
だけ書いていたのだが、それだとイベント発生時になにかエラーが起きて、アプリが終了してしまった。(どんなエラーだったかまでは見てない)
おそらく、イベント発生したときに、呼ぶべきメソッドが実装されてないので、エラーが起きたのかな?と思われる。
IMMNotificationClient
インターフェースが持っている全メソッドを定義して、実装するクラスでも全メソッドを実装したら、イベント発生時に各ハンドラを通ってくれるようになった。
参考
PCにマイクやスピーカー(オーディオデバイス)を抜きさししたときのイベントを取る(C++)
https://tera1707.com/entry/2023/10/11/205400
How to implement RegisterEndpointNotificationCallback method through a class and pass ComboBox as argument on it, in win32?
ここのコードをだいぶ参考にさせてもらった。ありがとうございます。