2010-04-16 1 views
9

저는 Excel 파일에서 일부 데이터를 읽으려면 C++ 응용 프로그램을 만들고 있습니다. 나는 그것을 작동 시키지만, 나는 한 부분에 대해서 혼란스러워한다. 여기에 코드가 있습니다 (첫 번째 셀만 읽도록 단순화되었습니다).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가 프로세스를 정리할 때 해제 될 것이기 때문에 동적으로 할당 된 메모리를 해제하지 않는 것과 비슷합니다) . 그러나 프로세스를 종료하려고하지 않을 때 다른 상황에서 처리하려고 할 때 들어갈 수있는 좋은 습관입니다. –

답변

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 호출)가 exit를 종료 할 때 호출된다는 것입니다. 이것은 기본적으로 앱과 COM 객체 간의 통신 채널을 종료하는 CoUnit 이후입니다.

CoUnit을 부르지 않은 결과는 무엇입니까? 수명이 짧은 in-process COM 서버의 경우 실제로 어떤 부정적인 결과도 없습니다.

+0

감사. 다른 곳에서도 비슷한 제안을 보았지만 그 설명은 이해가되지 않았습니다. 나를 지워 줘서 고마워. –

3

멋진 솔루션은 CoInitializeEx와 CoUninitialize를 자신의 클래스에 넣는 것입니다. 이 Raymond Chen article을 참조하십시오.

1

CoInitialize의 의미는 스레드를 아파트에 입력하는 것입니다. CoUninitialize은 아파트에서 실을 제거합니다.

아파트에 있지 않을 때 인터페이스 포인터를 사용하면 만든 아파트에서 원시 인터페이스 포인터 만 사용할 수 있기 때문에 문제가 발생합니다. (다른 아파트로 인터페이스 포인터를 마샬링하여 다른 아파트에서 사용).

인터페이스 포인터를 통해 호출하고 개체가 다른 아파트에있는 경우 (이 경우 true), 인터페이스 포인터는 아파트의 프록시 개체로 호출 한 다음 RPC를 통해 스텁으로 통신합니다 대상 아파트에서. 아파트를 나간 경우 (CoUninitialize)이 교통편을 더 이상 이용할 수 없으므로 오류가 발생합니다.

in-process 서버를 사용하는 경우에는 전송 계층이 없기 때문에 때때로 Release를 호출하기 전에 CoUninitialize를 수행 할 수 있지만 좋은 생각은 아닙니다.

BTW, CoInitialize의 두 번째 인수는 STA (즉, STA)를 입력할지 여부를 지정합니다.귀하의 스레드는 귀하의 아파트에있는 유일한 스레드 것입니다; 이 작업을 수행 할 때 새 아파트가 생성됨) 또는 MTA (프로세스 당 하나씩)가 생성됩니다.

옵션은 각각 COINIT_APARTMENTTHREADEDCOINIT_MULTITHREADED입니다. 실제로 COINIT_MULTITHREADED0을 지정했습니다. 이 셜은 매직 넘버가 아닌 코드에서 심볼 이름을 사용하는 것이 더 명확합니다.