Aufrufen asynchroner Methoden
In Media Foundation werden viele Vorgänge asynchron ausgeführt. Asynchrone Vorgänge können die Leistung verbessern, da sie den aufrufenden Thread nicht blockieren. Ein asynchroner Vorgang in Media Foundation wird durch ein Methodenpaar mit der Benennungskonvention Begin... und End....definiert. Zusammen definieren diese beiden Methoden einen asynchronen Lesevorgang für den Stream.
Um einen asynchronen Vorgang zu starten, rufen Sie die Begin-Methode auf. Die Begin-Methode enthält immer mindestens zwei Parameter:
- Ein Zeiger auf die IMFAsyncCallback-Schnittstelle . Die Anwendung muss diese Schnittstelle implementieren.
- Ein Zeiger auf ein optionales Zustandsobjekt.
Die IMFAsyncCallback-Schnittstelle benachrichtigt die Anwendung, wenn der Vorgang abgeschlossen ist. Ein Beispiel für die Implementierung dieser Schnittstelle finden Sie unter Implementieren des asynchronen Rückrufs. Das Zustandsobjekt ist optional. Falls angegeben, muss die IUnknown-Schnittstelle implementiert werden. Sie können das Zustandsobjekt verwenden, um alle Zustandsinformationen zu speichern, die für Ihren Rückruf benötigt werden. Wenn Sie keine Zustandsinformationen benötigen, können Sie diesen Parameter auf NULL festlegen. Die Begin-Methode kann zusätzliche Parameter enthalten, die für diese Methode spezifisch sind.
Wenn die Begin-Methode einen Fehlercode zurückgibt, bedeutet dies, dass der Vorgang sofort fehlgeschlagen ist und der Rückruf nicht aufgerufen wird. Wenn die Begin-Methode erfolgreich ist, bedeutet dies, dass der Vorgang aussteht und der Rückruf nach Abschluss des Vorgangs aufgerufen wird.
Die IMFByteStream::BeginRead-Methode startet beispielsweise einen asynchronen Lesevorgang für einen Bytestream:
BYTE data[DATA_SIZE];
ULONG cbRead = 0;
CMyCallback *pCB = new (std::nothrow) CMyCallback(pStream, &hr);
if (pCB == NULL)
{
hr = E_OUTOFMEMORY;
}
// Start an asynchronous request to read data.
if (SUCCEEDED(hr))
{
hr = pStream->BeginRead(data, DATA_SIZE, pCB, NULL);
}
Die Rückrufmethode ist IMFAsyncCallback::Invoke. Es verfügt über einen einzelnen Parameter, pAsyncResult, der ein Zeiger auf die IMFAsyncResult-Schnittstelle ist. Um den asynchronen Vorgang abzuschließen, rufen Sie die End-Methode auf, die mit der asynchronen Begin-Methode übereinstimmt. Die End-Methode verwendet immer einen IMFAsyncResult-Zeiger . Übergeben Sie immer denselben Zeiger, den Sie in der Invoke-Methode erhalten haben. Der asynchrone Vorgang ist erst abgeschlossen, wenn Sie die End-Methode aufrufen. Sie können die End-Methode aus der Invoke-Methode oder aus einem anderen Thread aufrufen.
Um beispielsweise den IMFByteStream::BeginRead für einen Bytestream abzuschließen, rufen Sie IMFByteStream::EndRead auf:
STDMETHODIMP Invoke(IMFAsyncResult* pResult)
{
m_hrStatus = m_pStream->EndRead(pResult, &m_cbRead);
SetEvent(m_hEvent);
return S_OK;
}
Der Rückgabewert der End-Methode gibt die status des asynchronen Vorgangs an. In den meisten Fällen führt Ihre Anwendung nach Abschluss des asynchronen Vorgangs einige Aufgaben aus, unabhängig davon, ob er erfolgreich abgeschlossen wurde oder nicht. Sie haben mehrere Möglichkeiten:
- Führen Sie die Arbeit innerhalb der Invoke-Methode aus. Dieser Ansatz ist akzeptabel, solange Ihre Anwendung niemals den aufrufenden Thread blockiert oder innerhalb der Invoke-Methode auf etwas wartet. Wenn Sie den aufrufenden Thread blockieren, können Sie die Streamingpipeline blockieren. Beispielsweise können Sie einige Zustandsvariablen aktualisieren, aber keinen synchronen Lesevorgang ausführen oder auf ein Mutex-Objekt warten.
- Legen Sie in der Invoke-Methode ein Ereignis fest, und geben Sie sofort zurück. Der aufrufende Thread der Anwendung wartet auf das Ereignis und erledigt die Arbeit, wenn das Ereignis signalisiert wird.
- Posten Sie in der Invoke-Methode eine Meldung über ein privates Fenster in Ihrem Anwendungsfenster, und kehren Sie sofort zurück. Die Anwendung führt die Arbeit an ihrer Nachrichtenschleife aus. (Stellen Sie sicher, dass Sie die Nachricht posten, indem Sie PostMessage und nicht SendMessage aufrufen, da SendMessage den Rückrufthread blockieren kann.)
Beachten Sie, dass Invoke von einem anderen Thread aufgerufen wird. Ihre Anwendung ist dafür verantwortlich, sicherzustellen, dass alle in Invoke ausgeführten Arbeiten threadsicher sind.
Standardmäßig macht der Thread, der Invoke aufruft, keine Annahmen über das Verhalten Ihres Rückrufobjekts. Optional können Sie die IMFAsyncCallback::GetParameters-Methode implementieren, um Informationen zu Ihrer Rückrufimplementierung zurückzugeben. Andernfalls kann diese Methode E_NOTIMPL zurückgeben:
STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
Wenn Sie ein Zustandsobjekt in der Begin-Methode angegeben haben, können Sie es in Ihrer Rückrufmethode abrufen, indem Sie IMFAsyncResult::GetState aufrufen.