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

nom7f22z  于 2023-08-08  发布在  其他
关注(0)|答案(4)|浏览(90)

我正在编写一个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会导致异常?
1.不调用CoUninitialize的后果是什么?
1.我做错什么了吗?

eqfvzcg8

eqfvzcg81#

你遇到的问题是范围问题。简短的答案是将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服务器,实际上没有任何负面影响。

f0ofjuux

f0ofjuux2#

一个优雅的解决方案是将CoInitializeEx和CoUninitialize放在它们自己的类中。看这个Raymond Chen article

holgip5t

holgip5t3#

CoInitialize的意思是将你的线程输入到一个单元中; CoUninitialize从公寓中删除线程。
当您不在单元中时使用接口指针会导致问题,因为您只被允许在创建它的单元中使用原始接口指针。(您可以封送指向另一个单元的接口指针,以便在另一个单元中使用它)。
当您通过接口指针进行调用时,对象驻留在另一个单元中(在本例中为真),您的接口指针将调用单元中的代理对象,然后该代理对象通过RPC与目标单元中的存根进行通信。如果你已经离开了公寓(通过执行CoUninitialize),那么这个传输将不再可用,导致你的错误。
如果偶尔使用进程内服务器,您可以在调用Release之前执行CoUninitialize,因为不涉及传输层,但这不是一个好主意。
顺便说一句,CoInitialize的第二个参数指定您是否要输入STA(即你的线将是你公寓里唯一的线;这样做时会创建一个新单元)或MTA(每个进程都有一个MTA)。
选项分别为COINIT_APARTMENTTHREADEDCOINIT_MULTITHREADED;你指定的0实际上是COINIT_MULTITHREADED。恕我直言,在代码中使用符号名称比使用一个神奇的数字更清楚。

相关问题