方法: WRL を使用して従来の COM コンポーネントを作成する

Windows ランタイム C++ テンプレート ライブラリ (WRL) を使用して、デスクトップ アプリで使用する基本的なクラシック COM コンポーネントを作成できます。また、ユニバーサル Windows プラットフォーム (UWP) アプリにも使用できます。 COM コンポーネントを作成する場合、Windows ランタイム C++ テンプレート ライブラリでは ATL よりも少ないコードが必要な場合があります。 Windows ランタイム C++ テンプレート ライブラリがサポートしている COM のサブセットの詳細については、「Windows ランタイム C++ テンプレート ライブラリ (WRL)」を参照してください。

このドキュメントでは、Windows ランタイム C++ テンプレート ライブラリを使用して基本的な COM コンポーネントを作成する方法について説明します。 ニーズに最適な配置メカニズムを使用できますが、このドキュメントでは、デスクトップ アプリから COM コンポーネントを登録して使用する基本的な方法についても説明します。

Windows ランタイム C++ テンプレート ライブラリを使用して、基本的なクラシック COM コンポーネントを作成するには

  1. Visual Studio で、空のソリューション プロジェクトを作成します。 プロジェクトに WRLClassicCOM などの名前を付けます。

  2. このソリューションに Win32 プロジェクトを追加します。 プロジェクトに CalculatorComponent などの名前を付けます。 [アプリケーション設定] タブで [DLL] を選択します。

  3. プロジェクトに MIDL ファイル (.idl) ファイルを追加します。 ファイルに名前を付けます (例: CalculatorComponent.idl)。

  4. CalculatorComponent.idl に次のコードを追加します。

    import "ocidl.idl";
    
    [uuid(0DBABB94-CE99-42F7-ACBD-E698B2332C60), version(1.0)] 
    interface ICalculatorComponent : IUnknown
    {
        HRESULT Add([in] int a, [in] int b, [out, retval] int* value);
    }
    
    [uuid(9D3E6826-CB8E-4D86-8B14-89F0D7EFCD01), version(1.0)]
    library CalculatorComponentLib
    {
        [uuid(E68F5EDD-6257-4E72-A10B-4067ED8E85F2), version(1.0)]
        coclass CalculatorComponent
        {
            [default] interface ICalculatorComponent;
        }
    };
    
  5. CalculatorComponent.cpp で、CalculatorComponent クラスを定義します。 CalculatorComponent クラスは Microsoft::WRL::RuntimeClass から継承されます。 Microsoft::WRL::RuntimeClassFlags<ClassicCom> は、クラスが IInspectable ではなく IUnknown から派生することを指定します。 (IInspectableアプリ コンポーネント CoCreatableClass Windows ランタイムでのみ使用できます)。CoCreateInstance などの関数で使用できるクラスのファクトリを作成します。

    #include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
    
    #include "CalculatorComponent_h.h"
    #include <wrl.h>
    
    using namespace Microsoft::WRL;
    
    class CalculatorComponent: public RuntimeClass<RuntimeClassFlags<ClassicCom>, ICalculatorComponent>
    {
    public:
        CalculatorComponent()
        {
        }
    
        STDMETHODIMP Add(_In_ int a, _In_ int b, _Out_ int* value)
        {
            *value = a + b;
            return S_OK;
        }
    };
    
    CoCreatableClass(CalculatorComponent);
    
  6. 次のコードを使用して、dllmain.cpp のコードを置換します。 このファイルで DLL のエクスポート関数が定義されます。 これらの関数は、Microsoft::WRL::Module クラスを使用して、モジュールのクラス ファクトリを管理します。

    #include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
    #include <wrl\module.h>
    
    using namespace Microsoft::WRL;
    
    #if !defined(__WRL_CLASSIC_COM__)
    STDAPI DllGetActivationFactory(_In_ HSTRING activatibleClassId, _COM_Outptr_ IActivationFactory** factory)
    {
        return Module<InProc>::GetModule().GetActivationFactory(activatibleClassId, factory);
    }
    #endif
    
    #if !defined(__WRL_WINRT_STRICT__)
    STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv)
    {
        return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
    }
    #endif
    
    STDAPI DllCanUnloadNow()
    {
        return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
    }
    
    STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*)
    {
        if (reason == DLL_PROCESS_ATTACH)
        {
            DisableThreadLibraryCalls(hinst);
        }
        return TRUE;
    }
    
  7. モジュール定義ファイル (.def) ファイルをプロジェクトに追加します。 ファイルに名前を付けます (例: CalculatorComponent.def)。 このファイルによって、エクスポートされる関数の名前がリンカーに設定されます。 プロジェクトの [プロパティ ページ] ダイアログを開き、[構成プロパティ]>[リンカー]>[入力][モジュール定義ファイル] プロパティを DEF ファイルに設定します。

  8. CalculatorComponent.def に次のコードを追加します。

    LIBRARY
    
    EXPORTS
        DllGetActivationFactory PRIVATE
        DllGetClassObject       PRIVATE
        DllCanUnloadNow         PRIVATE
    
  9. リンカー行に runtimeobject.lib を追加します。 詳細については、「リンカー入力としての .Lib ファイル」を参照してください。

デスクトップ アプリから COM コンポーネントを使用するには

  1. Windows レジストリを使用して COM コンポーネントを登録します。 これを行うには、登録エントリ ファイルを作成して RegScript.reg という名前を付け、次のテキストを追加します。 <dll-path> を DLL のパスに置換します (例: C:\temp\WRLClassicCOM\Debug\CalculatorComponent.dll)。

    Windows Registry Editor Version 5.00
    
    [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{E68F5EDD-6257-4E72-A10B-4067ED8E85F2}]
    @="CalculatorComponent Class"
    
    [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{E68F5EDD-6257-4E72-A10B-4067ED8E85F2}\InprocServer32]
    @="<dll-path>"
    "ThreadingModel"="Apartment"
    
    [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{E68F5EDD-6257-4E72-A10B-4067ED8E85F2}\Programmable]
    
    [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{E68F5EDD-6257-4E72-A10B-4067ED8E85F2}\TypeLib]
    @="{9D3E6826-CB8E-4D86-8B14-89F0D7EFCD01}"
    
    [HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{E68F5EDD-6257-4E72-A10B-4067ED8E85F2}\Version]
    @="1.0"
    
  2. RegScript.reg を実行するか、プロジェクトの ビルド後イベントに追加します。 詳細については、「[ビルド前に実行するコマンド ライン] / [ビルド後に実行するコマンド ライン] ダイアログ ボックス」を参照してください。

  3. ソリューションに Win32 コンソール アプリケーション プロジェクトを追加します。 プロジェクトに Calculator などの名前を付けます。

  4. 次のコードを使用して Calculator.cpp の内容を置換します。

    #include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
    
    #include "..\CalculatorComponent\CalculatorComponent_h.h"
    
    const IID IID_ICalculatorComponent = {0x0DBABB94,0xCE99,0x42F7,0xAC,0xBD,0xE6,0x98,0xB2,0x33,0x2C,0x60};
    const CLSID CLSID_CalculatorComponent = {0xE68F5EDD,0x6257,0x4E72,0xA1,0x0B,0x40,0x67,0xED,0x8E,0x85,0xF2};
    
    // Prints an error string for the provided source code line and HRESULT
    // value and returns the HRESULT value as an int.
    int PrintError(unsigned int line, HRESULT hr)
    {
        wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
        return hr;
    }
    
    int wmain()
    {
        HRESULT hr;
    
        // Initialize the COM library.
        hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
        if (FAILED(hr))
        {
            return PrintError(__LINE__, hr);
        }
    
        ICalculatorComponent* calc = nullptr; // Interface to COM component.
    
        // Create the CalculatorComponent object.
        hr = CoCreateInstance(CLSID_CalculatorComponent, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&calc));
        if (SUCCEEDED(hr))
        {
            // Test the component by adding two numbers.
            int result;
            hr = calc->Add(4, 5, &result);
            if (FAILED(hr))
            {
                PrintError(__LINE__, hr);
            }
            else
            {
                wprintf_s(L"result = %d\n", result);
            }
    
            // Free the CalculatorComponent object.
            calc->Release();
        }
        else
        {
            // Object creation failed. Print a message.
            PrintError(__LINE__, hr);
        }
    
        // Free the COM library.
        CoUninitialize();
    
        return hr;
    }
    /* Output:
    result = 9
    */
    

信頼性の高いプログラミング

このドキュメントでは、Windows ランタイム C++ テンプレート ライブラリを使用して COM コンポーネントを作成し、あらゆる COM 対応テクノロジで利用可能にできることを、標準の COM 関数を使用して示します。 また、Microsoft::WRL::ComPtr などの Windows ランタイム C++ テンプレート ライブラリの型をデスクトップ アプリで使用して、COM や他のオブジェクトの有効期間を管理することもできます。 次のコードでは、Windows ランタイム C++ テンプレート ライブラリを使用して、ICalculatorComponent ポインターの有効期間を管理します。 CoInitializeWrapper クラスは RAII ラッパーでの 1 つで、COM ライブラリが解放されることと、COM ライブラリの有効期間が ComPtr スマート ポインター オブジェクトよりも長く続くことを保証します。

#include "pch.h" // Use stdafx.h in Visual Studio 2017 and earlier
#include <wrl.h>

#include "..\CalculatorComponent\CalculatorComponent_h.h"

using namespace Microsoft::WRL;

const IID IID_ICalculatorComponent = {0x0DBABB94,0xCE99,0x42F7,0xAC,0xBD,0xE6,0x98,0xB2,0x33,0x2C,0x60};
const CLSID CLSID_CalculatorComponent = {0xE68F5EDD,0x6257,0x4E72,0xA1,0x0B,0x40,0x67,0xED,0x8E,0x85,0xF2};

// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
    wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
    return hr;
}

int wmain()
{
    HRESULT hr;

    // RAII wrapper for managing the lifetime of the COM library.
    class CoInitializeWrapper
    {
        HRESULT _hr;
    public:
        CoInitializeWrapper(DWORD flags)
        {
            _hr = CoInitializeEx(nullptr, flags);
        }
        ~CoInitializeWrapper()
        {
            if (SUCCEEDED(_hr))
            {
                CoUninitialize();
            }
        }
        operator HRESULT()
        {
            return _hr;
        }

    };

    // Initialize the COM library.
    CoInitializeWrapper initialize(COINIT_APARTMENTTHREADED);
    if (FAILED(initialize))
    {
        return PrintError(__LINE__, initialize);
    }

    ComPtr<ICalculatorComponent> calc; // Interface to COM component.

    // Create the CalculatorComponent object.
    hr = CoCreateInstance(CLSID_CalculatorComponent, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(calc.GetAddressOf()));
    if (SUCCEEDED(hr))
    {
        // Test the component by adding two numbers.
        int result;
        hr = calc->Add(4, 5, &result);
        if (FAILED(hr))
        {
            return PrintError(__LINE__, hr);
        }
        wprintf_s(L"result = %d\n", result);
    }
    else
    {
        // Object creation failed. Print a message.
        return PrintError(__LINE__, hr);
    }

    return 0;
}

関連項目

Windows ランタイム C++ テンプレート ライブラリ (WRL)