사용자 도구

사이트 도구


kb:excelautomationusingcpp

Excel Automation / C++

비슷한 일을 해도 COM/C++은 피곤하구나…

COM 인터페이스를 이용하는 것은 JET 드라이버와 비교해서는 서로 장단이 있다.

  • JET 드라이버
    • 아무 컴퓨터에서나 돌아간다.
    • 빠르다.
    • 임포트할 때는 셀 길이의 제한이 없지만, 익스포트할 때는 컬럼의 길이가 255자로 제한된다.
    • 익스포트할 때, 색깔이라든지 폰트라던지를 지정할 수 없다.
  • COM
    • 엑셀이 설치된 컴퓨터에서만 돌아간다.
    • 느려! 아악.
    • 프로그래밍이 매우 귀찮다.

소스

될 수 있는 한 다른 소스와의 디펜던시를 없애기 위해서 풀어쓰다보니, 하는 일에 비해 코드가 좀 길어졌다. include는 알아서…

ExcelWorkbook.h
#pragma once
 
#include <ole2.h>
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \class cExcelWorkbook
/// \brief 엑셀 자동화 관련 기능 함수 모음
///
/// <pre>
/// cExcelWorkbook a;
/// if (a.Open("C:\\test.xls"))
/// {
///     if (a.SetActiveSheet(1))
///     {
///         int rows = a.GetRowCount();
///         int cols = a.GetColumnCount();
/// 
///         a.SetCellData(1, 1, L"Hello world");
///
///         wchar_t buf[1024];
///         a.GetCellData(1, 1, buf, ARRAYSIZE(buf));
///     }
/// }
/// </pre>
////////////////////////////////////////////////////////////////////////////////////////////////////
 
class cExcelWorkbook
{
private:
    LPDISPATCH m_ExcelApp;   ///< 엑셀 애플리케이션
    LPDISPATCH m_ExcelBooks; ///< 엑셀 문서 집합
    LPDISPATCH m_ExcelBook;  ///< 엑셀 문서
    LPDISPATCH m_ExcelSheet; ///< 엑셀 시트
 
 
public:
    /// \brief 생성자
    cExcelWorkbook();
 
    /// \brief 소멸자
    virtual ~cExcelWorkbook();
 
 
public:    
    /// \brief 엑셀 파일을 연다.
    bool Open(LPCWSTR fileName);
 
    /// \brief 엑셀 문서를 저장한다.
    void Save();
 
    /// \brief 엑셀 문서를 닫는다.
    void Close();
 
    /// \brief 엑셀 애플리케이션 가시성 설정
    bool SetVisible(bool visible);
 
 
public:
    /// \brief 시트의 갯수를 반환한다.
    int GetSheetCount() const;
 
    /// \brief 해당 인덱스의 시트 이름을 반환한다.
    bool GetSheetName(int sheetIndex, LPWSTR name, size_t length) const;
 
    /// \brief 해당 인덱스의 시트 이름을 설정한다.
    bool SetSheetName(int sheetIndex, LPWSTR name);
 
    /// \brief 활성화된 시트를 설정한다.
    int GetActiveSheet() const;
 
    /// \brief 활성화된 시트를 설정한다.
    bool SetActiveSheet(int sheetIndex);
 
    /// \brief 활성화된 시트가 있는지 체크한다.
    bool HasActiveSheet() const { return m_ExcelSheet != NULL; }
 
 
public:
    /// \brief 사용 중인 행의 갯수를 반환한다.
    int GetRowCount() const;
 
    /// \brief 사용 중인 열의 갯수를 반환한다.
    int GetColumnCount() const;
 
    /// \brief 현재 시트에서 데이터를 가져온다.
    bool GetCellData(int row, int column, LPWSTR buffer, size_t length) const;
 
    /// \brief 현재 시트의 셀 데이터를 설정한다.
    bool SetCellData(int row, int column, LPCWSTR data);
 
 
public:
    /// \brief 정수 기반 인덱스를 컬럼 헤더 문자열로 변환한다.
    static bool ConvertColumnHeader(int column, LPWSTR result);
 
    /// \brief 정수 기반 위치를 문자열 형태의 위치로 변환한다.
    static bool ConvertCellPosition(int row, int column, LPWSTR result, size_t length);
 
 
private:
    /// \brief Range 객체 생성. 사용 후에는 릴리즈해줘야 한다.
    LPDISPATCH CreateRangeObject(LPCWSTR position) const;
 
    /// \brief Range 객체 생성. 사용 후에는 릴리즈해줘야 한다.
    LPDISPATCH CreateRangeObject(int row, int column) const;
 
 
private:
    /// \brief 복사 생성 금지
    cExcelWorkbook(const cExcelWorkbook&) {}
 
    /// \brief 대입 금지
    cExcelWorkbook& operator = (const cExcelWorkbook&) { return *this; }
};
ExcelWorkbook.cpp
#include "ExcelWorkbook.h"
 
#include <comdef.h>
 
namespace
{
    ///---------------------------------------------------------------------------------------------
    /// Release 까먹지 말자고 만든 클래스
 
    class cScopedDispatch
    {
    private:
        LPDISPATCH m_Dispatch; ///< 실제 인터페이스 포인터
 
    public:
        cScopedDispatch(LPDISPATCH dispatch) : m_Dispatch(dispatch) {}
        ~cScopedDispatch() { if (m_Dispatch) m_Dispatch->Release(); }
 
        bool IsGood() const { return m_Dispatch != NULL; }
        operator LPDISPATCH() { return m_Dispatch; }
 
    private:
        cScopedDispatch(const cScopedDispatch&) {}
        cScopedDispatch& operator = (const cScopedDispatch&) { return *this; }
    };
 
 
    ///---------------------------------------------------------------------------------------------
    /// 자동화 헬퍼 함수
 
    HRESULT Invoke(LPDISPATCH dispatch, WORD autoType, LPVARIANT result, LPOLESTR propertyName, int argCount, ...) 
    {
        HRESULT hr = !ERROR_SUCCESS;
        VARIANT* argArray = new VARIANT[argCount + 1];
 
        va_list marker;
        va_start(marker, argCount);
 
        try
        {
            if (dispatch == NULL)
                throw L"null dispatch interface";
 
            DISPPARAMS parameters = { NULL, NULL, 0, 0 };
            DISPID dispidNamed = DISPID_PROPERTYPUT;
            DISPID propertyId;
 
            hr = dispatch->GetIDsOfNames(IID_NULL, &propertyName, 1, LOCALE_USER_DEFAULT, &propertyId);
            if (FAILED(hr)) 
                throw L"cannot convert name to dispatch id";
 
            for(int i=0; i<argCount; i++) 
                argArray[i] = va_arg(marker, VARIANT);
 
            parameters.cArgs = argCount;
            parameters.rgvarg = argArray;
 
            if (autoType & DISPATCH_PROPERTYPUT)
            {
                parameters.cNamedArgs = 1;
                parameters.rgdispidNamedArgs = &dispidNamed;
            }
 
            hr = dispatch->Invoke(propertyId, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &parameters, result, NULL, NULL);
            if (FAILED(hr))
            {
                LPCWSTR err = NULL;
 
                switch (hr)
                {
                case DISP_E_BADPARAMCOUNT:    err = L"DISP_E_BADPARAMCOUNT";    break;
                case DISP_E_BADVARTYPE:       err = L"DISP_E_BADVARTYPE";       break;
                case DISP_E_EXCEPTION:        err = L"DISP_E_EXCEPTION";        break;
                case DISP_E_MEMBERNOTFOUND:   err = L"DISP_E_MEMBERNOTFOUND";   break;
                case DISP_E_NONAMEDARGS:      err = L"DISP_E_NONAMEDARGS";      break;
                case DISP_E_OVERFLOW:         err = L"DISP_E_OVERFLOW";         break;
                case DISP_E_PARAMNOTFOUND:    err = L"DISP_E_PARAMNOTFOUND";    break;
                case DISP_E_TYPEMISMATCH:     err = L"DISP_E_TYPEMISMATCH";     break;
                case DISP_E_UNKNOWNINTERFACE: err = L"DISP_E_UNKNOWNINTERFACE"; break;
                case DISP_E_UNKNOWNLCID:      err = L"DISP_E_UNKNOWNLCID";      break;
                case DISP_E_PARAMNOTOPTIONAL: err = L"DISP_E_PARAMNOTOPTIONAL"; break;
                }
 
                throw err;
            }
        }
        catch (LPCWSTR)
        {
            //Trace::Error(_T("%s"), e);
        }
 
        va_end(marker);
 
        delete [] argArray;
 
        return hr;
    }
 
    ///---------------------------------------------------------------------------------------------
    /// 인터페이스 가져오기
 
    LPDISPATCH CreateObject(LPDISPATCH objParent, LPOLESTR name)
    {
        LPDISPATCH objResult = NULL;
 
        VARIANT vDispatchResult;
        VariantInit(&vDispatchResult);
 
        if (SUCCEEDED(Invoke(objParent, DISPATCH_PROPERTYGET, &vDispatchResult, name, 0)))
            objResult = vDispatchResult.pdispVal;
 
        return objResult;
    }
 
    LPDISPATCH CreateObject(LPDISPATCH objParent, LPOLESTR name, int arg)
    {
        LPDISPATCH objResult = NULL;
 
        VARIANT vDispatchResult;
        VariantInit(&vDispatchResult);
 
        VARIANT vDispatchArg;
        vDispatchArg.vt = VT_I4;
        vDispatchArg.lVal = arg;
 
        HRESULT hr = Invoke(objParent, DISPATCH_PROPERTYGET, &vDispatchResult, name, 1, vDispatchArg);
        if (SUCCEEDED(hr))
            objResult = vDispatchResult.pdispVal;
 
        return objResult;
    }
 
    LPDISPATCH CreateObject(LPDISPATCH objParent, LPOLESTR name, LPCWSTR arg)
    {
        LPDISPATCH objResult = NULL;
 
        VARIANT vDispatchResult;
        VariantInit(&vDispatchResult);
 
        VARIANT vDispatchArg;
        vDispatchArg.vt = VT_BSTR;
        vDispatchArg.bstrVal = ::SysAllocString(arg);
 
        HRESULT hr = Invoke(objParent, DISPATCH_PROPERTYGET, &vDispatchResult, name, 1, vDispatchArg);
        if (SUCCEEDED(hr))
            objResult = vDispatchResult.pdispVal;
 
        VariantClear(&vDispatchArg);
 
        return objResult;
    }
 
    ///---------------------------------------------------------------------------------------------
    /// 오브젝트에서 정수형/문자열 값 가져오기/설정하기
 
    bool GetIntProperty(LPDISPATCH object, LPOLESTR name, LPINT result)
    {
        bool success = false;
 
        VARIANT vQueryResult;
        VariantInit(&vQueryResult);
 
        if (SUCCEEDED(Invoke(object, DISPATCH_PROPERTYGET, &vQueryResult, name, 0)))
        {
            if (*result)
                *result = vQueryResult.intVal;
 
            success = true;
        }
 
        VariantClear(&vQueryResult);
 
        return success;
    }
 
    bool SetIntProperty(LPDISPATCH object, LPOLESTR name, INT value)
    {
        bool success = false;
 
        VARIANT vValueArg;
        vValueArg.vt = VT_I4;
        vValueArg.lVal = value;
 
        if (SUCCEEDED(Invoke(object, DISPATCH_PROPERTYPUT, NULL, name, 1, vValueArg)))
            success = true;
 
        VariantClear(&vValueArg);
 
        return success;
    }
 
    bool GetStringProperty(LPDISPATCH object, LPOLESTR name, LPWSTR result, size_t length)
    {
        bool success = false;
 
        VARIANT vValueResult;
        vValueResult.vt = VT_BSTR;
 
        if (SUCCEEDED(Invoke(object, DISPATCH_PROPERTYGET, &vValueResult, name, 0, 0)))
        {
            if (vValueResult.vt == VT_EMPTY)
            {
                if (length > 0)
                    result[0] = L'\0';
            }
            else
            {
                VariantChangeType(&vValueResult, &vValueResult, VARIANT_NOUSEROVERRIDE, VT_BSTR);
                wcscpy_s(result, length, _bstr_t(vValueResult.bstrVal));
            }
 
            success = true;
        }
 
        VariantClear(&vValueResult);
 
        return success;
    }
 
    bool SetStringProperty(LPDISPATCH object, LPOLESTR name, LPCWSTR value)
    {
        bool success = false;
 
        VARIANT vValueArg;
        vValueArg.vt = VT_BSTR;
        vValueArg.bstrVal = ::SysAllocString(_bstr_t(value));
 
        if (SUCCEEDED(Invoke(object, DISPATCH_PROPERTYPUT, NULL, name, 1, vValueArg)))
            success = true;
 
        VariantClear(&vValueArg);
 
        return success;
    }
 
    ///---------------------------------------------------------------------------------------------
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 생성자
////////////////////////////////////////////////////////////////////////////////////////////////////
cExcelWorkbook::cExcelWorkbook()
:
m_ExcelApp(NULL),
m_ExcelBooks(NULL),
m_ExcelBook(NULL),
m_ExcelSheet(NULL)
{
    ::CoInitialize(NULL);
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 소멸자
////////////////////////////////////////////////////////////////////////////////////////////////////
cExcelWorkbook::~cExcelWorkbook()
{
    Save();
    Close();
    CoUninitialize();
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 엑셀 파일을 연다.
/// \param fileName 파일 이름
/// \return bool 무사히 열었다면 true, 에러가 발생했다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::Open(LPCWSTR fileName)
{
    Close();
 
    do 
    {
        if (fileName == NULL)
            break;
 
        CLSID classId;
        if (FAILED(CLSIDFromProgID(L"Excel.Application", &classId)))
            break;
 
        if (FAILED(CoCreateInstance(classId, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&m_ExcelApp)))
            break;
 
        m_ExcelBooks = CreateObject(m_ExcelApp, L"Workbooks");
        if (m_ExcelBooks == NULL)
            break;
 
        m_ExcelBook = CreateObject(m_ExcelBooks, L"Open", fileName);
 
    } while (0);
 
    if (m_ExcelApp && m_ExcelBooks && m_ExcelBook)
    {
        return true;
    }
    else
    {
        Close();
        return false;
    }
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 엑셀 문서를 저장한다.
////////////////////////////////////////////////////////////////////////////////////////////////////
void cExcelWorkbook::Save()
{
    if (m_ExcelBook)
        Invoke(m_ExcelBook, DISPATCH_METHOD, NULL, L"Save", 0);
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 엑셀 문서를 닫는다.
////////////////////////////////////////////////////////////////////////////////////////////////////
void cExcelWorkbook::Close()
{
    if (m_ExcelApp)
    {
        Invoke(m_ExcelApp, DISPATCH_METHOD, NULL, L"Quit", 0);
 
        if (m_ExcelSheet) { m_ExcelSheet->Release(); m_ExcelSheet = NULL; }
        if (m_ExcelBook)  { m_ExcelBook->Release();  m_ExcelBook  = NULL; }
        if (m_ExcelBooks) { m_ExcelBooks->Release(); m_ExcelBooks = NULL; }
        if (m_ExcelApp)   { m_ExcelApp->Release();   m_ExcelApp   = NULL; }
    }
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 엑셀 애플리케이션 가시성 설정
/// \param visible 보이게 하고 싶다면 true, 아니라면 false
/// \return bool 무사히 설정했다면 true, 에러가 발생했다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::SetVisible(bool visible)
{
    return m_ExcelApp && SetIntProperty(m_ExcelApp, L"Visible", visible ? 1 : 0);
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 시트의 갯수를 반환한다.
/// \return int 시트의 갯수. 에러 발생시 -1
////////////////////////////////////////////////////////////////////////////////////////////////////
int cExcelWorkbook::GetSheetCount() const
{
    int count = -1;
 
    if (m_ExcelApp)
    {
        cScopedDispatch objWorksheets(CreateObject(m_ExcelApp, L"Worksheets"));
        if (objWorksheets.IsGood())
            GetIntProperty(objWorksheets, L"Count", &count);
    }
 
    return count;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 해당 인덱스의 시트 이름을 반환한다.
/// \param sheetIndex 시트 인덱스
/// \param result 이름을 집어넣을 버퍼
/// \param length 버퍼의 길이
/// \return bool 무사히 이름을 얻어왔다면 true, 에러가 발생했다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::GetSheetName(int sheetIndex, LPWSTR name, size_t length) const
{
    bool success = false;
 
    if (m_ExcelApp)
    {
        cScopedDispatch objSheet(CreateObject(m_ExcelApp, L"Worksheets", sheetIndex));
        if (objSheet.IsGood())
            success = GetStringProperty(objSheet, L"Name", name, length);
    }
 
    return success;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 해당 인덱스의 시트 이름을 설정한다.
/// \param sheetIndex 시트 인덱스
/// \param name 이름
/// \return bool 무사히 이름을 설정했다면 true, 에러가 발생했다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::SetSheetName(int sheetIndex, LPWSTR name)
{
    bool success = false;
 
    if (m_ExcelApp)
    {
        cScopedDispatch objSheet(CreateObject(m_ExcelApp, L"Worksheets", sheetIndex));
        if (objSheet.IsGood())
            success = SetStringProperty(objSheet, L"Name", name);
    }
 
    return success;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 활성화된 시트를 설정한다.
/// \return int 시트 인덱스. 에러 발생시 -1
////////////////////////////////////////////////////////////////////////////////////////////////////
int cExcelWorkbook::GetActiveSheet() const
{
    int sheetIndex = -1;
 
    if (m_ExcelSheet)
        GetIntProperty(m_ExcelSheet, L"Index", &sheetIndex);
 
    return sheetIndex;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 활성화된 시트를 설정한다.
/// \param sheetIndex 시트 인덱스
/// \return bool 무사히 설정했다면 true, 뭔가 에러가 생겼다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::SetActiveSheet(int sheetIndex)
{
    if (m_ExcelSheet)
    {
        m_ExcelSheet->Release();
        m_ExcelSheet = NULL;
    }
 
    if (m_ExcelApp)
        m_ExcelSheet = CreateObject(m_ExcelApp, L"Worksheets", sheetIndex);
 
    return m_ExcelSheet != NULL;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 사용 중인 행의 갯수를 반환한다.
/// \return int 행의 갯수. 에러 발생시 < 0
////////////////////////////////////////////////////////////////////////////////////////////////////
int cExcelWorkbook::GetRowCount() const
{
    int count = -1;
 
    if (m_ExcelSheet)
    {
        cScopedDispatch objUsedRange(CreateObject(m_ExcelSheet, L"UsedRange"));
        if (objUsedRange.IsGood())
        {
            cScopedDispatch objRows(CreateObject(objUsedRange, L"Rows"));
            if (objRows.IsGood())
                GetIntProperty(objRows, L"Count", &count);
        }
    }
 
    return count;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 사용 중인 열의 갯수를 반환한다.
/// \return int 열의 갯수. 에러 발생시 < 0
////////////////////////////////////////////////////////////////////////////////////////////////////
int cExcelWorkbook::GetColumnCount() const
{
    int count = -1;
 
    if (m_ExcelSheet)
    {
        cScopedDispatch objUsedRange(CreateObject(m_ExcelSheet, L"UsedRange"));
        if (objUsedRange.IsGood())
        {
            cScopedDispatch objCols(CreateObject(objUsedRange, L"Columns"));
            if (objCols.IsGood())
                GetIntProperty(objCols, L"Count", &count);
        }
    }
 
    return count;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 현재 시트에서 데이터를 가져온다.
/// \param row 셀 위치. 1부터 시작한다.
/// \param column 셀 위치. 1부터 시작한다.
/// \param buffer 셀 데이터를 집어넣을 버퍼 포인터
/// \param length 버퍼의 길이 (바이트 길이가 아니라 WORD 길이)
/// \return bool 무사히 가져왔다면 true, 에러가 발생했다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::GetCellData(int row, int column, LPWSTR buffer, size_t length) const
{
    bool success = false;
 
    if (m_ExcelSheet && buffer)
    {
        cScopedDispatch objRange(CreateRangeObject(row, column));
        if (objRange.IsGood())
            success = GetStringProperty(objRange, L"Value", buffer, length);
    }
 
    return success;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 현재 시트의 셀 데이터를 설정한다.
/// \param row 셀 위치. 1부터 시작한다.
/// \param column 셀 위치. 1부터 시작한다.
/// \param data 셀 데이터 포인터
/// \return bool 무사히 설정했다면 true, 에러가 발생했다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::SetCellData(int row, int column, LPCWSTR data)
{
    bool success = false;
 
    if (m_ExcelSheet && data)
    {
        cScopedDispatch objRange(CreateRangeObject(row, column));
        if (objRange.IsGood())
            success = SetStringProperty(objRange, L"Value", data);
    }
 
    return success;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief Range 객체 생성. 사용 후에는 릴리즈해줘야 한다.
/// \param position 위치 문자열
/// \return LPDISPATCH 생성한 Range 객체
////////////////////////////////////////////////////////////////////////////////////////////////////
LPDISPATCH cExcelWorkbook::CreateRangeObject(LPCWSTR position) const
{
    LPDISPATCH objRange = NULL;
 
    if (m_ExcelSheet && position)
    {
        _bstr_t rangeSpec;
        rangeSpec = position;
        rangeSpec += L":";
        rangeSpec += position;
        objRange = CreateObject(m_ExcelSheet, L"Range", rangeSpec);
    }
 
    return objRange;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief Range 객체 생성. 사용 후에는 릴리즈해줘야 한다.
/// \param row 셀 위치. 1부터 시작한다.
/// \param column 셀 위치. 1부터 시작한다.
/// \return LPDISPATCH 생성한 Range 객체
////////////////////////////////////////////////////////////////////////////////////////////////////
LPDISPATCH cExcelWorkbook::CreateRangeObject(int row, int column) const
{
    WCHAR position[64] = {0, };
    return ConvertCellPosition(row, column, position, ARRAYSIZE(position)) ? CreateRangeObject(position) : NULL;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 정수 기반 인덱스를 컬럼 헤더 문자열로 변환한다.
/// \param column 컬럼 인덱스
/// \param result 결과 문자열. 최소 3자, 즉 6바이트 이상 쓸 수 있어야 한다.
/// \return bool 무사히 변환했다면 true, 입력이 잘못되었다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::ConvertColumnHeader(int column, LPWSTR result)
{
    static const WCHAR chars[] = 
    { 
        L'A',  L'B',  L'C',  L'D',  L'E',  L'F',  L'G',  L'H',  L'I',  L'J',  L'K',  L'L',  L'M',
        L'N',  L'O',  L'P',  L'Q',  L'R',  L'S',  L'T',  L'U',  L'V',  L'W',  L'X',  L'Y',  L'Z'
    };
 
    bool success = false;
 
    if (1 <= column && column <= 256 && result)
    {
        if (column <= 26)
        {
            result[0] = chars[column-1];
            result[1] = 0;
        }
        else
        {
            result[0] = chars[(column / 26)-1];
            result[1] = chars[(column-1) % 26];
            result[2] = 0;
        }
 
        success = true;
    }
 
    return success;
}
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief 정수 기반 위치를 문자열 형태의 위치로 변환한다.
/// \param row 행
/// \param column 열
/// \param result 결과 문자열이 들어갈 버퍼
/// \param length 결과 버퍼의 길이
/// \return bool 무사히 변환했다면 true, 입력이 잘못되었다면 false
////////////////////////////////////////////////////////////////////////////////////////////////////
bool cExcelWorkbook::ConvertCellPosition(int row, int column, LPWSTR result, size_t length)
{
    bool success = false;
 
    if (row >= 1 && result && length > 0)
    {
        WCHAR cols[3] = {0, };
        if (ConvertColumnHeader(column, cols))
        {
            swprintf_s(result, length, L"%s%d", cols, row);
            success = true;
        }
    }
 
    return success;
}

샘플

cExcelWorkbook a;
if (a.Open("C:\\test.xls"))
{
    if (a.SetActiveSheet(1))
    {
        int rows = a.GetRowCount();
        int cols = a.GetColumnCount();
 
        a.SetCellData(1, 1, L"Hello world");
 
        wchar_t buf[1024];
        a.GetCellData(1, 1, buf, ARRAYSIZE(buf));
    }
}

kb/excelautomationusingcpp.txt · 마지막으로 수정됨: 2014/11/08 14:15 (바깥 편집)