Learning When an Event Occurs
A version of this page is also available for
4/8/2010
In addition to retrieving event notifications from the queue, as discussed in Retrieving Events, an application needs a way to find out when events are waiting in the queue. The Filter Graph Manager provides two ways to do this.
- Using window notification, the Filter Graph Manager sends a Windows message to an application window whenever there is a new event.
- Using event handles, the application retrieves a handle to a Windows manual-reset event. The Filter Graph Manager signals the manual-reset event when there are event notifications in the queue and resets it when the queue is empty.
The following sections describe each technique.
- Window Notification
- Event Handles
Window Notification
To set up window notification, call the IMediaEventEx::SetNotifyWindow method and specify a private message. Applications can use message numbers in the range from WM_APP through 0xBFFF as private messages. Whenever the Filter Graph Manager places a new event notification in the queue, it posts this message to the designated window. The application responds to the message from within the window's message loop.
The following code example shows how to set the notification window.
#define WM_GRAPHNOTIFY WM_APP + 1 // Private message.
pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
The message is an ordinary Windows message, and is posted separately from the DirectShow event notification queue. The advantage of this approach is that most applications already implement a message loop. Therefore, you can incorporate DirectShow event handling without much additional work.
The following code example shows an outline of how to respond to the notification message. For a complete example, see Responding to Events.
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));
}
Because event notification and the message loop are both asynchronous, the queue might contain more than one event by the time your application responds to the message. Also, events can sometimes be cleared from the queue if they become invalid. Therefore, in your event handling code, call GetEvent until it returns a failure code, indicating that the queue is empty.
Event Handles
The filter graph keeps a manual-reset event that reflects the state of the event queue. If the queue contains pending event notifications, the filter graph signals the manual-reset event. If the queue is empty, a call to the IMediaEvent::GetEvent method resets the event. An application can use this event to determine the state of the queue.
Note
The terminology can be confusing here. The manual-reset event is the type of event created by the Windows CreateEvent function; it has nothing to do with the event notifications defined by DirectShow.
The IMediaEvent::GetEventHandle method retrieves a handle to the manual-reset event. Wait for the event to be signaled by calling a function such as WaitForMultipleObjects. Once the event is signaled, call the IMediaEvent::GetEvent method to retrieve the event notification.
The following code example illustrates this approach. It retrieves the event handle, then waits in 100-millisecond intervals for the event to be signaled. If the event is signaled, it calls GetEvent and prints the event code and event parameters to the console window. The loop terminates when the EC_USERABORT event occurs, indicating that the user has interrupted playback. This example uses the DirectShow CAMEvent class, a helper class that represents a manual-reset or automatic-reset event. The CAMEvent::Wait method waits until the event is signaled or a specified timeout elapses.
CAMEvent hEvent;
long evCode, param1, param2;
BOOLEAN bDone = FALSE;
HRESULT hr = pEvent->GetEventHandle((OAEVENT*)&hEvent);
while(!bDone) {
if (hEvent.Wait(100)) {
if (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr)) {
printf("Event code: %#04x\n\tParams: %d, %d\n", evCode, param1, param2);
hr = pEvent->FreeEventParams(evCode, param1, param2);
bDone = ((EC_COMPLETE == evCode) || (EC_USERABORT == evCode));
}
}
}
Because the filter graph automatically sets or resets the event when appropriate, your application should not do so. Also, when you release the filter graph, the filter graph closes the event handle, so do not use the event handle after that point.