是否可以从控制台应用程序发送 Toast 通知?

Posted

技术标签:

【中文标题】是否可以从控制台应用程序发送 Toast 通知?【英文标题】:Is it possible to send Toast notification from console application? 【发布时间】:2016-10-29 22:51:51 【问题描述】:

是否可以使用 ToastNotificationManager 从控制台应用程序发送 Toast 通知?

我知道可以从 Windows 通用应用程序发送 Toast 通知:

var toast = new ToastNotification(doc);
ToastNotificationManager.CreateToastNotifier().Show(toast);

*doc - Toast 存储在 XML 字符串中

要使用 ToastNotificaionManager,我需要在控制台应用程序项目中无法引用的 Windows.UI.Notifications 库。

我之前提到的库实际上是由 WinRT 使用的。是否可以在 Windows 控制台应用程序中使用 WinRT API?

【问题讨论】:

控制台应用程序没有 GUI,因此 Toast 没有意义,不是吗? 干杯什么?您似乎已经回答了自己的问题。 Notifications 有很多应用,在这种情况下我真的很需要它 控制台应用程序应该在后台运行并在事件发生时发送通知 ***.com/questions/12745703/… 可能就是你要找的东西 【参考方案1】:

首先您需要声明您的程序将使用 winRT 库:

    右键单击您的项目,选择卸载项目 右键单击 yourProject(不可用),然后单击 Edit yourProject.csproj 添加新的属性组:<targetplatformversion>8.0</targetplatformversion> 重新加载项目 从Windows > Core添加参考Windows

现在你需要添加这段代码:

using Windows.UI.Notifications;

您将能够使用此代码发送通知:

var toast = new ToastNotification(doc);
ToastNotificationManager.CreateToastNotifier().Show(toast);

参考:How to call WinRT APIs in Windows 8 from C# Desktop Applications - WinRT Diagram

【讨论】:

编辑后,我的控制台应用程序没有以引用错误启动:无法加载文件或程序集'System.Runtime,Version=4.0.20.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a'跨度> 它有效,这是 MS 建议的,但这不是最好的方法。这会导致应用程序复制大量 DLL。如果您只是手动添加所需的五个或六个引用,则无需一个额外的 DLL,只需使用系统程序集即可使其工作。【参考方案2】:

我在使用 Evaldas B 的代码时遇到了一些问题,我缺少一个字符串。 (这里说需要字符串)

.CreateToastNotifier(<needed a string here>).Show(toast);

警告我是 C# 的新手,所以我的代码可能很糟糕——但它确实有效,而且非常简单,对于我找到的大多数解决方案来说,这比我能说的要多

另外,我在阅读 xml 文档时也遇到了麻烦。我正在与 System.xml(我认为)和 Windows.Data.Dom.Xml(也不完全确定)作斗争。 最后,我决定为我的示例文件制作硬编码字符串,并使用 switch 语句在它们之间切换。 我找到了很多人,在堆栈溢出时寻找我想出的解决方案。似乎将 toast 通知系统与控制台或后台应用程序一起使用会非常有用,并且围绕带有 windows 应用程序的 toast 通知系统的文档都表明它需要与应用程序一起使用。操作中心对于通知与 NotificationTray/NotifyIcon 路由非常有用。我在网络上的其他任何地方都没有找到完整的解决方案。这是示例代码。

/*
At first you need to declare that your program will be using winRT libraries:
1. Right click on your yourProject, select Unload Project
2. Right click on your youProject(unavailable) and click Edit yourProject.csproj
3. Add a new property group:<TargetPlatformVersion>8.0</TargetPlatformVersion>
4. Reload project
5. Add referece Windows from Windows > Core
*/
using System;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Notifications;

namespace ConsoleApplication6

    public class NewToastNotification
    
        public NewToastNotification(string input, int type)
        
            string NotificationTextThing = input;
            string Toast = "";
            switch (type)
            
                case 1:
                    
                        //Basic Toast
                        Toast = "<toast><visual><binding template=\"ToastImageAndText01\"><text id = \"1\" >";
                        Toast += NotificationTextThing;
                        Toast += "</text></binding></visual></toast>";
                        break;
                    
                default:
                    
                        Toast = "<toast><visual><binding template=\"ToastImageAndText01\"><text id = \"1\" >";
                        Toast += "Default Text String";
                        Toast += "</text></binding></visual></toast>";
                        break;
                    
            
            XmlDocument tileXml = new XmlDocument();
            tileXml.LoadXml(Toast);
            var toast = new ToastNotification(tileXml);
            ToastNotificationManager.CreateToastNotifier("New Toast Thing").Show(toast);
        


    class Program
    
        static void Main(string[] args)
        
            NewToastNotification Window = new NewToastNotification("Yes",1);


        
    

【讨论】:

有人对此有答案吗?传递给 CreateToastNotifier 调用的应用程序 ID 应该是什么?如何找到该应用程序ID?我尝试了许多可能的变化来查看通用应用程序清单,但没有一个起作用。显示吐司,好吧,但是当我点击通用应用程序内的按钮时,没有启动。实际上,在我的情况下,通用应用程序是桌面桥通用应用程序,WPF 包装在 UWP 容器中。【参考方案3】:

1) 要使用控制台或桌面应用程序显示 Toast 通知,您的应用程序必须在开始菜单上有快捷方式。

2) 要让应用程序在 Windows 的开始菜单中有快捷图标(不是磁贴图标),您的应用程序必须有一个 AppId。 要为您的应用程序创建一个快捷方式,请创建一个名为 ShellHelpers.cs 的新类并将此代码粘贴到其中。

using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using MS.WindowsAPICodePack.Internal;

 namespace DesktopToastsSample.ShellHelpers
 
     internal enum STGM : long
     
        STGM_READ = 0x00000000L,
    STGM_WRITE = 0x00000001L,
    STGM_READWRITE = 0x00000002L,
    STGM_SHARE_DENY_NONE = 0x00000040L,
    STGM_SHARE_DENY_READ = 0x00000030L,
    STGM_SHARE_DENY_WRITE = 0x00000020L,
    STGM_SHARE_EXCLUSIVE = 0x00000010L,
    STGM_PRIORITY = 0x00040000L,
    STGM_CREATE = 0x00001000L,
    STGM_CONVERT = 0x00020000L,
    STGM_FAILIFTHERE = 0x00000000L,
    STGM_DIRECT = 0x00000000L,
    STGM_TRANSACTED = 0x00010000L,
    STGM_NOSCRATCH = 0x00100000L,
    STGM_NOSNAPSHOT = 0x00200000L,
    STGM_SIMPLE = 0x08000000L,
    STGM_DIRECT_SWMR = 0x00400000L,
    STGM_DELETEONRELEASE = 0x04000000L,


internal static class ShellIIDGuid

    internal const string IShellLinkW = "000214F9-0000-0000-C000-000000000046";
    internal const string CShellLink = "00021401-0000-0000-C000-000000000046";
    internal const string IPersistFile = "0000010b-0000-0000-C000-000000000046";
    internal const string IPropertyStore = "886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99";


[ComImport,
Guid(ShellIIDGuid.IShellLinkW),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellLinkW

    UInt32 GetPath(
        [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
        int cchMaxPath,
        //ref _WIN32_FIND_DATAW pfd,
        IntPtr pfd,
        uint fFlags);
    UInt32 GetIDList(out IntPtr ppidl);
    UInt32 SetIDList(IntPtr pidl);
    UInt32 GetDescription(
        [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
        int cchMaxName);
    UInt32 SetDescription(
        [MarshalAs(UnmanagedType.LPWStr)] string pszName);
    UInt32 GetWorkingDirectory(
        [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir,
        int cchMaxPath
        );
    UInt32 SetWorkingDirectory(
        [MarshalAs(UnmanagedType.LPWStr)] string pszDir);
    UInt32 GetArguments(
        [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs,
        int cchMaxPath);
    UInt32 SetArguments(
        [MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
    UInt32 GetHotKey(out short wHotKey);
    UInt32 SetHotKey(short wHotKey);
    UInt32 GetShowCmd(out uint iShowCmd);
    UInt32 SetShowCmd(uint iShowCmd);
    UInt32 GetIconLocation(
        [Out(), MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszIconPath,
        int cchIconPath,
        out int iIcon);
    UInt32 SetIconLocation(
        [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath,
        int iIcon);
    UInt32 SetRelativePath(
        [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel,
        uint dwReserved);
    UInt32 Resolve(IntPtr hwnd, uint fFlags);
    UInt32 SetPath(
        [MarshalAs(UnmanagedType.LPWStr)] string pszFile);


[ComImport,
Guid(ShellIIDGuid.IPersistFile),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersistFile

    UInt32 GetCurFile(
        [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile
    );
    UInt32 IsDirty();
    UInt32 Load(
        [MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
        [MarshalAs(UnmanagedType.U4)] STGM dwMode);
    UInt32 Save(
        [MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
        bool fRemember);
    UInt32 SaveCompleted(
        [MarshalAs(UnmanagedType.LPWStr)] string pszFileName);

[ComImport]
[Guid(ShellIIDGuid.IPropertyStore)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IPropertyStore

    UInt32 GetCount([Out] out uint propertyCount);
    UInt32 GetAt([In] uint propertyIndex, out PropertyKey key);
    UInt32 GetValue([In] ref PropertyKey key, [Out] PropVariant pv);
    UInt32 SetValue([In] ref PropertyKey key, [In] PropVariant pv);
    UInt32 Commit();



[ComImport,
Guid(ShellIIDGuid.CShellLink),
ClassInterface(ClassInterfaceType.None)]
internal class CShellLink  

public static class ErrorHelper

    public static void VerifySucceeded(UInt32 hresult)
    
        if (hresult > 1)
        
            throw new Exception("Failed with HRESULT: " + hresult.ToString("X"));
        
    


用于创建快捷方式的代码(此代码可以添加到您将显示 toast 的同一类中)

public bool TryCreateShortcut()
    
        String shortcutPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Microsoft\\Windows\\Start Menu\\Programs\\FixSus Toasts Sample .lnk";
        if (!File.Exists(shortcutPath))
        
            InstallShortcut(shortcutPath);
            return true;
        
        return false;
    

    private void InstallShortcut(String shortcutPath)
    
        // Find the path to the current executable
        String exePath = Process.GetCurrentProcess().MainModule.FileName;
        IShellLinkW newShortcut = (IShellLinkW)new CShellLink();

        // Create a shortcut to the exe
        DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath));
        DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetArguments(""));

        // Open the shortcut property store, set the AppUserModelId property
        IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut;

        using (PropVariant appId = new PropVariant(APP_ID))
        
            DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(SystemProperties.System.AppUserModel.ID, appId));
            DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
        

        // Commit the shortcut to disk
        IPersistFile newShortcutSave = (IPersistFile)newShortcut;

        DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true));
    

现在您可以创建一个展示祝酒词

// Get a toast XML template
        XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText04);

        // Fill in the text elements
        XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
        stringElements[1].AppendChild(toastXml.CreateTextNode("Message" + newMessage));


        // Specify the absolute path to an image
        string codeWebFolderPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\"));
        String imagePath = "file:///" + Path.GetFullPath(codeWebFolderPath+ "Resources\\FixSus.png");
        XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
        imageElements[0].Attributes.GetNamedItem("src").NodeValue = imagePath;

        // Create the toast and attach event listeners
        ToastNotification toast = new ToastNotification(toastXml);

        toast.Activated += ToastActivated;
        toast.Dismissed += ToastDismissed;
        toast.Failed += ToastFailed;

        // Show the toast. Be sure to specify the AppUserModelId on your application's shortcut!
        ToastNotificationManager.CreateToastNotifier(APP_ID).Show(toast);

APP_ID 可以是任何字符串。就我而言,它是“NotificationTest.KEY” 注意:不要修改 ShellHelper 类。 编辑:首先遵循 Evaldas B 的回答,然后应用此解决方案。

【讨论】:

如何在快捷方式中保存 CLSID?

以上是关于是否可以从控制台应用程序发送 Toast 通知?的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中执行点击操作后,如何从通知中发送 Toast?

应用程序中收到的 WP7 推送通知

Windows Phone 8 中的 Toast 通知在 10 秒后消失

从 Windows 服务发送 toast 通知会引发 HRESULT: 0x80070005 (E_ACCESSDENIED)

控制推送通知

Windows Phone 使用 PHP 推送 TOAST 通知