イベント発生時の確認
DirectShow のイベントを処理するために、アプリケーションにはイベントがキュー内で待機状態であることを検出する方法が必要である。フィルタ グラフ マネージャはそのために 2 つの方法を提供している。
- ウィンドウ通知 :フィルタ グラフ マネージャは、新しいイベントがあるときには常にユーザー定義の Windows メッセージをアプリケーション ウィンドウに送信する。
- イベント送信 :フィルタ グラフ マネージャは、キューに DirectShow イベントがあるときは Windows イベントを送信し、キューが空になるとそのイベントをリセットする。
アプリケーションでは、いずれかの手法を使える。通常、ウィンドウ通知の方が単純である。
ウィンドウ通知
ウィンドウ通知をセットアップするには、IMediaEventEx::SetNotifyWindow メソッドを呼び出し、プライベート メッセージを指定する。アプリケーションでは、WM_APP から 0xBFFF までの範囲のメッセージ番号をプライベート メッセージとして使える。フィルタ グラフ マネージャは、新しいイベント通知をキューに入れると、指定されたウィンドウにこのメッセージを送信する。アプリケーションはウィンドウのメッセージ ループ内からメッセージに応答する。
次のサンプル コードは、通知ウィンドウの設定方法を示している。
#define WM_GRAPHNOTIFY WM_APP + 1 // プライベート メッセージ。
pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
メッセージは通常の Windows メッセージであり、DirectShow のイベント通知キューからは独立して送信される。この方法の利点は、ほとんどのアプリケーションが既にメッセージ ループを実装しているため、多くの追加作業を必要とせずに DirectShow のイベント処理を組み込める点である。
次のサンプル コードは、通知メッセージに応答する方法の概要を示している。完全な例については、「イベントへの応答」を参照すること。
LRESULT CALLBACK WindowProc( HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
switch (msg)
{
case WM_GRAPHNOTIFY:
HandleEvent(); // アプリケーション定義の関数。
break;
// ここで他の Windows メッセージも処理する。
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
イベント通知とメッセージ ループは両方とも非同期であるため、アプリケーションがメッセージに応答するときまでに、複数のイベントがキューに入っていることがある。また、イベントが無効になったときはキューからイベントがクリアされることがある。したがって、イベント処理コードでは、キューが空であることを示すエラー コードが返されるまで GetEvent を呼び出すこと。
IMediaEventEx ポインタを解放する前に、NULL ポインタを指定して SetNotifyWindow を呼び出すことによってイベント通知を取り消す。イベント処理コードでは、GetEvent を呼び出す前に、IMediaEventEx ポインタが有効であるかどうかをチェックすること。これらの手順によって、アプリケーションが IMediaEventEx ポインタを解放した後でイベント通知を受信するというエラーを防止できる。
イベント送信
フィルタ グラフ マネージャはイベント キューの状態を反映した手動リセット イベントを保持する。キューにペンディング状態のイベント通知が含まれている場合、フィルタ グラフ マネージャは手動リセット イベントを送信する。キューが空の場合は、IMediaEvent::GetEvent メソッドを呼び出すとイベントがリセットされる。アプリケーションはこのイベントを使ってキューの状態を確認できる。
注 : ここで用語について混乱しないこと。手動リセット イベントは、Windows の CreateEvent 関数によって作成されるタイプのイベントであり、DirectShow で定義されているイベントとは無関係である。
手動リセット イベントのハンドルを取得するには、IMediaEvent::GetEventHandle メソッドを呼び出す。WaitForMultipleObjects などの関数を呼び出して、イベントが通知済みになるまで待機する。イベントが通知済みになったら IMediaEvent::GetEvent を呼び出して DirectShow イベントを取得する。
次のサンプル コードは、この処理方法を示している。イベント ハンドルを取得し、イベントが通知済みになるのを 100 ミリ秒間隔で待機する。イベントが通知済みになると、GetEvent を呼び出してイベント コードとイベント パラメータをコンソール ウィンドウに出力する。再生が完了したことを示す EC_COMPLETE イベントが発生すると、ループが終了する。
HANDLE hEvent;
long evCode, param1, param2;
BOOLEAN bDone = FALSE;
HRESULT hr = S_OK;
hr = pEvent->GetEventHandle((OAEVENT*)&hEvent);
if (FAILED(hr)
{
/* エラー処理コードをここに挿入する。 */
}
while(!bDone)
{
if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 100))
{
while (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr))
{
printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
pEvent->FreeEventParams(evCode, param1, param2);
bDone = (EC_COMPLETE == evCode);
}
}
}
フィルタ グラフが必要に応じてイベントを自動的に設定またはリセットするので、アプリケーションでイベントの設定またはリセットを行うべきではない。さらに、フィルタ グラフを解放するとフィルタ グラフはイベント ハンドルを閉じる。そのため、その後はイベント ハンドルを使わないこと。