사용자 도구

사이트 도구


kb:excelautomationusingcpp

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

kb:excelautomationusingcpp [2014/11/08 14:15] (현재)
줄 1: 줄 1:
 +====== Excel Automation / C++ ======
 +비슷한 일을 해도 COM/C++은 피곤하구나...
 +
 +COM 인터페이스를 이용하는 것은 JET 드라이버와 비교해서는 서로 장단이 있다. ​
 +
 +  * **JET 드라이버**
 +    * 아무 컴퓨터에서나 돌아간다.
 +    * 빠르다.
 +    * 임포트할 때는 셀 길이의 제한이 없지만, ''​익스포트할 때는 컬럼의 길이가 255자로 제한된다.''​
 +    * 익스포트할 때, 색깔이라든지 폰트라던지를 지정할 수 없다.
 +
 +  * **COM**
 +    * ''​엑셀이 설치된 컴퓨터에서만 돌아간다.''​
 +    * 느려! 아악.
 +    * 프로그래밍이 매우 귀찮다.
 +
 +
 +====== 소스 ======
 +될 수 있는 한 다른 소스와의 디펜던시를 없애기 위해서 풀어쓰다보니,​ 하는 일에 비해 코드가 좀 길어졌다. include는 알아서...
 +
 +<file cpp 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; }
 +};
 +</​file>​
 +
 +<file cpp 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;
 +}
 +</​file>​
 +
 +
 +====== 샘플 ======
 +<code cpp>
 +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));​
 +    }
 +}
 +</​code>​
 +
 +----
 +  * see also [[ExcelAutomation]],​ [[Cpp|C++]]
  
kb/excelautomationusingcpp.txt · 마지막으로 수정됨: 2014/11/08 14:15 (바깥 편집)