我正在使用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有什么后果?
我在这里做错了吗?
答案 0 :(得分:14)
您遇到的问题是范围之一。简短的回答是将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之后,它基本上会关闭你的应用程序和COM对象之间的通信通道。
不调用CoUnit有什么后果?对于短期进程中的COM服务器,确实没有任何负面后果。
答案 1 :(得分:3)
一个优雅的解决方案是将CoInitializeEx和CoUninitialize放在他们自己的类中。见Raymond Chen article。
答案 2 :(得分:2)
CoInitialize
的含义是进入公寓;并CoUninitialize
从公寓中删除你的帖子。
当您不在公寓时使用接口指针会导致问题,因为您只能在创建它的公寓中使用原始接口指针。(您可以将接口指针封送到另一个公寓以便使用它在另一间公寓里。)
当您通过接口指针进行调用,并且该对象驻留在另一个单元中时(在这种情况下为true),您的接口指针会调用公寓中的代理对象,然后通过RPC与存根进行通信。目的地公寓。如果您离开了公寓(通过CoUninitialize
),那么此运输将不再可用,从而导致您的错误。
如果偶尔使用进程内服务器,您可以在调用Release之前完成CoUninitialize,因为没有涉及传输层,但这不是一个好主意。
BTW,CoInitialize
的第二个参数指定是否要输入STA(即您的线程将是您公寓中唯一的线程;当您执行此操作时创建新公寓)或MTA (每个过程有一个)。
选项分别为COINIT_APARTMENTTHREADED
和COINIT_MULTITHREADED
;您指定的0
实际上是COINIT_MULTITHREADED
。恕我直言,你可以更清楚地在代码中使用符号名称而不是幻数。
答案 3 :(得分:0)
0不是COINIT_MULTITHREADED
。
0是COINIT_APARTMENTTHREADED
。请参阅https://docs.microsoft.com/en-us/windows/win32/api/objbase/ne-objbase-coinit