COM-összetevők létrehozása C++/WinRT használatával

A C++/WinRT segítségével klasszikus Component Object Model (COM)-összetevőket (vagy koosztályokat) írhat, ugyanúgy, ahogyan a Windows-futtatókörnyezet-osztályokat is. Ez a témakör bemutatja, hogyan.

A C++/WinRT működése alapértelmezés szerint a COM-felületek tekintetében

A C++/WinRT winrt::implements sablonja az az alap, amelyből a futtatókörnyezeti osztályok és az aktiválási gyárak közvetlenül vagy közvetve származnak.

Alapértelmezés szerint a winrt::implementálások csendesen figyelmen kívül hagyják a klasszikus COM-felületeket. A klasszikus COM-felületekre irányuló QueryInterface (QI) hívások ennek következtében E_NOINTERFACE hibával meghiúsulnak. Alapértelmezés szerint a winrt::implements csak C++/WinRT interfészeket támogat.

  • winrt::Az IUnknown egy C++/WinRT felület, ezért a winrt::implements támogatja a winrt::IUnknown-alapú interfészeket.
  • a winrt::implements alapértelmezés szerint nem támogatja a ::IUnknown önmagát.

Egy pillanat alatt megtudhatja, hogyan háríthatja el az alapértelmezés szerint nem támogatott eseteket. Először azonban egy példakód látható, amely bemutatja, mi történik alapértelmezés szerint.

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

Itt pedig a mintaosztályt használó ügyfélkód látható.

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

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

A klasszikus COM-támogatás engedélyezése

A jó hír az, hogy mindössze annyit kell tenni ahhoz, hogy a winrt::implements támogassa a klasszikus COM-felületeket, hogy a unknwn.h fejlécfájlt be kell illeszteni, mielőtt bármilyen C++/WinRT-fejlécet beillesztene.

Ezt megteheti kifejezetten, vagy közvetve is valamely másik fejlécfájl, például a(z) ole2.h beillesztésével. Az egyik ajánlott módszer a wil\cppwinrt.h fejlécfájl beillesztése, amely a Windows implementációs függvénytárak (WIL) része. A wil\cppwinrt.h fejlécfájl nem csak arról gondoskodik, hogy unknwn.h korábban szerepeljen winrt/base.hbenne, hanem úgy is beállítja a dolgokat, hogy a C++/WinRT és a WIL megértse egymás kivételeit és hibakódjait.

Ezután a klasszikus COM-felületekhez hasonlóan<> a fenti példában szereplő kód is lefordítható.

Note

A fenti példában még akkor is, ha a kliensben (az osztályt használó kódban) engedélyezte a klasszikus COM-támogatást, ha a szerverben (az osztályt megvalósító kódban) nem engedélyezte azt is, akkor a kliensben a as<> hívás összeomlik, mert az IInitializeWithWindow QI-ja sikertelen lesz.

Helyi (nem előre jelzett) osztály

A helyi osztály egy olyan osztály, amely ugyanabban a fordítási egységben (alkalmazásban vagy más bináris fájlban) van implementálva és felhasználva; és így nincs kivetítés rá.

Íme egy példa egy helyi osztályra, amely csak a klasszikus COM-felületeket implementálja.

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

Ha implementálja ezt a példát, de nem engedélyezi a klasszikus COM-támogatást, az alábbi kód meghiúsul.

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

Az IInitializeWithWindow nem ismerhető fel COM-felületként, ezért a C++/WinRT figyelmen kívül hagyja. A LocalObject-példa esetében a COM-felületek figyelmen kívül hagyásának eredménye azt jelenti, hogy a LocalObject egyáltalán nem rendelkezik adapterekkel. De minden COM-osztálynak legalább egy felületet kell implementálnia.

Egyszerű példa a COM-összetevőkre

Íme egy egyszerű példa egy C++/WinRT használatával írt COM-összetevőre. Ez egy minialkalmazás teljes listája, így kipróbálhatja a kódot, ha beilleszti egy pch.hmain.cpp új Windows konzolalkalmazás (C++/WinRT) projektbe.

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

Lásd még: COM-összetevők felhasználása C++/WinRT használatával.

Reálisabb és érdekesebb példa

A témakör további része egy minimális konzolalkalmazás-projekt létrehozását ismerteti, amely c++/WinRT használatával implementál egy alapszintű coclass (COM-összetevő vagy COM-osztály) és osztály-előállítót. A példaalkalmazás bemutatja, hogyan kézbesíthet bejelentési értesítést egy visszahívási gombbal, és a coclass (amely implementálja az INotificationActivationCallback COM-felületet) lehetővé teszi az alkalmazás elindítását és visszahívását, amikor a felhasználó a bejelentésen erre a gombra kattint.

A bejelentési értesítési funkció területével kapcsolatos további hátteret a Helyi bejelentési értesítés küldése című témakörben találja. A dokumentáció ezen szakaszában szereplő kódpéldák egyike sem használ C++/WinRT kódot, ezért javasoljuk, hogy a jelen témakörben látható kódot válassza.

Windows konzolalkalmazás-projekt létrehozása (ToastAndCallback)

Először hozzon létre egy új projektet a Microsoft Visual Studióban. Hozzon létre egy Windows konzolalkalmazási (C++/WinRT) projektet, és nevezze el ToastAndCallbacknek.

Nyissa meg a(z) pch.h fájlt, és adja hozzá a(z) #include <unknwn.h> elemet a C++/WinRT-fejlécek include sorai elé. Íme az eredmény; a pch.h tartalmát lecserélheti az alábbi listára.

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

Nyissa meg main.cpp, és távolítsa el a projektsablon által létrehozott irányelveket. Helyettük szúrja be a következő kódot (amely megadja a szükséges libeket, fejléceket és típusneveket). Íme az eredmény; a main.cpp tartalmát lecserélheti erre a kódlistára (az alábbi kódlistában a main kódját is eltávolítottuk, mert később ezt a függvényt fogjuk lecserélni).

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

A projektet egyelőre nem lehet lefordítani; miután végzett a kód hozzáadásával, a rendszer kérni fogja, hogy fordítsa le és futtassa.

A coclass és class factory implementálása

A C++/WinRT-ben a koosztályokat és az osztálygyárakat a winrt::implements ősstruktúrából származtatva valósítja meg. Közvetlenül a fent látható három using direktíva után (és a(z) main előtt) illessze be ezt a kódot a toast értesítési COM-aktiváló összetevő implementálásához.

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

A fenti coclass implementációja ugyanazt a mintát követi, amelyet a Author API-kban a C++/WinRT használatával mutatunk be. Így ugyanezt a technikát használhatja COM-felületek, valamint Windows-futtatókörnyezet felületek implementálásához. A COM-összetevők és Windows-futtatókörnyezet osztályok interfészeken keresztül teszik elérhetővé a szolgáltatásaikat. Minden COM-felület végső soron az IUnknown felületi felületből származik. A Windows-futtatókörnyezet a COM-n alapul – az egyik különbség az, hogy a Windows-futtatókörnyezet interfészek végső soron az IInspectable felületből származnak (és az IInspectable az IUnknown-ből származik).

A fenti kód coclass-ban implementáljuk az INotificationActivationCallback::Activate metódust , amely az a függvény, amelyet akkor hívunk meg, amikor a felhasználó a visszahívás gombra kattint egy bejelentési értesítésen. Ahhoz azonban, hogy a függvény meghívható legyen, létre kell hozni a társosztály egy példányát, és ez az IClassFactory::CreateInstance függvény feladata.

Az imént implementált társosztályt com-aktivátornak nevezzük az értesítésekhez, és az osztályazonosítója (CLSID) a callback_guid fent látható azonosító ( GUID típusú) formájában jelenik meg. Ezt az azonosítót később egy Start menü helyi menüjének és egy Windows beállításjegyzék-bejegyzésnek a formájában fogjuk használni. A COM-aktivátor CLSID-je és a hozzá társított COM-kiszolgáló elérési útja (vagyis az itt létrehozott végrehajtható fájl elérési útja) szolgál mechanizmusként ahhoz, hogy egy toast értesítés tudja, melyik osztályból hozzon létre egy példányt, amikor a visszahívógombra kattintanak (akár a Műveletközpontban kattintanak az értesítésre, akár nem).

Ajánlott eljárások COM-metódusok implementálására

A hibakezelés és az erőforrás-kezelés technikái kéz a kézben járnak. Kényelmesebb és praktikusabb a kivételek használata, mint a hibakódok. Ha pedig az erőforrás-beszerzés inicializálási (RAII) kifejezését használja, akkor elkerülheti a hibakódok explicit ellenőrzését, majd az erőforrások explicit kiadását. Az ilyen explicit ellenőrzések a szükségesnél konvolúciósabbá teszik a kódot, és rengeteg rejtekhelyet adnak a hibáknak. Ehelyett használjon RAII-t, és dobjon, illetve kapjon el kivételeket. Így az erőforrás-foglalások kivételmentesek, és a kód egyszerű.

A COM-metódus implementációi alól azonban nem szabad kivételeket engedélyeznie. Ezt úgy biztosíthatja, hogy a noexcept megadót használja a COM-metódusain. Rendben van, ha a metódus hívási gráfjának bármely pontján kivételek keletkeznek, feltéve, hogy kezeli ezeket, mielőtt a metódus kilép. Ha a noexcept elemet használja, de ezután hagyja, hogy egy kivétel kijusson a metódusából, az alkalmazás le fog állni.

Segédtípusok és függvények hozzáadása

Ebben a lépésben hozzáadunk néhány segédtípust és függvényt, amelyeket a kód többi része használ. Tehát közvetlenül main előtt illessze be a következőt.

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

A fennmaradó függvények és a wmain belépési pont függvény implementálása

Törölje a(z) main függvényt, és a helyére illessze be ezt a kódblokkot, amely tartalmazza a coclass regisztrálásához szükséges kódot, valamint egy olyan toast értesítés küldéséhez szükséges kódot, amely vissza tud hívni az alkalmazásba.

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

A példaalkalmazás tesztelése

Hozza létre az alkalmazást, majd futtassa legalább egyszer rendszergazdaként, hogy a regisztráció és más beállítási kód fusson. Ennek egyik módja, hogy rendszergazdaként indítja el a Visual Studiót, majd a Visual Studióból futtatja az alkalmazást. Kattintson a jobb gombbal a tálcán lévő Visual Studio ikonjára az ugrólista megjelenítéséhez, majd kattintson a jobb gombbal a Visual Studio elemre az ugrólistán, végül kattintson a Futtatás rendszergazdaként parancsra. Fogadja el a kérést, majd nyissa meg a projektet. Az alkalmazás futtatásakor megjelenik egy üzenet, amely jelzi, hogy az alkalmazás rendszergazdaként fut-e. Ha nem, akkor a regisztráció és az egyéb beállítás nem fog futni. A regisztrációnak és az egyéb beállításoknak legalább egyszer futniuk kell ahhoz, hogy az alkalmazás megfelelően működjön.

Függetlenül attól, hogy rendszergazdaként futtatja-e az alkalmazást, nyomja meg a „T” billentyűt egy felugró értesítés megjelenítéséhez. Ezután a ToastAndCallback visszahívása gombra kattinthat közvetlenül a felugró toast értesítésben vagy a Műveletközpontban, és az alkalmazás elindul, a coclass példányosodik, majd az INotificationActivationCallback::Activate metódus végrehajtódik.

Folyamatban lévő COM-kiszolgáló

A fenti ToastAndCallback példaalkalmazás helyi (vagy folyamaton kívüli) COM-kiszolgálóként működik. Ezt az a LocalServer32 Windows rendszerleíró kulcs jelzi, amelyet a coclass CLSID-jének regisztrálására használ. A helyi COM-kiszolgáló egy végrehajtható bináris (an) fájlban tárolja a társosztály(ok)t .exe.

Alternatívaként (és feltehetően ez a valószínűbb) dönthet úgy, hogy a coclass(ok)at egy dinamikus kapcsolódású könyvtárban helyezi el (egy .dll). A DLL formájában megvalósított COM-kiszolgálót folyamaton belüli COM-kiszolgálónak nevezzük, és ezt az jelzi, hogy a CLSID-k az InprocServer32 Windows beállításjegyzékbeli kulcs használatával vannak regisztrálva.

A folyamaton belüli COM-kiszolgáló létrehozását úgy kezdheti el, hogy létrehoz egy új projektet a Microsoft Visual Studióban. Hozzon létre egy Visual C++>Windows Desktop>Dynamic-Link Library (DLL) projektet.

Ha C++/WinRT-támogatást szeretne hozzáadni az új projekthez, kövesse az Windows asztali alkalmazásprojekt módosítása C++/WinRT-támogatás hozzáadásához című szakasz lépéseit.

A coclass, az osztálygyár és az in-proc kiszolgáló exportjainak implementálása

Nyissa meg dllmain.cpp, és adja hozzá az alább látható kódlistát.

Ha már rendelkezik olyan DLL-sel, amely C++/WinRT Windows-futtatókörnyezet osztályokat implementál, akkor a DllCanUnloadNow függvény alább látható. Ha társosztályokat szeretne hozzáadni a DLL-hez, hozzáadhatja a DllGetClassObject függvényt .

Ha nem rendelkezik meglévő Windows-futtatókörnyezet C++ sablontár (WRL) kóddal, amellyel kompatibilis szeretne maradni, akkor eltávolíthatja a WRL-részeket a megjelenített kódból.

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

Gyenge hivatkozások támogatása

Lásd még: Gyenge referenciák a C++/WinRT-ben.

A C++/WinRT (pontosabban a winrt::implementálja az alapstruktúrasablont ) az IWeakReferenceSource-t implementálja Önnek, ha a típus az IInspectablet (vagy az IInspectable-ból származó bármely felület) implementálását valósítja meg.

Ennek az az oka, hogy az IWeakReferenceSource és az IWeakReference Windows-futtatókörnyezet típusúak. Így a coclass gyenge referenciáinak támogatását egyszerűen engedélyezheti úgy, hogy hozzáadja a winrt::Windows::Foundation::IInspectable típust (vagy egy IInspectable-ból származó felületet) az implementációhoz.

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

Implementáljon egy másikból származó COM-interfészt

Az interfész származtatása a klasszikus COM egyik funkciója (és szándékosan hiányzik a Windows-futtatókörnyezet). Íme egy példa arra, hogyan néz ki az interfész származtatása.

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

Ha olyan osztályt ír, amelyet végre kell hajtania, például az IFileSystemBindData és az IFileSystemBindData2 is, akkor a kifejezés első lépése, hogy deklarálja, hogy csak a származtatott felületet implementálja, például ezt.

// 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()
...

A következő lépés annak biztosítása, hogy a QueryInterface sikeresen lefusson, amikor azt (közvetlenül vagy közvetve) a MyFileSystemBindData egy példányán a IID_IFileSystemBindData (alap interfész) esetén hívják meg. Ezt a winrt::is_guid_of függvénysablonra vonatkozó specializációval teheti meg.

winrt::is_guid_of variadikus, így megadhatja az interfészek listáját. Az alábbiakban bemutatunk egy specializációt, hogy az IFileSystemBindData2 ellenőrzése tartalmazza az IFileSystemBindData tesztelését is.

// 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.
}

A winrt::is_guid_of specializációnak azonosnak kell lennie a projekt összes fájljában, és azon a ponton láthatónak kell lennie, amikor a winrt::implements vagy winrt::d elegate sablon használja a felületet. Általában egy közös fejlécfájlba tennéd.

Fontos API-k