Comment : créer un composant COM classique à l'aide de WRL

Vous pouvez utiliser la bibliothèque de modèles C++ Windows Runtime (WRL) pour créer des composants COM classiques de base à utiliser dans les applications de bureau, en plus de l’utiliser pour les applications plateforme Windows universelle (UWP). Pour la création de composants COM, la bibliothèque de modèles C++ Windows Runtime peut nécessiter moins de code que l’ATL. Pour plus d’informations sur le sous-ensemble de COM pris en charge par la bibliothèque de modèles C++ Windows Runtime, consultez la bibliothèque de modèles C++ Windows Runtime (WRL).

Ce document montre comment utiliser la bibliothèque de modèles C++ Windows Runtime pour créer un composant COM de base. Bien que vous puissiez utiliser le mécanisme de déploiement qui correspond le mieux à vos besoins, ce document contient également une méthode de base pour inscrire et utiliser le composant COM à partir d'une application de bureau.

Pour utiliser la bibliothèque de modèles C++ Windows Runtime pour créer un composant COM classique de base

  1. Dans Visual Studio, créez un projet solution vide. Nommez le projet, par exemple WRLClassicCOM.

  2. Ajoutez un projet Win32 à la solution. Nommez le projet, par exemple CalculatorComponent. Sous l’onglet Application Paramètres, sélectionnez DLL.

  3. Ajoutez un fichier Midl File (.idl) au projet. Nommez le fichier, par exemple CalculatorComponent.idl.

  4. Ajoutez ce code à 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. Dans CalculatorComponent.cpp, définissez la classe CalculatorComponent. La CalculatorComponent classe hérite de Microsoft ::WRL ::RuntimeClass. Microsoft ::WRL ::RuntimeClassFlags ClassicCom spécifie que la classe dérive d’IUnknownet non iInspectable.>< (IInspectable est disponible uniquement pour les composants d’application Windows Runtime.) CoCreatableClass crée une fabrique pour la classe qui peut être utilisée avec des fonctions telles que 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. Utilisez le code suivant pour remplacer le code dans dllmain.cpp. Ce fichier définit les fonctions d'exportation DLL. Ces fonctions utilisent la classe Microsoft ::WRL ::Module pour gérer les fabriques de classes pour le 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. Ajoutez un fichier de fichier de définition de module (.def) au projet. Nommez le fichier, par exemple CalculatorComponent.def. Ce fichier fournit à l'éditeur de liens les noms des fonctions à exporter. Ouvrez la boîte de dialogue Pages de propriétés de votre projet, puis, sous Entrée de l’éditeur>de liens propriétés>de configuration, définissez la propriété Fichier de définition de module sur votre fichier DEF.

  8. Ajoutez ce code à CalculatorComponent.def :

    LIBRARY
    
    EXPORTS
        DllGetActivationFactory PRIVATE
        DllGetClassObject       PRIVATE
        DllCanUnloadNow         PRIVATE
    
  9. Ajoutez runtimeobject.lib à la ligne de l'éditeur de liens. Pour en savoir plus, consultez .Lib Fichiers en tant qu’entrée de l’éditeur de liens.

Pour utiliser le composant COM à partir d'une application de bureau

  1. Inscrivez le composant COM dans le Registre Windows. Pour ce faire, créez un fichier d’entrées d’inscription, nommez-le RegScript.reget ajoutez le texte suivant. Remplacez <dll-path par le chemin d’accès> de votre DLL, par exempleC:\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. Exécutez RegScript.reg ou ajoutez-le à l’événement post-build de votre projet. Pour plus d’informations, consultez la boîte de dialogue Pré-build Event/Post-build Event Command Line.

  3. Ajoutez un projet d’application console Win32 à la solution. Nommez le projet, par exemple Calculator.

  4. Utilisez ce code pour remplacer le contenu de 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
    */
    

Programmation fiable

Ce document utilise des fonctions COM standard pour démontrer que vous pouvez utiliser la bibliothèque de modèles C++ Windows Runtime pour créer un composant COM et le rendre disponible pour toute technologie COMPATIBLE COM. Vous pouvez également utiliser des types de bibliothèque de modèles C++ Windows Runtime tels que Microsoft ::WRL ::ComPtr dans votre application de bureau pour gérer la durée de vie de COM et d’autres objets. Le code suivant utilise la bibliothèque de modèles C++ Windows Runtime pour gérer la durée de vie du ICalculatorComponent pointeur. La classe CoInitializeWrapper est un wrapper RAII qui garantit que la bibliothèque COM est libérée et également que la durée de vie de la bibliothèque COM est supérieure à celle de l'objet pointeur intelligent de 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;
}

Voir aussi

Bibliothèque de modèles C++ Windows Runtime (WRL)