2010-04-16 6 views
9

Я работаю над приложением C++ для чтения некоторых данных из файла Excel. У меня это работает, но я смущен одной частью. Вот код (упрощенный читать только первую ячейку).Почему CoUninitialize вызывает ошибку при выходе?

//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, для чего он стоит.

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

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

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

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

  3. Я делаю что-то совершенно неправильное здесь?

+0

AFAIK на самом деле нет вреда в том, что вы не вызываете CoUninitialize в этой ситуации, потому что ваш процесс все равно закрывается (подобно тому, как это нормально, чтобы не освобождать динамически выделенную память, поскольку он освободится, когда ОС очистит процесс) , Но называть это хорошей привычкой вступать, потому что, когда вы можете делать это в другой ситуации, когда процесс не закончится. –

ответ

12

Проблема, с которой вы сталкиваетесь, является одной из областей. Короткий ответ заключается в том, чтобы переместить 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) в настоящее время называется на выходе из основных. Это после CoUnit, который, в основном, отключает канал связи между вашим приложением и COM-объектом.

Каковы последствия не вызова CoUnit? Для короткоживущих COM-серверов на производстве действительно нет никаких негативных последствий.

+0

Спасибо. Я видел подобное предложение где-то еще, но объяснение не имело смысла. Спасибо, что расчистил его для меня. –

3

Элегантное решение состоит в том, чтобы поместить CoInitializeEx и CoUninitialize в свой класс. См. Это Raymond Chen article.

1

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

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

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

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

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

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

Смежные вопросы