为什么CoUninitialize在退出时会导致错误?

我正在使用C ++应用程序从Excel文件读取一些数据。 我有它的工作,但我对一部分感到困惑。 这里是代码(简化为只读第一个单元格)。

//Mostly copied from http://www.codeproject.com/KB/wtl/WTLExcel.aspx #import "c:\Program Files\Common Files\Microsoft Shared\OFFICE11\MSO.DLL" #import "c:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB" #import "C:\Program Files\Microsoft Office\Office11\excel.exe" rename ("DialogBox","ExcelDialogBox") rename("RGB","ExcelRGB") rename("CopyFile", "ExcelCopyFile") rename("ReplaceText", "ExcelReplaceText") exclude("IFont", "IPicture") _variant_t varOption((long) DISP_E_PARAMNOTFOUND, VT_ERROR); int _tmain(int argc, _TCHAR* argv[]) { DWORD dwCoInit = 0; CoInitializeEx(NULL, dwCoInit); Excel::_ApplicationPtr pExcel; pExcel.CreateInstance(_T("Excel.Application")); Excel::_WorkbookPtr pBook; pBook = pExcel->Workbooks->Open("c:\\test.xls", varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption); Excel::_WorksheetPtr pSheet = pBook->Sheets->Item[1]; Excel::RangePtr pRange = pSheet->GetRange(_bstr_t(_T("A1"))); _variant_t vItem = pRange->Value2; printf(_bstr_t(vItem.bstrVal)); pBook->Close(VARIANT_FALSE); pExcel->Quit(); //CoUninitialize(); return 0; } 

我不得不将CoUninitialize的调用注释掉,以使程序正常工作。 当CoUninitialize未注释时,我在程序出口的comip.h中的_Release函数中出现访问冲突。

这是来自comip.h的代码,它是值得的。

 void _Release() throw() { if (m_pInterface != NULL) { m_pInterface->Release(); } } 

我对COM编程不是很有经验,所以可能有一些明显的缺失。

  1. 为什么对CoUninitialize的调用会导致exception?

  2. 不叫CoUninitialize有什么后果?

  3. 我在这里做了完全错误的事吗?

你遇到的问题是范围之一。 简短的答案是将CoInit和CoUninit从Ptrs移动到外部范围。 例如:

 //Mostly copied from http://www.codeproject.com/KB/wtl/WTLExcel.aspx #import "c:\Program Files\Common Files\Microsoft Shared\OFFICE11\MSO.DLL" #import "c:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB" #import "C:\Program Files\Microsoft Office\Office11\excel.exe" rename ("DialogBox","ExcelDialogBox") rename("RGB","ExcelRGB") rename("CopyFile", "ExcelCopyFile") rename("ReplaceText", "ExcelReplaceText") exclude("IFont", "IPicture") _variant_t varOption((long) DISP_E_PARAMNOTFOUND, VT_ERROR); int _tmain(int argc, _TCHAR* argv[]) { DWORD dwCoInit = 0; CoInitializeEx(NULL, dwCoInit); { Excel::_ApplicationPtr pExcel; pExcel.CreateInstance(_T("Excel.Application")); Excel::_WorkbookPtr pBook; pBook = pExcel->Workbooks->Open("c:\\test.xls", varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption); Excel::_WorksheetPtr pSheet = pBook->Sheets->Item[1]; Excel::RangePtr pRange = pSheet->GetRange(_bstr_t(_T("A1"))); _variant_t vItem = pRange->Value2; printf(_bstr_t(vItem.bstrVal)); pBook->Close(VARIANT_FALSE); pExcel->Quit(); } CoUninitialize(); return 0; } 

更长的回答是,在从main中退出时调用了Ptrs析构函数(调用Release)。 这是CoUnit之后,基本上,closures您的应用程序和COM对象之间的通信渠道。

不叫CoUnit有什么后果? 对于短暂的进程内COM服务器,确实没有任何负面的后果。

一个优雅的解决scheme是将CoInitializeEx和CoUninitialize放在自己的类中。 看到这个雷蒙德陈文章 。

CoInitialize的含义是将你的线索input一个公寓; CoUninitialize会从公寓中删除你的线程。

当你不在公寓时,使用界面指针会导致这个问题,因为你只能在创build它的公寓中使用原始接口指针。(你可以将接口指针编组到另一个公寓中,以便在另一个公寓中使用公寓)。

当你通过接口指针进行调用,并且对象驻留在另一个房间(在这种情况下,这是真实的),你的接口指针调用一个代理对象,然后通过RPC与目的地公寓中的一个存根。 如果你已经离开了公寓(通过CoUninitialize ),那么这个传输将不再可用,导致你的错误。

如果偶尔使用进程内服务器,在调用Release之前,可以不用CoUninitialize,因为没有涉及到传输层,但这不是一个好主意。

顺便说一句, CoInitialize的第二个参数指定是否要input一个STA(即,你的线程将是你公寓中的唯一线程;当你这样做的时候创build一个新的房间),或者MTA(其中每一个处理)。

选项分别是COINIT_APARTMENTTHREADEDCOINIT_MULTITHREADED ; 你指定了0 ,这实际上是COINIT_MULTITHREADED 。 恕我直言,它会更清晰地使用代码中的符号名称,而不是一个神奇的数字。

Interesting Posts