Porady: tworzenie klasycznego składnika COM za pomocą biblioteki WRL

Możesz użyć biblioteki szablonów języka C++ środowisko wykonawcze systemu Windows C++ (WRL), aby utworzyć podstawowe klasyczne składniki COM do użycia w aplikacjach klasycznych, a także używać ich w aplikacjach platformy platforma uniwersalna systemu Windows (UWP). W przypadku tworzenia składników COM biblioteka szablonów języka C++ środowisko wykonawcze systemu Windows może wymagać mniejszego kodu niż ATL. Aby uzyskać informacje o podzestawie modelu COM obsługiwanego przez bibliotekę szablonów języka C++ środowisko wykonawcze systemu Windows, zobacz środowisko wykonawcze systemu Windows biblioteki szablonów języka C++ (WRL).

W tym dokumencie pokazano, jak utworzyć podstawowy składnik COM za pomocą biblioteki szablonów języka środowisko wykonawcze systemu Windows C++. Chociaż można użyć mechanizmu wdrażania, który najlepiej odpowiada Twoim potrzebom, ten dokument przedstawia również podstawowy sposób rejestrowania i korzystania ze składnika COM z aplikacji klasycznej.

Aby użyć biblioteki szablonów języka C++ środowisko wykonawcze systemu Windows do utworzenia podstawowego klasycznego składnika COM

  1. W programie Visual Studio utwórz projekt Puste rozwiązanie . Nadaj projektowi nazwę , na przykład WRLClassicCOM.

  2. Dodaj projekt Win32 do rozwiązania. Nadaj projektowi nazwę , na przykład CalculatorComponent. Na karcie Ustawienia aplikacji wybierz pozycję DLL.

  3. Dodaj plik Midl File (idl) do projektu. Nazwij plik, na przykład CalculatorComponent.idl.

  4. Dodaj ten kod do pliku 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. W pliku CalculatorComponent.cpp zdefiniuj klasę CalculatorComponent . Klasa CalculatorComponent dziedziczy z klasy Microsoft::WRL::RuntimeClass. Microsoft::WRL::RuntimeClassFlags<ClassicCom> określa, że klasa pochodzi z elementu IUnknown , a nie IInspectable. (IInspectable jest dostępny tylko do środowisko wykonawcze systemu Windows składników aplikacji). CoCreatableClass Tworzy fabrykę dla klasy, która może być używana z funkcjami, takimi jak 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. Użyj następującego kodu, aby zastąpić kod w pliku dllmain.cpp. Ten plik definiuje funkcje eksportu bibliotek DLL. Te funkcje używają klasy Microsoft::WRL::Module do zarządzania fabrykami klas dla modułu.

    #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. Dodaj plik definicji modułu (.def) do projektu. Nazwij plik, na przykład CalculatorComponent.def. Ten plik udostępnia konsolidatorowi nazwy funkcji do wyeksportowania. Otwórz okno dialogowe Strony właściwości dla projektu, a następnie w obszarze Właściwości konfiguracji Dane>wejściowe konsolidatora>ustaw właściwość Plik definicji modułu na plik DEF.

  8. Dodaj ten kod do pliku CalculatorComponent.def:

    LIBRARY
    
    EXPORTS
        DllGetActivationFactory PRIVATE
        DllGetClassObject       PRIVATE
        DllCanUnloadNow         PRIVATE
    
  9. Dodaj element runtimeobject.lib do wiersza konsolidatora. Aby dowiedzieć się, jak to zrobić, zobacz .Lib Pliki jako dane wejściowe konsolidatora.

Aby korzystać ze składnika COM z aplikacji klasycznej

  1. Zarejestruj składnik COM w rejestrze systemu Windows. W tym celu utwórz plik wpisów rejestracji, nadaj mu RegScript.regnazwę i dodaj następujący tekst. Zastąp ścieżkę dll ścieżką> do biblioteki DLL — na przykład 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. Uruchom polecenie RegScript.reg lub dodaj go do zdarzenia po kompilacji projektu. Aby uzyskać więcej informacji, zobacz Okno dialogowe Wiersza polecenia zdarzenia przed kompilacją/po kompilacji.

  3. Dodaj projekt aplikacji konsolowej Win32 do rozwiązania. Nadaj projektowi nazwę , na przykład Calculator.

  4. Użyj tego kodu, aby zastąpić zawartość elementu 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
    */
    

Niezawodne programowanie

Ten dokument używa standardowych funkcji COM, aby zademonstrować, że można użyć biblioteki szablonów języka C++ środowisko wykonawcze systemu Windows do utworzenia składnika COM i udostępnienia go dowolnej technologii z obsługą modelu COM. Można również użyć środowisko wykonawcze systemu Windows typów bibliotek szablonów języka C++, takich jak Microsoft::WRL::ComPtr w aplikacji klasycznej, aby zarządzać okresem istnienia modelu COM i innych obiektów. Poniższy kod używa biblioteki szablonów języka C++ środowisko wykonawcze systemu Windows do zarządzania okresem ICalculatorComponent istnienia wskaźnika. Klasa CoInitializeWrapper to otoka RAII, która gwarantuje, że biblioteka COM jest zwolniona, a także gwarantuje, że okres istnienia biblioteki COM przetrwa ComPtr inteligentny obiekt wskaźnika.

#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;
}

Zobacz też

Biblioteka szablonów języka C++ środowiska uruchomieniowego systemu Windows (WRL)