Freigeben über


Senden einer lokalen app Benachrichtigung von einem WRL-C++-Desktop app

Verpackte und entpackte Desktop-Apps können interaktive app Benachrichtigungen wie UWP-Apps (Universelle Windows-Plattform) senden. Dies umfasst verpackte Apps (siehe Erstellen eines neuen Projekts für einen verpackten WinUI 3-Desktop app), verpackte Apps mit externem Speicherort (siehe Gewähren Sie der Paketidentität durch Verpackung mit externem Speicherort) und unverpackte Apps (siehe Erstellen Sie ein neues Projekt für einen unverpackten WinUI 3-Desktop app).

Für einen entpackten Desktop appgibt es jedoch einige spezielle Schritte. Das liegt an den verschiedenen Aktivierungsschemas und an fehlender Paketidentität zur Laufzeit.

Note

Der Begriff "toast notification" wird durch "app notification" ersetzt. Diese Begriffe beziehen sich beide auf dieselbe Funktion von Windows, aber im Laufe der Zeit werden wir die Verwendung von "toast Benachrichtigung" in der Dokumentation einstellen.

Important

Wenn Sie eine UWP appschreiben, 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 Ihr 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

Schließen Sie die Kompatibilitätsbibliotheks-Headerdatei sowie die Headerdateien und Namespaces ein, die für die Windows-Benachrichtigungs-APIs benötigt werden.

#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 Benachrichtigungsaktivierung app implementieren, damit app etwas ausführen kann, wenn der Benutzer auf die Benachrichtigung klickt. Dies ist erforderlich, damit Ihre Benachrichtigung im Info-Center beibehalten wird (da möglicherweise Tage später auf die Benachrichtigung geklickt wird, wenn Ihr 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 Ihr app verpackt oder entpackt ist. 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).

Packaged

Wenn Ihr app verpackt ist (siehe Erstellen eines neuen Projekts für einen verpackten WinUI 3-Desktop app) oder mit einem externen Speicherort verpackt ist (siehe Vergeben der Paketidentität durch Verpackung mit externem Speicherort), oder wenn Sie beides unterstützen, dann 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, dass Sie das Arguments="-ToastActivated" einfügen, damit Sie wissen, dass Ihre Auslösung durch eine app Benachrichtigung erfolgt ist.
  5. Desktop:Erweiterung für windows.toastNotificationActivation, um die CLSID (den GUID aus Schritt Nr. 4) Ihres Benachrichtigungsaktivators 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>

Unpackaged

Wenn Ihr app nicht verpackt ist (siehe Erstellen eines neuen Projekts für einen entpackten WinUI 3-Desktop app) oder wenn Sie beides unterstützen, müssen Sie die Anwendungsbenutzermodell-ID (AUMID) und den Aktivator-CLSID, (die GUID aus Schritt #4), in der Startmenü-Verknüpfung Ihres toast deklarieren.

Wählen Sie eine eindeutige AUMID aus, die Ihr 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>

Important

Um Benachrichtigungen tatsächlich zu verwenden, müssen Sie Ihr app einmal über den Installer installieren, bevor Sie normal debuggen, damit die Start-Verknü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.

Schritt 5.2: Registrieren des AUMID- und COM-Servers

Rufen Sie dann unabhängig von Ihrem Installationsprogramm im appStartcode (vor dem Aufrufen von Benachrichtigungs-APIs) die RegisterAumidAndComServer-Methode auf, und geben Sie ihre Benachrichtigungsaktivatorklasse aus Schritt 4 und Ihre 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 die verpackte als auch die entpackte Bereitstellung unterstützt, können Sie diese Methode problemlos jederzeit 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 sowohl verpackte als auch entpackte Apps müssen Sie Ihren Benachrichtigungsaktivatortyp registrieren, damit Sie toast-Aktivierungen verarbeiten können.

appRufen Sie im Startcode die folgende RegisterActivator-Methode auf. Dies muss aufgerufen werden, damit Sie toast Aktivierungen erhalten können.

// 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 einem entpackten appDokument speichert die compat-Bibliothek die AUMID zwischen, die Sie beim Aufrufen von RegisterAumidAndComServer angegeben haben, sodass Sie sich keine Gedanken darüber machen müssen, wann sie die AUMID bereitstellen oder nicht bereitstellen.

Stellen Sie sicher, dass Sie die ToastGeneric-Bindung wie unten dargestellt verwenden, da die Benachrichtigungsvorlagen für ältere Windows 8.1-Benachrichtigungen toast nicht den COM-Benachrichtigungsaktivator aktivieren, den Sie in Schritt 4 erstellt haben.

Important

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

Important

Desktop-Apps können keine Legacyvorlagen toast (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: Aktivierung durchführen

Wenn der Benutzer auf Ihre app Benachrichtigung oder Schaltflächen in der Benachrichtigung klickt, wird die Activate-Methode Ihrer NotificationActivator-Klasse aufgerufen.

Innerhalb der Activate-Methode können Sie die Argumente analysieren, die Sie in der Benachrichtigung angegeben haben, die Benutzereingabe abrufen, die der Benutzer eingegeben oder ausgewählt hat, und dann app entsprechend aktivieren.

Note

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 ordnungsgemäß zu unterstützen, wenn Ihr app geschlossen ist, sollten Sie in Ihrer WinMain-Funktion ermitteln, ob Sie von einer app Benachrichtigung aus gestartet werden oder nicht. Wenn die Anwendung über eine Benachrichtigung gestartet wird, gibt es ein Startargument von "-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 Ihr app bereits läuft, gehen Sie wie folgt vor:

  1. Aktivieren von in Ihrem NotificationActivator- wird aufgerufen.

Wenn Ihr app nicht ausgeführt wird, gehen Sie wie folgt vor:

  1. Ihr app wurde als EXE gestartet, Sie erhalten Befehlszeilenargumente von "-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 dem Code Ihres app, zu entscheiden, ob ein Fenster angezeigt wird oder ob einfach einige Aufgaben ausgeführt und dann beendet werden sollen. Die Angabe eines Aktivierungstyps im Hintergrund in Ihrem app Benachrichtigungsinhalt ändert daher das Verhalten nicht.

Schritt 9: Entfernen und Verwalten von Benachrichtigungen

Das Entfernen und Verwalten von Benachrichtigungen ist identisch mit UWP-Apps. Wir empfehlen Jedoch, dass Sie unsere Compat-Bibliothek verwenden, um ein DesktopNotificationHistoryCompat zu erhalten, damit Sie sich keine Gedanken über die Bereitstellung der AUMID für einen Desktop appmachen 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 Ihres Pakets appfinden Sie unter Ausführen, Debuggen und Testen eines verpackten Desktops app.

Um Ihren Desktop app zu bereitstellen und zu debuggen, müssen Sie Ihre app einmal mithilfe des Installationsprogramms installieren, bevor Sie normalerweise debuggen, damit die Verknüpfung "Start" 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 einfach nicht auf dem Desktop app angezeigt werden (und keine Ausnahmen ausgelöst werden), liegt es wahrscheinlich daran, dass die Startverknüpfung fehlt (installieren Sie Ihre app über das Installationsprogramm), oder die im Code verwendete AUMID entspricht nicht der AUMID in Ihrer Startverknüpfung.

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 die verpackte als auch die entpackte Desktop app-Version installiert haben, beachten Sie, dass die verpackte app die entpackte app bei der Verarbeitung von toast-Aktivierungen überschreiben wird. Dies bedeutet, dass app-Benachrichtigungen aus der entpackten app die verpackte app starten, wenn darauf geklickt wird. Durch die Deinstallation des Pakets app werden Aktivierungen wieder auf das entpackte Paket zurückgesetzt app.

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

Wenn Sie beim Aufrufen der Compat-APIs HRESULT 0x8000000e A method was called at an unexpected time. empfangen, bedeutet dies wahrscheinlich, dass Sie die erforderlichen Registermethoden nicht aufgerufen haben (oder wenn app ein Paket enthalten ist, werden Sie zurzeit app nicht unter dem verpackten Kontext ausgeführt).

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

Behandlung ä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 hat Benachrichtigungen eingeführttoast, aber die Legacyvorlagen toastwie ToastText01 verwendet. 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 Info-Centers erforderte einen COM-Aktivator, damit Ihr toast Tage nach dessen Erstellung aktiviert werden kann.

OS ToastGeneric COM-Aktivator Legacy Vorlagen toast
Windows 10 und höher Supported Supported Unterstützt (aber wird den COM-Server nicht aktivieren)
Windows 8.1 / 8 N/A N/A Supported
Windows 7 und niedriger N/A N/A N/A

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: App wird nach dem Klicken toast nicht fokussiert: In Builds 15063 und früher wurden die Vordergrundrechte nicht auf Ihre Anwendung übertragen, wenn wir den COM-Server aktiviert hatten. Daher würde app einfach aufblinken, wenn Sie versuchen würden, sie in den Vordergrund zu verschieben. Für dieses Problem gab es keine Problemumgehung. Das Problem wurde in Builds 16299 oder höher behoben.

Resources