COM-onderdelen maken met C++/WinRT

C++/WinRT kan u helpen bij het ontwerpen van klassieke COM-onderdelen (Component Object Model) (of coklassen), net zoals u hiermee Windows Runtime klassen kunt maken. In dit onderwerp wordt uitgelegd hoe u dit doet.

Hoe C++/WinRT zich standaard gedraagt met betrekking tot COM-interfaces

De sjabloon winrt::implements van C++/WinRT is de basis waarvan uw runtimeklassen en activeringsfabrieken direct of indirect afstammen.

Standaard negeert winrt::implements klassieke COM-interfaces stilzwijgend. QueryInterface-aanroepen (QI) voor klassieke COM-interfaces mislukken daarom met E_NOINTERFACE. Winrt::implementeert ondersteunt standaard alleen C++/WinRT-interfaces.

  • winrt::IUnknown is een C++/WinRT-interface, dus winrt::implements ondersteunt winrt::IUnknown-gebaseerde interfaces.
  • winrt::implements biedt standaard geen ondersteuning voor ::IUnknown zelf.

Op een moment ziet u hoe u de gevallen kunt overwinnen die niet standaard worden ondersteund. Maar eerst volgt een codevoorbeeld om te illustreren wat er standaard gebeurt.

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

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

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

En hier is clientcode om de voorbeeldklasse te gebruiken.

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

// This next line doesn't compile yet.
sample.as<IInitializeWithWindow>()->Initialize(hwnd); 

Klassieke COM-ondersteuning inschakelen

Het goede nieuws is dat u alleen maar het headerbestand unknwn.h hoeft op te nemen voordat u C++/WinRT-headers opneemt om winrt::implements ondersteuning te laten bieden voor klassieke COM-interfaces.

U kunt dat expliciet doen, of indirect door een ander headerbestand op te nemen, zoals ole2.h. Een aanbevolen methode is om het wil\cppwinrt.h headerbestand op te nemen, dat deel uitmaakt van de Windows Implementation Libraries (WIL). Het wil\cppwinrt.h headerbestand zorgt er niet alleen voor dat het unknwn.h eerder winrt/base.his opgenomen, maar stelt ook dingen in zodat C++/WinRT en WIL elkaars uitzonderingen en foutcodes begrijpen.

U kunt vervolgens as<> gebruiken voor klassieke COM-interfaces, en de code in het bovenstaande voorbeeld kan worden gecompileerd.

Note

In het bovenstaande voorbeeld, zelfs na het inschakelen van klassieke COM-ondersteuning in de client (de code die de klasse gebruikt), als u geen klassieke COM-ondersteuning op de server hebt ingeschakeld (de code die de klasse implementeert), loopt de aanroep naar zoals<> in de client vast omdat de QI voor IInitializeWithWindow mislukt.

Een lokale (niet-geprojecteerde) klasse

Een lokale klasse is een klasse die zowel wordt geïmplementeerd als gebruikt in dezelfde compilatie-eenheid (app of een ander binair bestand); en er is dus geen projectie voor.

Hier volgt een voorbeeld van een lokale klasse die alleen klassieke COM-interfaces implementeert.

struct LocalObject :
    winrt::implements<LocalObject, IInitializeWithWindow>
{
    ...
};

Als u dat voorbeeld implementeert, maar u geen klassieke COM-ondersteuning inschakelt, mislukt de volgende code.

winrt::make<LocalObject>(); // error: ‘first_interface’: is not a member of ‘winrt::impl::interface_list<>’

Opnieuw wordt IInitializeWithWindow niet herkend als com-interface, dus C++/WinRT negeert het. In het geval van het voorbeeld LocalObject betekent het resultaat van het negeren van COM-interfaces dat LocalObject helemaal geen interfaces heeft. Maar elke COM-klasse moet ten minste één interface implementeren.

Een eenvoudig voorbeeld van een COM-onderdeel

Hier volgt een eenvoudig voorbeeld van een COM-onderdeel dat is geschreven met C++/WinRT. Dit is de volledige broncode van een mini-applicatie, zodat u de code kunt uitproberen door deze in de pch.h en main.cpp van een nieuw Windows Console Application (C++/WinRT)-project te plakken.

// 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());
}

Zie Ook COM-onderdelen gebruiken met C++/WinRT.

Een realistischer en interessanter voorbeeld

In de rest van dit onderwerp wordt uitgelegd hoe u een minimaal consoletoepassingsproject maakt dat gebruikmaakt van C++/WinRT om een standaard coklasse (COM-onderdeel of COM-klasse) en klassefactory te implementeren. De voorbeeldtoepassing laat zien hoe u een toastmelding met daarop een callbackknop kunt weergeven, en de coklasse (die de INotificationActivationCallback-COM-interface implementeert) zorgt ervoor dat de toepassing kan worden gestart en een callback ontvangt wanneer de gebruiker op die knop in de toastmelding klikt.

Meer achtergrondinformatie over het functiegebied voor toastmeldingen is te vinden op Een lokale toastmelding verzenden. Geen van de codevoorbeelden in die sectie van de documentatie maken echter gebruik van C++/WinRT, dus we raden u aan de voorkeur te geven aan de code die in dit onderwerp wordt weergegeven.

Een Windows Consoletoepassingsproject maken (ToastAndCallback)

Begin met het maken van een nieuw project in Microsoft Visual Studio. Maak een Windows ConsoleToepassingsproject (C++/WinRT) en geef het de naam ToastAndCallback.

Open pch.h en voeg #include <unknwn.h> toe vóór de include-instructies voor C++/WinRT-headers. Hier is het resultaat; u kunt de inhoud van uw pch.h vervangen door deze lijst.

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

Open main.cppen verwijder de using-instructies die door de projectsjabloon worden gegenereerd. Voeg in hun plaats de volgende code in (waarmee we de bibliotheken, headers en typenamen krijgen die we nodig hebben). Dit is het resultaat. u kunt de inhoud van uw main.cpp bestand vervangen door deze vermelding (we hebben ook de code uit main de onderstaande vermelding verwijderd, omdat deze functie later wordt vervangen).

// 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() { }

Het project wordt nog niet gebouwd; Nadat we klaar zijn met het toevoegen van code, wordt u gevraagd om te bouwen en uit te voeren.

De coklasse en klassefactory implementeren

In C++/WinRT implementeert u coklassen en klassefabrieken door de basisstruct van winrt::te implementeren . Plak direct na de drie gebruiksrichtlijnen die hierboven (en vóór main) worden weergegeven, deze code om uw COM-activatoronderdeel voor toastmeldingen te implementeren.

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

De implementatie van de bovenstaande coklasse volgt hetzelfde patroon dat wordt gedemonstreerd in Author-API's met C++/WinRT. U kunt dus dezelfde techniek gebruiken om COM-interfaces en Windows Runtime interfaces te implementeren. COM-onderdelen en Windows Runtime klassen maken hun functies beschikbaar via interfaces. Elke COM-interface is uiteindelijk afgeleid van de interface IUnknown . De Windows Runtime is gebaseerd op COM, met dien verstande dat Windows Runtime-interfaces uiteindelijk zijn afgeleid van de IInspectable-interface (en IInspectable is afgeleid van IUnknown).

In de coclass in de bovenstaande code implementeren we de methode INotificationActivationCallback::Activate, de functie die wordt aangeroepen wanneer de gebruiker op de callbackknop in een toastmelding klikt. Maar voordat deze functie kan worden aangeroepen, moet er een exemplaar van de coklasse worden gemaakt en dat is de taak van de functie IClassFactory::CreateInstance .

De coklasse die we zojuist hebben geïmplementeerd, staat bekend als de COM-activator voor meldingen en heeft de klasse-id (CLSID) in de vorm van de id (van het callback_guid type GUID) die u hierboven ziet. Die id zullen we later gebruiken in de vorm van een snelkoppeling in het menu Start en een vermelding in het Windows-register. De CLSID van de COM-activator en het pad naar de bijbehorende COM-server (dat wil zeggen: het pad naar het uitvoerbare bestand dat we hier bouwen) vormen het mechanisme waarmee een toastmelding weet van welke klasse een instantie moet worden gemaakt wanneer op de callbackknop wordt geklikt (ongeacht of in het Actiecentrum op de melding wordt geklikt of niet).

Aanbevolen procedures voor het implementeren van COM-methoden

Technieken voor foutafhandeling en voor resourcebeheer kunnen hand in hand gaan. Het is handiger en praktisch om uitzonderingen te gebruiken dan foutcodes. En als u het RAII-idioom (resource acquisition is initialization) gebruikt, kunt u voorkomen dat u expliciet op foutcodes moet controleren en resources vervolgens expliciet moet vrijgeven. Dergelijke expliciete controles zorgen ervoor dat uw code ingewikkelder is dan nodig is, en het biedt bugs voldoende plekken om te verbergen. Gebruik in plaats daarvan RAII en throw/catch-uitzonderingen. Op die manier zijn uw resourcetoewijzingen uitzonderingsveilig en is uw code eenvoudig.

U mag echter geen uitzonderingen laten ontsnappen uit de implementaties van uw COM-methoden. U kunt ervoor zorgen dat u de noexcept aanduiding op uw COM-methoden gebruikt. Het is prima als er ergens in de aanroepgrafiek van uw methode uitzonderingen worden opgeworpen, zolang u deze afhandelt voordat uw methode wordt beëindigd. Als u noexcept gebruikt, maar vervolgens toestaat dat een uitzondering uit uw methode ontsnapt, wordt uw toepassing beëindigd.

Helpertypen en -functies toevoegen

In deze stap voegen we enkele helpertypen en functies toe waarvan de rest van de code gebruikmaakt. Voeg dus direct vóór main, het volgende toe.

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

Implementeer de resterende functies en de ingangspuntfunctie wmain

Verwijder je main-functie en plak in plaats daarvan dit codefragment, dat code bevat om je COM-klasse te registreren en vervolgens een toastmelding weer te geven die je toepassing kan terugroepen.

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

De voorbeeldtoepassing testen

Bouw de toepassing en voer deze ten minste één keer uit als beheerder om ervoor te zorgen dat de registratie en andere installatiecode worden uitgevoerd. Een manier om dit te doen, is door Visual Studio uit te voeren als beheerder en vervolgens de app uit te voeren vanuit Visual Studio. Klik met de rechtermuisknop op Visual Studio op de taakbalk om de snellijst weer te geven, klik met de rechtermuisknop op Visual Studio in de snellijst en klik vervolgens op Als administrator uitvoeren. Ga akkoord met de prompt en open het project. Wanneer u de toepassing uitvoert, wordt er een bericht weergegeven dat aangeeft of de toepassing wordt uitgevoerd als beheerder. Als dat niet het geval is, zullen de registratie en andere configuratiestappen niet worden uitgevoerd. Deze registratie en andere installatie moeten ten minste één keer worden uitgevoerd om de toepassing correct te laten werken.

Of u de toepassing nu wel of niet als beheerder uitvoert, druk op 'T' om een toastmelding weer te geven. U kunt vervolgens op de knop Callback ToastAndCallback klikken, rechtstreeks vanuit de toastmelding die wordt weergegeven of vanuit het Actiecentrum, waarna uw toepassing wordt gestart, de coklasse wordt geïnstantieerd en de methode INotificationActivationCallback::Activate wordt uitgevoerd.

In-process COM server

De bovenstaande voorbeeld-app ToastAndCallback fungeert als een lokale (of out-of-process) COM-server. Dit wordt aangegeven door de LocalServer32 Windows Registersleutel die u gebruikt om de CLSID van de bijbehorende coklasse te registreren. Een lokale COM-server fungeert als host voor de coclass(sen) in een uitvoerbaar binair bestand (een .exe).

U kunt er ook voor kiezen om uw coclass(n) te hosten in een dynamic-link-bibliotheek (a .dll). Een COM-server in de vorm van een DLL wordt een in-process COM-server genoemd en wordt aangegeven door CLSID's die worden geregistreerd met behulp van de InprocServer32 Windows Registersleutel.

U kunt beginnen met het maken van een in-process COM-server door een nieuw project te maken in Microsoft Visual Studio. Maak een Visual C++>Windows Desktop>Dynamic-Link Library-project (DLL).

Als u C++/WinRT-ondersteuning wilt toevoegen aan het nieuwe project, volgt u de stappen die worden beschreven in Een Windows Bureaubladtoepassingsproject wijzigen om C++/WinRT-ondersteuning toe te voegen.

De coclass-, klassefactory- en in-proc-serverexports implementeren

Open dllmain.cppen voeg er de onderstaande codevermelding aan toe.

Als u al een DLL hebt waarmee C++/WinRT-Windows Runtime klassen worden geïmplementeerd, beschikt u al over de functie DllCanUnloadNow die hieronder wordt weergegeven. Als u coklassen wilt toevoegen aan dat DLL-bestand, kunt u de functie DllGetClassObject toevoegen.

Als u geen bestaande Windows Runtime C++-sjabloonbibliotheekcode (WRL) hebt waarmee u compatibel wilt blijven, kunt u de WRL-onderdelen verwijderen uit de weergegeven code.

// 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();
    }
}

Ondersteuning voor zwakke verwijzingen

Zie ook Zwakke verwijzingen in C++/WinRT.

C++/WinRT (met name de winrt::implementeert basisstructsjabloon) implementeert IWeakReferenceSource voor u als uw type IInspectable (of een interface die is afgeleid van IInspectable) implementeert.

Dit komt doordat IWeakReferenceSource en IWeakReference zijn ontworpen voor Windows Runtime typen. U kunt dus zwakke referentieondersteuning voor uw coklasse inschakelen door winrt::Windows::Foundation::IInspectable (of een interface die is afgeleid van IInspectable) toe te voegen aan uw implementatie.

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

Een COM-interface implementeren die is afgeleid van een andere

Afleiding van interfaces is een kenmerk van klassiek COM (en ontbreekt opzettelijk in de Windows Runtime). Hier volgt een voorbeeld van hoe interface-afleiding eruitziet.

IFileSystemBindData2 : public IFileSystemBindData { /* ... */  };

Als u een klasse schrijft die bijvoorbeeld zowel IFileSystemBindData als IFileSystemBindData2 moet implementeren, dan is de eerste stap om dat uit te drukken dat u verklaart dat u alleen de afgeleide interface implementeert, zoals hier.

// pch.h
#pragma once
#include <Shobjidl.h>
...

// main.cpp
...
struct MyFileSystemBindData :
    implements<MyFileSystemBindData,
    IFileSystemBindData2>
{
    // IFileSystemBindData
    IFACEMETHOD(SetFindData)(const WIN32_FIND_DATAW* pfd) override { /* ... */ return S_OK; };
    IFACEMETHOD(GetFindData)(WIN32_FIND_DATAW* pfd) override { /* ... */ return S_OK; };

    // IFileSystemBindData2
    IFACEMETHOD(SetFileID)(LARGE_INTEGER liFileID) override { /* ... */ return S_OK; };
    IFACEMETHOD(GetFileID)(LARGE_INTEGER* pliFileID) override { /* ... */ return S_OK; };
    IFACEMETHOD(SetJunctionCLSID)(REFCLSID clsid) override { /* ... */ return S_OK; };
    IFACEMETHOD(GetJunctionCLSID)(CLSID* pclsid) override { /* ... */ return S_OK; };
};
...
int main()
...

De volgende stap is ervoor te zorgen dat QueryInterface slaagt wanneer deze (direct of indirect) wordt aangeroepen voor IID_IFileSystemBindData (de basisinterface ) voor een exemplaar van MyFileSystemBindData. U doet dit door een specialisatie op te geven voor de winrt::is_guid_of functiesjabloon.

winrt::is_guid_of is variadisch, dus kunt u er een lijst met interfaces aan doorgeven. U kunt als volgt een specialisatie opgeven, zodat een controle op IFileSystemBindData2 ook een test voor IFileSystemBindData bevat.

// pch.h
...
namespace winrt
{
    template<>
    inline bool is_guid_of<IFileSystemBindData2>(guid const& id) noexcept
    {
        return is_guid_of<IFileSystemBindData2, IFileSystemBindData>(id);
    }
}

// main.cpp
...
int main()
{
    ...
    auto mfsbd{ winrt::make<MyFileSystemBindData>() };
    auto a{ mfsbd.as<IFileSystemBindData2>() }; // Would succeed even without the **is_guid_of** specialization.
    auto b{ mfsbd.as<IFileSystemBindData>() }; // Needs the **is_guid_of** specialization in order to succeed.
}

De specialisatie van winrt::is_guid_of moet identiek zijn in alle bestanden in het project en zichtbaar zijn op het punt waar de interface wordt gebruikt door de sjabloon winrt::implements of winrt::delegate. Normaal gesproken plaatst u het in een gemeenschappelijk headerbestand.

Belangrijke API's