有没有可能从控制台应用程序发送Toast通知?

21

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

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

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

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

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

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


一个控制台应用程序没有GUI,因此Toast是无意义的,不是吗? - ManoDestra
干杯为了什么?看起来你已经回答了自己的问题。 - David L
通知具有许多应用程序,在这种情况下,我实际上非常需要它。 - Evaldas B
控制台应用程序应在后台运行,并在事件发生时发送通知。 - Evaldas B
@Evaldas B:它们可以有清单和弹出式通知。有很多用例。告诉别人他们不需要他们需要的东西真的没有太大用处,不是吗? :-) - Gábor
显示剩余2条评论
3个回答

20

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

  1. 右键单击项目,选择取消加载项目
  2. 右键单击不可用的项目并点击编辑 yourProject.csproj
  3. 添加新的属性组:<targetplatformversion>8.0</targetplatformversion>
  4. 重新加载项目。
  5. Windows > Core中添加引用Windows
    enter image description here

现在您需要添加以下代码:

using Windows.UI.Notifications;

你将能够使用这段代码发送通知:

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

参考: 如何从C#桌面应用程序中调用Windows 8中的WinRT API - WinRT Diagram


在那次编辑之后,我的控制台应用程序出现了引用错误:无法加载文件或程序集'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a',导致无法启动。 - Jamby
它可以工作,这是由微软建议的,但这不是最好的方法。这会导致大量的DLL文件与应用程序一起复制。如果您只手动添加所需的五到六个引用,您可以在不使用任何额外DLL的情况下使其工作,只需使用系统程序集即可。 - Gábor

7

在Evaldas B的代码中,我遇到了一些问题,我缺少了一个字符串。 (需要字符串的地方)

.CreateToastNotifier(<需要在这里添加字符串>).Show(toast);

警告:我对C#还比较陌生,所以我的代码可能不太好-但它确实能够工作并且相当简单,这比我找到的大多数解决方案都要好。

此外,我花费了很长时间才能让xml文档读取。我一直在使用System.xml(我想是这个)和Windows.Data.Dom.Xml(也不完全确定)进行斗争。 最终,我将它们硬编码为我的示例文件的字符串,并使用switch语句在它们之间进行切换。 我在stackoverflow上发现了很多人正在寻找我想出的解决方案。似乎将toast通知系统与控制台或后台应用程序一起使用会非常有用,而围绕窗口应用程序的toast通知系统的文档都建议必须与应用程序一起使用。 与NotificationTray / NotifyIcon路线相比,Action Center对于通知非常有用。 我在其他任何地方都没有找到完整的解决方案。以下是示例代码。

/*
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?我尝试了许多可能的变化,查看了通用应用程序清单,但它们都没有起作用。Toast被显示出来了,但是当我点击通用应用程序内部的按钮时,它并没有启动。实际上,在我的情况下,通用应用程序是桌面桥接通用应用程序,WPF包装成UWP容器。 - Alex

3

1) 如果要在控制台或桌面应用程序中显示弹出通知,您的应用程序必须在开始菜单上有一个快捷方式。

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

现在您可以创建并显示一个toast。
// 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? - Luca Ziegler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接