Bagikan melalui


Mengirim pemberitahuan toast lokal dari aplikasi desktop WRL C++

Aplikasi desktop yang dikemas dan tidak dikemas dapat mengirim pemberitahuan toast interaktif seperti yang dapat dilakukan aplikasi platform Universal Windows (UWP). Itu termasuk aplikasi paket (lihat Membuat proyek baru untuk aplikasi desktop WinUI 3 yang dikemas); aplikasi paket dengan lokasi eksternal (lihat Memberikan identitas paket dengan pengemasan dengan lokasi eksternal); dan aplikasi yang tidak dikemas (lihat Membuat proyek baru untuk aplikasi desktop WinUI 3 yang tidak dipaket).

Namun, untuk aplikasi desktop yang tidak dikemas, ada beberapa langkah khusus. Itu karena skema aktivasi yang berbeda, dan kurangnya identitas paket saat runtime.

Penting

Jika Anda menulis aplikasi UWP, silakan lihat dokumentasi UWP. Untuk bahasa desktop lainnya, silakan lihat Desktop C#.

Langkah 1: Aktifkan Windows SDK

Jika Anda belum mengaktifkan Windows SDK untuk aplikasi Anda, maka Anda harus melakukannya terlebih dahulu. Ada beberapa langkah utama.

  1. Tambahkan runtimeobject.lib ke Dependensi Tambahan.
  2. Targetkan Windows SDK.

Klik kanan proyek Anda dan pilih Properti.

Di menu Konfigurasi atas, pilih Semua Konfigurasi sehingga perubahan berikut diterapkan ke Debug dan Rilis.

Di bawah Linker -> Input, tambahkan runtimeobject.lib ke Dependensi Tambahan.

Kemudian di bawah Umum, pastikan bahwa Versi Windows SDK diatur ke versi 10.0 atau yang lebih baru.

Langkah 2: Salin kode pustaka kompat

Salin file DesktopNotificationManagerCompat.h dan DesktopNotificationManagerCompat.cpp dari GitHub ke dalam proyek Anda. Pustaka kompat mengabstraksi banyak kompleksitas pemberitahuan desktop. Instruksi berikut memerlukan pustaka kompat.

Jika Anda menggunakan header yang telah dikommpilasikan sebelumnya, pastikan untuk #include "stdafx.h" sebagai baris pertama file DesktopNotificationManagerCompat.cpp.

Langkah 3: Sertakan file header dan namespace

Sertakan file header pustaka kompat, dan file header dan namespace yang terkait dengan menggunakan API roti bakar Windows.

#include "DesktopNotificationManagerCompat.h"
#include <NotificationActivationCallback.h>
#include <windows.ui.notifications.h>

using namespace ABI::Windows::Data::Xml::Dom;
using namespace ABI::Windows::UI::Notifications;
using namespace Microsoft::WRL;

Langkah 4: Terapkan aktivator

Anda harus menerapkan handler untuk aktivasi toast, sehingga ketika pengguna mengklik toast Anda, aplikasi Anda dapat melakukan sesuatu. Ini diperlukan agar roti panggang Anda bertahan di Pusat Tindakan (karena toast dapat diklik beberapa hari kemudian saat aplikasi Anda ditutup). Kelas ini dapat ditempatkan di mana saja dalam proyek Anda.

Terapkan antarmuka INotificationActivationCallback seperti yang terlihat di bawah ini, termasuk UUID, dan juga panggil CoCreatableClass untuk menandai kelas Anda sebagai COM creatable. Untuk UUID Anda, buat GUID unik menggunakan salah satu dari banyak generator GUID online. GUID CLSID (pengidentifikasi kelas) ini adalah bagaimana Pusat Tindakan tahu kelas apa yang akan diaktifkan COM.

// The UUID CLSID must be unique to your app. Create a new GUID if copying this code.
class DECLSPEC_UUID("replaced-with-your-guid-C173E6ADF0C3") NotificationActivator WrlSealed WrlFinal
    : public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
{
public:
    virtual HRESULT STDMETHODCALLTYPE Activate(
        _In_ LPCWSTR appUserModelId,
        _In_ LPCWSTR invokedArgs,
        _In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data,
        ULONG dataCount) override
    {
        // TODO: Handle activation
    }
};

// Flag class as COM creatable
CoCreatableClass(NotificationActivator);

Langkah 5: Daftar dengan platform pemberitahuan

Kemudian, Anda harus mendaftar dengan platform pemberitahuan. Ada langkah-langkah yang berbeda tergantung pada apakah aplikasi Anda dikemas atau tidak dikemas. Jika Anda mendukung keduanya, maka Anda harus melakukan kedua set langkah (namun, tidak perlu membuat fork kode Anda karena pustaka kami menanganinya untuk Anda).

Paket

Jika aplikasi Anda dikemas (lihat Membuat proyek baru untuk aplikasi desktop WinUI 3 yang dikemas) atau dikemas dengan lokasi eksternal (lihat Memberikan identitas paket dengan pengemasan dengan lokasi eksternal), atau jika Anda mendukung keduanya, maka di Package.appxmanifest Anda tambahkan:

  1. Deklarasi untuk xmlns:com
  2. Deklarasi untuk xmlns:desktop
  3. Di atribut IgnorableNamespaces, com dan desktop
  4. com:Extension untuk aktivator COM menggunakan GUID dari langkah #4. Pastikan untuk menyertakan Arguments="-ToastActivated" sehingga Anda tahu peluncuran Anda berasal dari roti panggang
  5. desktop:Extension untuk windows.toastNotificationActivation untuk mendeklarasikan CLSID aktivator roti panggang Anda (GUID dari langkah #4).

Package.appxmanifest

<Package
  ...
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
  xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
  IgnorableNamespaces="... com desktop">
  ...
  <Applications>
    <Application>
      ...
      <Extensions>

        <!--Register COM CLSID LocalServer32 registry key-->
        <com:Extension Category="windows.comServer">
          <com:ComServer>
            <com:ExeServer Executable="YourProject\YourProject.exe" Arguments="-ToastActivated" DisplayName="Toast activator">
              <com:Class Id="replaced-with-your-guid-C173E6ADF0C3" DisplayName="Toast activator"/>
            </com:ExeServer>
          </com:ComServer>
        </com:Extension>

        <!--Specify which CLSID to activate when toast clicked-->
        <desktop:Extension Category="windows.toastNotificationActivation">
          <desktop:ToastNotificationActivation ToastActivatorCLSID="replaced-with-your-guid-C173E6ADF0C3" /> 
        </desktop:Extension>

      </Extensions>
    </Application>
  </Applications>
 </Package>

Tidak dikemas

Jika aplikasi Anda dibongkar (lihat Membuat proyek baru untuk aplikasi desktop WinUI 3 yang tidak dikemas), atau jika Anda mendukung keduanya, maka Anda harus mendeklarasikan ID Model Pengguna Aplikasi (AUMID) dan clsID aktivator roti panggang (GUID dari langkah #4) di pintasan aplikasi Anda di Mulai.

Pilih AUMID unik yang akan mengidentifikasi aplikasi Anda. Ini biasanya dalam bentuk [CompanyName]. [AppName]. Tetapi Anda ingin memastikan bahwa ini unik di semua aplikasi (jadi jangan ragu untuk menambahkan beberapa digit di akhir).

Langkah 5.1: Penginstal WiX

Jika Anda menggunakan WiX untuk penginstal, edit file Product.wxs untuk menambahkan dua properti pintasan ke pintasan menu Mulai Anda seperti yang terlihat di bawah ini. Pastikan GUID Anda dari langkah #4 diapit seperti yang terlihat di {} bawah ini.

Product.wxs

<Shortcut Id="ApplicationStartMenuShortcut" Name="Wix Sample" Description="Wix Sample" Target="[INSTALLFOLDER]WixSample.exe" WorkingDirectory="INSTALLFOLDER">
                    
    <!--AUMID-->
    <ShortcutProperty Key="System.AppUserModel.ID" Value="YourCompany.YourApp"/>
    
    <!--COM CLSID-->
    <ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{replaced-with-your-guid-C173E6ADF0C3}"/>
    
</Shortcut>

Penting

Untuk benar-benar menggunakan pemberitahuan, Anda harus menginstal aplikasi Anda melalui alat penginstal sekali sebelum penelusuran kesalahan secara normal, sehingga pintasan Mulai dengan AUMID dan CLSID Anda ada. Setelah pintasan Mulai ada, Anda dapat men-debug menggunakan F5 dari Visual Studio.

Langkah 5.2: Mendaftarkan server AUMID dan COM

Kemudian, terlepas dari penginstal Anda, dalam kode startup aplikasi Anda (sebelum memanggil API pemberitahuan apa pun), panggil metode RegisterAumidAndComServer , menentukan kelas aktivator pemberitahuan Anda dari langkah #4 dan AUMID Anda yang digunakan di atas.

// Register AUMID and COM server (for a packaged app, this is a no-operation)
hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"YourCompany.YourApp", __uuidof(NotificationActivator));

Jika aplikasi Anda mendukung penyebaran yang dikemas dan tidak dikemas, jangan ragu untuk memanggil metode ini terlepas dari itu. Jika Anda menjalankan paket (yaitu, dengan identitas paket saat runtime), maka metode ini hanya akan segera kembali. Tidak perlu membuat fork kode Anda.

Metode ini memungkinkan Anda memanggil API kompat untuk mengirim dan mengelola pemberitahuan tanpa harus terus-menerus memberikan AUMID Anda. Dan menyisipkan kunci registri LocalServer32 untuk server COM.

Langkah 6: Daftarkan aktivator COM

Untuk aplikasi yang dikemas dan tidak dikemas, Anda harus mendaftarkan jenis aktivator pemberitahuan, sehingga Anda dapat menangani aktivasi toast.

Di kode startup aplikasi Anda, panggil metode RegisterActivator berikut. Ini harus dipanggil agar Anda menerima aktivasi roti panggang apa pun.

// Register activator type
hr = DesktopNotificationManagerCompat::RegisterActivator();

Langkah 7: Mengirim pemberitahuan

Mengirim pemberitahuan identik dengan aplikasi UWP, kecuali Anda akan menggunakan DesktopNotificationManagerCompat untuk membuat ToastNotifier. Pustaka kompat secara otomatis menangani perbedaan antara aplikasi yang dikemas dan tidak dikemas, sehingga Anda tidak perlu membuat fork kode Anda. Untuk aplikasi yang tidak dikemas, pustaka kompat menyimpan cache AUMID yang Anda berikan saat Anda memanggil RegisterAumidAndComServer sehingga Anda tidak perlu khawatir tentang kapan harus menyediakan atau tidak menyediakan AUMID.

Pastikan Anda menggunakan pengikatan ToastGeneric seperti yang terlihat di bawah ini, karena templat pemberitahuan toast Windows 8.1 warisan tidak akan mengaktifkan aktivator pemberitahuan COM yang Anda buat di langkah #4.

Penting

Gambar http hanya didukung dalam aplikasi paket yang memiliki kemampuan internet dalam manifesnya. Aplikasi yang tidak dikemas tidak mendukung gambar http; Anda harus mengunduh gambar ke data aplikasi lokal Anda, dan mereferensikannya secara lokal.

// Construct XML
ComPtr<IXmlDocument> doc;
hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(
    L"<toast><visual><binding template='ToastGeneric'><text>Hello world</text></binding></visual></toast>",
    &doc);
if (SUCCEEDED(hr))
{
    // See full code sample to learn how to inject dynamic text, buttons, and more

    // Create the notifier
    // Desktop apps must use the compat method to create the notifier.
    ComPtr<IToastNotifier> notifier;
    hr = DesktopNotificationManagerCompat::CreateToastNotifier(&notifier);
    if (SUCCEEDED(hr))
    {
        // Create the notification itself (using helper method from compat library)
        ComPtr<IToastNotification> toast;
        hr = DesktopNotificationManagerCompat::CreateToastNotification(doc.Get(), &toast);
        if (SUCCEEDED(hr))
        {
            // And show it!
            hr = notifier->Show(toast.Get());
        }
    }
}

Penting

Aplikasi desktop tidak dapat menggunakan templat roti panggang warisan (seperti ToastText02). Aktivasi templat warisan akan gagal ketika COM CLSID ditentukan. Anda harus menggunakan templat Windows ToastGeneric, seperti yang terlihat di atas.

Langkah 8: Menangani aktivasi

Saat pengguna mengklik roti panggang Anda, atau tombol dalam toast, metode Aktifkan kelas NotificationActivator Anda dipanggil.

Di dalam metode Aktifkan, Anda dapat mengurai arg yang Anda tentukan dalam roti panggang dan mendapatkan input pengguna yang di ketik atau dipilih pengguna, lalu mengaktifkan aplikasi Anda.

Catatan

Metode Aktifkan dipanggil pada utas terpisah dari utas utama Anda.

// The GUID must be unique to your app. Create a new GUID if copying this code.
class DECLSPEC_UUID("replaced-with-your-guid-C173E6ADF0C3") NotificationActivator WrlSealed WrlFinal
    : public RuntimeClass<RuntimeClassFlags<ClassicCom>, INotificationActivationCallback>
{
public: 
    virtual HRESULT STDMETHODCALLTYPE Activate(
        _In_ LPCWSTR appUserModelId,
        _In_ LPCWSTR invokedArgs,
        _In_reads_(dataCount) const NOTIFICATION_USER_INPUT_DATA* data,
        ULONG dataCount) override
    {
        std::wstring arguments(invokedArgs);
        HRESULT hr = S_OK;

        // Background: Quick reply to the conversation
        if (arguments.find(L"action=reply") == 0)
        {
            // Get the response user typed.
            // We know this is first and only user input since our toasts only have one input
            LPCWSTR response = data[0].Value;

            hr = DesktopToastsApp::SendResponse(response);
        }

        else
        {
            // The remaining scenarios are foreground activations,
            // so we first make sure we have a window open and in foreground
            hr = DesktopToastsApp::GetInstance()->OpenWindowIfNeeded();
            if (SUCCEEDED(hr))
            {
                // Open the image
                if (arguments.find(L"action=viewImage") == 0)
                {
                    hr = DesktopToastsApp::GetInstance()->OpenImage();
                }

                // Open the app itself
                // User might have clicked on app title in Action Center which launches with empty args
                else
                {
                    // Nothing to do, already launched
                }
            }
        }

        if (FAILED(hr))
        {
            // Log failed HRESULT
        }

        return S_OK;
    }

    ~NotificationActivator()
    {
        // If we don't have window open
        if (!DesktopToastsApp::GetInstance()->HasWindow())
        {
            // Exit (this is for background activation scenarios)
            exit(0);
        }
    }
};

// Flag class as COM creatable
CoCreatableClass(NotificationActivator);

Untuk mendukung peluncuran dengan benar saat aplikasi ditutup, dalam fungsi WinMain, Anda harus menentukan apakah Anda diluncurkan dari roti panggang atau tidak. Jika diluncurkan dari roti panggang, akan ada arg peluncuran "-ToastActivated". Ketika Anda melihat ini, Anda harus berhenti melakukan kode aktivasi peluncuran normal, dan mengizinkan NotificationActivator Anda untuk menangani jendela peluncuran jika diperlukan.

// Main function
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE, _In_ LPWSTR cmdLineArgs, _In_ int)
{
    RoInitializeWrapper winRtInitializer(RO_INIT_MULTITHREADED);

    HRESULT hr = winRtInitializer;
    if (SUCCEEDED(hr))
    {
        // Register AUMID and COM server (for a packaged app, this is a no-operation)
        hr = DesktopNotificationManagerCompat::RegisterAumidAndComServer(L"WindowsNotifications.DesktopToastsCpp", __uuidof(NotificationActivator));
        if (SUCCEEDED(hr))
        {
            // Register activator type
            hr = DesktopNotificationManagerCompat::RegisterActivator();
            if (SUCCEEDED(hr))
            {
                DesktopToastsApp app;
                app.SetHInstance(hInstance);

                std::wstring cmdLineArgsStr(cmdLineArgs);

                // If launched from toast
                if (cmdLineArgsStr.find(TOAST_ACTIVATED_LAUNCH_ARG) != std::string::npos)
                {
                    // Let our NotificationActivator handle activation
                }

                else
                {
                    // Otherwise launch like normal
                    app.Initialize(hInstance);
                }

                app.RunMessageLoop();
            }
        }
    }

    return SUCCEEDED(hr);
}

Urutan aktivasi peristiwa

Urutan aktivasi adalah sebagai berikut...

Jika aplikasi Anda sudah berjalan:

  1. Aktifkan di NotificationActivator Anda dipanggil

Jika aplikasi Anda tidak berjalan:

  1. Aplikasi Anda adalah EXE diluncurkan, Anda mendapatkan arg baris perintah "-ToastActivated"
  2. Aktifkan di NotificationActivator Anda dipanggil

Aktivasi latar depan vs latar belakang

Untuk aplikasi desktop, aktivasi latar depan dan latar belakang ditangani secara identik—aktivator COM Anda dipanggil. Terserah kode aplikasi Anda untuk memutuskan apakah akan menampilkan jendela atau hanya melakukan beberapa pekerjaan lalu keluar. Oleh karena itu, menentukan activationTypelatar belakang dalam konten roti panggang Anda tidak mengubah perilaku.

Langkah 9: Menghapus dan mengelola pemberitahuan

Menghapus dan mengelola pemberitahuan identik dengan aplikasi UWP. Namun, kami sarankan Anda menggunakan pustaka kompat kami untuk mendapatkan DesktopNotificationHistoryCompat sehingga Anda tidak perlu khawatir tentang menyediakan AUMID untuk aplikasi desktop.

std::unique_ptr<DesktopNotificationHistoryCompat> history;
auto hr = DesktopNotificationManagerCompat::get_History(&history);
if (SUCCEEDED(hr))
{
    // Remove a specific toast
    hr = history->Remove(L"Message2");

    // Clear all toasts
    hr = history->Clear();
}

Langkah 10: Menyebarkan dan men-debug

Untuk menyebarkan dan men-debug aplikasi paket Anda, lihat Menjalankan, men-debug, dan menguji aplikasi desktop yang dipaketkan.

Untuk menyebarkan dan men-debug aplikasi desktop, Anda harus menginstal aplikasi melalui alat penginstal sekali sebelum penelusuran kesalahan secara normal, sehingga pintasan Mulai dengan AUMID dan CLSID Anda ada. Setelah pintasan Mulai ada, Anda dapat men-debug menggunakan F5 dari Visual Studio.

Jika pemberitahuan Anda gagal muncul di aplikasi desktop Anda (dan tidak ada pengecualian yang dilemparkan), itu mungkin berarti pintasan Mulai tidak ada (instal aplikasi Anda melalui alat penginstal), atau AUMID yang Anda gunakan dalam kode tidak cocok dengan AUMID di pintasan Mulai Anda.

Jika pemberitahuan Anda muncul tetapi tidak bertahan di Pusat Tindakan (menghilang setelah popup dihentikan), itu berarti Anda belum menerapkan aktivator COM dengan benar.

Jika Anda telah menginstal aplikasi desktop yang dikemas dan tidak dikemas, perhatikan bahwa aplikasi paket akan menggantikan aplikasi yang tidak dikemas saat menangani aktivasi toast. Itu berarti bahwa roti panggang dari aplikasi yang tidak dikemas akan meluncurkan aplikasi yang dikemas saat diklik. Menghapus instalan aplikasi yang dikemas akan mengembalikan aktivasi kembali ke aplikasi yang tidak dikemas.

Jika Anda menerima HRESULT 0x800401f0 CoInitialize has not been called., pastikan untuk memanggil CoInitialize(nullptr) di aplikasi Anda sebelum memanggil API.

Jika Anda menerima HRESULT 0x8000000e A method was called at an unexpected time. saat memanggil API Compat, itu mungkin berarti Anda gagal memanggil metode Register yang diperlukan (atau jika aplikasi yang dipaketkan, Saat ini Anda tidak menjalankan aplikasi anda dalam konteks paket).

Jika Anda mendapatkan banyak unresolved external symbol kesalahan kompilasi, Anda mungkin lupa menambahkan runtimeobject.lib ke Dependensi Tambahan di langkah #1 (atau Anda hanya menambahkannya ke konfigurasi Debug dan bukan Konfigurasi rilis).

Menangani versi Windows yang lebih lama

Jika Anda mendukung Windows 8.1 atau yang lebih rendah, Anda harus memeriksa pada waktu proses apakah Anda menjalankan windows sebelum memanggil API DesktopNotificationManagerCompat atau mengirim toastGeneric apa pun.

Windows 8 memperkenalkan pemberitahuan toast, tetapi menggunakan templat roti panggang warisan, seperti ToastText01. Aktivasi ditangani oleh peristiwa Diaktifkan dalam memori pada kelas ToastNotification karena toast hanya popup singkat yang tidak bertahan. Windows 10 memperkenalkan toastGeneric interaktif, dan juga memperkenalkan Pusat Tindakan tempat pemberitahuan dipertahankan selama beberapa hari. Pengenalan Pusat Tindakan memerlukan pengenalan aktivator COM, sehingga roti panggang Anda dapat diaktifkan beberapa hari setelah Anda membuatnya.

OS ToastGeneric Aktivator COM Templat roti panggang warisan
Windows 10 dan yang lebih baru Didukung Didukung Didukung (tetapi tidak akan mengaktifkan server COM)
Windows 8.1 / 8 T/A T/A Didukung
Windows 7 dan yang lebih rendah T/A T/A T/A

Untuk memeriksa apakah Anda menjalankan Windows 10 atau yang lebih baru, sertakan <VersionHelpers.h> header, dan periksa metode IsWindows10OrGreater . Jika itu mengembalikan true, maka lanjutkan memanggil semua metode yang dijelaskan dalam dokumentasi ini.

#include <VersionHelpers.h>

if (IsWindows10OrGreater())
{
    // Running on Windows 10 or later, continue with sending toasts!
}

Masalah umum

DIPERBAIKI: Aplikasi tidak menjadi fokus setelah mengklik roti panggang: Dalam build 15063 dan yang lebih lama, hak latar depan tidak ditransfer ke aplikasi Anda saat kami mengaktifkan server COM. Oleh karena itu, aplikasi Anda hanya akan berkedip ketika Anda mencoba memindahkannya ke latar depan. Tidak ada solusi untuk masalah ini. Kami memperbaiki ini dalam build 16299 atau yang lebih baru.

Sumber