Handling CoInitialize (and CoUninitialize!)
I'm covering this topic because I've seen a couple of drivers now that are calling CoUninitialize a few too many times and causing subsequent app failures, but the topic is probably useful for anyone doing anything related to COM.
CoInitialize initializes COM on a per-thread basis. That is, for each thread on which you want to use COM objects, you need to call CoInitialize. CoInitialize also reference counts. Each time it succeeds, it needs to have a corresponding CoUninitialize called. The key word there is succeeds, and this is where I see the errors. CoInitializeEx allows you to specify the mode that you want, but if COM is already initialized, you can't change modes. So this code:
CoInitializeEx(COINIT_APARTMENTTHREADED)
CoInitializeEx(COINIT_MULTITHREADED)
CoUninitialize()
CoUninitialize()
... causes one too many CoUninitialize calls to occur, because the second CoInitializeEx returns RPC_E_CHANGED_MODE, and does not increment the thread's COM reference count. If you're at the top of the application, that might go un-noticed, since the second CoUninitialize will silently fail. However, if you're a component running deep within an application, you may have just caused COM to shut-down on the thread while the caller is still holding COM objects.
The way to fix this is to check the return value on CoInitialize / CoInitializeEx, and make sure it succeeded before calling CoUninitialize. RPC_E_CHANGED_MODE is a failure code that means COM is already initialized in another mode. Depending on your application, that may be a failure that you can, for the most part, ignore if you don't really require one mode or the other. However, if you ignore the RPC_E_CHANGED_MODE, you still need to make certain that you don't call the CoUninitialize that pairs with that call.
If you're writing exception based code, you need to either use a catch() clause, or use an object whose constructor calls CoInitialize(Ex) and, whose destructor calls CoUninitialize if needed.
Oh, and Zune is coming, and looking very, very good.
https://www.microsoft.com/presspass/press/2006/sep06/09-14ZuneUnveilingPR.mspx
https://www.microsoft.com/presspass/presskits/zune/default.mspx
Comments
Anonymous
October 04, 2006
Hi, I wonder about the monolithic driver case.In IPrintTicketProvider::GetPrintCapabilities(), I did the followings,CComPtr pCapDoc(NULL);pCapDoc.CoCreateInstance(CLSID_DOMDocument60);pCapDoc->loadXML (bstrXml, &vtbSuccess);ppCapabilities = pCapDoc.Detach();First I added CoInitializeEx(NULL, OINIT_APARTMENTTHREADED) and CoUninitialize() . After reading your article, I thought the system (prntvpt.dll) may have done this. So I removed the pair, and it's still working.Do I need the pair in GetPrintCapabilities() of a monolithic driver? or Did the print sub system call them already (in the same thread)?In another scenario where I created a win32 console app to test my driver, here's the code,//CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);CComPtr pCapabilities(NULL);PTOpenProvider(bstrPrinterName, 1, &m_hProvider);pCapabilities.CoCreateInstance(CLSID_DOMDocument60);pCapabilities->QueryInterface(IID_IStream, reinterpret_cast<VOID*>(&pPCOut));PTGetPrintCapabilities(m_hProvider, pPTIn, pPCOut, &bstrError);//CoUninitialize();With or without the pair, the code is all working fine. Why it works here without initalizing the COM?Thanks,zoomAnonymous
October 05, 2006
re: zoom: The print susbystem will ensure that CoInitialize has been called before your PrintTicket & PrintCapabilities code is called. This does not necessarily hold true for other printing APIs. Since your code may be invoked in the application, it's possible that the threading mode could be any of COMs available threading models. MSXML will work in any mode, but you should be aware of this with regard to any other COM objects you use.Anonymous
October 06, 2006
Thanks Ben for the quick response. In my my second example, it is a pure simple win32 console app without calling CoInitialize/CoUninitialize pair. But it's still working fine. int _tmain() { ... CComPtr<IXMLDOMDocument2> pCapabilities(NULL); pCapabilities.CoCreateInstance(CLSID_DOMDocument60); PTOpenProvider(bstrPrinterName, 1, &m_hProvider); ... } Why CoCreateInstance() is still succeeded? Before this point, there's no other function calls involved and has not gone into the print subsystem yet. Thanks, ZoomAnonymous
October 06, 2006
That does seem unusual. If your project is managed C++, the CLR will always initialize COM. Otherwise, there must be something in the '...' that's using and initializing COM where you weren't expecting it to.Anonymous
October 06, 2006
Sorry, my fault.You are right, I have PTOpenProvider() called before CoCreateInstance().Thanks,ZoomAnonymous
June 16, 2009
PingBack from http://topalternativedating.info/story.php?id=12586