デュアルインターフェース - サンプルCOMサーバ

いちごパック > COM/ActiveXの解説 > デュアルインターフェース - サンプルCOMサーバ
デュアルインターフェースのサンプルCOMサーバです。 EXEサーバとして実装しています。
このソースコードのほかに、サンプルMIDLコードで作成した.idlファイルも必要になります。 このソースコードでは、.idlファイルの名前をichigocom4_if.idlとし、 自動生成されるichigocom4_if.hとichigocom4_if_i.cを利用しています。
コード全体をコピーしたい場合は、著作権表示といちごパックへのリンクを残した形でコピーしてください。 サンプルコードとしてその一部を利用、参照いただくぶんには問題ありません。
ヘルパ関数については、説明を簡潔にするために別ページでまとめています。 サンプルで用いたヘルパ関数もあわせてご覧ください。
COMを使う32ビットまたは64ビットプログラムの場合は、リンク時に次のライブラリを与えてください。
kernel32.libuser32.libadvapi32.librpcrt4.libole32.liboleaut32.lib

このコードをVisual C++のプロジェクトとしてビルドしたい場合は、次の準備を行ったうえでビルドしてください。
  • Visual C++ → Win32 → Win32プロジェクトを選択して空のプロジェクトを作成
  • このサンプルコードを.cppファイルとして追加
  • サンプル.idlファイルのファイル名をichigocom4_if.idlとして、プロジェクトに追加
  • プロジェクトのプロパティを開き、リンカの設定でリンク時のライブラリとして上記のライブラリを追加

  • //
    // Copyright (c) 2016 The Ichigopack Project (http://ichigopack.net/).
    //
    
    #include "ichigocomhelper.h"
    #include <olectl.h>
    #include <sstream>
    #include <iomanip>
    #include <string.h>
    #include <new>
    #include <exception>
    #include <vector>
    #include "ichigocom4_if.h"
    
    
    #define LOGFILENAME L"c:\\tmp\\ichigo4.txt"
    
    #define PROGID_Ichigo4          L"Ichigo4.Logwriter"
    
    
    // {287AF1C7-7618-4C81-952C-B70238A474CB}
    static const GUID CLSID_Ichigo4 =
    { 0x287af1c7, 0x7618, 0x4c81, { 0x95, 0x2c, 0xb7, 0x2, 0x38, 0xa4, 0x74, 0xcb } };
    
    
    class CIchigo4Factory : public IClassFactory
    {
    public:
        CIchigo4Factory();
        ~CIchigo4Factory();
    
        // IUnknown
        HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, LPVOID* ppv );
        ULONG STDMETHODCALLTYPE AddRef();
        ULONG STDMETHODCALLTYPE Release();
        // IClassFactory
        HRESULT STDMETHODCALLTYPE CreateInstance( IUnknown* pUnkOuter,
                REFIID riid, LPVOID* ppv );
        HRESULT STDMETHODCALLTYPE LockServer( BOOL fLock );
    };
    
    class CIchigo4 : public IIchigo4
    {
    public:
        CIchigo4();
        ~CIchigo4();
    
        HRESULT Init();
    
        // IUnknown
        HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, LPVOID* ppv );
        ULONG STDMETHODCALLTYPE AddRef();
        ULONG STDMETHODCALLTYPE Release();
        // IDispatch
        HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT* pcTInfo);
        HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid,
                                              ITypeInfo** ppTInfo);
        HRESULT STDMETHODCALLTYPE GetIDsOfNames(
            REFIID riidNull, LPOLESTR* rgszNames, UINT cNames,
            LCID lcid, DISPID* rgDispId);
        HRESULT STDMETHODCALLTYPE Invoke(
            DISPID dispIdMember, REFIID riidNull, LCID lcid,
            WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
            EXCEPINFO* pExcepInfo, UINT* puArgErr);
        // IIchigo4
        HRESULT STDMETHODCALLTYPE Write(BSTR bstrWrite);
    
    private:
        LONG    m_nRef;
        ITypeInfo*  m_pti;
    };
    
    /***********************************************************************/
    
    HRESULT RunRegisterServer( HINSTANCE hinstEXE,
                               bool per_user_flag ) throw()
    {
        try {
            std::wstring    wstrName = suppGetModuleFileName( hinstEXE );
            HKEY    hkeyRoot;
            std::wstring    wstrSubkey;
            std::wstring    wstrValue;
            LONG    regerr;
            // Coclass
            wstrSubkey = suppGetRegKeyCLSID( &hkeyRoot, per_user_flag, CLSID_Ichigo4,
                                             L"LocalServer32" );
            if ( wstrSubkey.empty() || wstrName.empty() ) {
                return E_FAIL;
            }
            regerr = suppRegSetStringW(hkeyRoot, wstrSubkey.c_str(), NULL,
                                       wstrName.c_str() );
            if ( regerr != ERROR_SUCCESS ) {
                return SELFREG_E_CLASS;
            }
            // ProgID
            wstrSubkey = suppGetRegKeyCLSID( &hkeyRoot, per_user_flag, CLSID_Ichigo4,
                                             L"ProgID" );
            if ( wstrSubkey.empty() ) {
                return E_FAIL;
            }
            regerr = suppRegSetStringW(hkeyRoot, wstrSubkey.c_str(), NULL,
                                       PROGID_Ichigo4 );
            if ( regerr != ERROR_SUCCESS ) {
                return SELFREG_E_CLASS;
            }
            wstrSubkey = suppGetRegKeyProgID( &hkeyRoot, per_user_flag,
                                              PROGID_Ichigo4, L"CLSID" );
            wstrValue = suppGuidToString( CLSID_Ichigo4, true );
            if ( wstrSubkey.empty() || wstrValue.empty() ) {
                return E_FAIL;
            }
            regerr = suppRegSetStringW(hkeyRoot, wstrSubkey.c_str(), NULL,
                                       wstrValue.c_str() );
            if ( regerr != ERROR_SUCCESS ) {
                return SELFREG_E_CLASS;
            }
            // TypeLib
            ITypeLib* ptl = NULL;
            HRESULT hr;
            hr = LoadTypeLib( wstrName.c_str(), &ptl );
            if ( SUCCEEDED(hr) ) {
                if ( per_user_flag ) {
                    hr = RegisterTypeLibForUser( ptl, (OLECHAR*) wstrName.c_str(), NULL );
                } else {
                    hr = RegisterTypeLib( ptl, wstrName.c_str(), NULL );
                }
                ptl->Release();
            }
            if ( FAILED(hr) ) {
                return hr;
            }
        } catch ( const std::exception& ) {
            return E_FAIL;
        }
        return S_OK;
    }
    
    HRESULT RunUnregisterServer( bool per_user_flag ) throw()
    {
        try {
            HKEY    hkeyRoot;
            std::wstring    wstrSubkey = suppGetRegKeyCLSID( &hkeyRoot, per_user_flag,
                                         CLSID_Ichigo4, NULL );
            LONG    regerr;
            if ( wstrSubkey.empty() ) {
                return E_FAIL;
            }
            regerr = suppRegRemoveKeyW(hkeyRoot, wstrSubkey.c_str());
            if ( regerr != ERROR_SUCCESS ) {
                return SELFREG_E_CLASS;
            }
            HRESULT hr;
    #if defined(WIN64) || defined(_WIN64)
            const SYSKIND syskind = SYS_WIN64;
    #else
            const SYSKIND syskind = SYS_WIN32;
    #endif
            if ( per_user_flag ) {
                hr = UnRegisterTypeLibForUser( LIBID_Ichigo4TypeLib, 1, 0, LOCALE_NEUTRAL,
                                               syskind );
            } else {
                hr = UnRegisterTypeLib( LIBID_Ichigo4TypeLib, 1, 0, LOCALE_NEUTRAL,
                                        syskind );
            }
            if ( FAILED(hr) ) {
                return hr;
            }
        } catch ( const std::exception& ) {
            return E_FAIL;
        }
        return S_OK;
    }
    
    /***********************************************************************/
    
    void LockServerProcess()
    {
        CoAddRefServerProcess();
    }
    
    void UnlockServerProcess()
    {
        if ( CoReleaseServerProcess() == 0 ) {
            PostQuitMessage(0);
        }
    }
    
    HRESULT RunEmbedding( HINSTANCE hinstEXE )
    {
        CIchigo4Factory factory;
        HRESULT hr;
        DWORD   dwRegister = 0;
        hr = CoRegisterClassObject(
                 CLSID_Ichigo4, static_cast<IClassFactory*>(&factory),
                 CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED, &dwRegister);
        if ( FAILED(hr) ) {
            return hr;
        }
        hr = CoResumeClassObjects();
        if ( SUCCEEDED(hr) ) {
            MSG msg;
            while (GetMessage(&msg, NULL, 0, 0) > 0) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        CoRevokeClassObject(dwRegister);
        return hr;
    }
    
    EXTERN_C int APIENTRY WinMain( HINSTANCE hinstEXE, HINSTANCE hinstNull,
                                   LPSTR lpCmdLine, int nCmdShow )
    {
        HRESULT hr;
        hr = CoInitialize(NULL);
        if ( FAILED(hr) ) {
            MessageBoxW( NULL, L"CoInitialize()", L"IchigoCom4", MB_OK );
            return 1;
        }
        const char* pcommand;
        bool invalid_command = false;
        pcommand = strchr(lpCmdLine, '/');
        if ( pcommand == NULL ) {
            pcommand = strchr(lpCmdLine, '-');
        }
        if ( pcommand == NULL ) {
            invalid_command = true;
        }
        if ( pcommand != NULL ) {
            char* ptoken = _strdup(pcommand + 1);
            if ( ptoken == NULL ) {
                hr = E_OUTOFMEMORY;
            } else {
                strtok( ptoken, " \t\r\n" );
                if ( _stricmp(ptoken, "embedding") == 0 ) {
                    hr = RunEmbedding( hinstEXE );
                } else if ( _stricmp(ptoken, "regserver") == 0 ) {
                    hr = RunRegisterServer( hinstEXE, false );
                } else if ( _stricmp(ptoken, "regserverperuser") == 0 ) {
                    hr = RunRegisterServer( hinstEXE, true );
                } else if ( _stricmp(ptoken, "unregserver") == 0 ) {
                    hr = RunUnregisterServer( false );
                } else if ( _stricmp(ptoken, "unregserverperuser") == 0 ) {
                    hr = RunUnregisterServer( true );
                } else {
                    invalid_command = true;
                }
                free(ptoken);
            }
        }
        CoUninitialize();
        if ( invalid_command ) {
            MessageBoxW( NULL, L"ichigocom4 /regserver|/unregserver", L"IchigoCom4",
                         MB_OK );
            return 1;
        }
        if ( FAILED(hr) ) {
            std::wostringstream wos;
            wos << L"error " << std::hex << std::setfill(L'0') << std::setw(8) << hr;
            MessageBoxW( NULL, wos.str().c_str(), L"IchigoCom4", MB_OK );
            return 1;
        }
        return 0;
    }
    
    /***********************************************************************/
    
    CIchigo4Factory::CIchigo4Factory()
    {
    }
    
    CIchigo4Factory::~CIchigo4Factory()
    {
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4Factory::QueryInterface( REFIID riid,
            LPVOID* ppv )
    {
        *ppv = NULL;
        if ( riid == IID_IUnknown || riid == IID_IClassFactory ) {
            *ppv = static_cast<IClassFactory*>( this );
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    
    ULONG STDMETHODCALLTYPE CIchigo4Factory::AddRef()
    {
        return 1;
    }
    
    ULONG STDMETHODCALLTYPE CIchigo4Factory::Release()
    {
        return 1;
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4Factory::CreateInstance(
        IUnknown* pUnkOuter, REFIID riid, LPVOID* ppv )
    {
        *ppv = NULL;
        if ( pUnkOuter != NULL ) {
            return CLASS_E_NOAGGREGATION;
        }
        CIchigo4* pObj = new (std::nothrow) CIchigo4();
        if ( pObj == NULL ) {
            return E_OUTOFMEMORY;
        }
        HRESULT hr;
        hr = pObj->Init();
        if ( SUCCEEDED(hr) ) {
            hr = pObj->QueryInterface( riid, ppv );
        }
        pObj->Release();
        return hr;
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4Factory::LockServer( BOOL fLock )
    {
        if ( fLock ) {
            LockServerProcess();
        } else {
            UnlockServerProcess();
        }
        return S_OK;
    }
    
    /***********************************************************************/
    
    CIchigo4::CIchigo4() : m_nRef(1), m_pti(NULL)
    {
        LockServerProcess();
    }
    
    CIchigo4::~CIchigo4()
    {
        if ( m_pti != NULL ) {
            m_pti->Release();
        }
        UnlockServerProcess();
    }
    
    HRESULT CIchigo4::Init()
    {
        HRESULT hr;
        ITypeLib* ptl = NULL;
        hr = LoadRegTypeLib( LIBID_Ichigo4TypeLib, 1, 0, LOCALE_NEUTRAL, &ptl );
        if ( SUCCEEDED(hr) ) {
            hr = ptl->GetTypeInfoOfGuid( IID_IIchigo4, &m_pti );
            ptl->Release();
        }
        return hr;
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4::QueryInterface( REFIID riid,
            LPVOID* ppv )
    {
        *ppv = NULL;
        if ( riid == IID_IUnknown || riid == IID_IDispatch ||
             riid == IID_IIchigo4 ) {
            *ppv = static_cast<IIchigo4*>( this );
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    
    ULONG STDMETHODCALLTYPE CIchigo4::AddRef()
    {
        return ++m_nRef;
    }
    
    ULONG STDMETHODCALLTYPE CIchigo4::Release()
    {
        if ( --m_nRef == 0 ) {
            delete this;
            return 0;
        }
        return m_nRef;
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4::GetTypeInfoCount(UINT* pcTInfo)
    {
        *pcTInfo = (m_pti != NULL) ? 1 : 0;
        return S_OK;
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4::GetTypeInfo(UINT iTInfo, LCID lcid,
            ITypeInfo** ppTInfo)
    {
        *ppTInfo = NULL;
        if ( iTInfo != 0 ) {
            return DISP_E_BADINDEX;
        }
        *ppTInfo = m_pti;
        m_pti->AddRef();
        return S_OK;
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4::GetIDsOfNames(
        REFIID riidNull, LPOLESTR* rgszNames, UINT cNames,
        LCID lcid, DISPID* rgDispId)
    {
        if ( riidNull != IID_NULL ) {
            return DISP_E_UNKNOWNINTERFACE;
        }
        return DispGetIDsOfNames( m_pti, rgszNames, cNames, rgDispId );
    }
    
    HRESULT STDMETHODCALLTYPE CIchigo4::Invoke(
        DISPID dispIdMember, REFIID riidNull, LCID lcid,
        WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
        EXCEPINFO* pExcepInfo, UINT* puArgErr)
    {
        if ( riidNull != IID_NULL ) {
            return DISP_E_UNKNOWNINTERFACE;
        }
        return DispInvoke(
                   this, m_pti, dispIdMember, wFlags, pDispParams,
                   pVarResult, pExcepInfo, puArgErr );
    }
    
    
    HRESULT STDMETHODCALLTYPE CIchigo4::Write( BSTR bstrWrite )
    {
        try {
            UINT    cLen = SysStringLen(bstrWrite);
            std::vector<char>   buf;
            int     cLenMB;
            cLenMB = WideCharToMultiByte( CP_ACP, 0, bstrWrite, cLen, NULL, 0, NULL,
                                          NULL );
            if ( cLenMB <= 0 ) {
                return E_FAIL;
            }
            buf.resize( cLenMB );
            cLenMB = WideCharToMultiByte( CP_ACP, 0, bstrWrite, cLen, &buf[0],
                                          buf.size(), NULL, NULL );
            if ( cLenMB <= 0 ) {
                return E_FAIL;
            }
            return suppLogFileAppendW( LOGFILENAME, &buf[0], cLenMB );
        } catch ( const std::exception& ) {
            return E_FAIL;
        }
    }
    
    
    

    関連ページ

  • COM/ActiveXの解説ページ 目次
  • タイプライブラリの登録とその利用