From: Akihiro Sagawa Subject: [1/4] mciqtz32: Guarantees that CoInitialize and CoUninitialize work on the same thread. Message-Id: <20150310004612.DBD9.375B48EC@gmail.com> Date: Tue, 10 Mar 2015 00:50:53 +0900 In short, mciOpen and mciClose (both for the same device) can call in different threads. Without this patch, mciqtz32 may call CoUninitialize (mciClose) in another thread which doesn't call CoInitialize (mciOpen). This patch prevents Open General[1]'s following message: err:ole:CoUninitialize Mismatched CoUninitialize [1] http://sourceforge.net/projects/opengeneral/ --- dlls/mciqtz32/mciqtz.c | 177 +++++++++++++++++++++++++++++++++++------ dlls/mciqtz32/mciqtz_private.h | 14 +++- 2 files changed, 166 insertions(+), 25 deletions(-) diff --git a/dlls/mciqtz32/mciqtz.c b/dlls/mciqtz32/mciqtz.c index a72f1af..ec6adbc 100644 --- a/dlls/mciqtz32/mciqtz.c +++ b/dlls/mciqtz32/mciqtz.c @@ -30,7 +30,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(mciqtz); -static DWORD MCIQTZ_mciClose(UINT, DWORD, LPMCI_GENERIC_PARMS); +static DWORD CALLBACK MCIQTZ_taskThread(LPVOID arg); static DWORD MCIQTZ_mciStop(UINT, DWORD, LPMCI_GENERIC_PARMS); /*======================================================================* @@ -67,6 +67,34 @@ static WINE_MCIQTZ* MCIQTZ_mciGetOpenDev(UINT wDevID) return wma; } +/*************************************************************************** + * MCIQTZ_sendTaskMessage [internal] + */ +static LRESULT MCIQTZ_sendTaskMessage(DWORD_PTR dwDevID, UINT wMsg, + DWORD dwFlags, LPARAM lpParms) +{ + WINE_MCIQTZ *wma; + LRESULT res; + TRACE("(%08lX, %08x, %08x, %08lx)\n", dwDevID, wMsg, dwFlags, lpParms); + + wma = MCIQTZ_mciGetOpenDev(dwDevID); + if (!wma) + return MCIERR_INVALID_DEVICE_ID; + EnterCriticalSection(&wma->cs); + wma->task.devid = dwDevID; + wma->task.msg = wMsg; + wma->task.flags = dwFlags; + wma->task.parms = lpParms; + SetEvent(wma->task.start); + if (WaitForSingleObject(wma->task.sync, INFINITE) == WAIT_OBJECT_0) + res = wma->task.res; + else + res = MCIERR_INTERNAL; + LeaveCriticalSection(&wma->cs); + + return res; +} + /************************************************************************** * MCIQTZ_drvOpen [internal] */ @@ -85,12 +113,25 @@ static DWORD MCIQTZ_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) if (!wma) return 0; + wma->task.start = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!wma->task.start) goto err; + wma->task.sync = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!wma->task.sync) goto err; + wma->task.thread = CreateThread(NULL, 0, MCIQTZ_taskThread, &wma->task, 0, NULL); + if (!wma->task.thread) goto err; + InitializeCriticalSection(&wma->cs); + wma->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINE_MCIQTZ"); modp->wType = MCI_DEVTYPE_DIGITAL_VIDEO; wma->wDevID = modp->wDeviceID; modp->wCustomCommandTable = wma->command_table = mciLoadCommandResource(MCIQTZ_hInstance, mciAviWStr, 0); mciSetDriverData(wma->wDevID, (DWORD_PTR)wma); return modp->wDeviceID; +err: + if (wma->task.start) CloseHandle(wma->task.start); + if (wma->task.sync) CloseHandle(wma->task.sync); + HeapFree(GetProcessHeap(), 0, wma); + return 0; } /************************************************************************** @@ -106,9 +147,15 @@ static DWORD MCIQTZ_drvClose(DWORD dwDevID) if (wma) { /* finish all outstanding things */ - MCIQTZ_mciClose(dwDevID, MCI_WAIT, NULL); + MCIQTZ_sendTaskMessage(dwDevID, MCI_CLOSE_DRIVER, MCI_WAIT, 0); mciFreeCommandResource(wma->command_table); + MCIQTZ_sendTaskMessage(dwDevID, MCI_CLOSE, MCI_WAIT, 0); + WaitForSingleObject(wma->task.thread, INFINITE); + CloseHandle(wma->task.start); + CloseHandle(wma->task.sync); + CloseHandle(wma->task.thread); + DeleteCriticalSection(&wma->cs); mciSetDriverData(dwDevID, 0); HeapFree(GetProcessHeap(), 0, wma); return 1; @@ -159,9 +206,6 @@ static DWORD MCIQTZ_mciOpen(UINT wDevID, DWORD dwFlags, MCIQTZ_mciStop(wDevID, MCI_WAIT, NULL); - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - wma->uninit = SUCCEEDED(hr); - hr = CoCreateInstance(&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (LPVOID*)&wma->pgraph); if (FAILED(hr)) { TRACE("Cannot create filtergraph (hr = %x)\n", hr); @@ -268,10 +312,6 @@ err: IMediaControl_Release(wma->pmctrl); wma->pmctrl = NULL; - if (wma->uninit) - CoUninitialize(); - wma->uninit = FALSE; - return MCIERR_INTERNAL; } @@ -298,8 +338,6 @@ static DWORD MCIQTZ_mciClose(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpP IMediaEvent_Release(wma->mevent); IGraphBuilder_Release(wma->pgraph); IMediaControl_Release(wma->pmctrl); - if (wma->uninit) - CoUninitialize(); wma->opened = FALSE; } @@ -947,6 +985,94 @@ static DWORD MCIQTZ_mciSetAudio(UINT wDevID, DWORD dwFlags, LPMCI_DGV_SETAUDIO_P return ret; } +/*************************************************************************** + * MCIQTZ_taskThread [internal] + */ +static DWORD CALLBACK MCIQTZ_taskThread(LPVOID arg) +{ + HRESULT hr; + WINE_MCIQTZ_TASK *task = (WINE_MCIQTZ_TASK *)arg; + + hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + + for (;;) { + DWORD ret = WaitForSingleObject(task->start, INFINITE); + if (ret != WAIT_OBJECT_0) { + TRACE("Got error (%u)\n", ret); + continue; + } + if (task->msg == MCI_CLOSE) { + /* Special message (internal) */ + SetEvent(task->sync); + break; + } + + switch (task->msg) { + case MCI_OPEN_DRIVER: + task->res = MCIQTZ_mciOpen(task->devid, task->flags, + (LPMCI_DGV_OPEN_PARMSW)task->parms); + break; + case MCI_CLOSE_DRIVER: + task->res = MCIQTZ_mciClose(task->devid, task->flags, + (LPMCI_GENERIC_PARMS)task->parms); + break; + case MCI_PLAY: + task->res = MCIQTZ_mciPlay(task->devid, task->flags, + (LPMCI_PLAY_PARMS)task->parms); + break; + case MCI_SEEK: + task->res = MCIQTZ_mciSeek(task->devid, task->flags, + (LPMCI_SEEK_PARMS)task->parms); + break; + case MCI_STOP: + task->res = MCIQTZ_mciStop(task->devid, task->flags, + (LPMCI_GENERIC_PARMS)task->parms); + break; + case MCI_PAUSE: + task->res = MCIQTZ_mciPause(task->devid, task->flags, + (LPMCI_GENERIC_PARMS)task->parms); + break; + case MCI_GETDEVCAPS: + task->res = MCIQTZ_mciGetDevCaps(task->devid, task->flags, + (LPMCI_GETDEVCAPS_PARMS)task->parms); + break; + case MCI_SET: + task->res = MCIQTZ_mciSet(task->devid, task->flags, + (LPMCI_DGV_SET_PARMS)task->parms); + break; + case MCI_STATUS: + task->res = MCIQTZ_mciStatus(task->devid, task->flags, + (LPMCI_DGV_STATUS_PARMSW)task->parms); + break; + case MCI_WHERE: + task->res = MCIQTZ_mciWhere(task->devid, task->flags, + (LPMCI_DGV_RECT_PARMS)task->parms); + break; + case MCI_SETAUDIO: + task->res = MCIQTZ_mciSetAudio(task->devid, task->flags, + (LPMCI_DGV_SETAUDIO_PARMSW)task->parms); + break; + case MCI_UPDATE: + task->res = MCIQTZ_mciUpdate(task->devid, task->flags, + (LPMCI_DGV_UPDATE_PARMS)task->parms); + break; + case MCI_WINDOW: + task->res = MCIQTZ_mciWindow(task->devid, task->flags, + (LPMCI_DGV_WINDOW_PARMSW)task->parms); + break; + default: + FIXME("Shouldn't receive another message (%04x)\n", task->msg); + task->res = MCIERR_UNRECOGNIZED_COMMAND; + } + SetEvent(task->sync); + } + + if (SUCCEEDED(hr)) + CoUninitialize(); + + return 0; +} + /*======================================================================* * MCI QTZ entry points * *======================================================================*/ @@ -978,22 +1104,25 @@ LRESULT CALLBACK MCIQTZ_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, return 1; switch (wMsg) { - case MCI_OPEN_DRIVER: return MCIQTZ_mciOpen (dwDevID, dwParam1, (LPMCI_DGV_OPEN_PARMSW) dwParam2); - case MCI_CLOSE_DRIVER: return MCIQTZ_mciClose (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); - case MCI_PLAY: return MCIQTZ_mciPlay (dwDevID, dwParam1, (LPMCI_PLAY_PARMS) dwParam2); - case MCI_SEEK: return MCIQTZ_mciSeek (dwDevID, dwParam1, (LPMCI_SEEK_PARMS) dwParam2); - case MCI_STOP: return MCIQTZ_mciStop (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); - case MCI_PAUSE: return MCIQTZ_mciPause (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); - case MCI_GETDEVCAPS: return MCIQTZ_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS) dwParam2); - case MCI_SET: return MCIQTZ_mciSet (dwDevID, dwParam1, (LPMCI_DGV_SET_PARMS) dwParam2); - case MCI_STATUS: return MCIQTZ_mciStatus (dwDevID, dwParam1, (LPMCI_DGV_STATUS_PARMSW) dwParam2); - case MCI_WHERE: return MCIQTZ_mciWhere (dwDevID, dwParam1, (LPMCI_DGV_RECT_PARMS) dwParam2); + case MCI_OPEN_DRIVER: + case MCI_PLAY: + case MCI_SEEK: + case MCI_GETDEVCAPS: + case MCI_SET: + case MCI_STATUS: + case MCI_WHERE: + if (!dwParam2) return MCIERR_NULL_PARAMETER_BLOCK; + return MCIQTZ_sendTaskMessage(dwDevID, wMsg, dwParam1, dwParam2); + case MCI_CLOSE_DRIVER: + case MCI_STOP: + case MCI_PAUSE: + return MCIQTZ_sendTaskMessage(dwDevID, wMsg, dwParam1, dwParam2); /* Digital Video specific */ - case MCI_SETAUDIO: return MCIQTZ_mciSetAudio (dwDevID, dwParam1, (LPMCI_DGV_SETAUDIO_PARMSW) dwParam2); + case MCI_SETAUDIO: case MCI_UPDATE: - return MCIQTZ_mciUpdate(dwDevID, dwParam1, (LPMCI_DGV_UPDATE_PARMS)dwParam2); case MCI_WINDOW: - return MCIQTZ_mciWindow(dwDevID, dwParam1, (LPMCI_DGV_WINDOW_PARMSW)dwParam2); + if (!dwParam2) return MCIERR_NULL_PARAMETER_BLOCK; + return MCIQTZ_sendTaskMessage(dwDevID, wMsg, dwParam1, dwParam2); case MCI_PUT: case MCI_RECORD: case MCI_RESUME: diff --git a/dlls/mciqtz32/mciqtz_private.h b/dlls/mciqtz32/mciqtz_private.h index 7263a26..939133f 100644 --- a/dlls/mciqtz32/mciqtz_private.h +++ b/dlls/mciqtz32/mciqtz_private.h @@ -26,9 +26,19 @@ #include "dshow.h" typedef struct { + HANDLE thread; + HANDLE start; + HANDLE sync; + DWORD msg; + DWORD_PTR devid; + DWORD flags; + DWORD_PTR parms; + LRESULT res; +} WINE_MCIQTZ_TASK; + +typedef struct { MCIDEVICEID wDevID; BOOL opened; - BOOL uninit; IGraphBuilder* pgraph; IMediaControl* pmctrl; IMediaSeeking* seek; @@ -39,6 +49,8 @@ typedef struct { DWORD time_format; UINT command_table; HWND parent; + CRITICAL_SECTION cs; + WINE_MCIQTZ_TASK task; } WINE_MCIQTZ; #endif /* __WINE_PRIVATE_MCIQTZ_H */