了解事件发生时

[与此页面关联的功能 DirectShow 是旧版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获所取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能在 Media Foundation 中使用 MediaPlayerIMFMediaEngine音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]

若要处理 DirectShow 事件,应用程序需要一种方法来查明事件何时在队列中等待。 Filter Graph 管理器提供了两种执行此操作的方法:

  • 窗口通知: 每当有新事件时,Filter Graph 管理器会将用户定义的 Windows 消息发送到应用程序窗口。
  • 事件信号: 如果队列中有 DirectShow 事件,Filter Graph 管理器会发出 Windows 事件信号,如果队列为空,则重置事件。

应用程序可以使用任一技术。 窗口通知通常更简单。

窗口通知

若要设置窗口通知,请调用 IMediaEventEx::SetNotifyWindow 方法并指定私人消息。 应用程序可以使用从WM_APP到0xBFFF范围内的消息号作为私人消息。 每当 Filter Graph 管理器在队列中放置新事件通知时,它会将此消息发布到指定的窗口。 应用程序从窗口的消息循环中响应消息。

下面的代码示例演示如何设置通知窗口。

#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 管理器保留一个反映事件队列状态的手动重置事件。 如果队列包含挂起的事件通知,Filter Graph 管理器会发出手动重置事件的信号。 如果队列为空,则对 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, &param1, &param2, 0)) 
        {
            printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
            pEvent->FreeEventParams(evCode, param1, param2);
            bDone = (EC_COMPLETE == evCode);
        }
    }
} 

由于筛选器图会在适当时自动设置或重置事件,因此应用程序不应这样做。 此外,释放筛选器图时,筛选器图将关闭事件句柄,因此不要在该点之后使用事件句柄。