學習事件發生時
[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayer、 IMFMediaEngine和 Media Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayer、 IMFMediaEngine 和 音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。
若要處理 DirectShow 事件,應用程式需要一種方式來找出事件在佇列中等候時。 Filter Graph 管理員提供兩種方式來執行此動作:
- 視窗通知: 每當有新的事件時,Filter Graph 管理員就會將使用者定義的 Windows 訊息傳送至應用程式視窗。
- 事件訊號: 如果佇列中有 DirectShow 事件,則 Filter Graph 管理員會發出 Windows 事件的訊號,並在佇列是空的時重設事件。
應用程式可以使用任一技術。 視窗通知通常更簡單。
視窗通知
若要設定視窗通知,請呼叫 IMediaEventEx::SetNotifyWindow 方法並指定私人訊息。 應用程式可以使用範圍中的訊息號碼,從WM_APP到0xBFFF作為私人訊息。 每當 Filter Graph Manager 在佇列中放置新的事件通知時,就會將此訊息張貼到指定的視窗。 應用程式會從視窗的訊息迴圈內回應訊息。
下列程式碼範例示範如何設定通知視窗。
#define WM_GRAPHNOTIFY WM_APP + 1 // Private message.
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(); // Application-defined function.
break;
// Handle other Windows messages here too.
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
因為事件通知和訊息迴圈都是非同步,所以佇列可能會在應用程式回應訊息時包含一個以上的事件。 此外,如果事件變成無效,有時可能會從佇列中清除。 因此,在您的事件處理常式代碼中,呼叫 IAMMediaEvent::GetEvent 直到傳回失敗碼為止,指出佇列是空的。
在釋放IMediaEventEx指標之前,請使用Null指標呼叫SetNotifyWindow來取消事件通知。 在事件處理常式代碼中,請先檢查 IMediaEventEx 指標是否有效,再呼叫 GetEvent。 這些步驟可防止發生錯誤,在此錯誤中,應用程式會在釋放 IMediaEventEx 指標之後收到事件通知。
事件信號
Filter Graph Manager 會保留手動重設事件,以反映事件佇列的狀態。 如果佇列包含擱置的事件通知,篩選圖形管理員會發出手動重設事件的訊號。 如果佇列是空的, 則 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))
{
/* Insert failure-handling code here. */
}
while(!bDone)
{
if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 100))
{
while (S_OK == pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0))
{
printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
pEvent->FreeEventParams(evCode, param1, param2);
bDone = (EC_COMPLETE == evCode);
}
}
}
因為篩選圖形會在適當時自動設定或重設事件,所以您的應用程式不應該這麼做。 此外,當您釋放篩選圖形時,篩選圖表會關閉事件控制碼,因此請勿在該點之後使用事件控制碼。