3

オーディオ セッションを監視するアプリケーションを作成しようとしています (SndVol のように)。IAudioSessionManager2 をアクティブ化し、IAudioSessionEnumerator を介して現在のオーディオ セッションのリストを取得し、オーディオ セッション マネージャーの RegisterSessionNotification メソッドを使用して通知用に登録しました。ただし、私のアプリケーションは通知 OnSessionCreated() を受け取りません。もちろん、私はここで同様のトピックを読みました: Notifications not sent , IAudioSessionNotification

すべての回答を注意深く読みましたが、間違っていることを見つけることができませんでした。これが私のコードです(Delphi):

TDNAudioSessionManager = class(TDNUnknownObject, IAudioSessionNotification)
private
  FManager: IAudioSessionManager2;
  FList: TDNAudioSessionList;
  function IsManagerValid: Boolean;
protected
  procedure ActivateSessionManager(out AManager: IAudioSessionManager2);
  procedure GetCurrentSessions;
  // IAudioSessionNotification
  function OnSessionCreated(ANewSession: IAudioSessionControl): HRESULT; stdcall;
public
  constructor Create;
  destructor Destroy; override;
end;

constructor TDNAudioSessionManager.Create;
begin
  inherited Create;
  ActivateSessionManager(FManager);
  if IsManagerValid then
  begin
    FList := TDNAudioSessionList.Create;
    FManager.RegisterSessionNotification(Self);
    GetCurrentSessions;
   end;
 end;

destructor TDNAudioSessionManager.Destroy;
begin
  if IsManagerValid then
  begin
    FManager.UnregisterSessionNotification(Self);
    FManager := nil;
  end;
  FreeAndNil(FList);
  inherited Destroy;
 end;

function TDNAudioSessionManager.IsManagerValid: Boolean;
begin
  Result := Assigned(FManager);
end;

procedure TDNAudioSessionManager.GetCurrentSessions;
var
  AEnumerator: IAudioSessionEnumerator;
  ASession: IAudioSessionControl;
  ACount: Integer;
  I: Integer;
begin
  if IsManagerValid then
    if FManager.GetSessionEnumerator(AEnumerator) = S_OK then
    try
      AEnumerator.GetCount(ACount);
      for I := 0 to ACount - 1 do
      begin
        AEnumerator.GetSession(I, ASession);
        FList.Add(ASession);
      end;
    finally
      AEnumerator := nil;
    end;
end;

function TDNAudioSessionManager.OnSessionCreated(ANewSession: IAudioSessionControl): HRESULT;
begin
  FList.Add(ANewSession);
  Result := S_OK;
end;

procedure TDNAudioSessionManager.ActivateSessionManager(out AManager: IAudioSessionManager2);
var
  AEnumerator: IMMDeviceEnumerator;
  ADefault: IMMDevice;
  ARes: HRESULT;
begin
  AManager := nil;
  ARes := CoCreateInstance(CLSID_MMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, AEnumerator);
  if not ARes = S_OK then Exit;
  if AEnumerator.GetDefaultAudioEndpoint(eRender, eConsole,  ADefault) = S_OK then
  try
    ADefault.Activate(IID_IAudioSessionManager2, CLSCTX_INPROC_SERVER, nil, AManager);
  finally
    ADefault := nil;
  end;
end;

initialization
  CoInitializeEx(nil, COINIT_MULTITHREADED);
end.

注: TDNUnknownObject は、IUnknown メソッドを実装するクラスです。

もう 1 つの質問: アプリケーションが閉じられたときにオーディオ セッションが送信するイベントは何ですか? (SndVol ではリストから削除されます)。OnSessionDisconnected、OnStateChanged (状態 AudioSessionExpired) を試しましたが、アプリもそれらを受信しません。

前もって感謝します!

TDNUnknownObject:

TDNUnknownObject = class(TObject, IUnknown)
protected
  // IUnknown
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;
  function QueryInterface(const IID: TGUID; out Obj): HRESULT; virtual; stdcall;
end;

function TDNUnknownObject._AddRef: Integer; stdcall;
begin
  Result := -1;
end;

function TDNUnknownObject._Release: Integer; stdcall;
begin
  Result := -1;
end;

function TDNUnknownObject.QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
begin
  if GetInterface(IID, Obj) then
    Result := S_OK
  else
    Result := E_NOINTERFACE;
end;
4

1 に答える 1

-1

さて、私はこれについて頭を悩ませています.. 最初の質問が出てきます: TInterfacedObject を使用しないのはなぜですか? 多くの作業を節約し、正しく動作することが証明されています。このオブジェクトは IUnknown から派生し、IInterface にエイリアスされており、まさにここでしようとしていることを実行します。もちろん、独自のバージョンを作成することもできますが、Delphi コンパイラはより厳密になるため、二重実装や名前の競合などの落とし穴に簡単に直面することに注意してください。これらが原因で、コンパイラ、リンカ、およびデバッガが奇妙な動作をすることがあります。

MS からの短いメモ: 非 UI スレッドで CoInitializeEx(Nil, COINIT_MULTITHREADED) を呼び出して、アプリケーションがマルチスレッド アパートメント (MTA) モデルで COM を初期化することを確認してください。MTA が初期化されていない場合、アプリケーションはセッション マネージャーからセッション通知を受信しません。アプリケーションのユーザー インターフェイスを実行するスレッドは、アパートメント スレッド モデルで初期化する必要があります。

ほら、あなたの初期化セクションが正しくありません。そのはず:

initialization
  CoInitializeEx(Nil,
                 COINIT_APARTMENTTHREADED);
于 2016-08-21T21:06:35.660 に答える