Apartment(STA)와 Single(Legacy Single)의 차이점을 몰라서 엄청 헤매던 끝에 해답을 준 아티클입니다.
Windows 서버에서 COM의 동접이나 성능에 관련된 문제를 겪으시는 분들을 읽어보세요.
이걸로 해!결!

Part1
http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa 


Part2
http://www.codeproject.com/Articles/9506/Understanding-The-COM-Single-Threaded-Apartment-Pa  
Posted by 배트
MinGW를 사용해 openssl 0.9.8r을 빌드하려고 했는데,
Win32의 include 경로가 제대로 잡히지 않는지 make 단계에서 에러가 발생한다.

결국 기존의 방식대로 ActivePerl과 MS C Compiler를 사용해 빌드하기로 했다.

OpenSSL의 Config은 Perl 스크립트로 되어있고,
MS-WIN32라는 환경 프리셋을 제공하므로,
Perl만 사용할 수 있으면 Win32에서 빌드할 수 있다.
(Perl에는 지식이 없지만, 아마 이런 스크립트들은 모두 Perl로 되어있으리라 추정한다.)

1. MS Visual Studio가 설치되어 있어야 한다.

2. ActivePerl을 다운받고 설치한다.
   http://www.activestate.com/
   다른 Perl 도구가 있으면 사용해도 되겠다.

3. OpenSSL의 소스를 다운받고 적당한 곳에 압축을 푼다. (여기에선 C:\W32Build 아래에)

4. 커맨드 프롬프트 창을 연다.

5. MS Visual Studio 개발 환경의 환경변수가 모두 지정되어 있지 않다면,
   아래와 같은 배치명령어로 환경변수를 등록한다.
   VC6 : C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT
   VC9 : C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat

6. OpenSSL의 소스가 있는 곳으로 가서 Configure를 실행한다.
   C:\W32Build\openssl-0.9.8k>perl Configure VC-WIN32 --openssldir=C:\OpenSSL
   Configure는 순식간에 끝난다.

7. 성능을 높이기 위해 MS assembler(MASM)을 사용하려면,
    아래 사이트에서 MASM32 SDK를 다운받고 설치한다.
    http://www.masm32.com/

8. 이제 Make 파일을 만든다.
   어셈블러 코드를 포함한 Make 파일을 만드려면
       C:\W32Build\openssl-0.9.8k>ms\do_ms.bat
   모두 C 코드로 빌드하는 Make 파일을 만드려면
       C:\W32Build\openssl-0.9.8k>ms\do_msam.bat
   을 실행한다.
   그러면 ms\nt.mak와 ms\ntdll.mak가 생성된다.

9. nt.mak는 OpenSSL 프로그램을 빌드하고, ntdll.mak는 OpenSSL의 DLL을 빌드한다.
   DLL을 빌드하려면,
      nmake -f ms\ntdll.mak
   을 실행하면 된다.
   빌드된 파일들은 out32dll 폴더와 설정시 입력했던 C:\OpenSSL 폴더 아래에 있다.

Good luck!

출처: http://www.lovelgw.com/Blog/61
Posted by 배트
TAG OpenSSL
// to do: build the code and execute the binary twice at the same time!

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hMutex;
    DWORD dwWaitResult;

    for( int i=0 ; i<3 ; i++ )
    {
        // if the mutex with the same name exists, it opens the mutex with MUTEX_ALL_ACCESS right.
        hMutex = CreateMutex(NULL, FALSE, _T("XXXXXXX_uid:0a0b6203-cd11-4b50-877b-3965da7275cc"));

        // waiting for other thread release the mutex
        // then this thread will get the mutex
        _tprintf( _T("Waiting for the mutex... ") );
        dwWaitResult = WaitForSingleObject(hMutex, INFINITE);
        switch(dwWaitResult)
        {
        case WAIT_OBJECT_0:
            // This thread got a mutex!
            _tprintf(_T ("thread got mutex ! \n") );
            break;
        case WAIT_TIMEOUT:
            // A time out occurs...
            _tprintf(_T ("timer expired ! \n") );
            break;
        case WAIT_ABANDONED:
            // Another thread may not release the mutex before it terminated.
            // The mutex has been abandoned.... how poor....
            _tprintf(_T ("abandoned ! \n") );
            break;
        }

        _tprintf( _T("Thread Running (%d) ! \n"), i+1 );
        for( int j=0 ; j<10 ; j++)
        {
            _puttch(_T('.'));
            Sleep(100);
        }
        _puttch(_T('\n'));

        // should release the mutex
        _tprintf( _T("One loop ended, the mutex will be released! \n\n") );
        ReleaseMutex(hMutex);
    }

    _tprintf( _T("Test has terminated.\nPress any key to continue... \n") );
    _gettch();

    return 0;
}
Posted by 배트
언젠가 유용하게 쓰겠지...
The temporary file handling class for Win32.

class TempFileUtility {
public:
    TempFileUtility()
    {
        ResetData();
    }

    virtual ~TempFileUtility()
    {
        CloseTempFile();
    }

    int OpenTempFile( BOOL bCloseAfterCreated )
    {
        if( m_bInitialized==TRUE )
            return ERROR_ALREADY_INITIALIZED;

        const int cMaxTempDir = MAX_PATH-14;        
        TCHAR szTempDir[cMaxTempDir];
        
        int nResult = 0;
        
        nResult = GetTempPath( cMaxTempDir, szTempDir );
        if( nResult==0 || nResult>cMaxTempDir )
        {
            nResult = SHGetSpecialFolderPath( NULL, szTempDir, CSIDL_MYDOCUMENTS, TRUE );
            
            if( nResult==FALSE )
                return ERROR_CANNOT_MAKE;
        }
        
        nResult = GetTempFileName( szTempDir, _T(""), 0, m_szTempFile );
        if( nResult==FALSE )
        {
            ResetData();
            return ERROR_CANNOT_MAKE;
        }

        if( bCloseAfterCreated==TRUE )
        {    
            m_hFile = CreateFile( m_szTempFile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY, NULL );
            if( m_hFile==INVALID_HANDLE_VALUE )
            {
                DeleteFile( m_szTempFile );
                ResetData();
                return ERROR_OPEN_FAILED;
            }
        }

        m_bInitialized = TRUE;
        return ERROR_SUCCESS;
    }

    int WriteTempFile(
        LPCVOID lpBuffer,
        DWORD nNumberOfBytesToWrite
        )
    {
        if( m_bInitialized==FALSE )
            return ERROR_NOT_READY;

        if( m_hFile==INVALID_HANDLE_VALUE )        
            return ERROR_NOT_READY;

        BOOL bSuccess;
        DWORD dwWritten = 0;

        bSuccess = WriteFile( m_hFile, lpBuffer, nNumberOfBytesToWrite, &dwWritten, NULL );

        if( bSuccess==FALSE )
            return GetLastError();

        return ERROR_SUCCESS;
    }


    int CloseTempFile()
    {
        if( m_bInitialized==FALSE )
            return ERROR_NOT_READY;

        if( m_hFile!=INVALID_HANDLE_VALUE )
            CloseHandle( m_hFile );

        DeleteFile( m_szTempFile );
        ResetData();
        return ERROR_SUCCESS;
    }

    LPCTSTR GetTempFilePath() {return m_szTempFile;}
    HANDLE GetTempFileHandle() {return m_hFile;}

protected:
    BOOL m_bInitialized;
    TCHAR m_szTempFile[MAX_PATH];
    HANDLE m_hFile;

    void ResetData()
    {
        m_bInitialized = FALSE;
        memset( m_szTempFile, 0, MAX_PATH );
        m_hFile = INVALID_HANDLE_VALUE;
    }
};

Posted by 배트
IE ActiveX는 버리고 CEF로 가자!
아..쥰내 아름답네. 한두가지 모자라긴 하지만.
특히 Attribute 세팅이 자동으로 UI에 적용되어야 하는데...그건 당장 구현하기가 귀찮아서 일단은 이렇게 하자.

Posted by 배트
CEF는 크로미움의 웹 브라우저 기능을 Win32 Windows 기반의 컨트롤로써 사용할 수 있게 해준다.
기존 IE ActiveX를 애플리케이션에 Embedding하는 방식을 써왔다면, CEF가 좋은 대안이 될 수 있다.

CEF를 사용하면 IE의 불안정과 속도 문제, 불편한 인터페이스를 해결할 수 있다.
CEF는 라이브러리로 제공되기 때문에 IE 또는 인터넷 설정과의 종속성을 해결할 수 있다.
IE가 제공하는 것보다 기능이 적다고 여겨지나,
개발자에게 필요한 기능은 거의 다 있고 사용법이 간단하다.
웹 표준은 말할 것도 없고.

앞으로 웹을 Embedding하는 Windows 애플리케이션은 모두 CEF를 사용할 생각이다.
여기서 소스와 라이브러리를 받을 수 있다. http://code.google.com/p/chromiumembedded/
Posted by 배트
Child Process가 제대로 동작하는지 테스트하기 위해 개발한 클래스.
당연하게도 콘솔이 있는 애플리케이션에게만 사용이 가능하고,
콘솔에다 뭔가를 출력하고 있어야 정상 동작한다고 인식한다.

This class is developed for checking whether an child process is working properly by monitoring its console.
It determines the child process is working properly, if the child process outputs some texts on its console.

for more information, please refer to the following link. This article is based on it.

//////////////////////////////////////////////////////////////////////////
// Copyright(C)2010 All Rights Reserved by Junseok Oh
//
// author: Junseok Oh
// email: batt22@naver.com, swolf22@nate.com, batt842@gmail.com
// 08.Nov.2010
//
// This source code is free to deploy and modify.
// You don't need to keep this license header.
// An acknowledgement is not required.
// Because it's simple. :-)

class ChildProcess {
public:
    ChildProcess() :
        m_bInit(FALSE),
        m_hOutputRead(INVALID_HANDLE_VALUE),
        m_hOutputWrite(INVALID_HANDLE_VALUE),
        m_hInputRead(INVALID_HANDLE_VALUE),
        m_hInputWrite(INVALID_HANDLE_VALUE),
        m_hErrorWrite(INVALID_HANDLE_VALUE),
        m_nShowCmd(SW_SHOWNORMAL),
        m_doMonitor(FALSE),
        m_nMaxSilentPeriod(5000),
        m_bMonitorResult(TRUE),
        m_bVerbose(FALSE),
        m_hReadActionEvent(INVALID_HANDLE_VALUE)
    {
        HANDLE hOutputReadTmp, hInputWriteTmp;
        SECURITY_ATTRIBUTES sa;

        // Set up the security attributes struct.
        sa.nLength= sizeof(SECURITY_ATTRIBUTES);
        sa.lpSecurityDescriptor = NULL;
        sa.bInheritHandle = TRUE;

        // Create the child output pipe.
        if (!CreatePipe(&hOutputReadTmp,&m_hOutputWrite,&sa,0))
            return;

        // Create a duplicate of the output write handle for the std error
        // write handle. This is necessary in case the child application
        // closes one of its std output handles.
        if (!DuplicateHandle(GetCurrentProcess(),m_hOutputWrite,
            GetCurrentProcess(),&m_hErrorWrite,0,
            TRUE,DUPLICATE_SAME_ACCESS))
            return;

        // Create the child input pipe.
        if (!CreatePipe(&m_hInputRead,&hInputWriteTmp,&sa,0))
            return;

        // Create new output read handle and the input write handles. Set
        // the Properties to FALSE. Otherwise, the child inherits the
        // properties and, as a result, non-closeable handles to the pipes
        // are created.
        if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
            GetCurrentProcess(),
            &m_hOutputRead, // Address of new handle.
            0,FALSE, // Make it uninheritable.
            DUPLICATE_SAME_ACCESS))
            return;

        if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
            GetCurrentProcess(),
            &m_hInputWrite, // Address of new handle.
            0,FALSE, // Make it uninheritable.
            DUPLICATE_SAME_ACCESS))
            return;

        // Close inheritable copies of the handles you do not want to be
        // inherited.
        if (!CloseHandle(hOutputReadTmp))
            return;
        if (!CloseHandle(hInputWriteTmp))
            return;

        m_bInit = TRUE;
    }
    ~ChildProcess()
    {
        if(!m_bInit)
            return;

        CloseHandlesForParent();
        CloseHandlesForChild();
    }

    void SetInfomation( LPCTSTR szCmdLine, LPCTSTR szWorkingDirectory, int nShowCmd )
    {
        if(!m_bInit)
            return;

        _tcscpy_s( m_szCmdLine, MAX_PATH, szCmdLine );
        _tcscpy_s( m_szWorkingDirectory, MAX_PATH, szWorkingDirectory );
        m_nShowCmd = nShowCmd;
    }

    void SetMonitorPolicy( BOOL doMonitor, long nMaxSilentPeriod, BOOL bVerbose=FALSE )
    {
        if(!m_bInit)
            return;

        m_doMonitor = doMonitor;
        m_nMaxSilentPeriod = nMaxSilentPeriod;
        m_bVerbose = bVerbose;
    }



    BOOL CreateProcess()
    {
        int nResult = 0;
        DWORD ExitCode = 0;
        LPTSTR szWorkingDirectory = NULL;

        STARTUPINFO si;
        PROCESS_INFORMATION pi;

        if( _tcslen(m_szWorkingDirectory)>0 )
            szWorkingDirectory = m_szWorkingDirectory;

        ZeroMemory(&si, sizeof(STARTUPINFO));
        ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

        si.cb = sizeof(STARTUPINFO);
        si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW ;
        si.hStdInput = m_hInputRead;//GetStdHandle(STD_INPUT_HANDLE);
        si.hStdOutput = m_hOutputWrite;//GetStdHandle(STD_OUTPUT_HANDLE);
        si.hStdError = m_hErrorWrite;//GetStdHandle(STD_ERROR_HANDLE);
        si.wShowWindow = m_nShowCmd;

        nResult = ::CreateProcess( NULL, m_szCmdLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE|CREATE_SUSPENDED, NULL, szWorkingDirectory, &si, &pi );
        CloseHandlesForChild();
        if( m_doMonitor )
            CreateThread( NULL, 0, DoMonitorThread, (LPVOID)this, 0, NULL);
        CreateThread( NULL, 0, ReadChildProcessConsoleThread, (LPVOID)this, 0, NULL);
        ResumeThread( pi.hThread );

        m_bMonitorResult = TRUE;

        if (nResult==TRUE)
        {
            while( WaitForSingleObject( pi.hProcess, 100 )==WAIT_TIMEOUT && m_bMonitorResult )
            {};
        }

        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);

        CloseHandlesForParent();

        return nResult&m_bMonitorResult;
    }

private:

    void DoMonitor()
    {
        if(!m_bInit)
            return;

        m_bMonitorResult = TRUE;
        m_hReadActionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        while( WaitForSingleObject( m_hReadActionEvent, m_nMaxSilentPeriod )==WAIT_OBJECT_0 )
        {}

        // Timeout or process terminated
        m_bMonitorResult = FALSE;
        CloseHandle( m_hReadActionEvent );
        m_hReadActionEvent = INVALID_HANDLE_VALUE;
    }

    static DWORD WINAPI DoMonitorThread( void* p )
    {
        ((ChildProcess*)p)->DoMonitor();
        return 0;
    }

    void ReadChildProcessConsole()
    {
        if(!m_bInit)
            return;

        const int nBufferSize = 256;
        CHAR lpBuffer[nBufferSize+1];
        DWORD nBytesRead;

        while(TRUE)
        {
            if (!ReadFile(m_hOutputRead,lpBuffer,nBufferSize,&nBytesRead,NULL) || !nBytesRead)
            {
                if (GetLastError() == ERROR_BROKEN_PIPE)
                    break; // pipe done - normal exit path.
            }

            if(m_bVerbose)
            {
                lpBuffer[nBytesRead] = '\0';
                fputs( lpBuffer, stdout );
            }

            if( m_hReadActionEvent!=INVALID_HANDLE_VALUE )
                SetEvent( m_hReadActionEvent );
        }
    }

    static DWORD WINAPI ReadChildProcessConsoleThread( void* p )
    {
        ((ChildProcess*)p)->ReadChildProcessConsole();
        return 0;
    }

    void CloseHandlesForParent()
    {
        if(!m_bInit)
            return;

        if( hOutputRead!=INVALID_HANDLE_VALUE )
        {
            CloseHandle(hOutputRead);
            hOutputRead = INVALID_HANDLE_VALUE;
        }
        if( hInputWrite!=INVALID_HANDLE_VALUE )
        {
            CloseHandle(hInputWrite);
            hInputWrite = INVALID_HANDLE_VALUE;
        }
    }

    void CloseHandlesForChild()
    {
        if(!m_bInit)
            return;

        // Close pipe handles (do not continue to modify the parent).
        // You need to make sure that no handles to the write end of the
        // output pipe are maintained in this process or else the pipe will
        // not close when the child process exits and the ReadFile will hang.
        if( hOutputWrite!=INVALID_HANDLE_VALUE )
        {
            CloseHandle(hOutputWrite);
            hOutputWrite = INVALID_HANDLE_VALUE;
        }
        if( hInputRead!=INVALID_HANDLE_VALUE )
        {
            CloseHandle(hInputRead);
            hInputRead = INVALID_HANDLE_VALUE;
        }
        if( hErrorWrite!=INVALID_HANDLE_VALUE )
        {
            CloseHandle(hErrorWrite);
            hErrorWrite = INVALID_HANDLE_VALUE;
        }
    }

private:
    BOOL m_bInit;

    HANDLE m_hOutputRead, m_hOutputWrite;
    HANDLE m_hInputRead, m_hInputWrite;
    HANDLE m_hErrorWrite;

    TCHAR m_szCmdLine[MAX_PATH];
    TCHAR m_szWorkingDirectory[MAX_PATH];
    int m_nShowCmd;

    BOOL m_doMonitor;
    BOOL m_bMonitorResult;
    BOOL m_bVerbose;
    int m_nMaxSilentPeriod;
    HANDLE m_hReadActionEvent;
};


int _tmain(int argc, _TCHAR* argv[])
{
        BOOL bResult = FALSE;
        ChildProcess childProcess;
        childProcess.SetInfomation( _T("netstat"), _T(""), SW_SHOWNORMAL );
        childProcess.SetMonitorPolicy( TRUE, 4000, TRUE );
        bResult = childProcess.CreateProcess();
}
Posted by 배트
이제보니 프롬프트에서 VBS 파일 이름만 입력해도 그냥 실행이 되는구나...
wscript나 cscript는 옵션 줄 때만 필요한 거군.

set WshShell = WScript.CreateObject("WScript.Shell")
set fso = CreateObject("Scripting.FileSystemObject")
 
strDesktop = WshShell.SpecialFolders("Desktop")
strPrograms = WshShell.SpecialFolders("Programs")
strProgramGroup = strPrograms & "\Group"
strCurrFolder = fso.GetParentFolderName(WScript.ScriptFullName)
 
set oShellLinkDesktop = WshShell.CreateShortcut(strDesktop & "\MyApplication.lnk")
oShellLinkDesktop.TargetPath = strCurrFolder & "\MyApplication.exe"
oShellLinkDesktop.WindowStyle = 1
oShellLinkDesktop.Hotkey = ""
oShellLinkDesktop.IconLocation = strCurrFolder & "\MyApplication.exe, 0"
oShellLinkDesktop.Description = "MyApplication"
oShellLinkDesktop.WorkingDirectory = strCurrFolder
oShellLinkDesktop.Save
 
If (fso.FolderExists(strProgramGroup)=False) Then
    fso.CreateFolder(strProgramGroup)
End If
 
set oShellLinkPrograms = WshShell.CreateShortcut(strProgramGroup & "\MyApplication.lnk")
oShellLinkPrograms.TargetPath = strCurrFolder & "\MyApplication.exe"
oShellLinkPrograms.WindowStyle = 1
oShellLinkPrograms.Hotkey = ""
oShellLinkPrograms.IconLocation = strCurrFolder & "\MyApplication.exe, 0"
oShellLinkPrograms.Description = "MyApplication"
oShellLinkPrograms.WorkingDirectory = strCurrFolder
oShellLinkPrograms.Save 
Posted by 배트
출처 : http://alones.byus.net/moniwiki/wiki.php/c_cpp_windows_tip_excel?action=show

목차

1 Excel Automation without MFC
2 CAloExcelAuto.h
3 CAloExcelAuto.cpp
4 Example code
5 Binary
5.1 source code
5.2 chm file
6 References


 

1 Excel Automation without MFC #

MFC에서 MS Office의 excel을 이용해서 자동 생성된 코드로 엑셀 (excel)을 자동화 (ole autoamtion) 하는 방법을 정리 하고 이 것을 class로 만들어 보았습니다.
즉, Win32로 엑셀을 자동화하는 클래스입니다.
실제 자료를 찾아 보면 excel의 몇 몇 기초적인 기능들을 사용하기 위한 예제 코드를 찾기가 쉽지 않기도 해서 정리해보았습니다.
우선 MFC를 이용하지 않고 Win32를 이용해서 엑셀을 자동화하면 다음과 같은 이점들이 있을 것입니다.

  • 원하는 것을 정확히 수행할 수 있습니다.
  • 코드가 MFC를 이용한 것 보다 작고 빠릅니다.
  • 디버깅 하기 쉽습니다.
  • 가장 큰 이유는 오피스 버전에 종속되지 않아도 되는 것입니다.
    • 실제로 이게 가장 큰 이유인 것 같습니다. Office 2003에서 MFC를 이용해서 자동화 코드를 만들었다면 하위 버전에서 동작하지 않아서 힘든 경우가 많았습니다.

※ VB 등으로 쉽게 엑셀을 핸들링 할 수도 있겠지만 -_-;;

2 CAloExcelAuto.h #


/**
 * @file        AloExcelAuto.h
 * @brief   CAloExcelAuto class 
 * @author      alones
 * @date initial version: 2007.07.22 
 */

#include "ole2.h"               // OLE2 Definitions

/**
 * @class CAloExcelAuto
 * @brief excel automation class
 */
class CAloExcelAuto
{
private:
        bool            m_bCOMInited;   /**< check if com ini*/
        bool            m_bInit ;               /**< check if excel init */

        struct PROPERTIES
        {
                IDispatch *pXlApp;              /**< Excel.exe */
                IDispatch *pXlBooks;    /**< Ptr To MainFrame -> Can open new Workbooks */
                IDispatch *pXlBook;     /**< A Workbook. Has more Sheets (Z-Layers) */
                IDispatch *pXlSheet;    /**< What U see. Has X and Y*/
        } m_instance;

public:
        /**
         * @brief make excel app visible or not
         * @param[in]   bVisible(bool) if true, make excel app visibile
         * @return      int     if success 1, otherwise negative number
        */
        int SetVisible(bool bVisible);

        /**
         * @brief open excel file 
         * @return              int     if success 1, otherwise negative number
         */
        int Open(const char* file);

        /**
         * @brief set active sheet to read and to write
         */
        int SetActiveSheet(int nSheet);

        /**
         * @brief               get cell data on current active sheet
         * @param[in]   pPos (char*)    cell position e.g. B4
         * @param[out]  pData (char*)   cell data. string
         */
        bool GetData(char* pPos, char* pData);

        /**
         * @brief               set cell data on current active sheet
         * @param[in]   pPos (char*)    cell position e.g. B4
         * @param[in]   pData (char*)   cell data. string
         */
        bool SetData(char* pPos, char* pData);

        /**
         * @brief save workbook
         */
        void Save();

        /**
         * @brief close workbook
         */
        void Close();

protected:
        /**
         * @brief Open a new Excel Window and create a new worksheet
         */
        int Init();

        /**
         * @brief       check if excel init
         * @return      if excel init, return true, otherwise false
         */
        bool CheckExcelInit(){return m_bInit;};

        // constructor & destructor
public:
        /**
         * @brief constructor
         */
        CAloExcelAuto();

        /**
         * @brief destructor
         */
        virtual ~CAloExcelAuto();

};


//


3 CAloExcelAuto.cpp #


/**
 * @file        AloExcelAuto.cpp
 * @brief   CAloExcelAuto class impl.
 * @author      alones
 * @date initial version: 2007.07.22
 */

#include "AloExcelAuto.h"
#include "stdio.h"
#include <comdef.h>

/**
 * @brief 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);
                switch(hr)
                {
                case DISP_E_BADPARAMCOUNT:
                        MessageBox(NULL, "DISP_E_BADPARAMCOUNT", "Error:", 0x10010);
                        break;
                case DISP_E_BADVARTYPE:
                        MessageBox(NULL, "DISP_E_BADVARTYPE", "Error:", 0x10010);
                        break;
                case DISP_E_EXCEPTION:
                        MessageBox(NULL, "DISP_E_EXCEPTION", "Error:", 0x10010);
                        break;
                case DISP_E_MEMBERNOTFOUND:
                        MessageBox(NULL, "DISP_E_MEMBERNOTFOUND", "Error:", 0x10010);
                        break;
                case DISP_E_NONAMEDARGS:
                        MessageBox(NULL, "DISP_E_NONAMEDARGS", "Error:", 0x10010);
                        break;
                case DISP_E_OVERFLOW:
                        MessageBox(NULL, "DISP_E_OVERFLOW", "Error:", 0x10010);
                        break;
                case DISP_E_PARAMNOTFOUND:
                        MessageBox(NULL, "DISP_E_PARAMNOTFOUND", "Error:", 0x10010);
                        break;
                case DISP_E_TYPEMISMATCH:
                        MessageBox(NULL, "DISP_E_TYPEMISMATCH", "Error:", 0x10010);
                        break;
                case DISP_E_UNKNOWNINTERFACE:
                        MessageBox(NULL, "DISP_E_UNKNOWNINTERFACE", "Error:", 0x10010);
                        break;
                case DISP_E_UNKNOWNLCID:
                        MessageBox(NULL, "DISP_E_UNKNOWNLCID", "Error:", 0x10010);
                        break;
                case DISP_E_PARAMNOTOPTIONAL:
                        MessageBox(NULL, "DISP_E_PARAMNOTOPTIONAL", "Error:", 0x10010);
                        break;
                }
                // _exit(0);
                return hr;
        }
    // End variable-argument section...
    va_end(marker);

    delete [] pArgs;

    return hr;
}


/**
 * @brief constructor
 */
CAloExcelAuto::CAloExcelAuto()
{
        // initialize COM for this thread...
        if(CoInitialize(NULL)!=S_OK)
                m_bCOMInited = false;
        else
                m_bCOMInited = true;
}

/**
 * @brief destructor
 */
CAloExcelAuto::~CAloExcelAuto()
{
        // Uninitialized COM for this thread...
        if (m_bCOMInited)
                CoUninitialize();
}

/**
 * @brief Open a new Excel Window and create a new worksheet
 */
int CAloExcelAuto::Init()
{
        // 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...   
   hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&m_instance.pXlApp);
   if(FAILED(hr))
   {
      ::MessageBox(NULL, "Excel not registered properly", "Error", 0x10010);
      return -2;
   }

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

   m_bInit = true;

   return 1;
}

/**
 * @brief make excel app visible or not
 */
int CAloExcelAuto::SetVisible(bool bVisible)
{
        if( !CheckExcelInit() )
        {
                ::MessageBox(NULL, "Excel is not initialzed", "Error", 0x10010);
                return -1;
        }

        // Make it visible (i.e. app.visible = 1)       
        VARIANT x;
        x.vt = VT_I4;
        x.lVal = bVisible ? 1 : 0;
        HRESULT hr = AutoWrap(DISPATCH_PROPERTYPUT, NULL, m_instance.pXlApp, L"Visible", 1, x);
        if( FAILED(hr))
        {
                return -1;
        }

        return 1;
}

/**
 * @brief opne excel file 
 * @param[in]   file (const char*) the full path of the excel file to open 
 * @return              int     if success 1, otherwise negative number  
 */
int CAloExcelAuto::Open(const char* file)
{
        if( Init() < 0)
        {
                ::MessageBox(NULL, "Fail to init excel", "Error", 0x10010);
                return -1;
        }

        if( !CheckExcelInit() )
        {
                ::MessageBox(NULL, "Excel is not initialzed", "Error", 0x10010);
                return -1;
        }

        if( file == NULL)
        {
                ::MessageBox(NULL, "file name is null", "Error", 0x10010);
                return -1;
        }

        VARIANT result;
        VariantInit(&result);

        _bstr_t str = file;

        VARIANT vaPath;
        vaPath.vt = VT_BSTR;
        vaPath.bstrVal = ::SysAllocString(str);

        AutoWrap(DISPATCH_METHOD, &result, m_instance.pXlBooks, L"Open", 1, vaPath);
        m_instance.pXlBook = result.pdispVal;
        SysFreeString(vaPath.bstrVal);

        return 1;
}

/**
 * @brief set active sheet
 */
int CAloExcelAuto::SetActiveSheet(int nSheet)
{
        // Get ActiveSheet object


        VARIANT result;
        VariantInit(&result);

        VARIANT vaSheet;
        vaSheet.vt = VT_I4;
        vaSheet.lVal = nSheet;

        AutoWrap(DISPATCH_PROPERTYGET, &result, m_instance.pXlApp, L"Worksheets", 1, vaSheet);
        m_instance.pXlSheet = result.pdispVal;

        return 1;

}

/**
 * @brief save workbook
 */
void CAloExcelAuto::Save()
{
        // Set .Saved property of workbook to 'Saved'
        //VARIANT x;
        //x.vt = VT_I4;
        //x.lVal = bAsk ? 1 : 0;
        //AutoWrap(DISPATCH_PROPERTYPUT, NULL, m_instance.pXlBook, L"Saved", 1, x);     

        AutoWrap(DISPATCH_METHOD, NULL, m_instance.pXlApp, L"Save", 0);

}

/**
 * @brief close workbook
 */
void CAloExcelAuto::Close()
{
        //Save();       
        // Tell Excel to quit (i.e. App.Quit)
        AutoWrap(DISPATCH_METHOD, NULL, m_instance.pXlApp, L"Quit", 0);
        m_instance.pXlSheet->Release();
        m_instance.pXlBook->Release();
        m_instance.pXlBooks->Release();
        m_instance.pXlApp->Release();

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

/**
 * @brief               get cell data on current active sheet
 * @param[in]   pPos (char*)    cell position e.g. B4
 * @param[out]  pData (char*)   cell data. string
 */
bool CAloExcelAuto::GetData(char* pPos, char* pData)
{
        if( pPos == NULL || pData == NULL)
                return false;

        // Get Range object for the Range A1:O15...
        _bstr_t str;
        str=pPos; // BSTR
        str+=":";
        str+=pPos;

        IDispatch *pXlRange;
        {
                VARIANT parm;
                parm.vt = VT_BSTR;
                parm.bstrVal = ::SysAllocString(str);
                VARIANT result;
                VariantInit(&result);
                AutoWrap(DISPATCH_PROPERTYGET, &result, m_instance.pXlSheet, L"Range", 1, parm);
                VariantClear(&parm);
                pXlRange = result.pdispVal;
        }

        VARIANT tmp;
        tmp.vt=VT_BSTR;
        AutoWrap(DISPATCH_PROPERTYGET, &tmp, pXlRange, L"Value", 0, 0);

        if (tmp.vt==VT_EMPTY)
        {
                pData[0]='\0';
        }
        else
        {
                VariantChangeType(&tmp, &tmp, VARIANT_NOUSEROVERRIDE, VT_BSTR);
                str=tmp.bstrVal;
                strcpy(pData, str);
        }
        // Release references...
        pXlRange->Release();
        return true;
}

/**
 * @brief               set cell data on current active sheet
 * @param[in]   pPos (char*)    cell position e.g. B4
 * @param[in]   pData (char*)   cell data. string
 */
bool CAloExcelAuto::SetData(char* pPos, char* pData)
{
        if( pPos == NULL || pData == NULL)
                return false;

        _bstr_t str=pData;

        VARIANT tmp;
        tmp.vt=VT_BSTR;
        tmp.bstrVal=::SysAllocString(str);

        // Get Range object for the Range       
        str=pPos;
        str+=":";
        str+=pPos;

        IDispatch *pXlRange;
        {
                VARIANT parm;
                parm.vt = VT_BSTR;
                parm.bstrVal = ::SysAllocString(str);
                VARIANT result;
                VariantInit(&result);
                AutoWrap(DISPATCH_PROPERTYGET, &result, m_instance.pXlSheet, L"Range", 1, parm);
                VariantClear(&parm);

                pXlRange = result.pdispVal;
        }

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

        // Release references...
        pXlRange->Release();


        return true;
}

//


4 Example code #


#include "windows.h"
#include "AloExcelAuto.h"
#include <iostream>

void main()
{
        CAloExcelAuto excel;


        // 1. open an excel file
        excel.Open("C:\\source\\Test\\ExcelAutomation\\test.xls");

        // 2. set visible 굳이 필요하진 않습니다. 쇼용?
        excel.SetVisible(true);

        // 3. set active sheet. 2번 째 시트의 값을 읽고 쓰기 위해서
        excel.SetActiveSheet(2);

        // 4. get data C4의 값을 가져와보기
        char pData[256];
        excel.GetData("C4", pData);
        std::cout<<"data: "<<pData<<std::endl;

        // set data. C5에 쓰기
        memset(pData, 0x00, 256);
        strcpy( pData, "테스트");
        excel.SetData("C5", pData);

        // save
        excel.Save();

        // close
        excel.Close();
}

//


5 Binary #


5.1 source code #


6 References #

Posted by 배트
1.트레이아이콘 생성을  위한 준비

(1)트레이아이콘의 행위를 정의하기위한 사용자 정의 메시지만들기

-사용자 정의 메시지 id 부여
    ex) #define  UM_TRAYICON WM_USER + 1

생성방법:트레이아이콘의 메시지를 받을 윈도우에 멤버함수로 사용자정의메시지수신시 호출될 함수 정의

-함수의 정의내용 - 트레이아이콘에 대해서 어떤 버튼을 눌렀을 때 무슨 일을 하게 할 것인가를 정의하는 것이다.

ex)
LONG CCTrayIconTestDlg::TrayIconMessage(WPARAM wParam,LPARAM lParam)
{
    if(lParam == WM_LBUTTONDOWN) //트레이아이콘을 한 번클릭시
    {
        //메뉴를 로드한다.
        CMenu menu, *pMenu;
        CPoint pt;

        menu.LoadMenu(IDR_MENU1);
        pMenu = menu.GetSubMenu(0);//첫번째 주메뉴를 호출한다.
        GetCursorPos(&pt);
        pMenu->TrackPopupMenu(TPM_RIGHTALIGN,pt.x,pt.y,this); //트레이아이콘의 메뉴를 X,Y위치에 뛰운다.
    }

    //트레이 아이콘을 더블클릭했을때 윈도우가 보여지게 한다.
    if(lParam == WM_LBUTTONDBLCLK)
    {
        ShowWindow(SW_SHOW); //윈도우를 보여준다.
    }

-메시지루프에 등록
    ON_MESSAGE(UM_TRAYICON,TrayIconMessage)

 
(2)트레이 아이콘이 사용할 메뉴만들기

-메인메뉴를 그대로 사용한다면 상관없지만 ,메인메뉴가 없는 프로그램이라면 리소스에서 메뉴를 만들고 각각의 메뉴에 대해서 이벤트를 생성하자.

(3)트레이아이콘 리소스 만들기

-트레이아이콘 리소스를 만들고 그에 대한 컨트롤 아이디값을 부여하자.
    ex)IDI_TRAY와 같이


2.트레이아이콘 생성하기

(1)생성위치

-프로그램 초기화시가 적당하다.

(2)생성방법

NOTIFYICONDATA nid;
nid.cbSize = sizeof(nid);
nid.hWnd = m_hWnd;    //트레이아이콘과 통신할 윈도우 핸들
nid.uID = IDI_TRAY;   //트레이아이콘의 리소스ID(Data측면)
nid.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
nid.uCallbackMessage = UM_TRAYICON; //트레이아이콘의 메시지가 수신시 수신윈도우가 처리할 메시지
nid.hIcon = AfxGetApp()->LoadIcon(IDI_TRAY);//아이콘리소스(UI측면)
lstrcpy(nid.szTip,"트레이아이콘"); // 툴팁
// taskBar상태영역에 아이콘 추가,삭제,수정할때 시스템에 메시지 전달
Shell_NotifyIcon(NIM_ADD,&nid); //구조체내용으로 트레이아이콘을 등록한다.(Data측면)

SendMessage(WM_SETICON,(WPARAM)TRUE,(LPARAM)nid.hIcon);//트레이화면에 붙이기(UI측면)

 
3.생성후 고려사항

(1)메시지처리

메시지처리는 트레이아이콘과 연결된 윈도우가 수신시 TrayIconMessage가 호출되어 LPARM으로 전달된 트레이 아이콘의 행위에 따른 동작을 실시하면 된다.

 
(2) X버튼 클릭시

X버튼 클릭시 프로그램을 닫아버리면 트레이아이콘을 사용하는프로그램으로서의 가치가 없다.
따라서 다음과 같이 처리하는 것이 정석이다.

void CCTrayIconTestDlg::OnClose()
{
    // TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
    ShowWindow(SW_HIDE);//트레이아이콘으로만 표시되게한다.
    //CDialog::OnClose(); //WM_CLOSE의 자동생성코드인 이 부분을 주석처리한다.
}
 

(3)종료처리

-우선 트레이아이콘메뉴에 종료처리메뉴를 둔다

-WM_DESTORY메시지처리부분은 다음과 같이 처리한다.


void CCTrayIconTestDlg::OnDestroy()
{
    CDialog::OnDestroy();

    //리소스 해제
    NOTIFYICONDATA nid;
    nid.cbSize = sizeof(nid);
    nid.hWnd = m_hWnd;
    nid.uID = IDR_MENU1;

    Shell_NotifyIcon(NIM_DELETE,&nid);//삭제플래그를 준다.
}

-종료처리메뉴 클릭시 WM_DESTORY메시지를 호출되도록 PostQuitMessage(0);을 호출한다.

//(4)프로그램 종료후에도 트레이아이콘의 잔상이 남는 경우의 처리

//메인의 소멸자부분에서

//AfxGetApp()->LoadIcon(IDI_TRAY);/

[출처] [MFC]트레이아이콘 만들기|작성자 붉은바다
Posted by 배트


티스토리 툴바