Membuat komponen COM 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

C++/WinRT's winrt::implements template adalah dasar dari mana kelas runtime dan pabrik aktivasi Anda secara langsung atau tidak langsung diturunkan.

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 mengonsumsi kelas Contoh.

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

Nota

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 untuk sebuah aplikasi mini, sehingga Anda dapat mencoba kode tersebut dengan menempelkannya ke pch.h dan main.cpp dari proyek baru Aplikasi Konsol Windows (C++/WinRT) .

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

Topik ini membahas pembuatan proyek aplikasi konsol minimal yang menggunakan C++/WinRT untuk mengimplementasikan kelas dasar (komponen COM atau kelas COM) dan pabrik kelas. Contoh aplikasi menunjukkan cara mengirimkan pemberitahuan toast dengan tombol panggilan balik, dan coclass (yang mengimplementasikan antarmuka INotificationActivationCallback COM) memungkinkan aplikasi diluncurkan dan diaktifkan kembali ketika pengguna mengklik tombol tersebut pada toast.

Informasi lebih lanjut tentang area fitur notifikasi toast dapat ditemukan di Kirimkan notifikasi 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. Pada posisinya, masukkan kode berikut (yang memberikan kita libs, header, dan nama tipe 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 menurunkan dari winrt::implements base struct. Segera setelah tiga arahan 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 IUnknown antarmuka. Windows Runtime didasarkan pada COM—satu perbedaan adalah bahwa antarmuka Windows Runtime pada akhirnya berasal dari antarmuka IInspectable (dan IInspectable berasal dari IUnknown).

Dalam coclass dalam kode di atas, kami mengimplementasikan metode INotificationActivationCallback::Activate, yang merupakan fungsi yang dipanggil saat pengguna mengklik tombol callback pada notifikasi toast. Tetapi sebelum fungsi tersebut dapat dipanggil, instans dari coclass perlu dibuat, dan itulah pekerjaan fungsi IClassFactory::CreateInstance.

Kokelas yang baru saja kami terapkan dikenal sebagai aktivator COM untuk pemberitahuan, dan memiliki id kelasnya (CLSID) dalam bentuk pengidentifikasi callback_guid (jenis GUID) yang Anda lihat di atas. Kami akan menggunakan pengidentifikasi tersebut nanti, dalam bentuk pintasan menu Mulai dan entri Windows Registry. Aktivator COM CLSID, dan jalur ke server COM terkait (yang merupakan jalur ke file yang dapat dieksekusi yang kita bangun di sini) adalah mekanisme yang memungkinkan notifikasi toast menentukan kelas untuk membuat instance ketika tombol callback-nya diklik, baik pemberitahuan diklik di Pusat Aksi 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 eksplisit terhadap kode kesalahan dan pelepasan sumber daya secara eksplisit. 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 dari pengecualian, dan kode Anda tetap sederhana.

Namun, Anda tidak boleh mengizinkan pengecualian untuk keluar dari implementasi metode COM Anda. Anda dapat memastikan hal itu dengan menggunakan penentu noexcept pada metode COM Anda. Tidak apa-apa jika pengecualian dilempar di mana saja dalam graf panggilan metode Anda, selama Anda menanganinya sebelum metode Anda berakhir. 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;
}

Mengimplementasikan fungsi-fungsi yang tersisa dan fungsi titik masuk wmain

Hapus fungsi main Anda, 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

Membangun aplikasi, lalu jalankan setidaknya sekali sebagai administrator agar pendaftaran dan kode penyiapan lainnya dapat 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.

Baik Anda menjalankan aplikasi sebagai administrator atau tidak, tekan 'T' untuk menampilkan toast. Anda kemudian dapat mengklik tombol Panggil kembali ToastAndCallback baik langsung dari pemberitahuan toast yang muncul, atau dari Pusat Tindakan, dan aplikasi Anda akan diluncurkan, coclass diinstansiasi, dan metode INotificationActivationCallback::Aktifkan dijalankan.

Server COM dalam proses

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

Atau, yang bisa dibilang lebih mungkin, Anda dapat memilih untuk meng-host coclass Anda di dalam dynamic-link library (.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 Library (DLL) Visual C++Windows DesktopDynamic-Link.

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 Anda tidak memiliki kode Windows Runtime C++ Template Library (WRL) yang ingin Anda pertahankan kompatibilitasnya, 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, winrt::mengimplementasikan templat struct dasar) 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 coclass 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

Pewarisan antarmuka adalah fitur dari COM klasik (dan kebetulan sengaja tidak ada pada 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 (baik secara langsung maupun 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, jadi Anda dapat memberikannya sebuah 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 ketika antarmuka tersebut digunakan oleh winrt::implements atau winrt::delegate template. Biasanya Anda akan memasukkannya ke dalam file header umum.

API penting