Почему CoUninitialize вызывает ошибку при выходе? - PullRequest
9 голосов
/ 16 апреля 2010

Я работаю над приложением 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 не комментируется, я получаю нарушение прав доступа в функции _Release в comip.h при выходе из программы.

Вот код из comip.h, за что он стоит.

* +1007 *

Я не очень разбираюсь в программировании COM, так что, возможно, что-то очевидное мне не хватает.

  1. Почему вызов CoUninitialize вызывает исключение?

  2. Каковы последствия отказа от вызова CoUninitialize?

  3. Я что-то здесь не так делаю?

Ответы [ 3 ]

14 голосов
/ 16 апреля 2010

Проблема, с которой вы столкнулись, - это проблема. Короткий ответ - переместить 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-серверов на самом деле нет никаких отрицательных последствий.

3 голосов
/ 10 октября 2010

Элегантное решение состоит в том, чтобы поместить CoInitializeEx и CoUninitialize в их собственный класс. Смотрите эту статью Раймонда Чена .

2 голосов
/ 26 августа 2014

Значение CoInitialize - ввести вашу нить в квартиру; и CoUninitialize удаляет вашу ветку из квартиры.

Использование указателя интерфейса, когда вы не находитесь в квартире, вызывает проблему, потому что вам разрешено использовать необработанный указатель интерфейса только в той квартире, в которой он был создан. (Вы можете использовать указатель интерфейса для другой квартиры, чтобы использовать это в другой квартире).

Когда вы делаете вызов через указатель интерфейса, и объект находится в другой квартире (что в данном случае верно), указатель вашего интерфейса делает вызовы в прокси-объект в квартире, который затем связывается через RPC с заглушкой в квартира назначения. Если вы покинули квартиру (выполнив CoUninitialize), этот транспорт больше не будет доступен, что приведет к вашей ошибке.

Если вы время от времени используете внутрипроцессные серверы, вы можете сойти с CoUninitialize перед вызовом Release, потому что не задействован транспортный уровень, но это не очень хорошая идея.

Кстати, второй аргумент CoInitialize указывает, хотите ли вы ввести STA (т. Е. Ваша нить будет единственной нитью в вашей квартире; при этом будет создана новая квартира), или MTA (из который есть один на процесс).

Возможны варианты COINIT_APARTMENTTHREADED и COINIT_MULTITHREADED соответственно; Вы указали 0, что на самом деле COINIT_MULTITHREADED. ИМХО, было бы яснее использовать символическое имя в вашем коде, а не магическое число.

...