繁体   English   中英

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

[英]Why does CoUninitialize cause an error on exit?

我正在使用 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 会导致异常?

  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;
}

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

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

一个优雅的解决方案是将 CoInitializeEx 和 CoUninitialize 放在它们自己的类中。 请参阅这篇Raymond Chen 文章

CoInitialize的意思是将你的线程输入到一个单元中; CoUninitialize从公寓中删除您的线程。

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

当您通过接口指针进行调用,并且对象驻留在另一个单元中(在这种情况下为真),您的接口指针调用单元中的代理对象,然后通过 RPC 与目标单元中的存根通信. 如果您已离开公寓(通过执行CoUninitialize ),则此传输将不再可用,从而导致您的错误。

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

顺便说一句, CoInitialize的第二个参数指定您是要进入 STA(即您的线程将是您公寓中的唯一线程;并且当您这样做时会创建一个新公寓),还是 MTA(其中每个都有一个)过程)。

选项分别是COINIT_APARTMENTTHREADEDCOINIT_MULTITHREADED 您指定0实际上是COINIT_MULTITHREADED 恕我直言,在代码中使用符号名称而不是幻数会更清楚。

0 不是COINIT_MULTITHREADED 0 是COINIT_APARTMENTTHREADED 请参阅https://docs.microsoft.com/en-us/windows/win32/api/objbase/ne-objbase-coinit

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM