Komponen COM penulis dengan C++/WinRT

C++/WinRT dapat membantu Anda menulis komponen Model Objek Komponen (COM) klasik (atau kolase), sama seperti halnya membantu Anda menulis kelas Windows Runtime. Topik ini menunjukkan caranya.

Bagaimana C++/WinRT berulah, secara default, sehubungan dengan antarmuka COM

Templat winrt::implements C++/WinRT adalah dasar dari kelas runtime dan pabrik aktivasi Anda secara langsung atau tidak langsung berasal.

Secara default, winrt::implements secara diam-diam mengabaikan antarmuka COM klasik. Setiap panggilan QueryInterface (QI) untuk antarmuka COM klasik akibatnya akan gagal dengan E_NOINTERFACE. Secara default, winrt::implements hanya mendukung antarmuka C++/WinRT.

  • winrt::IUnknown adalah antarmuka C++/WinRT, jadi winrt::implements mendukung antarmuka berbasis winrt::IUnknown.
  • winrt::implements tidak mendukung ::IUnknown itu sendiri, secara default.

Dalam sekejap Anda akan melihat cara mengatasi kasus yang tidak didukung secara default. Tetapi pertama-tama, berikut adalah contoh kode untuk mengilustrasikan apa yang terjadi secara default.

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

Dan berikut adalah kode klien untuk menggunakan kelas Sampel .

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

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

Mengaktifkan dukungan COM klasik

Kabar baiknya adalah bahwa semua yang diperlukan untuk menyebabkan winrt::implements untuk mendukung antarmuka COM klasik adalah menyertakan unknwn.h file header sebelum Anda menyertakan header C++/WinRT apa pun.

Anda dapat melakukannya secara eksplisit, atau tidak langsung dengan menyertakan beberapa file header lain seperti ole2.h. Salah satu metode yang direkomendasikan adalah menyertakan wil\cppwinrt.h file header, yang merupakan bagian dari Windows Implementation Libraries (WIL). File wil\cppwinrt.h header tidak hanya memastikan bahwa unknwn.h disertakan sebelumnya winrt/base.h, file tersebut juga mengatur semuanya sehingga C++/WinRT dan WIL memahami pengecualian dan kode kesalahan satu sama lain.

Anda kemudian dapat menggunakan<> antarmuka COM klasik, dan kode dalam contoh di atas akan dikompilasi.

Catatan

Dalam contoh di atas, bahkan setelah mengaktifkan dukungan COM klasik di klien (kode yang menggunakan kelas ), jika Anda belum juga mengaktifkan dukungan COM klasik di server (kode yang mengimplementasikan kelas), maka panggilan ke seperti<> pada klien akan crash karena QI untuk IInitializeWithWindow akan gagal.

Kelas lokal (tidak diproyeksikan)

Kelas lokal adalah kelas yang diimplementasikan dan dikonsumsi dalam unit kompilasi yang sama (aplikasi, atau biner lainnya); dan jadi tidak ada proyeksi untuk itu.

Berikut adalah contoh kelas lokal yang hanya mengimplementasikan antarmuka COM klasik.

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

Jika Anda menerapkan contoh tersebut, tetapi Anda tidak mengaktifkan dukungan COM klasik, maka kode berikut gagal.

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

Sekali lagi, IInitializeWithWindow tidak dikenali sebagai antarmuka COM, sehingga C++/WinRT mengabaikannya. Dalam kasus contoh LocalObject , hasil mengabaikan antarmuka COM berarti bahwa LocalObject tidak memiliki antarmuka sama sekali. Tetapi setiap kelas COM harus mengimplementasikan setidaknya satu antarmuka.

Contoh sederhana komponen COM

Berikut adalah contoh sederhana komponen COM yang ditulis menggunakan C++/WinRT. Ini adalah daftar lengkap aplikasi mini, sehingga Anda dapat mencoba kode jika Anda menempelkannya ke dalam pch.h dan main.cpp proyek Aplikasi Konsol Windows (C++/WinRT) baru.

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

Lihat juga Mengonsumsi komponen COM dengan C++/WinRT.

Contoh yang lebih realistis dan menarik

Sisa topik ini berjalan melalui pembuatan proyek aplikasi konsol minimal yang menggunakan C++/WinRT untuk mengimplementasikan kolasis dasar (komponen COM, atau kelas COM) dan pabrik kelas. Contoh aplikasi menunjukkan cara mengirimkan pemberitahuan toast dengan tombol panggilan balik di atasnya, dan kolase (yang mengimplementasikan antarmuka COM INotificationActivationCallback ) memungkinkan aplikasi diluncurkan dan dipanggil kembali ketika pengguna mengklik tombol tersebut pada toast.

Latar belakang lainnya tentang area fitur pemberitahuan toast dapat ditemukan di Mengirim pemberitahuan toast lokal. Namun, tidak ada contoh kode di bagian dokumentasi tersebut yang menggunakan C++/WinRT, jadi kami sarankan Anda lebih suka kode yang ditampilkan dalam topik ini.

Membuat proyek Aplikasi Konsol Windows (ToastAndCallback)

Mulailah dengan membuat proyek baru di Microsoft Visual Studio. Buat proyek Aplikasi Konsol Windows (C++/WinRT), dan beri nama ToastAndCallback.

Buka pch.h, dan tambahkan #include <unknwn.h> sebelum menyertakan untuk header C++/WinRT apa pun. Berikut hasilnya; Anda dapat mengganti konten Anda pch.h dengan daftar ini.

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

Buka main.cpp, dan hapus direktif penggunaan yang dihasilkan templat proyek. Di tempat mereka, masukkan kode berikut (yang memberi kita libs, header, dan nama jenis yang kita butuhkan). Berikut hasilnya; Anda dapat mengganti konten Anda main.cpp dengan daftar ini (kami juga telah menghapus kode dari main dalam daftar di bawah ini, karena kami akan mengganti fungsi tersebut nanti).

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

Proyek belum akan dibangun; setelah selesai menambahkan kode, Anda akan diminta untuk membuat dan menjalankan.

Menerapkan coclass dan pabrik kelas

Di C++/WinRT, Anda menerapkan coclasses, dan pabrik kelas, dengan berasal dari winrt::implements base struct. Segera setelah tiga direktif penggunaan yang ditunjukkan di atas (dan sebelum main), tempelkan kode ini untuk mengimplementasikan komponen aktivator COM pemberitahuan toast Anda.

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

Implementasi coclass di atas mengikuti pola yang sama yang ditunjukkan dalam API Penulis dengan C++/WinRT. Jadi, Anda dapat menggunakan teknik yang sama untuk mengimplementasikan antarmuka COM serta antarmuka Windows Runtime. Komponen COM dan kelas Windows Runtime mengekspos fiturnya melalui antarmuka. Setiap antarmuka COM pada akhirnya berasal dari antarmuka antarmuka IUnknown. Windows Runtime didasarkan pada COM—satu perbedaan adalah bahwa antarmuka Windows Runtime pada akhirnya berasal dari antarmuka IInspectable (dan IInspectable berasal dari IUnknown).

Dalam kokelas dalam kode di atas, kami menerapkan metode INotificationActivationCallback::Activate , yang merupakan fungsi yang dipanggil ketika pengguna mengklik tombol panggil balik pada pemberitahuan toast. Tetapi sebelum fungsi tersebut dapat dipanggil, instans kolase perlu dibuat, dan itulah pekerjaan fungsi IClassFactory::CreateInstance .

Kolase yang baru saja kami terapkan dikenal sebagai aktivator COM untuk pemberitahuan, dan memiliki id kelasnya (CLSID) dalam bentuk callback_guid pengidentifikasi (jenis GUID) yang Anda lihat di atas. Kami akan menggunakan pengidentifikasi tersebut nanti, dalam bentuk pintasan menu Mulai dan entri Windows Registry. COM activator CLSID, dan jalur ke server COM terkait (yang merupakan jalur ke executable yang kita bangun di sini) adalah mekanisme di mana pemberitahuan toast tahu kelas apa yang akan membuat instans ketika tombol panggilan baliknya diklik (apakah pemberitahuan diklik di Pusat Tindakan atau tidak).

Praktik terbaik untuk menerapkan metode COM

Teknik untuk penanganan kesalahan dan untuk manajemen sumber daya dapat berjalan seiring. Lebih nyaman dan praktis untuk menggunakan pengecualian daripada kode kesalahan. Dan jika Anda menggunakan idiom resource-acquisition-is-initialization (RAII), maka Anda dapat menghindari pemeriksaan kode kesalahan secara eksplisit dan kemudian secara eksplisit merilis sumber daya. Pemeriksaan eksplisit seperti itu membuat kode Anda lebih berkonvolusi daripada yang diperlukan, dan memberi bug banyak tempat untuk bersembunyi. Sebagai gantinya, gunakan RAII, dan lempar/tangkap pengecualian. Dengan begitu, alokasi sumber daya Anda aman pengecualian, dan kode Anda sederhana.

Namun, Anda tidak boleh mengizinkan pengecualian untuk keluar dari implementasi metode COM Anda. Anda dapat memastikan bahwa dengan menggunakan penentu noexcept pada metode COM Anda. Tidak masalah bagi pengecualian yang akan dilemparkan di mana saja dalam grafik panggilan metode Anda, selama Anda menanganinya sebelum metode Anda keluar. Jika Anda menggunakan noexcept, tetapi Anda kemudian mengizinkan pengecualian untuk keluar dari metode Anda, maka aplikasi Anda akan dihentikan.

Menambahkan jenis dan fungsi pembantu

Dalam langkah ini, kita akan menambahkan beberapa jenis dan fungsi pembantu yang digunakan kode lainnya. Jadi, segera sebelum main, tambahkan yang berikut ini.

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

Menerapkan fungsi yang tersisa, dan fungsi titik masuk wmain

Hapus fungsi Anda main , dan di tempatnya tempelkan daftar kode ini, yang mencakup kode untuk mendaftarkan coclas Anda, lalu untuk mengirimkan toast yang mampu memanggil kembali aplikasi Anda.

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

Cara menguji aplikasi contoh

Bangun aplikasi, lalu jalankan setidaknya sekali sebagai administrator untuk menyebabkan pendaftaran, dan penyiapan lainnya, kode dijalankan. Salah satu cara untuk melakukannya adalah dengan menjalankan Visual Studio sebagai administrator, lalu menjalankan aplikasi dari Visual Studio. Klik kanan Visual Studio di taskbar untuk menampilkan daftar lompat, klik kanan Visual Studio pada daftar lompat, lalu klik Jalankan sebagai administrator. Setujui perintah, lalu buka proyek. Saat Anda menjalankan aplikasi, pesan ditampilkan yang menunjukkan apakah aplikasi berjalan sebagai administrator atau tidak. Jika tidak, pendaftaran dan penyiapan lainnya tidak akan berjalan. Pendaftaran tersebut dan penyiapan lainnya harus berjalan setidaknya sekali agar aplikasi berfungsi dengan benar.

Apakah Anda menjalankan aplikasi sebagai administrator atau tidak, tekan 'T' untuk menyebabkan toast ditampilkan. Anda kemudian dapat mengklik tombol Panggil kembali ToastAndCallback baik langsung dari pemberitahuan toast yang muncul, atau dari Pusat Tindakan, dan aplikasi Anda akan diluncurkan, kolase yang dibuat, dan metode INotificationActivationCallback::Activate dijalankan.

Server COM dalam proses

Aplikasi contoh ToastAndCallback di atas berfungsi sebagai server COM lokal (atau di luar proses). Ini ditunjukkan oleh kunci Registri Windows LocalServer32 yang Anda gunakan untuk mendaftarkan CLSID dari kolasisnya. Server COM lokal menghosting coclass-nya di dalam biner yang dapat dieksekusi ()..exe

Atau (dan bisa dibilang lebih mungkin), Anda dapat memilih untuk menghosting kolase Anda di dalam pustaka tautan dinamis ()..dll Server COM dalam bentuk DLL dikenal sebagai server COM dalam proses, dan ditunjukkan oleh CLSID yang didaftarkan dengan menggunakan kunci Registri Windows InprocServer32 .

Anda dapat memulai tugas membuat server COM dalam proses dengan membuat proyek baru di Microsoft Visual Studio. Buat proyek Visual C++>Windows Desktop>Dynamic-Link Library (DLL).

Untuk menambahkan dukungan C++/WinRT ke proyek baru, ikuti langkah-langkah yang dijelaskan dalam Mengubah proyek aplikasi Windows Desktop untuk menambahkan dukungan C++/WinRT.

Menerapkan ekspor server coclass, class factory, dan in-proc

Buka dllmain.cpp, dan tambahkan ke dalamnya daftar kode yang ditunjukkan di bawah ini.

Jika Anda sudah memiliki DLL yang mengimplementasikan kelas C++/WinRT Windows Runtime, maka Anda sudah memiliki fungsi DllCanUnloadNow yang ditunjukkan di bawah ini. Jika Anda ingin menambahkan coclasses ke DLL tersebut , maka Anda dapat menambahkan fungsi DllGetClassObject .

Jika tidak memiliki kode Windows Runtime C++ Template Library (WRL) yang ingin Anda gunakan untuk tetap kompatibel, maka Anda dapat menghapus bagian WRL dari kode yang ditampilkan.

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

Dukungan untuk referensi yang lemah

Lihat juga Referensi lemah di C++/WinRT.

C++/WinRT (khususnya, templat struct dasar winrt::implements) mengimplementasikan IWeakReferenceSource untuk Anda jika jenis Anda mengimplementasikan IInspectable (atau antarmuka apa pun yang berasal dari IInspectable).

Ini karena IWeakReferenceSource dan IWeakReference dirancang untuk jenis Windows Runtime. Jadi, Anda dapat mengaktifkan dukungan referensi yang lemah untuk kolas Anda hanya dengan menambahkan winrt::Windows::Foundation::IInspectable (atau antarmuka yang berasal dari IInspectable) ke implementasi Anda.

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

Menerapkan antarmuka COM yang berasal dari antarmuka lain

Derivasi antarmuka adalah fitur COM klasik (dan kebetulan tidak ada, sengaja, dari Windows Runtime). Berikut adalah contoh tampilan derivasi antarmuka.

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

Jika Anda menulis kelas yang perlu diimplementasikan, misalnya, IFileSystemBindData dan IFileSystemBindData2, maka langkah pertama dalam mengekspresikan yaitu menyatakan bahwa Anda hanya menerapkan antarmuka turunan, seperti ini.

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

Langkah selanjutnya adalah memastikan bahwa QueryInterface berhasil ketika dipanggil (secara langsung atau tidak langsung) untuk IID_IFileSystemBindData (antarmuka dasar) terhadap instans MyFileSystemBindData. Anda melakukannya dengan memberikan spesialisasi untuk templat fungsi winrt::is_guid_of .

winrt::is_guid_of bersifat variadik, sehingga Anda dapat menyediakannya daftar antarmuka. Berikut adalah cara Anda memberikan spesialisasi sehingga pemeriksaan untuk IFileSystemBindData2 juga menyertakan pengujian untuk IFileSystemBindData.

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

Spesialisasi winrt::is_guid_of harus identik di semua file dalam proyek, dan terlihat pada titik antarmuka digunakan oleh templat winrt::implements atau winrt::d elegate. Biasanya Anda akan memasukkannya ke dalam file header umum.

API penting