방법: WRL을 사용하여 기본 COM 구성 요소 만들기

Windows 런타임 C++ 템플릿 라이브러리(WRL)를 사용하여 데스크톱 앱에서 사용할 기본 클래식 COM 구성 요소를 만들고 UWP(유니버설 Windows 플랫폼) 앱에 사용할 수 있습니다. 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은> 클래스가 IUnknown에서 파생되고 IInspectable에서 파생되지 않도록 지정합니다. IInspectable 는 Windows 런타임 앱 구성 요소에만 사용할 수 있습니다. CoCreatableClassCoCreateInstance와 같은 함수와 함께 사용할 수 있는 클래스에 대한 팩터리를 만듭니다.

    #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한 다음 다음 텍스트를 추가합니다. 예를 들어 C:\temp\WRLClassicCOM\Debug\CalculatorComponent.dlldll-path>를 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
    */
    

강력한 프로그래밍

이 문서에서는 표준 COM 함수를 사용하여 Windows 런타임 C++ 템플릿 라이브러리를 사용하여 COM 구성 요소를 작성하고 COM 지원 기술에서 사용할 수 있도록 할 수 있음을 보여 줍니다. 데스크톱 앱에서 Microsoft::WRL::ComPtr과 같은 Windows 런타임 C++ 템플릿 라이브러리 형식을 사용하여 COM 및 기타 개체의 수명을 관리할 수도 있습니다. 다음 코드는 Windows 런타임 C++ 템플릿 라이브러리를 사용하여 포인터의 ICalculatorComponent 수명을 관리합니다. CoInitializeWrapper 클래스는 COM 라이브러리가 해제되도록 보장하고 COM 라이브러리의 수명이 ComPtr 스마트 포인터 개체보다 길도록 보장하는 RAII 래퍼입니다.

#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)