2010-04-16 97 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未註釋時,程序退出時,comip.h中的_Release函數中出現訪問衝突。

下面是來自comip.h的代碼,它是值得的。

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

我對COM編程不是很有經驗,所以可能有些東西顯而易見。

  1. 爲什麼對CoUninitialize的調用會導致異常?

  2. 不叫CoUninitialize有什麼後果?

  3. 我在做什麼完全錯誤的嗎?

+0

AFAIK在這種情況下調用CoUninitialize實際上並沒有什麼壞處,因爲無論如何你的進程正在關閉(類似於沒有釋放任何動態分配的內存,因爲當OS清理進程時它將被釋放) 。但稱這是一個很好的習慣,因爲當這個過程不會結束時,你可能會在不同的情況下做這件事。 – 2014-08-25 23:27:01

回答

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

較長的答案是在從main退出時調用了Ptrs析構函數(它調用Release)。這是在CoUnit之後,它基本上關閉了你的應用程序和COM對象之間的通信通道。

不叫CoUnit有什麼後果?對於短期的進程內COM服務器,確實沒有任何負面影響。

+0

謝謝。我在其他地方看到了類似的建議,但解釋沒有意義。感謝您爲我清理它。 – 2010-04-16 15:17:51

3

一個優雅的解決方案是將CoInitializeEx和CoUninitialize放在它們自己的類中。請參閱Raymond Chen article

1

CoInitialize的含義是進入你的線程到一個公寓;和CoUninitialize將您的線索從公寓中移除。

當您不在公寓時使用接口指針會導致問題,因爲您只能在創建它的公寓中使用原始接口指針。(您可以編組接口指針到另一個公寓以便在另一間公寓使用它)。

當您通過接口指針進行調用,並且對象駐留在另一個公寓(在本例中爲true)時,您的接口指針調用公寓中的代理對象,然後通過RPC與存根在目的地公寓。如果您已經離開了公寓(通過做CoUninitialize),那麼此運輸將不再可用,導致您的錯誤。

如果偶爾使用進程內服務器,您可以在調用Release之前免除CoUninitialize,因爲不涉及傳輸層,但這不是一個好主意。

順便說一句,CoInitialize的第二個參數指定是否要輸入一個STA(即,你的線程將成爲你公寓中唯一的線程;當你這樣做時會創建一個新的公寓),或者MTA(每個進程有一個)。

選項分別爲COINIT_APARTMENTTHREADEDCOINIT_MULTITHREADED;您指定的0實際上是COINIT_MULTITHREADED。恕我直言,它會更清晰地使用代碼中的符號名稱而不是幻數。

相關問題