Creare componenti COM con C++/WinRTAuthor COM components with C++/WinRT

C++/WinRT non solo ti consente di creare classi di Windows Runtime, ma ti aiuta anche a creare componenti COM (Component Object Model) classici (o coclassi).C++/WinRT can help you to author classic Component Object Model (COM) components (or coclasses), just as it helps you to author Windows Runtime classes. Questo argomento spiega come fare.This topic shows you how.

Comportamento predefinito di C++/WinRT rispetto alle interfacce COMHow C++/WinRT behaves, by default, with respect to COM interfaces

Il modello winrt::implements di C++/WinRT è la base da cui derivano direttamente e indirettamente le classi di runtime e le factory di attivazione.C++/WinRT's winrt::implements template is the base from which your runtime classes and activation factories directly or indirectly derive.

Per impostazione predefinita, winrt::implements supporta solo interfacce basate su IInspectable e ignora automaticamente le interfacce COM classiche.By default, winrt::implements supports only IInspectable-based interfaces, and it silently ignores classic COM interfaces. Le chiamate a QueryInterface (QI) per le interfacce COM classiche hanno quindi esito negativo con errore E_NOINTERFACE.Any QueryInterface (QI) calls for classic COM interfaces will consequently fail with E_NOINTERFACE.

Vedremo tra breve come risolvere questa situazione, ma prima ecco un esempio di codice per illustrare cosa accade per impostazione predefinita.In a moment you'll see how to overcome that situation, but first here's a code example to illustrate what happens by default.

// Sample.idl
runtimeclass Sample
{
    Sample();
    void DoWork();
}

// Sample.h
#include "pch.h"
#include <shobjidl.h> // Needed only for this file.

namespace winrt::MyProject
{
    struct Sample : implements<Sample, IInitializeWithWindow>
    {
        IFACEMETHOD(Initialize)(HWND hwnd);
        void DoWork();
    }
}

Di seguito è riportato il codice client per l'utilizzo della classe Sample.And here's client code to consume the Sample class.

// Client.cpp
Sample sample; // Construct a Sample object via its projection.

// This next line crashes, because the QI for IInitializeWithWindow fails.
sample.as<IInitializeWithWindow>()->Initialize(hwnd); 

La buona notizia è che per consentire il supporto delle interfacce COM classiche da parte di winrt::implements è sufficiente includere unknwn.h prima delle intestazioni C++/WinRT.The good news is that all it takes to cause winrt::implements to support classic COM interfaces is to include unknwn.h before you include any C++/WinRT headers.

Questa operazione può essere eseguita in modo esplicito oppure indirettamente, includendo un altro file di intestazione come ole2.h.You could do that explicitly, or indirectly by including some other header file such as ole2.h. Un metodo consigliato è quello di includere il file di intestazione wil\cppwinrt.h, che fa parte delle librerie di implementazione di Windows (WIL).One recommended method is to include the wil\cppwinrt.h header file, which is part of the Windows Implementation Libraries (WIL). Il file di intestazione wil\cppwinrt.h non consente solo di assicurarsi che unknwn.h sia incluso prima winrt/base.h, ma configura il tutto in modo che C++/WinRT e le librerie WIL siano in grado di comprendere i rispettivi codici di errore ed eccezioni.The wil\cppwinrt.h header file not only makes sure that unknwn.h is included before winrt/base.h, it also sets things up so that C++/WinRT and WIL understand each other's exceptions and error codes.

Un esempio semplice di un componente COMA simple example of a COM component

Ecco un esempio semplice di un componente COM, scritto usando C++/WinRT.Here's a simple example of a COM component written using C++/WinRT. Questo è un listato completo di una piccola applicazione di cui puoi provare il codice incollandolo nei file pch.h e main.cpp di un nuovo progetto Applicazione console di Windows (C++/WinRT) .This is a full listing of a mini-application, so you can try the code out if you paste it into the pch.h and main.cpp of a new Windows Console Application (C++/WinRT) project.

// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

struct __declspec(uuid("ddc36e02-18ac-47c4-ae17-d420eece2281")) IMyComInterface : ::IUnknown
{
    virtual HRESULT __stdcall Call() = 0;
};

using namespace winrt;
using namespace Windows::Foundation;

int main()
{
    winrt::init_apartment();

    struct MyCoclass : winrt::implements<MyCoclass, IPersist, IStringable, IMyComInterface>
    {
        HRESULT __stdcall Call() noexcept override
        {
            return S_OK;
        }

        HRESULT __stdcall GetClassID(CLSID* id) noexcept override
        {
            *id = IID_IPersist; // Doesn't matter what we return, for this example.
            return S_OK;
        }

        winrt::hstring ToString()
        {
            return L"MyCoclass as a string";
        }
    };

    auto mycoclass_instance{ winrt::make<MyCoclass>() };
    CLSID id{};
    winrt::check_hresult(mycoclass_instance->GetClassID(&id));
    winrt::check_hresult(mycoclass_instance.as<IMyComInterface>()->Call());
}

Vedi anche Utilizzare componenti COM con C++/WinRT.Also see Consume COM components with C++/WinRT.

Un esempio più interessante e realisticoA more realistic and interesting example

La parte restante di questo argomento descrive come creare un progetto di applicazione console minimo che usa C++/WinRT per implementare una coclasse di base (componente COM o classe COM) e una class factory.The remainder of this topic walks through creating a minimal console application project that uses C++/WinRT to implement a basic coclass (COM component, or COM class) and class factory. L'applicazione di esempio illustra come fornire una notifica di tipo avviso popup che include un pulsante di callback e la coclasse (che implementa l'interfaccia COM INotificationActivationCallback) consente di avviare ed eseguire il callback dell'applicazione quando l'utente fa clic su questo pulsante sull'avviso popup.The example application shows how to deliver a toast notification with a callback button on it, and the coclass (which implements the INotificationActivationCallback COM interface) allows the application to be launched and called back when the user clicks that button on the toast.

Altre informazioni sull'area della funzionalità di notifica di tipo avviso popup sono disponibili in Inviare una notifica di tipo avviso popup locale.More background about the toast notification feature area can be found at Send a local toast notification. Tuttavia, nessun esempio di codice in quella sezione della documentazione usa C++/WinRT e quindi ti consigliamo di scegliere il codice riportato in questo argomento.None of the code examples in that section of the documentation use C++/WinRT, though, so we recommend that you prefer the code shown in this topic.

Creare un progetto Applicazione console di Windows (ToastAndCallback)Create a Windows Console Application project (ToastAndCallback)

Per iniziare, crea un nuovo progetto in Microsoft Visual Studio.Begin by creating a new project in Microsoft Visual Studio. Crea un progetto Applicazione console di Windows (C++/WinRT) e denominalo ToastAndCallback.Create a Windows Console Application (C++/WinRT) project, and name it ToastAndCallback.

Apri pch.h e aggiungi #include <unknwn.h> prima degli include per qualsiasi intestazione C++/WinRT.Open pch.h, and add #include <unknwn.h> before the includes for any C++/WinRT headers. Ecco il risultato; puoi sostituire il contenuto di pch.h con questo listato.Here's the result; you can replace the contents of your pch.h with this listing.

// pch.h
#pragma once
#include <unknwn.h>
#include <winrt/Windows.Foundation.h>

Apri main.cpp e rimuovi le direttive using generate dal modello del progetto.Open main.cpp, and remove the using-directives that the project template generates. In sostituzione inserisci il codice seguente, che fornisce le librerie, le intestazioni e i nomi dei tipi necessari.In their place, insert the following code (which gives us the libs, headers, and type names that we need). Ecco il risultato: puoi sostituire il contenuto di main.cpp con questo listato (abbiamo anche rimosso il codice da main nel listato riportato di seguito perché sostituiremo quella funzione in un secondo momento).Here's the result; you can replace the contents of your main.cpp with this listing (we've also removed the code from main in the listing below, because we'll be replacing that function later).

// main.cpp : Defines the entry point for the console application.

#include "pch.h"

#pragma comment(lib, "advapi32")
#pragma comment(lib, "ole32")
#pragma comment(lib, "shell32")

#include <iomanip>
#include <iostream>
#include <notificationactivationcallback.h>
#include <propkey.h>
#include <propvarutil.h>
#include <shlobj.h>
#include <winrt/Windows.UI.Notifications.h>
#include <winrt/Windows.Data.Xml.Dom.h>

using namespace winrt;
using namespace Windows::Data::Xml::Dom;
using namespace Windows::UI::Notifications;

int main() { }

Il progetto non sarà ancora compilato; dopo che abbiamo finito di aggiungere il codice ti verrà richiesto di eseguire la compilazione e l'esecuzione.The project won't build yet; after we've finished adding code, you'll be prompted to build and run.

Implementare la coclasse e la class factoryImplement the coclass and class factory

In C++/WinRT le coclassi e le class factory vengono implementate mediante derivazione dallo struct di base winrt::implements.In C++/WinRT, you implement coclasses, and class factories, by deriving from the winrt::implements base struct. Immediatamente dopo le tre direttive using illustrate in precedenza (e prima di main), incolla questo codice per implementare il componente attivatore COM della notifica di tipo avviso popup.Immediately after the three using-directives shown above (and before main), paste this code to implement your toast notification COM activator component.

static constexpr GUID callback_guid // BAF2FA85-E121-4CC9-A942-CE335B6F917F
{
    0xBAF2FA85, 0xE121, 0x4CC9, {0xA9, 0x42, 0xCE, 0x33, 0x5B, 0x6F, 0x91, 0x7F}
};

std::wstring const this_app_name{ L"ToastAndCallback" };

struct callback : winrt::implements<callback, INotificationActivationCallback>
{
    HRESULT __stdcall Activate(
        LPCWSTR app,
        LPCWSTR args,
        [[maybe_unused]] NOTIFICATION_USER_INPUT_DATA const* data,
        [[maybe_unused]] ULONG count) noexcept final
    {
        try
        {
            std::wcout << this_app_name << L" has been called back from a notification." << std::endl;
            std::wcout << L"Value of the 'app' parameter is '" << app << L"'." << std::endl;
            std::wcout << L"Value of the 'args' parameter is '" << args << L"'." << std::endl;
            return S_OK;
        }
        catch (...)
        {
            return winrt::to_hresult();
        }
    }
};

struct callback_factory : implements<callback_factory, IClassFactory>
{
    HRESULT __stdcall CreateInstance(
        IUnknown* outer,
        GUID const& iid,
        void** result) noexcept final
    {
        *result = nullptr;

        if (outer)
        {
            return CLASS_E_NOAGGREGATION;
        }

        return make<callback>()->QueryInterface(iid, result);
    }

    HRESULT __stdcall LockServer(BOOL) noexcept final
    {
        return S_OK;
    }
};

L'implementazione della coclasse precedente segue lo stesso modello illustrato in Creare API con C++/WinRT.The implementation of the coclass above follows the same pattern that's demonstrated in Author APIs with C++/WinRT. Puoi quindi usare la stessa tecnica per implementare le interfacce COM e le interfacce di Windows Runtime.So, you can use the same technique to implement COM interfaces as well as Windows Runtime interfaces. I componenti COM e le classi di Windows Runtime espongono le proprie funzionalità tramite interfacce.COM components and Windows Runtime classes expose their features via interfaces. Ogni interfaccia COM deriva in ultima analisi dall'interfaccia IUnknown.Every COM interface ultimately derives from the IUnknown interface interface. Windows Runtime è basato su COM— con la distinzione che le interfacce di Windows Runtime derivano in ultima analisi dall'interfaccia IInspectable (e IInspectable deriva da IUnknown).The Windows Runtime is based on COM—one distinction being that Windows Runtime interfaces ultimately derive from the IInspectable interface (and IInspectable derives from IUnknown).

Nella coclasse nel codice precedente implementiamo il metodo INotificationActivationCallback::Activate, ovvero la funzione chiamata quando l'utente fa clic sul pulsante di callback su una notifica di tipo avviso popup.In the coclass in the code above, we implement the INotificationActivationCallback::Activate method, which is the function that's called when the user clicks the callback button on a toast notification. Prima che questa funzione possa essere chiamata, devi creare un'istanza della coclasse; questa azione viene eseguita dalla funzione IClassFactory::CreateInstance.But before that function can be called, an instance of the coclass needs to be created, and that's the job of the IClassFactory::CreateInstance function.

La coclasse che abbiamo appena implementato è nota come attivatore COM per le notifiche e ha il suo ID di classe (CLSID) sotto forma di identificatore callback_guid (di tipo GUID) che puoi vedere sopra.The coclass that we just implemented is known as the COM activator for notifications, and it has its class id (CLSID) in the form of the callback_guid identifier (of type GUID) that you see above. Useremo quell'identificatore in seguito, sotto forma di collegamento al menu Start e voce del Registro di sistema di Windows.We'll be using that identifier later, in the form of a Start menu shortcut and a Windows Registry entry. Il CLSID dell'attivatore COM e il percorso del server COM associato, ovvero il percorso del file eseguibile che stiamo creando qui, sono il meccanismo mediante il quale una notifica di avviso popup sa di quale classe creare un'istanza quando viene selezionato il relativo pulsante di callback (sia che la notifica venga selezionata in Centro notifiche o meno).The COM activator CLSID, and the path to its associated COM server (which is the path to the executable that we're building here) is the mechanism by which a toast notification knows what class to create an instance of when its callback button is clicked (whether the notification is clicked in Action Center or not).

Procedure consigliate per implementare i metodi COMBest practices for implementing COM methods

Le tecniche per la gestione degli errori e per la gestione delle risorse possono procedere in stretta associazione.Techniques for error handling and for resource management can go hand-in-hand. È più conveniente e pratico utilizzare le eccezioni rispetto ai codici di errore.It's more convenient and practical to use exceptions than error codes. Se utilizzi resource-acquisition-is-initialization (RAII), puoi evitare di controllare esplicitamente i codici di errore e quindi rilasciare esplicitamente le risorse.And if you employ the resource-acquisition-is-initialization (RAII) idiom, then you can avoid explicitly checking for error codes and then explicitly releasing resources. Questi controlli espliciti rendono il codice più complesso del necessario e offrono ai bug più posizioni in cui nascondersi.Such explicit checks make your code more convoluted than necessary, and it gives bugs plenty of places to hide. In alternativa, utilizza RAII e le eccezioni throw/catch.Instead, use RAII, and throw/catch exceptions. In questo modo le allocazioni delle risorse sono indipendenti dalle eccezioni e il codice è semplice.That way, your resource allocations are exception-safe, and your code is simple.

Non devi, tuttavia, consentire alle eccezioni di usare la sequenza di escape per le implementazioni del metodo COM.However, you mustn't allow exceptions to escape your COM method implementations. A questo scopo usa l'identificatore noexcept per i metodi COM.You can ensure that by using the noexcept specifier on your COM methods. È accettabile che le eccezioni vengano generate in un punto qualsiasi del grafico chiamate del metodo, a condizione che le gestisci prima dell'uscita dal metodo.It's ok for exceptions to be thrown anywhere in the call graph of your method, as long as you handle them before your method exits. Se usi noexcept, ma consenti a un'eccezione di eseguire la sequenza di escape per il metodo, l'applicazione verrà terminata.If you use noexcept, but you then allow an exception to escape your method, then your application will terminate.

Aggiungere le funzioni e i tipi di helperAdd helper types and functions

In questo passaggio aggiungeremo alcune funzioni e alcuni tipi di helper di cui il resto del codice fa uso.In this step, we'll add some helper types and functions that the rest of the code makes use of. Immediatamente prima di main aggiungi quanto segue.So, immediately before main, add the following.

struct prop_variant : PROPVARIANT
{
    prop_variant() noexcept : PROPVARIANT{}
    {
    }

    ~prop_variant() noexcept
    {
        clear();
    }

    void clear() noexcept
    {
        WINRT_VERIFY_(S_OK, ::PropVariantClear(this));
    }
};

struct registry_traits
{
    using type = HKEY;

    static void close(type value) noexcept
    {
        WINRT_VERIFY_(ERROR_SUCCESS, ::RegCloseKey(value));
    }

    static constexpr type invalid() noexcept
    {
        return nullptr;
    }
};

using registry_key = winrt::handle_type<registry_traits>;

std::wstring get_module_path()
{
    std::wstring path(100, L'?');
    uint32_t path_size{};
    DWORD actual_size{};

    do
    {
        path_size = static_cast<uint32_t>(path.size());
        actual_size = ::GetModuleFileName(nullptr, path.data(), path_size);

        if (actual_size + 1 > path_size)
        {
            path.resize(path_size * 2, L'?');
        }
    } while (actual_size + 1 > path_size);

    path.resize(actual_size);
    return path;
}

std::wstring get_shortcut_path()
{
    std::wstring format{ LR"(%ProgramData%\Microsoft\Windows\Start Menu\Programs\)" };
    format += (this_app_name + L".lnk");

    auto required{ ::ExpandEnvironmentStrings(format.c_str(), nullptr, 0) };
    std::wstring path(required - 1, L'?');
    ::ExpandEnvironmentStrings(format.c_str(), path.data(), required);
    return path;
}

Implementare le funzioni rimanenti e la funzione del punto di ingresso wmainImplement the remaining functions, and the wmain entry point function

Elimina la funzione main e al suo posto incolla questo listato di codice che include il codice per registrare la coclasse e quindi per fornire un avviso popup in grado di eseguire il callback dell'applicazione.Delete your main function, and in its place paste this code listing, which includes code to register your coclass, and then to deliver a toast capable of calling back your application.

void register_callback()
{
    DWORD registration{};

    winrt::check_hresult(::CoRegisterClassObject(
        callback_guid,
        make<callback_factory>().get(),
        CLSCTX_LOCAL_SERVER,
        REGCLS_SINGLEUSE,
        &registration));
}

void create_shortcut()
{
    auto link{ winrt::create_instance<IShellLink>(CLSID_ShellLink) };
    std::wstring module_path{ get_module_path() };
    winrt::check_hresult(link->SetPath(module_path.c_str()));

    auto store = link.as<IPropertyStore>();
    prop_variant value;
    winrt::check_hresult(::InitPropVariantFromString(this_app_name.c_str(), &value));
    winrt::check_hresult(store->SetValue(PKEY_AppUserModel_ID, value));
    value.clear();
    winrt::check_hresult(::InitPropVariantFromCLSID(callback_guid, &value));
    winrt::check_hresult(store->SetValue(PKEY_AppUserModel_ToastActivatorCLSID, value));

    auto file{ store.as<IPersistFile>() };
    std::wstring shortcut_path{ get_shortcut_path() };
    winrt::check_hresult(file->Save(shortcut_path.c_str(), TRUE));

    std::wcout << L"In " << shortcut_path << L", created a shortcut to " << module_path << std::endl;
}

void update_registry()
{
    std::wstring key_path{ LR"(SOFTWARE\Classes\CLSID\{????????-????-????-????-????????????})" };
    ::StringFromGUID2(callback_guid, key_path.data() + 23, 39);
    key_path += LR"(\LocalServer32)";
    registry_key key;

    winrt::check_win32(::RegCreateKeyEx(
        HKEY_CURRENT_USER,
        key_path.c_str(),
        0,
        nullptr,
        0,
        KEY_WRITE,
        nullptr,
        key.put(),
        nullptr));
    ::RegDeleteValue(key.get(), nullptr);

    std::wstring path{ get_module_path() };

    winrt::check_win32(::RegSetValueEx(
        key.get(),
        nullptr,
        0,
        REG_SZ,
        reinterpret_cast<BYTE const*>(path.c_str()),
        static_cast<uint32_t>((path.size() + 1) * sizeof(wchar_t))));

    std::wcout << L"In " << key_path << L", registered local server at " << path << std::endl;
}

void create_toast()
{
    XmlDocument xml;

    std::wstring toastPayload
    {
        LR"(
<toast>
  <visual>
    <binding template='ToastGeneric'>
      <text>)"
    };
    toastPayload += this_app_name;
    toastPayload += LR"(
      </text>
    </binding>
  </visual>
  <actions>
    <action content='Call back )";
    toastPayload += this_app_name;
    toastPayload += LR"(
' arguments='the_args' activationKind='Foreground' />
  </actions>
</toast>)";
    xml.LoadXml(toastPayload);

    ToastNotification toast{ xml };
    ToastNotifier notifier{ ToastNotificationManager::CreateToastNotifier(this_app_name) };
    notifier.Show(toast);
    ::Sleep(50); // Give the callback chance to display.
}

void LaunchedNormally(HANDLE, INPUT_RECORD &, DWORD &);
void LaunchedFromNotification(HANDLE, INPUT_RECORD &, DWORD &);

int wmain(int argc, wchar_t * argv[], wchar_t * /* envp */[])
{
    winrt::init_apartment();

    register_callback();

    HANDLE consoleHandle{ ::GetStdHandle(STD_INPUT_HANDLE) };
    INPUT_RECORD buffer{};
    DWORD events{};
    ::FlushConsoleInputBuffer(consoleHandle);

    if (argc == 1)
    {
        LaunchedNormally(consoleHandle, buffer, events);
    }
    else if (argc == 2 && wcscmp(argv[1], L"-Embedding") == 0)
    {
        LaunchedFromNotification(consoleHandle, buffer, events);
    }
}

void LaunchedNormally(HANDLE consoleHandle, INPUT_RECORD & buffer, DWORD & events)
{
    try
    {
        bool runningAsAdmin{ ::IsUserAnAdmin() == TRUE };
        std::wcout << this_app_name << L" is running" << (runningAsAdmin ? L" (administrator)." : L" (NOT as administrator).") << std::endl;

        if (runningAsAdmin)
        {
            create_shortcut();
            update_registry();
        }

        std::wcout << std::endl << L"Press 'T' to display a toast notification (press any other key to exit)." << std::endl;

        ::ReadConsoleInput(consoleHandle, &buffer, 1, &events);
        if (towupper(buffer.Event.KeyEvent.uChar.UnicodeChar) == L'T')
        {
            create_toast();
        }
    }
    catch (winrt::hresult_error const& e)
    {
        std::wcout << L"Error: " << e.message().c_str() << L" (" << std::hex << std::showbase << std::setw(8) << static_cast<uint32_t>(e.code()) << L")" << std::endl;
    }
}

void LaunchedFromNotification(HANDLE consoleHandle, INPUT_RECORD & buffer, DWORD & events)
{
    ::Sleep(50); // Give the callback chance to display its message.
    std::wcout << std::endl << L"Press any key to exit." << std::endl;
    ::ReadConsoleInput(consoleHandle, &buffer, 1, &events);
}

Come testare l'applicazione di esempioHow to test the example application

Compila l'applicazione ed eseguila almeno una volta come amministratore per far si che vengano eseguite la registrazione e le altre impostazioni.Build the application, and then run it at least once as an administrator to cause the registration, and other setup, code to run. Un modo per farlo è eseguire Visual Studio come amministratore ed eseguire l'app da Visual Studio.One way to do that is to run Visual Studio as an administrator, and then run the app from Visual Studio. Fai clic con il pulsante destro del mouse su Visual Studio nella barra delle applicazioni per visualizzare la jump list, fai clic con il pulsante destro del mouse su Visual Basic sulla jump list, quindi fai clic su Esegui come amministratore.Right-click Visual Studio in the taskbar to display the jump list, right-click Visual Studio on the jump list, and then click Run as administrator. Conferma il prompt e quindi apri il progetto.Agree to the prompt, and then open the project. Quando esegui l'applicazione, viene visualizzato un messaggio che indica se l'applicazione è in esecuzione come amministratore.When you run the application, a message is displayed indicating whether or not the application is running as an administrator. In caso contrario, la registrazione e le altre impostazioni non verranno eseguite.If it isn't, then the registration and other setup won't run. La registrazione e le altre impostazioni devono essere eseguite almeno una volta affinché l'applicazione funzioni correttamente.That registration and other setup has to run at least once in order for the application to work correctly.

Indipendentemente dal fatto che tu stia eseguendo l'applicazione come amministratore, premi "T" per visualizzare un avviso popup.Whether or not you're running the application as an administrator, press 'T' to cause a toast to be displayed. Puoi quindi fare clic sul pulsante Richiama ToastAndCallback direttamente dalla notifica del tipo di avviso popup o dal Centro notifiche; verrà avviata l'applicazione, verrà istanziata la coclasse e verrà eseguito il metodo INotificationActivationCallback::Activate.You can then click the Call back ToastAndCallback button either directly from the toast notification that pops up, or from the Action Center, and your application will be launched, the coclass instantiated, and the INotificationActivationCallback::Activate method executed.

Server COM in-processIn-process COM server

L'app di esempio precedente ToastAndCallback funziona come server COM locale (o out-of-process).The ToastAndCallback example app above functions as a local (or out-of-process) COM server. Questa condizione è indicata dalla chiave del Registro di sistema di Windows LocalServer32 usata per registrare il CLSID della relativa coclasse.This is indicated by the LocalServer32 Windows Registry key that you use to register the CLSID of its coclass. Un server COM locale ospita la coclasse o le coclassi all'interno di un file eseguibile binario (.exe).A local COM server hosts its coclass(es) inside an executable binary (an .exe).

In alternativa e verosimilmente con maggiore probabilità, puoi scegliere di ospitare le coclassi all'interno di una libreria di collegamento dinamico (.dll).Alternatively (and arguably more likely), you can choose to host your coclass(es) inside a dynamic-link library (a .dll). Un server COM sotto forma di DLL è noto come server COM in-process ed è indicato dai CLSID in fase di registrazione utilizzando la chiave del registro di sistema di Windows InprocServer32.A COM server in the form of a DLL is known as an in-process COM server, and it's indicated by CLSIDs being registered by using the InprocServer32 Windows Registry key.

Puoi iniziare l'attività di creazione di un server COM in-process creando un nuovo progetto in Microsoft Visual Studio.You can begin the task of creating an in-process COM server by creating a new project in Microsoft Visual Studio. Crea un progetto Visual C++ > Windows Desktop > Libreria di collegamento dinamico (DLL) .Create a Visual C++ > Windows Desktop > Dynamic-Link Library (DLL) project.

Per aggiungere il supporto C++/WinRT al nuovo progetto, segui i passaggi descritti in Modificare un progetto di applicazione desktop di Windows per aggiungere il supporto di C++/WinRT.To add C++/WinRT support to the new project, follow the steps described in Modify a Windows Desktop application project to add C++/WinRT support.

Implementare la coclasse, la class factory e le esportazioni del server in-processImplement the coclass, class factory, and in-proc server exports

Apri dllmain.cpp e aggiungi il listato di codice illustrato di seguito.Open dllmain.cpp, and add to it the code listing shown below.

Se disponi già di una DLL che implementa le classi di Windows Runtime C++/WinRT, disponi già della funzione DllCanUnloadNow illustrata di seguito.If you already have a DLL that implements C++/WinRT Windows Runtime classes, then you'll already have the DllCanUnloadNow function shown below. Se vuoi aggiungere coclassi a quella DLL, puoi aggiungere la funzione DllGetClassObject.If you want to add coclasses to that DLL, then you can add the DllGetClassObject function.

Se non disponi di un codice Windows Runtime C++ Template Library (WRL) con cui desideri mantenere la compatibilità, puoi rimuovere le parti WRL dal codice illustrato.If don't have existing Windows Runtime C++ Template Library (WRL) code that you want to stay compatible with, then you can remove the WRL parts from the code shown.

// dllmain.cpp

struct MyCoclass : winrt::implements<MyCoclass, IPersist>
{
    HRESULT STDMETHODCALLTYPE GetClassID(CLSID* id) noexcept override
    {
        *id = IID_IPersist; // Doesn't matter what we return, for this example.
        return S_OK;
    }
};

struct __declspec(uuid("85d6672d-0606-4389-a50a-356ce7bded09"))
    MyCoclassFactory : winrt::implements<MyCoclassFactory, IClassFactory>
{
    HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) noexcept override
    {
        try
        {
            return winrt::make<MyCoclass>()->QueryInterface(riid, ppvObject);
        }
        catch (...)
        {
            return winrt::to_hresult();
        }
    }

    HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) noexcept override
    {
        // ...
        return S_OK;
    }

    // ...
};

HRESULT __stdcall DllCanUnloadNow()
{
#ifdef _WRL_MODULE_H_
    if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate())
    {
        return S_FALSE;
    }
#endif

    if (winrt::get_module_lock())
    {
        return S_FALSE;
    }

    winrt::clear_factory_cache();
    return S_OK;
}

HRESULT __stdcall DllGetClassObject(GUID const& clsid, GUID const& iid, void** result)
{
    try
    {
        *result = nullptr;

        if (clsid == __uuidof(MyCoclassFactory))
        {
            return winrt::make<MyCoclassFactory>()->QueryInterface(iid, result);
        }

#ifdef _WRL_MODULE_H_
        return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetClassObject(clsid, iid, result);
#else
        return winrt::hresult_class_not_available().to_abi();
#endif
    }
    catch (...)
    {
        return winrt::to_hresult();
    }
}

Supporto per riferimenti deboliSupport for weak references

Vedi anche Riferimenti deboli in C++/WinRT.Also see Weak references in C++/WinRT.

C++/WinRT (in particolare, il modello dello struct di base winrt::implements) implementa IWeakReferenceSource automaticamente se il tuo tipo implementa IInspectable (o qualsiasi interfaccia che deriva da IInspectable).C++/WinRT (specifically, the winrt::implements base struct template) implements IWeakReferenceSource for you if your type implements IInspectable (or any interface that derives from IInspectable).

Ciò è dovuto al fatto che IWeakReferenceSource e IWeakReference sono progettati per i tipi di Windows Runtime.This is because IWeakReferenceSource and IWeakReference are designed for Windows Runtime types. Puoi quindi attivare il supporto dei riferimenti deboli per la coclasse semplicemente aggiungendo winrt::Windows::Foundation::IInspectable (o un'interfaccia che deriva da IInspectable) all'implementazione.So, you can turn on weak reference support for your coclass simply by adding winrt::Windows::Foundation::IInspectable (or an interface that derives from IInspectable) to your implementation.

struct MyCoclass : winrt::implements<MyCoclass, IMyComInterface, winrt::Windows::Foundation::IInspectable>
{
    //  ...
};

API importantiImportant APIs