开发者

Why does CoUninitialize cause an error on exit?

开发者 https://www.devze.com 2022-12-26 22:10 出处:网络
I\'m working on a C++ application to read some data from an Excel file. I\'ve got it working, but I\'m confused about one part. Here\'s the code (simplified to read only the first cell).

I'm working on a C++ application to read some data from an Excel file. I've got it working, but I'm confused about one part. Here's the code (simplified to read only the first cell).

//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")));
    _vari开发者_运维百科ant_t vItem = pRange->Value2;
    printf(_bstr_t(vItem.bstrVal));    
    pBook->Close(VARIANT_FALSE);
    pExcel->Quit();
    //CoUninitialize();
    return 0;
}

I had to comment out the call to CoUninitialize for the program to work. When CoUninitialize is uncommented, I get an access violation in the _Release function in comip.h on program exit.

Here's the code from comip.h, for what it's worth.

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

I'm not very experienced with COM programming, so there's probably something obvious I'm missing.

  1. Why does the call to CoUninitialize cause an exception?

  2. What are the consequences of not calling CoUninitialize?

  3. Am I doing something completely wrong here?


The problem you are having is one of scope. The short answer is to move the CoInit and CoUninit into an outer scope from the Ptrs. For example:

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

The longer answer is that the Ptrs destructors (which calls Release) are being called on exit from main. This is after CoUnit which, basically, shuts down the communication channel between your app and the COM object.

What are the consequences of not calling CoUnit? For short lived in-process COM servers, there really isn't any negative consequence.


An elegant solution is to put CoInitializeEx and CoUninitialize in their own class. See this Raymond Chen article.


The meaning of CoInitialize is to enter your thread into an apartment; and CoUninitialize removes your thread from the apartment.

Using an interface pointer when you are not in an apartment causes the problem because you are only permitted to use a raw interface pointer in the apartment it was created in. (You can marshal the interface pointer to another apartment in order to use it in another apartment).

When you make a call through the interface pointer, and the object resides in another apartment (which is true in this case), your interface pointer makes calls into a proxy object in the apartment which then communicates via RPC with a stub in the destination apartment. If you had left the apartment (by doing CoUninitialize) then this transport won't be available any more, causing your error.

If using in-process servers occasionally you can get away with doing CoUninitialize before calling Release because there is no transport layer involved, but it's not a good idea.

BTW, the second argument to CoInitialize specifies whether you want to enter an STA (i.e. your thread will be the only thread in your apartment; and a new apartment is created when you do this), or the MTA (of which there is one per process).

The options are COINIT_APARTMENTTHREADED and COINIT_MULTITHREADED respectively; you specified 0 which is actually COINIT_MULTITHREADED. IMHO it'd be clearer to use the symbolic name in your code rather than a magic number.


0 is not COINIT_MULTITHREADED. 0 is COINIT_APARTMENTTHREADED. Refer https://learn.microsoft.com/en-us/windows/win32/api/objbase/ne-objbase-coinit

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号