pcurtis
pcurtis

Reputation: 81

Delphi - Sound change notification (mute / unmute)

how can I get notified of system audio changes?

Or how to use the callback functions

function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): Integer; stdcall;
function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): Integer; stdcall;
function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): Hresult; stdcall;

Upvotes: 1

Views: 1317

Answers (2)

fpiette
fpiette

Reputation: 12292

I have some code for you, 3 source code files: A unit with a class handling volume control notification, a unit to interface with Windows API and a simple demo program. The demo is actually all you have to look in details. The rest can be considered as obscure support routines :-)

Let's see the demo program. It is a simple VCL form having only a TMemo on it. It register for volume control notification and display a simple message in the memo (You probably want a nice UI instead).

The code is really very simple: create an interface pointing to TVolumeControl, assign an event handler to the OnVolumeChange and call the Initialize method. When the event fires, call GetLevelInfo to get the information and display it. When the form is destroyed, call Dispose method to stop getting notification.

unit SoundChangeNotificationDemoMain;

interface

uses
    Winapi.Windows, Winapi.Messages, Winapi.ActiveX,
    System.SysUtils, System.Variants, System.Classes, System.SyncObjs,
    System.Win.ComObj,
    Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
    Ovb.VolumeMonitor,
    Ovb.MMDevApi;

type
    TSoundChangeDemoForm = class(TForm)
        Memo1: TMemo;
    protected
        FVolumeMonitor  : IVolumeMonitor;
        procedure VolumeMonitorVolumeChange(Sender : TObject);
    public
        constructor Create(AOwner : TComponent); override;
        destructor  Destroy; override;
    end;

var
    SoundChangeDemoForm: TSoundChangeDemoForm;

implementation
{$R *.dfm}

constructor TSoundChangeDemoForm.Create(AOwner: TComponent);
var
    HR : HRESULT;
begin
    inherited;
    FVolumeMonitor                := TVolumeMonitor.Create;
    FVolumeMonitor.OnVolumeChange := VolumeMonitorVolumeChange;
    HR                            := FVolumeMonitor.Initialize();
    if not SUCCEEDED(HR) then
        ShowMessage('Volume control initialization failed');
end;

destructor TSoundChangeDemoForm.Destroy;
begin
    FVolumeMonitor.Dispose;
    inherited Destroy;
end;

procedure TSoundChangeDemoForm.VolumeMonitorVolumeChange(Sender: TObject);
var
    Info: TVOLUME_INFO;
begin
    FVolumeMonitor.GetLevelInfo(Info);
    Memo1.Lines.Add(Format('Volume change: nStep=%d cSteps=%d Mute=%d',
                           [Info.nStep, Info.cSteps, Ord(Info.bMuted)]));
end;

The hard work is done is a unit I named Ovb.VolumeMonitor. This unit interact with Windows API to request notification when the volume is changed on the default audio device.

Note that this is not a component but a class and you use this class thru an interface. See the demo app above.

unit Ovb.VolumeMonitor;

interface

uses
    Winapi.Windows, Winapi.Messages, Winapi.ActiveX,
    System.SysUtils, System.Variants, System.Classes, System.SyncObjs,
    System.Win.ComObj,
    Ovb.MMDevApi;

const
    WM_VOLUMECHANGE     = (WM_USER + 12);
    WM_ENDPOINTCHANGE   = (WM_USER + 13);   // Not implemented yet

type
    IVolumeMonitor = interface
        ['{B06EE2E9-E707-4086-829A-D5664978069F}']
        function  Initialize() : HRESULT;
        procedure Dispose;
        function  GetLevelInfo(var Info: TVOLUME_INFO) : HRESULT;
        function  GetOnVolumeChange: TNotifyEvent;
        procedure SetOnVolumeChange(const Value: TNotifyEvent);
        property OnVolumeChange : TNotifyEvent   read  GetOnVolumeChange
                                                 write SetOnVolumeChange;
    end;

    TVolumeMonitor = class(TInterfacedObject,
                           IVolumeMonitor,
                           IMMNotificationClient,
                           IAudioEndpointVolumeCallback)
    private
        FRegisteredForEndpointNotifications : BOOL;
        FRegisteredForVolumeNotifications   : BOOL;
        FDeviceEnumerator                   : IMMDeviceEnumerator;
        FAudioEndpoint                      : IMMDevice;
        FAudioEndpointVolume                : IAudioEndpointVolume;
        FEndPointCritSect                   : TRTLCriticalSection;
        FWindowHandle                       : HWND;
        FOnVolumeChange                     : TNotifyEvent;
        procedure WndProc(var Msg: TMessage);
        procedure WMVolumeChange(var Msg: TMessage);
        function  GetOnVolumeChange: TNotifyEvent;
        procedure SetOnVolumeChange(const Value: TNotifyEvent);
        function  AttachToDefaultEndpoint() : HRESULT;
        function  OnNotify(pNotify : PAUDIO_VOLUME_NOTIFICATION_DATA) : HRESULT; stdcall;
    public
        constructor Create; virtual;
        destructor  Destroy; override;
        function  Initialize() : HRESULT;
        procedure DetachFromEndpoint();
        procedure Dispose;
        function GetLevelInfo(var Info: TVOLUME_INFO) : HRESULT;
        property OnVolumeChange : TNotifyEvent   read  GetOnVolumeChange
                                                 write SetOnVolumeChange;
    end;

implementation

{ TVolumeMonitor }

constructor TVolumeMonitor.Create;
begin
    inherited Create;
    FWindowHandle                         := AllocateHWnd(WndProc);
    FRegisteredForEndpointNotifications := FALSE;
    FRegisteredForVolumeNotifications   := FALSE;
    FEndPointCritSect.Initialize();
end;

destructor TVolumeMonitor.Destroy;
begin
    if FWindowHandle <> INVALID_HANDLE_VALUE then begin
        DeallocateHWnd(FWindowHandle);
        FWindowHandle := INVALID_HANDLE_VALUE;
    end;
    FEndPointCritSect.Free;
    inherited Destroy;
end;

//  Initialize this object.  Call after constructor.
function TVolumeMonitor.Initialize: HRESULT;
var
    hr : HRESULT;
begin
    hr := CoCreateInstance(CLASS_IMMDeviceEnumerator,
                           nil,
                           CLSCTX_INPROC_SERVER,
                           IID_IMMDeviceEnumerator,
                           FDeviceEnumerator);
    if SUCCEEDED(hr) then begin
        hr := FDeviceEnumerator.RegisterEndpointNotificationCallback(Self);
        if SUCCEEDED(hr) then
            hr := AttachToDefaultEndpoint();
    end;
    Result := hr;
end;

function TVolumeMonitor.AttachToDefaultEndpoint: HRESULT;
var
    hr : HRESULT;
begin
    FEndPointCritSect.Enter();

    // Get the default music & movies playback device
    hr := FDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eMultimedia, FAudioEndpoint);
    if SUCCEEDED(hr) then begin
        // Get the volume control for it
        hr := FAudioEndpoint.Activate(IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, FAudioEndpointVolume);
        if SUCCEEDED(hr) then begin
            // Register for callbacks
            hr := FAudioEndpointVolume.RegisterControlChangeNotify(self);
            FRegisteredForVolumeNotifications := SUCCEEDED(hr);
        end;
    end;

    FEndPointCritSect.Leave();

    Result := hr;
end;

//  Stop monitoring the device and release all associated references
procedure TVolumeMonitor.DetachFromEndpoint();
begin
    FEndPointCritSect.Enter();

    if FAudioEndpointVolume <> nil then begin
        // be sure to unregister...
        if FRegisteredForVolumeNotifications then begin
            FAudioEndpointVolume.UnregisterControlChangeNotify(Self);
            FRegisteredForVolumeNotifications := FALSE;
        end;

        FAudioEndpointVolume := nil
    end;

    if FAudioEndpoint <> nil then
        FAudioEndpoint := nil;

    FEndPointCritSect.Leave();
end;

//  Call when the app is done with this object before calling release.
//  This detaches from the endpoint and releases all audio service references.
procedure TVolumeMonitor.Dispose;
begin
    DetachFromEndpoint();

    if FRegisteredForEndpointNotifications then begin
        FDeviceEnumerator.UnregisterEndpointNotificationCallback(Self);
        FRegisteredForEndpointNotifications := FALSE;
    end;
end;

function TVolumeMonitor.GetLevelInfo(var Info: TVOLUME_INFO): HRESULT;
var
    hr : HRESULT;
begin
    hr := E_FAIL;
    FEndPointCritSect.Enter();
    if FAudioEndpointVolume <> nil then begin
        hr := FAudioEndpointVolume.GetMute(Info.bMuted);
        if SUCCEEDED(hr) then
            hr := FAudioEndpointVolume.GetVolumeStepInfo(Info.nStep, Info.cSteps);
    end;
    FEndPointCritSect.Leave();
    Result := hr;
end;

function TVolumeMonitor.GetOnVolumeChange: TNotifyEvent;
begin
    Result := FOnVolumeChange;
end;

// Callback for Windows API
function TVolumeMonitor.OnNotify(
    pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
begin
    if FWindowHandle <> INVALID_HANDLE_VALUE then
        PostMessage(FWindowHandle, WM_VOLUMECHANGE, 0, 0);
    Result := S_OK;
end;

procedure TVolumeMonitor.SetOnVolumeChange(const Value: TNotifyEvent);
begin
    FOnVolumeChange := Value;
end;

procedure TVolumeMonitor.WMVolumeChange(var Msg: TMessage);
begin
    if Assigned(FOnVolumeChange) then
        FOnVolumeChange(Self);
end;

procedure TVolumeMonitor.WndProc(var Msg: TMessage);
begin
    case Msg.Msg of
    WM_VOLUMECHANGE  : WMVolumeChange(Msg);
    else
        Winapi.Windows.DefWindowProc(FWindowHandle, Msg.Msg, Msg.WParam, Msg.LParam);
    end;
end;

Finally, tho interact with Windows API, we need a few declarations for structure and interfaces that Windows make use.

unit Ovb.MMDevApi;

interface

uses
    WinApi.Windows,
    WinApi.ActiveX;

const
    CLASS_IMMDeviceEnumerator : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
    IID_IMMDeviceEnumerator   : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
    IID_IAudioEndpointVolume  : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';

    // Data-flow direction
    eRender         = $00000000;
    eCapture        = $00000001;
    eAll            = $00000002;

    // Role constant
    eConsole        = $00000000;
    eMultimedia     = $00000001;
    eCommunications = $00000002;

type
    TAUDIO_VOLUME_NOTIFICATION_DATA = record
        guidEventContext : TGUID;
        Muted            : BOOL;
        fMasterVolume    : Single;
        nChannels        : UINT;
        afChannelVolumes : array [1..1] of Single;
    end;
    PAUDIO_VOLUME_NOTIFICATION_DATA = ^TAUDIO_VOLUME_NOTIFICATION_DATA;

    TVOLUME_INFO = record
        nStep  : UINT;
        cSteps : UINT;
        bMuted : BOOL;
    end;
    PVOLUME_INFO = ^TVOLUME_INFO;

    IAudioEndpointVolumeCallback = interface(IUnknown)
        ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
        function OnNotify(pNotify : PAUDIO_VOLUME_NOTIFICATION_DATA) : HRESULT; stdcall;
    end;

    IAudioEndpointVolume = interface(IUnknown)
        ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
        function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
        function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
        function GetChannelCount(out PInteger): HRESULT; stdcall;
        function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
        function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
        function GetMasterVolumeLevel(out fLevelDB: single): HRESULT; stdcall;
        function GetMasterVolumeLevelScaler(out fLevelDB: single): HRESULT; stdcall;
        function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
        function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
        function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
        function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;
        function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
        function GetMute(out bMute: BOOL): HRESULT; stdcall;
        function GetVolumeStepInfo(out pnStep: UINT; out pnStepCount: UINT): HRESULT; stdcall;
        function VolumeStepUp(pguidEventContext: PGUID): HRESULT; stdcall;
        function VolumeStepDown(pguidEventContext: PGUID): HRESULT; stdcall;
        function QueryHardwareSupport(out pdwHardwareSupportMask): HRESULT; stdcall;
        function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): HRESULT; stdcall;
    end;

    IAudioMeterInformation = interface(IUnknown)
        ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
    end;

    IPropertyStore = interface(IUnknown)
    end;

    IMMDevice = interface(IUnknown)
        ['{D666063F-1587-4E43-81F1-B948E807363F}']
        function Activate(const refId: TGUID; dwClsCtx: DWORD;  pActivationParams: PInteger; out pEndpointVolume: IAudioEndpointVolume): HRESULT; stdCall;
        function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
        function GetId(out ppstrId: PLPWSTR): HRESULT; stdcall;
        function GetState(out State: Integer): HRESULT; stdcall;
    end;


    IMMDeviceCollection = interface(IUnknown)
      ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
    end;

    IMMNotificationClient = interface(IUnknown)
        ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
    end;

    IMMDeviceEnumerator = interface(IUnknown)
        ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
        function EnumAudioEndpoints(dataFlow: TOleEnum; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): HRESULT; stdcall;
        function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): HRESULT; stdcall;
        function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
        function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
        function UnregisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
    end;

implementation

end.

Upvotes: 2

Andreas Rejbrand
Andreas Rejbrand

Reputation: 108948

First a disclaimer: I am not an expert on the audio APIs. Still, I can get it to work using the documentation.

First, we need to get hold of an IMMDeviceEnumerator interface using CoCreateInstance. Then we use the IMMDeviceEnumerator.GetDefaultAudioEndpoint method to obtain the default audio output device. Using the device's Activate method, we request an IAudioEndpointVolume interface and call its RegisterControlChangeNotify method to subscribe to volume notifications, including mute and unmute.

We must provide a recipient for these notifications, and that recipient must implement the IAudioEndpointVolumeCallback interface, which specifies how the recipient object actually does receive the notifications.

In a single-form GUI application, like the demo application I wrote for this answer, it makes sense to use the main form. Hence, we must let the form implement the IAudioEndpointVolumeCallback.OnNotify method. This method is called by the audio system when the volume is changed (or (un)muted), and the notification data is passed in a AUDIO_VOLUME_NOTIFICATION_DATA structure.

I don't want to touch the GUI or risk raising exceptions in this method, so just to feel safe I only let this method post a message to the form with the required data.

Full code:

unit OSD;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, ActiveX,
  ComObj, AudioEndpoint, Gauge;

  // Gauge: https://specials.rejbrand.se/dev/controls/gauge/

const
  WM_VOLNOTIFY = WM_USER + 1;

type
  TSndVolFrm = class(TForm, IAudioEndpointVolumeCallback)
    ArcGauge: TArcGauge;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FDeviceEnumerator: IMMDeviceEnumerator;
    FMMDevice: IMMDevice;
    FAudioEndpointVolume: IAudioEndpointVolume;
    function OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
      stdcall;
    procedure WMVolNotify(var Msg: TMessage); message WM_VOLNOTIFY;
  public
  end;

var
  SndVolFrm: TSndVolFrm;

implementation

uses
  Math;

{$R *.dfm}

procedure TSndVolFrm.FormCreate(Sender: TObject);
begin

  if not Succeeded(CoInitialize(nil)) then
    ExitProcess(1);

  OleCheck(CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER,
    IID_IMMDeviceEnumerator, FDeviceEnumerator));
  OleCheck(FDeviceEnumerator.GetDefaultAudioEndpoint(0, 0, FMMDevice));
  OleCheck(FMMDevice.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, FAudioEndpointVolume));
  OleCheck(FAudioEndpointVolume.RegisterControlChangeNotify(Self));

end;

procedure TSndVolFrm.FormDestroy(Sender: TObject);
begin
  CoUninitialize;
end;

function TSndVolFrm.OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT;
begin
  if pNotify = nil then
    Exit(E_POINTER);
  try
    PostMessage(Handle, WM_VOLNOTIFY, WPARAM(pNotify.bMuted <> False), LPARAM(Round(100 * pNotify.fMasterVolume)));
    Result := S_OK;
  except
    Result := E_UNEXPECTED;
  end;
end;

procedure TSndVolFrm.WMVolNotify(var Msg: TMessage);
begin

  var LMute := Msg.WParam <> 0;
  var LVolume := Msg.LParam;

  if LMute then
  begin
    ArcGauge.ShowCaption := False;
    ArcGauge.FgBrush.Color := $777777;
  end
  else
  begin
    ArcGauge.ShowCaption := True;
    ArcGauge.FgBrush.Color := clHighlight;
  end;

  ArcGauge.Position := LVolume;

end;

end.

Screen recorder of application in action, responding to volume changes including mute and unmute.

Interface unit:

unit AudioEndpoint;

interface

uses
  Windows,
  Messages,
  SysUtils,
  ActiveX,
  ComObj;

const
  CLASS_IMMDeviceEnumerator : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  IID_IMMDeviceEnumerator : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  IID_IAudioEndpointVolume : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';

type
  PAUDIO_VOLUME_NOTIFICATION_DATA = ^AUDIO_VOLUME_NOTIFICATION_DATA;
  AUDIO_VOLUME_NOTIFICATION_DATA = record
    guidEventContext: TGUID;
    bMuted: BOOL;
    fMasterVolume: Single;
    nChannels: UINT;
    afChannelVolumes: Single;
  end;

  IAudioEndpointVolumeCallback = interface(IUnknown)
    ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
    function OnNotify(pNotify: PAUDIO_VOLUME_NOTIFICATION_DATA): HRESULT; stdcall;
  end;

  IAudioEndpointVolume = interface(IUnknown)
    ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
    function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
    function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
    function GetChannelCount(out PInteger): HRESULT; stdcall;
    function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMasterVolumeLevel(out fLevelDB: single): HRESULT; stdcall;
    function GetMasterVolumeLevelScaler(out fLevelDB: single): HRESULT; stdcall;
    function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
    function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;
    function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMute(out bMute: Boolean): HRESULT; stdcall;
    function GetVolumeStepInfo(pnStep: Integer; out pnStepCount: Integer): HRESULT; stdcall;
    function VolumeStepUp(pguidEventContext: PGUID): HRESULT; stdcall;
    function VolumeStepDown(pguidEventContext: PGUID): HRESULT; stdcall;
    function QueryHardwareSupport(out pdwHardwareSupportMask): HRESULT; stdcall;
    function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): HRESULT; stdcall;
  end;

  IAudioMeterInformation = interface(IUnknown)
  ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
  end;

  IPropertyStore = interface(IUnknown)
  end;

  IMMDevice = interface(IUnknown)
  ['{D666063F-1587-4E43-81F1-B948E807363F}']
    function Activate(const refId: TGUID; dwClsCtx: DWORD;  pActivationParams: PInteger; out pEndpointVolume: IAudioEndpointVolume): HRESULT; stdCall;
    function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
    function GetId(out ppstrId: PLPWSTR): HRESULT; stdcall;
    function GetState(out State: Integer): HRESULT; stdcall;
  end;


  IMMDeviceCollection = interface(IUnknown)
  ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
  end;

  IMMNotificationClient = interface(IUnknown)
  ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
  end;

  IMMDeviceEnumerator = interface(IUnknown)
  ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    function EnumAudioEndpoints(dataFlow: TOleEnum; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): HRESULT; stdcall;
    function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): HRESULT; stdcall;
    function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
    function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

implementation

end.

Upvotes: 4

Related Questions