Excel Test

엑셀 Automation을 사용하여 네이티브 C++에서 엑셀에 접근해서 값을 읽고 저장 해보자.

1. 엑셀이 깔려 있어야 한다.
2. 빌드 당시의 엑셀의 위치와 실행 컴퓨터의 엑셀의 설치 폴더는 달라도 상관없음

C#을 이용하여 Native C++과 연결하려고 했으나 배열 처리가 너무 귀찮아서 네이티브 C++에서 엑셀 Automation을 사용하기로 하였다.

< Imaprt로 엑셀의 type library 알려주기 >

엑셀의 type library를 사용하기 import 지시자로 mso.dll과 엑셀 파일과 기타 파일을 알려준다.
빌드시의 DLL, EXE 위치와 실행시의 폴더 위치가 달라도 상관없다.
type library를 로딩하기 위해서는 #import 지시자가 필요한것만 숙지하고 더 자세히 알아 보진 않겠다.

//#define MY_EXCEL_POSITION

 

#import \

"C:\Program Files\Common Files\Microsoft Shared\OFFICE12\mso.dll" \

rename("DocumentProperties", "DocumentPropertiesXL") \

rename("RGB", "RBGXL")

//Microsoft VBA objects

#import \

"C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\vbe6ext.olb"

//Excel Application objects

 

#ifdef  MY_EXCEL_POSITION

#import "D:\OFFICE12\EXCEL.EXE" \

rename("DialogBox", "DialogBoxXL") rename("RGB", "RBGXL") \

rename("DocumentProperties", "DocumentPropertiesXL") \

rename("ReplaceText", "ReplaceTextXL") \

rename("CopyFile", "CopyFileXL") \

exclude("IFont", "IPicture") no_dual_interfaces

#else

#import "C:\Program Files\Microsoft Office\OFFICE12\EXCEL.EXE" \

rename("DialogBox", "DialogBoxXL") rename("RGB", "RBGXL") \

rename("DocumentProperties", "DocumentPropertiesXL") \

rename("ReplaceText", "ReplaceTextXL") \

rename("CopyFile", "CopyFileXL") \

exclude("IFont", "IPicture") no_dual_interfaces

#endif

< COM 초기화 >

시작하자마자 tempTest.csv파일을 삭제하는 것은 엑셀에서 csv 파일 포맷으로 저장 할 때, 똑같은 파일이 이미 존재 한다는 에러메세지가 안뜨도록 하기 위해서이다.

엑셀은 COM을 이용해서 사용하므로 COM 초기화 해준다.

    //시작시

    DeleteFile( GetFullPath( "tempTest.csv" ).c_str() );

    CoInitialize(NULL);

< 엑셀 관련 변수 선언 >

C++에서 엑셀 관련 변수는 스마트 포인터 형식으로 선언 한다.

    HRESULT hr = S_OK;

    Excel::_ApplicationPtr     xlApp;

    Excel::_WorkbookPtr        xlWorkBook;

    Excel::_WorksheetPtr       xlWorkSheet;

    Excel::RangePtr            range;

< 엑셀 파일 열기 >

엑셀 개체를 생성하고 , 실행중 보이지 않도록 한다. Workbooks->Open()으로 엑셀 파일을 로딩한다.

    xlApp.CreateInstance(L"Excel.Application");

    xlApp->Visible = false;

    xlWorkBook = xlApp->Workbooks->Open( GetFullPath( "test.xlsx" ).c_str() );

< csv 포맷의 파일로 저장 >

두 번째 인자는 파일 포맷을 나타낸다. 중요한 포맷별 인자는 다음과 같다.
csv 확장자 -------> Excel::xlCSV
xls 확장자  -------> Excel::xlExcel8
xlsx 확장자 -------> Excel::xlOpenXMLWorkbook
여기서는 csv로 저장한다.

    hr = xlWorkBook->SaveAs( GetFullPath( "tempTest.csv" ).c_str(), Excel::xlCSV,

            vtMissing, vtMissing, vtMissing, vtMissing, Excel::xlNoChange,

            vtMissing, vtMissing, vtMissing, vtMissing, vtMissing);

< 워크 시트 구하기 >

    //워크 시트 구하기

    xlWorkSheet = (Excel::_WorksheetPtr)xlWorkBook->Worksheets->GetItem(1);

< 유효한 행과 열의 개수 구하기 >

    //column, row 갯수 구하기

    range = xlWorkSheet->UsedRange;

    int column = range->GetColumns()->Count;

    int row = range->GetRows()->Count;

< 셀 값 읽기 >

셀의 GetItem()이나 item배열을 사용 한다. 둘다 행과 열의 순서로 1부터 시작한다. 0, 0 이 아니니 주의하자.

    //특정값 읽기, Int형이 아닌데 읽으면 죽으니 조심  할것

    int intNum0 = range->Cells->GetItem( 4, 1 );

    int intNum1 = range->Item[4][1];

    OutputDebugPrintf( "엑셀 INT 읽기 : %d, %d\n", intNum0, intNum1 );

 

    _bstr_t bsStr1 = xlWorkSheet->Cells->GetItem( 3, 3 );    LPCSTR psz1 = NULL;

    if( bsStr1.length() != 0 )

        psz1 = bsStr1;

    _bstr_t bsStr2 = xlWorkSheet->Cells->Item[3][3];        LPCSTR psz2 = NULL;

    if( bsStr2.length() != 0 )

        psz2 = bsStr2;

< 셀 값 수정하기 >

    //특정값 저장하기

    range->Item[3][1] = "한글열개";

< 엑셀 파일 저장하기 >

    xlWorkBook->Save();

< 엑셀 종료하기 >

Close()의 첫 번째 매개 변수는 저장 할것인지 나타내는 플래그이다.

    xlWorkBook->Close( VARIANT_FALSE );

    xlApp->Quit();

< 엑셀 스마트 포인터 해제 >

NULL로 해제 해주지 않으면 종료시 assert문이 뜬다. 하여튼 NULL로 초기화 해주자.

    range = NULL;

    xlWorkSheet = NULL;

    xlWorkBook = NULL;

    xlApp = NULL;

< COM 소멸 >

    CoUninitialize();   

< 새로운 워크 시트 만들기 >

새로운 워크시트를 만드는 엑셀 API는 Excel::Workbooks::Add( )이다. 리턴 값은 Excel::_WorkbookPtr이다.
엑셀 파일을 새로 생성 할 때는 Add( )시의 반환값인 _WorkbookPtr를 이용한다.
엑셀 파일을 새롭게 생성후 저장하는 예는 NewExcel( ) 함수를 참고 한다.

    xlWorkBook = xlApp->Workbooks->Add(Excel::xlWorksheet);

프로젝트 : Excel_test.zip