C++에서 MFC나 #import를 사용하지 않고 Excel을 자동화하는 방법

요약

C++에서 직접 자동화 코드를 작성하면 다음과 같은 몇 가지 장점이 있습니다. 원하는 작업을 정확하게 수행할 수 있을 뿐만 아니라 코드가 더 작아지고 빨라지며 디버깅하기가 쉬워집니다. 또한 어떤 라이브러리에도 종속되지 않습니다. MFC의 래퍼 클래스나 Visual C++의 기본 COM 지원(#import)만을 사용하는 경우에도 이들 프레임워크에서 일반적으로 발생하는 버그와 제한을 해결하려면 IDispatch와 COM 자동화의 내용을 자세히 조사해야 할 수 있습니다.

추가 정보

C++만을 사용하여 Microsoft Office Excel을 자동화하는 간단한 Visual C++ 6.0 콘솔 응용 프로그램을 작성하려면 다음과 같이 하십시오.
  1. Visual C++ 6.0을 시작하고 XlCpp라는 Win32 콘솔 응용 프로그램을 새로 만듭니다. "Hello, World!" 응용 프로그램을 선택하고 Finish를 누릅니다.
  2. 생성된 XlCpp.cpp를 열고 main() 함수 앞에 다음 코드를 추가합니다.
    #include <ole2.h> // OLE2 Definitions

    // AutoWrap() - Automation helper function...
    HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...) {
    // Begin variable-argument list...
    va_list marker;
    va_start(marker, cArgs);

    if(!pDisp) {
    MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010);
    _exit(0);
    }

    // Variables used...
    DISPPARAMS dp = { NULL, NULL, 0, 0 };
    DISPID dispidNamed = DISPID_PROPERTYPUT;
    DISPID dispID;
    HRESULT hr;
    char buf[200];
    char szName[200];


    // Convert down to ANSI
    WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);

    // Get DISPID for name passed...
    hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
    if(FAILED(hr)) {
    sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr);
    MessageBox(NULL, buf, "AutoWrap()", 0x10010);
    _exit(0);
    return hr;
    }

    // Allocate memory for arguments...
    VARIANT *pArgs = new VARIANT[cArgs+1];
    // Extract arguments...
    for(int i=0; i<cArgs; i++) {
    pArgs[i] = va_arg(marker, VARIANT);
    }

    // Build DISPPARAMS
    dp.cArgs = cArgs;
    dp.rgvarg = pArgs;

    // Handle special-case for property-puts!
    if(autoType & DISPATCH_PROPERTYPUT) {
    dp.cNamedArgs = 1;
    dp.rgdispidNamedArgs = &dispidNamed;
    }

    // Make the call!
    hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL);
    if(FAILED(hr)) {
    sprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr);
    MessageBox(NULL, buf, "AutoWrap()", 0x10010);
    _exit(0);
    return hr;
    }
    // End variable-argument section...
    va_end(marker);

    delete [] pArgs;

    return hr;
    }
  3. main() 함수 내부에서 printf() 줄을 다음 코드로 대체합니다.
       // Initialize COM for this thread...
    CoInitialize(NULL);

    // Get CLSID for our server...
    CLSID clsid;
    HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid);

    if(FAILED(hr)) {

    ::MessageBox(NULL, "CLSIDFromProgID() failed", "Error", 0x10010);
    return -1;
    }

    // Start server and get IDispatch...
    IDispatch *pXlApp;
    hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pXlApp);
    if(FAILED(hr)) {
    ::MessageBox(NULL, "Excel not registered properly", "Error", 0x10010);
    return -2;
    }

    // Make it visible (i.e. app.visible = 1)
    {

    VARIANT x;
    x.vt = VT_I4;
    x.lVal = 1;
    AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"Visible", 1, x);
    }

    // Get Workbooks collection
    IDispatch *pXlBooks;
    {
    VARIANT result;
    VariantInit(&result);
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"Workbooks", 0);
    pXlBooks = result.pdispVal;
    }

    // Call Workbooks.Add() to get a new workbook...
    IDispatch *pXlBook;
    {
    VARIANT result;
    VariantInit(&result);
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlBooks, L"Add", 0);
    pXlBook = result.pdispVal;
    }

    // Create a 15x15 safearray of variants...
    VARIANT arr;
    arr.vt = VT_ARRAY | VT_VARIANT;
    {
    SAFEARRAYBOUND sab[2];
    sab[0].lLbound = 1; sab[0].cElements = 15;
    sab[1].lLbound = 1; sab[1].cElements = 15;
    arr.parray = SafeArrayCreate(VT_VARIANT, 2, sab);
    }

    // Fill safearray with some values...
    for(int i=1; i<=15; i++) {
    for(int j=1; j<=15; j++) {
    // Create entry value for (i,j)
    VARIANT tmp;
    tmp.vt = VT_I4;
    tmp.lVal = i*j;
    // Add to safearray...
    long indices[] = {i,j};
    SafeArrayPutElement(arr.parray, indices, (void *)&tmp);
    }
    }

    // Get ActiveSheet object
    IDispatch *pXlSheet;
    {
    VARIANT result;
    VariantInit(&result);
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"ActiveSheet", 0);
    pXlSheet = result.pdispVal;
    }

    // Get Range object for the Range A1:O15...
    IDispatch *pXlRange;
    {
    VARIANT parm;
    parm.vt = VT_BSTR;
    parm.bstrVal = ::SysAllocString(L"A1:O15");

    VARIANT result;
    VariantInit(&result);
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlSheet, L"Range", 1, parm);
    VariantClear(&parm);

    pXlRange = result.pdispVal;
    }

    // Set range with our safearray...
    AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, L"Value", 1, arr);

    // Wait for user...
    ::MessageBox(NULL, "All done.", "Notice", 0x10000);

    // Set .Saved property of workbook to TRUE so we aren't prompted
    // to save when we tell Excel to quit...
    {
    VARIANT x;
    x.vt = VT_I4;
    x.lVal = 1;
    AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlBook, L"Saved", 1, x);
    }

    // Tell Excel to quit (i.e. App.Quit)
    AutoWrap(DISPATCH_METHOD, NULL, pXlApp, L"Quit", 0);

    // Release references...
    pXlRange->Release();
    pXlSheet->Release();
    pXlBook->Release();
    pXlBooks->Release();
    pXlApp->Release();
    VariantClear(&arr);

    // Uninitialize COM for this thread...
    CoUninitialize();
  4. 컴파일하고 실행합니다.
AutoWrap() 함수는 직접 IDispatch를 사용하는 것과 관련된 대부분의 하위 수준 세부 사항을 단순화합니다. 사용자 자신의 구현에서 자유롭게 사용하십시오. 한 가지 주의할 점은 여러 개의 매개 변수를 전달할 때는 역순으로 전달해야 한다는 것입니다. 예를 들면 다음과 같습니다.
    VARIANT parm[3];
parm[0].vt = VT_I4; parm[0].lVal = 1;
parm[1].vt = VT_I4; parm[1].lVal = 2;
parm[2].vt = VT_I4; parm[2].lVal = 3;
AutoWrap(DISPATCH_METHOD, NULL, pDisp, L"call", 3, parm[2], parm[1], parm[0]);

참조

Visual C++를 사용하여 Office를 자동화하는 방법에 대한 자세한 내용은 Microsoft 기술 자료의 다음 문서를 참조하십시오.

196776 Visual C++를 사용한 Office 자동화

216388 FILE: B2CSE.exe가 Visual Basic 자동화 코드를 Visual C++로 변환한다

(c) Microsoft Corporation 1999, All Rights Reserved. 기고: Joe Crump, Microsoft Corporation.

속성

문서 ID: 216686 - 마지막 검토: 2008. 5. 30. - 수정: 1

피드백