为啥 CoUninitialize 在退出时会导致错误?
Posted
技术标签:
【中文标题】为啥 CoUninitialize 在退出时会导致错误?【英文标题】:Why does CoUninitialize cause an error on exit?为什么 CoUninitialize 在退出时会导致错误? 【发布时间】:2011-02-08 20:59:33 【问题描述】:我正在开发一个 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 编程不是很有经验,所以我可能缺少一些明显的东西。
为什么调用 CoUninitialize 会导致异常?
不调用 CoUninitialize 会有什么后果?
我在这里做错了吗?
【问题讨论】:
AFAIK 在这种情况下不调用 CoUninitialize 实际上没有什么害处,因为您的进程无论如何都会关闭(类似于不释放任何动态分配的内存是可以的,因为它会在操作系统时被释放清理过程)。但是调用它是一个好习惯,因为当你可能在不同的情况下这样做时,过程还没有结束。 【参考方案1】:您遇到的问题是范围之一。简短的回答是将 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;
更长的答案是 Ptrs 析构函数(调用 Release)在从 main 退出时被调用。这是在 CoUnit 之后,它基本上关闭了您的应用程序和 COM 对象之间的通信通道。
不调用 CoUnit 会有什么后果?对于短暂的进程内 COM 服务器,确实没有任何负面影响。
【讨论】:
谢谢。我在其他地方看到了类似的建议,但解释没有意义。感谢您为我清理。【参考方案2】:一个优雅的解决方案是将 CoInitializeEx 和 CoUninitialize 放在它们自己的类中。请参阅此Raymond Chen article。
【讨论】:
【参考方案3】:CoInitialize
的意思是把你的线程进入一个公寓;并且CoUninitialize
将您的帖子从公寓中删除。
当您不在单元中时使用接口指针会导致问题,因为您只能在创建它的单元中使用原始接口指针。(您可以将接口指针编组到另一个单元以便使用它在另一间公寓里)。
当您通过接口指针进行调用,并且对象驻留在另一个单元中时(在这种情况下确实如此),您的接口指针会调用单元中的代理对象,然后通过 RPC 与目的地公寓。如果你离开了公寓(通过CoUninitialize
),那么这个交通工具将不再可用,导致你的错误。
如果偶尔使用进程内服务器,您可以在调用 Release 之前执行 CoUninitialize,因为不涉及传输层,但这不是一个好主意。
顺便说一句,CoInitialize
的第二个参数指定您是要进入 STA(即您的线程将是您公寓中的唯一线程;并且当您这样做时会创建一个新公寓),还是 MTA(每个进程有一个)。
选项分别为COINIT_APARTMENTTHREADED
和COINIT_MULTITHREADED
;您指定了0
,它实际上是COINIT_MULTITHREADED
。恕我直言,在代码中使用符号名称而不是幻数会更清楚。
【讨论】:
【参考方案4】:0 不是COINIT_MULTITHREADED
。
0 是COINIT_APARTMENTTHREADED
。参考https://docs.microsoft.com/en-us/windows/win32/api/objbase/ne-objbase-coinit
【讨论】:
感谢您的回复。如果您要回复 @MM,请将其添加为对他的回答的评论。 是的,约翰,这是对@M.M 的回应,但我无法添加评论,因为我还没有足够的“声誉”:-|发表评论。以上是关于为啥 CoUninitialize 在退出时会导致错误?的主要内容,如果未能解决你的问题,请参考以下文章
Heroku - 为啥在向 Heroku 推送版本时会出现错误 R12(退出超时)?
为啥在c#,mvc中退出并按下浏览器后退按钮时会转到一个页面
为啥 Pandas 在 read_csv 时会导致 ZeroDivisionError