#include "pa_debugprint.h"
#include "pa_ringbuffer.h"
+#include "pa_win_coinitialize.h"
+
/* This version of pa_asio.cpp is currently only targetted at Win32,
It would require a few tweaks to work with pre-OS X Macintosh.
To make configuration easier, we define WIN32 here to make sure
PaUtilAllocationGroup *allocations;
+ PaWinUtilComInitializationResult comInitializationResult;
+
AsioDrivers *asioDrivers;
void *systemSpecific;
}
/* Unload whatever we loaded in LoadAsioDriver().
- Also balance the call to CoInitialize(0).
*/
static void UnloadAsioDriver( void )
{
ASIOExit();
- CoUninitialize();
}
/*
ASIOError asioError;
int asioIsInitialized = 0;
- /*
- ASIO uses CoCreateInstance() to load a driver. That requires that
- CoInitialize(0) be called for every thread that loads a driver.
- It is OK to call CoInitialize(0) multiple times form one thread as long
- as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver().
-
- The V18 version called CoInitialize() starting on 2/19/02.
- That was removed from PA V19 for unknown reasons.
- Phil Burk added it back on 6/27/08 so that JSyn would work.
- */
- CoInitialize( 0 );
-
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
{
- /* If this returns an error then it might be because CoInitialize(0) was removed.
- It should be called right before this.
- */
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
goto error;
{
ASIOExit();
}
- CoUninitialize();
+
return result;
}
goto error;
}
+ /*
+ We initialize COM ourselves here and uninitialize it in Terminate().
+ This should be the only COM initialization needed in this module.
+
+ The ASIO SDK may also initialize COM but since we want to reduce dependency
+ on the ASIO SDK we manage COM initialization ourselves.
+
+ There used to be code that initialized COM in other situations
+ such as when creating a Stream. This made PA work when calling Pa_CreateStream
+ from a non-main thread. However we currently consider initialization
+ of COM in non-main threads to be the caller's responsibility.
+ */
+ result = PaWinUtil_CoInitialize( paASIO, &asioHostApi->comInitializationResult );
+ if( result != paNoError )
+ {
+ goto error;
+ }
+
asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
asioHostApi->allocations = PaUtil_CreateAllocationGroup();
/* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
try
{
- asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */
+ asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */
}
catch (std::bad_alloc)
{
delete asioHostApi->asioDrivers;
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
+ PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
+
PaUtil_FreeMemory( asioHostApi );
}
+
return result;
}
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
}
- delete asioHostApi->asioDrivers; /* calls CoUninitialize() */
+ delete asioHostApi->asioDrivers;
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
+ PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
+
PaUtil_FreeMemory( asioHostApi );
}
int asioIsInitialized = 0;
PaAsioHostApiRepresentation *asioHostApi;
PaAsioDeviceInfo *asioDeviceInfo;
+ PaWinUtilComInitializationResult comInitializationResult;
+ /* initialize COM again here, we might be in another thread */
+ result = PaWinUtil_CoInitialize( paASIO, &comInitializationResult );
+ if( result != paNoError )
+ return result;
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
if( result != paNoError )
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
- /* See notes about CoInitialize(0) in LoadAsioDriver(). */
- CoInitialize(0);
-
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
{
result = paUnanticipatedHostError;
goto error;
}
- CoUninitialize();
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
return result;
{
ASIOExit();
}
- CoUninitialize();
+
+ PaWinUtil_CoUninitialize( paASIO, &comInitializationResult );
return result;
}
#include "pa_win_ds_dynlink.h"
#include "pa_win_waveformat.h"
#include "pa_win_wdmks_utils.h"
+#include "pa_win_coinitialize.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" )
/* implementation specific data goes here */
- char comWasInitialized;
+ PaWinUtilComInitializationResult comInitializationResult;
} PaWinDsHostApiRepresentation;
int i, deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
-
PaWinDsDeviceInfo *deviceInfoArray;
- char comWasInitialized = 0;
-
- /*
- If COM is already initialized CoInitialize will either return
- FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
- threading mode. In either case we shouldn't consider it an error
- but we need to be careful to not call CoUninitialize() if
- RPC_E_CHANGED_MODE was returned.
- */
-
- HRESULT hr = CoInitialize(NULL);
- if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
- return paUnanticipatedHostError;
- if( hr != RPC_E_CHANGED_MODE )
- comWasInitialized = 1;
+ PaWinDs_InitializeDSoundEntryPoints();
/* initialise guid vectors so they can be safely deleted on error */
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
- PaWinDs_InitializeDSoundEntryPoints();
-
winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
if( !winDsHostApi )
{
goto error;
}
- winDsHostApi->comWasInitialized = comWasInitialized;
+ result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
+ if( result != paNoError )
+ {
+ goto error;
+ }
winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !winDsHostApi->allocations )
PaUtil_FreeAllAllocations( winDsHostApi->allocations );
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
-
+
+ PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
+
PaUtil_FreeMemory( winDsHostApi );
}
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
- if( comWasInitialized )
- CoUninitialize();
+ PaWinDs_TerminateDSoundEntryPoints();
return result;
}
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
- char comWasInitialized = winDsHostApi->comWasInitialized;
/*
IMPLEMENT ME:
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
+ PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
+
PaUtil_FreeMemory( winDsHostApi );
PaWinDs_TerminateDSoundEntryPoints();
-
- if( comWasInitialized )
- CoUninitialize();
}
#include "pa_debugprint.h"\r
#include "pa_ringbuffer.h"\r
\r
+#include "pa_win_coinitialize.h"\r
+\r
#ifndef NTDDI_VERSION\r
\r
#undef WINVER\r
\r
/* implementation specific data goes here */\r
\r
+ PaWinUtilComInitializationResult comInitializationResult;\r
+\r
//in case we later need the synch\r
IMMDeviceEnumerator *enumerator;\r
\r
void PaWasapi_FreeMemory(void *ptr);\r
\r
// Local statics\r
-static volatile BOOL g_WasapiCOMInit = FALSE;\r
-static volatile DWORD g_WasapiInitThread = 0;\r
\r
// ------------------------------------------------------------------------------------------\r
#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)\r
return paNoError;\r
}\r
\r
- /*\r
- If COM is already initialized CoInitialize will either return\r
- FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different\r
- threading mode. In either case we shouldn't consider it an error\r
- but we need to be careful to not call CoUninitialize() if\r
- RPC_E_CHANGED_MODE was returned.\r
- */\r
- hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);\r
- if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))\r
- {\r
- PRINT(("WASAPI: failed CoInitialize"));\r
- return paUnanticipatedHostError;\r
- }\r
- if (hr != RPC_E_CHANGED_MODE)\r
- g_WasapiCOMInit = TRUE;\r
-\r
- // Memorize calling thread id and report warning on Uninitialize if calling thread is different\r
- // as CoInitialize must match CoUninitialize in the same thread.\r
- g_WasapiInitThread = GetCurrentThreadId();\r
-\r
paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );\r
if (paWasapi == NULL)\r
{\r
goto error;\r
}\r
\r
+ result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult );\r
+ if( result != paNoError )\r
+ {\r
+ goto error;\r
+ }\r
+\r
paWasapi->allocations = PaUtil_CreateAllocationGroup();\r
if (paWasapi->allocations == NULL)\r
{\r
PaUtil_DestroyAllocationGroup(paWasapi->allocations);\r
}\r
\r
+ PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult );\r
+\r
PaUtil_FreeMemory(paWasapi);\r
\r
// Close AVRT\r
CloseAVRT();\r
-\r
- // Uninit COM (checking calling thread we won't unitialize user's COM if one is calling\r
- // Pa_Unitialize by mistake from not initializing thread)\r
- if (g_WasapiCOMInit)\r
- {\r
- DWORD calling_thread_id = GetCurrentThreadId();\r
- if (g_WasapiInitThread != calling_thread_id)\r
- {\r
- PRINT(("WASAPI: failed CoUninitializes calling thread[%d] does not match initializing thread[%d]\n",\r
- calling_thread_id, g_WasapiInitThread));\r
- }\r
- else\r
- {\r
- CoUninitialize();\r
- }\r
- }\r
}\r
\r
// ------------------------------------------------------------------------------------------\r
--- /dev/null
+/*\r
+ * Microsoft COM initialization routines\r
+ * Copyright (c) 1999-2011 Ross Bencina, Dmitry Kostjuchenko\r
+ *\r
+ * Based on the Open Source API proposed by Ross Bencina\r
+ * Copyright (c) 1999-2011 Ross Bencina, Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+/*\r
+ * The text above constitutes the entire PortAudio license; however,\r
+ * the PortAudio community also makes the following non-binding requests:\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version. It is also\r
+ * requested that these non-binding requests be included along with the\r
+ * license above.\r
+ */\r
+\r
+/** @file\r
+ @ingroup win_src\r
+\r
+ @brief Microsoft COM initialization routines.\r
+*/\r
+\r
+#include <windows.h>\r
+#include <objbase.h>\r
+\r
+#include "portaudio.h"\r
+#include "pa_util.h"\r
+#include "pa_debugprint.h"\r
+\r
+#include "pa_win_coinitialize.h"\r
+\r
+\r
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) && !defined(_WIN32_WCE) /* MSC version 6 and above */\r
+#pragma comment( lib, "ole32.lib" )\r
+#endif\r
+\r
+\r
+/* use some special bit patterns here to try to guard against uninitialized memory errors */\r
+#define PAWINUTIL_COM_INITIALIZED (0xb38f)\r
+#define PAWINUTIL_COM_NOT_INITIALIZED (0xf1cd)\r
+\r
+\r
+PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult )\r
+{\r
+ HRESULT hr;\r
+\r
+ comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED;\r
+\r
+ /*\r
+ If COM is already initialized CoInitialize will either return\r
+ FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different\r
+ threading mode. In either case we shouldn't consider it an error\r
+ but we need to be careful to not call CoUninitialize() if \r
+ RPC_E_CHANGED_MODE was returned.\r
+ */\r
+\r
+ hr = CoInitialize(0); /* use legacy-safe equivalent to CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) */\r
+ if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )\r
+ {\r
+ PA_DEBUG(("CoInitializeEx failed. hr=%d\n", hr));\r
+\r
+ if( hr == E_OUTOFMEMORY )\r
+ return paInsufficientMemory;\r
+\r
+ {\r
+ char *lpMsgBuf;\r
+ FormatMessage(\r
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,\r
+ NULL,\r
+ hr,\r
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+ (LPTSTR) &lpMsgBuf,\r
+ 0,\r
+ NULL\r
+ );\r
+ PaUtil_SetLastHostErrorInfo( hostApiType, hr, lpMsgBuf );\r
+ LocalFree( lpMsgBuf );\r
+ }\r
+\r
+ return paUnanticipatedHostError;\r
+ }\r
+\r
+ if( hr != RPC_E_CHANGED_MODE )\r
+ {\r
+ comInitializationResult->state = PAWINUTIL_COM_INITIALIZED;\r
+\r
+ /*\r
+ Memorize calling thread id and report warning on Uninitialize if \r
+ calling thread is different as CoInitialize must match CoUninitialize \r
+ in the same thread.\r
+ */\r
+ comInitializationResult->initializingThreadId = GetCurrentThreadId();\r
+ }\r
+\r
+ return paNoError;\r
+}\r
+\r
+\r
+void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult )\r
+{\r
+ if( comInitializationResult->state != PAWINUTIL_COM_NOT_INITIALIZED\r
+ && comInitializationResult->state != PAWINUTIL_COM_INITIALIZED ){\r
+ \r
+ PA_DEBUG(("ERROR: PaWinUtil_CoUninitialize called without calling PaWinUtil_CoInitialize\n"));\r
+ }\r
+\r
+ if( comInitializationResult->state == PAWINUTIL_COM_INITIALIZED )\r
+ {\r
+ DWORD currentThreadId = GetCurrentThreadId();\r
+ if( comInitializationResult->initializingThreadId != currentThreadId )\r
+ {\r
+ PA_DEBUG(("ERROR: failed PaWinUtil_CoUninitialize calling thread[%d] does not match initializing thread[%d]\n",\r
+ currentThreadId, comInitializationResult->initializingThreadId));\r
+ }\r
+ else\r
+ {\r
+ CoUninitialize();\r
+\r
+ comInitializationResult->state = PAWINUTIL_COM_NOT_INITIALIZED;\r
+ }\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/*\r
+ * Microsoft COM initialization routines\r
+ * Copyright (c) 1999-2011 Ross Bencina, Phil Burk\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+/*\r
+ * The text above constitutes the entire PortAudio license; however, \r
+ * the PortAudio community also makes the following non-binding requests:\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version. It is also \r
+ * requested that these non-binding requests be included along with the \r
+ * license above.\r
+ */\r
+\r
+/** @file\r
+ @ingroup win_src\r
+\r
+ @brief Microsoft COM initialization routines.\r
+*/\r
+\r
+#ifndef PA_WIN_COINITIALIZE_H\r
+#define PA_WIN_COINITIALIZE_H\r
+\r
+#ifdef __cplusplus\r
+extern "C"\r
+{\r
+#endif /* __cplusplus */\r
+\r
+\r
+/**\r
+ @brief Data type used to hold the result of an attempt to initialize COM\r
+ using PaWinUtil_CoInitialize. Must be retained between a call to \r
+ PaWinUtil_CoInitialize and a matching call to PaWinUtil_CoUninitialize.\r
+*/\r
+typedef struct PaWinUtilComInitializationResult{\r
+ int state;\r
+ int initializingThreadId;\r
+} PaWinUtilComInitializationResult;\r
+\r
+\r
+/**\r
+ @brief Initialize Microsoft COM subsystem on the current thread.\r
+\r
+ @param hostApiType the host API type id of the caller. Used for error reporting.\r
+\r
+ @param comInitializationResult An output parameter. The value pointed to by \r
+ this parameter stores information required by PaWinUtil_CoUninitialize \r
+ to correctly uninitialize COM. The value should be retained and later \r
+ passed to PaWinUtil_CoUninitialize.\r
+\r
+ If PaWinUtil_CoInitialize returns paNoError, the caller must later call\r
+ PaWinUtil_CoUninitialize once.\r
+*/\r
+PaError PaWinUtil_CoInitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult );\r
+\r
+\r
+/**\r
+ @brief Uninitialize the Microsoft COM subsystem on the current thread using \r
+ the result of a previous call to PaWinUtil_CoInitialize. Must be called on the same\r
+ thread as PaWinUtil_CoInitialize.\r
+\r
+ @param hostApiType the host API type id of the caller. Used for error reporting.\r
+\r
+ @param comInitializationResult An input parameter. A pointer to a value previously\r
+ initialized by a call to PaWinUtil_CoInitialize.\r
+*/\r
+void PaWinUtil_CoUninitialize( PaHostApiTypeId hostApiType, PaWinUtilComInitializationResult *comInitializationResult );\r
+\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif /* __cplusplus */\r
+#endif /* PA_WIN_COINITIALIZE_H */\r