Freigeben über


Senden einer lokalen Toast-Benachrichtigung von einer WRL-C++-Desktopanwendung

Verpackte und entpackte Desktop-Apps können interaktive Toast-Benachrichtigungen senden, genauso wie UWP-Apps (Universelle Windows-Plattform). Dazu gehören verpackte Apps (siehe Erstellen eines neuen Projekts für eine verpackte WinUI 3-Desktop-App); verpackte Apps mit externem Speicherort (siehe Erteilen der Paketidentität durch Verpacken mit externem Speicherort); und entpackte Apps (siehe Erstellen eines neuen Projekts für eine entpackte WinUI 3-Desktop-App).

Für eine entpackte Desktop-App gibt es jedoch einige spezielle Schritte. Das liegt an den verschiedenen Aktivierungsschemas und an fehlender Paketidentität zur Laufzeit.

Von Bedeutung

Wenn Sie eine UWP-App schreiben, lesen Sie die UWP-Dokumentation. Weitere Desktopsprachen finden Sie unter Desktop C#.

Schritt 1: Aktivieren des Windows SDK

Wenn Sie das Windows SDK für Ihre App nicht aktiviert haben, müssen Sie dies zuerst tun. Es gibt einige wichtige Schritte.

  1. Fügen Sie zusätzliche Abhängigkeiten runtimeobject.lib zu hinzu.
  2. Richten Sie das Windows SDK aus.

Klicken Sie mit der rechten Maustaste auf Ihr Projekt, und wählen Sie Eigenschaftenaus.

Wählen Sie im oberen KonfigurationsmenüAlle Konfigurationen aus, sodass die folgende Änderung sowohl auf "Debuggen" als auch auf "Release" angewendet wird.

Fügen Sie unter Linker -> Eingabe-der runtimeobject.liben hinzu.

Stellen Sie dann unter Allgemeinsicher, dass die Windows SDK-Version auf Version 10.0 oder höher festgelegt ist.

Schritt 2: Kopieren des Compat-Bibliothekscodes

Kopieren Sie die Datei "DesktopNotificationManagerCompat.h" und DesktopNotificationManagerCompat.cpp aus GitHub in Ihr Projekt. Die Compat-Bibliothek abstrahiert einen Großteil der Komplexität von Desktopbenachrichtigungen. Die folgenden Anweisungen erfordern die Compat-Bibliothek.

Wenn Sie vorkompilierte Kopfzeilen verwenden, stellen Sie sicher, dass Sie #include "stdafx.h" als erste Zeile in der Datei DesktopNotificationManagerCompat.cpp einfügen.

Schritt 3: Einbinden der Header-Dateien und Namespaces

Fügen Sie die Headerdatei der Kompatibilitätsbibliothek sowie die Headerdateien und Namespaces hinzu, die im Zusammenhang mit der Verwendung der Windows-Toast-APIs stehen.

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

Schritt 4: Implementieren des Aktivators

Sie müssen einen Handler für die Toast-Benachrichtigung aktivieren implementieren, damit Ihre App beim Klicken des Benutzers auf die Toast-Benachrichtigung etwas tun kann. Dies ist erforderlich, damit die Toast-Benachrichtigung im Info-Center beibehalten wird (da sie Tage später geklickt werden kann, wenn Ihre App geschlossen ist). Diese Klasse kann an einer beliebigen Stelle in Ihrem Projekt platziert werden.

Implementieren Sie die INotificationActivationCallback Schnittstelle, wie unten dargestellt, einschließlich einer UUID, und rufen Sie auch CoCreatableClass auf, um Ihre Klasse als COM creatable zu kennzeichnen. Erstellen Sie für Ihre UUID eine eindeutige GUID mit einem der vielen Online-GUID-Generatoren. Diese GUID CLSID (Klassenbezeichner) ist, wie das Aktionscenter ermittelt, welche Klasse COM aktiviert werden soll.

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

Schritt 5: Registrieren mit der Benachrichtigungsplattform

Anschließend müssen Sie sich bei der Benachrichtigungsplattform registrieren. Es gibt unterschiedliche Schritte, je nachdem, ob Ihre App verpackt oder entpackt wird. Wenn Sie beides unterstützen, müssen Sie beide Schrittfolgen ausführen (es ist jedoch nicht erforderlich, Ihren Code aufzuteilen, da unsere Bibliothek das für Sie übernimmt).

Verpackt

Wenn Ihre App verpackt ist (siehe Erstellen eines neuen Projekts für eine verpackte WinUI 3-Desktop-App) oder mit externem Speicherort verpackt (siehe Gewähren der Paketidentität durch Verpacken mit externem Speicherort), oder wenn Sie beide unterstützen, fügen Sie in Ihrem Package.appxmanifest Folgendes hinzu:

  1. Deklaration für xmlns:com
  2. Deklaration für xmlns:desktop
  3. Im Attribut IgnorableNamespacescom und Desktop
  4. com:Extension für den COM-Aktivator mithilfe der GUID aus Schritt 4. Stellen Sie sicher, die Arguments="-ToastActivated" einzuschließen, damit Sie wissen, dass Ihr Start aus einer Toast-Benachrichtigung stammt.
  5. desktop:Extension für "windows.toastNotificationActivation", um Ihren Toastaktivator-CLSID (die GUID aus Schritt 4) zu deklarieren.

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>

Unverpackt

Wenn Ihre App nicht gepackt ist (siehe Erstellen eines neuen Projekts für eine entpackte WinUI 3-Desktop-App), oder wenn Sie beides unterstützen, müssen Sie Ihre Anwendungsbenutzermodell-ID (Application User Model ID, AUMID) und Toast-Aktivator-CLSID (die GUID aus Schritt 4) in der Verknüpfung Ihrer App im Startmenü angeben.

Wählen Sie eine eindeutige AUMID aus, die Ihre App identifiziert. Dies ist in der Regel in der Form von [CompanyName].[AppName]. Sie möchten jedoch sicherstellen, dass es in allen Apps einzigartig ist (sie können also am Ende einige Ziffern hinzufügen).

Schritt 5.1: WiX Installer

Wenn Sie WiX für Ihr Installationsprogramm verwenden, bearbeiten Sie die Datei "Product.wxs", um die beiden Verknüpfungseigenschaften zu Ihrer Startmenüverknüpfung hinzuzufügen, wie unten dargestellt. Achten Sie darauf, dass die GUID aus dem Schritt 4 in {} eingeschlossen ist, wie unten gezeigt.

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>

Von Bedeutung

Um Benachrichtigungen tatsächlich verwenden zu können, müssen Sie Ihre App einmal vor dem Debuggen über den Installer installieren, damit die Startverknüpfung mit Ihrer AUMID und der CLSID vorhanden ist. Nachdem die Verknüpfung "Start" vorhanden ist, können Sie das Debuggen in Visual Studio mit F5 starten.

Schritt 5.2: Registrieren des AUMID- und COM-Servers

Rufen Sie dann unabhängig vom Installationsprogramm im Startcode Ihrer App (bevor Sie Benachrichtigungs-APIs aufrufen) die Methode RegisterAumidAndComServer auf. Geben Sie dabei Ihre in Schritt 4 angegebene Benachrichtigungsaktivatorklasse sowie die oben verwendete AUMID an.

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

Wenn Ihre App sowohl verpackte als auch entpackte Bereitstellungen unterstützt, können Sie diese Methode unabhängig davon aufrufen. Wenn Sie paketiert ausführen (d. h. mit Paketidentität zur Laufzeit), wird diese Methode einfach sofort zurückgegeben. Es ist nicht erforderlich, den Code zu forken.

Mit dieser Methode können Sie die compat-APIs aufrufen, um Benachrichtigungen zu senden und zu verwalten, ohne ihre AUMID ständig bereitstellen zu müssen. Außerdem wird der LocalServer32-Registrierungsschlüssel für den COM-Server eingefügt.

Schritt 6: Registrieren des COM-Aktivators

Für verpackte und entpackte Apps müssen Sie den Benachrichtigungsaktivatortyp registrieren, damit Sie Popupaktivierungen behandeln können.

Rufen Sie im Startcode Ihrer App die folgende RegisterActivator--Methode auf. Dies muss aufgerufen werden, um Toast-Benachrichtigungen zu erhalten.

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

Schritt 7: Senden einer Benachrichtigung

Das Senden einer Benachrichtigung ist mit UWP-Apps identisch, mit der Ausnahme, dass Sie DesktopNotificationManagerCompat verwenden, um einen ToastNotifierzu erstellen. Die Kompatibilitätsbibliothek behandelt automatisch den Unterschied zwischen gepackten und entpackten Apps, sodass Sie den Code nicht verzweigen müssen. Bei einer entpackten App speichert die Kompatibilitätsbibliothek die AUMID, die Sie beim Aufrufen von RegisterAumidAndComServer angegeben haben, damit Sie sich keine Gedanken darüber machen müssen, wann die AUMID bereitgestellt werden muss oder nicht.

Stellen Sie sicher, dass Sie die ToastGeneric- Bindung wie unten dargestellt verwenden, da die älteren Windows 8.1-Popupbenachrichtigungsvorlagen den COM-Benachrichtigungsaktivator, den Sie in Schritt 4 erstellt haben, nicht aktivieren.

Von Bedeutung

Http-Bilder werden nur in verpackten Apps unterstützt, die über die Internetfunktion in ihrem Manifest verfügen. Entpackte Apps unterstützen keine HTTP-Bilder; Sie müssen das Bild in Ihre lokalen App-Daten herunterladen und lokal darauf verweisen.

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

Von Bedeutung

Desktop-Apps können keine Legacy-Toastvorlagen (z. B. ToastText02) verwenden. Die Aktivierung der älteren Vorlagen schlägt fehl, wenn die COM-CLSID angegeben wird. Sie müssen die Windows ToastGeneric-Vorlagen wie oben gezeigt verwenden.

Schritt 8: Umgang mit der Aktivierung

Wenn der Benutzer auf das Popup oder die Schaltflächen im Popup klickt, wird die Activate-Methode Ihrer NotificationActivator-Klasse aufgerufen.

Innerhalb der Aktivieren-Methode können Sie die Argumente analysieren, die Sie im Toast angegeben haben, um die Benutzereingabe abzurufen, und dann Ihre App entsprechend aktivieren.

Hinweis

Die Activate-Methode wird für einen separaten Thread vom Hauptthread aufgerufen.

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

Um den Start zu unterstützen, während Ihre App geschlossen ist, sollten Sie in Ihrer WinMain-Funktion ermitteln, ob Sie über eine Toast-Benachrichtigung gestartet werden oder nicht. Wenn es von einem Toast gestartet wird, gibt es ein Startargument mit "-ToastActivated". Wenn dies angezeigt wird, sollten Sie die Ausführung eines normalen Startaktivierungscodes beenden und Ihrem NotificationActivator gestatten, das Starten von Fenstern bei Bedarf zu behandeln.

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

Aktivierungssequenz von Ereignissen

Die Aktivierungssequenz lautet wie folgt...

Wenn Ihre App bereits ausgeführt wird:

  1. Aktivieren von in Ihrem NotificationActivator- wird aufgerufen.

Wenn Ihre App nicht ausgeführt wird:

  1. Ihre App wird als EXE-Datei gestartet, und Sie erhalten ein Befehlszeilenargument mit dem Argument "-ToastActivated".
  2. Aktivieren von in Ihrem NotificationActivator- wird aufgerufen.

Vordergrund- und Hintergrundaktivierung

Bei Desktop-Apps wird die Vordergrund- und Hintergrundaktivierung identisch behandelt – Ihr COM-Aktivator wird aufgerufen. Es liegt an Ihrem App-Code, zu entscheiden, ob ein Fenster angezeigt oder einfach einige Aufgaben ausgeführt werden sollen, um dann zu beenden. Die Angabe eines activationType- von Hintergrund- in Ihrem Toast-Inhalt ändert daher nicht das Verhalten.

Schritt 9: Entfernen und Verwalten von Benachrichtigungen

Das Entfernen und Verwalten von Benachrichtigungen ist identisch mit UWP-Apps. Es wird jedoch empfohlen, unsere Compat-Bibliothek zu verwenden, um eine DesktopNotificationHistoryCompat zu erhalten, damit Sie sich keine Gedanken über die Bereitstellung der AUMID für eine Desktop-App machen müssen.

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

Schritt 10: Bereitstellen und Debuggen

Informationen zum Bereitstellen und Debuggen Ihrer verpackten App finden Sie unter Ausführen, Debuggen und Testen einer verpackten Desktop-App.

Um Ihre Desktop-App bereitzustellen und zu debuggen, müssen Sie Ihre App einmal mithilfe des Installationsprogramms installieren, bevor Sie normalerweise debuggen können. Dadurch wird sichergestellt, dass die Startverknüpfung mit Ihrer AUMID und CLSID vorhanden ist. Nachdem die Verknüpfung "Start" vorhanden ist, können Sie das Debuggen in Visual Studio mit F5 starten.

Wenn Ihre Benachrichtigungen in Ihrer Desktop-App einfach nicht angezeigt werden (und keine Ausnahmen ausgelöst werden), bedeutet dies wahrscheinlich, dass die Startverknüpfung nicht vorhanden ist (installieren Sie Ihre App über das Installationsprogramm), oder die im Code verwendete AUMID stimmt nicht mit der AUMID in Ihrer Startverknüpfung überein.

Wenn Ihre Benachrichtigungen im Info-Center angezeigt werden, aber nicht gespeichert bleiben (verschwinden, nachdem das Popup geschlossen wird), bedeutet das, dass Sie den COM-Aktivator nicht korrekt implementiert haben.

Wenn Sie sowohl Ihre gepackte als auch die nicht gepackte Desktop-App installiert haben, beachten Sie, dass die gepackte App die nicht gepackte App bei Toast-Aktivierungen ersetzt. Das bedeutet, dass Popups aus der entpackten App beim Klicken die verpackte App starten. Durch die Deinstallation der gepackten App werden Aktivierungen wieder auf die ungepackte App zurückgesetzt.

Wenn Sie HRESULT 0x800401f0 CoInitialize has not been called.erhalten, stellen Sie sicher, dass Sie CoInitialize(nullptr) in Ihrer App aufrufen, bevor Sie APIs aufrufen.

Wenn Sie beim Aufrufen der Compat-APIs HRESULT 0x8000000e A method was called at an unexpected time. erhalten, bedeutet dies wahrscheinlich, dass Sie es versäumt haben, die erforderlichen Registermethoden aufzurufen (oder wenn Ihre App in einem gepackten Format vorliegt, dass sie derzeit nicht im gepackten Kontext ausgeführt wird).

Wenn Sie zahlreiche unresolved external symbol-Kompilierungsfehler erhalten, haben Sie wahrscheinlich vergessen, runtimeobject.lib den zusätzlichen Abhängigkeiten in Schritt #1 hinzuzufügen (oder Sie haben es nur der Debug-Konfiguration und nicht der Release-Konfiguration hinzugefügt).

Behandeln älterer Versionen von Windows

Wenn Sie Windows 8.1 oder niedriger unterstützen, sollten Sie zur Laufzeit überprüfen, ob Ihre Anwendung auf Windows läuft, bevor Sie DesktopNotificationManagerCompat-APIs aufrufen oder ToastGeneric-Benachrichtigungen senden.

Windows 8 führte Popupbenachrichtigungen ein, verwendete jedoch die legacy-Popupvorlagen, z. B. ToastText01. Die Aktivierung wurde vom Activated-Ereignis im Arbeitsspeicher in der ToastNotification-Klasse gehandhabt, da diese Benachrichtigungen nur kurze Popups waren, die nicht beibehalten wurden. Windows 10 hat interaktive ToastGeneric-Popupseingeführt und auch das Info-Center, in dem Benachrichtigungen für mehrere Tage gespeichert werden. Die Einführung des Action Centers erforderte die Verwendung eines COM-Aktivators, damit Ihr Toast Tage nach der Erstellung aktiviert werden kann.

Betriebssystem ToastGeneric COM-Aktivator Legacy-Toastvorlagen
Windows 10 und höher Unterstützt Unterstützt Unterstützt (aber wird den COM-Server nicht aktivieren)
Windows 8.1 / 8 Nicht verfügbar Nicht verfügbar Unterstützt
Windows 7 und niedriger Nicht verfügbar Nicht verfügbar Nicht verfügbar

Um zu überprüfen, ob auf Ihrem Computer Windows 10 oder höher ausgeführt wird, schließen Sie den <VersionHelpers.h>-Header ein, und überprüfen Sie die IsWindows10OrGreater--Methode. Wenn dies truezurückgibt, dann fahren Sie fort, alle in dieser Dokumentation beschriebenen Methoden aufzurufen.

#include <VersionHelpers.h>

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

Bekannte Probleme

BEHOBEN: Die App wird nach dem Anklicken der Toast-Benachrichtigungnicht fokussiert: In Builds 15063 und früher wurden die Vordergrundrechte bei der Aktivierung des COM-Servers nicht auf Ihre Anwendung übertragen. Daher würde Ihre App einfach blinken, wenn Sie versucht haben, sie in den Vordergrund zu verschieben. Für dieses Problem gab es keine Problemumgehung. Das Problem wurde in Builds 16299 oder höher behoben.

Ressourcen