21

Is it possible to send Toast notifications from console application using ToastNotificationManager ?

I know that it is possible to send Toast notifications from Windows Universal app:

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

*doc - Toast stored in XML string

To use ToastNotificaionManager I need Windows.UI.Notifications library which I can't reference in console application project.

The library I mentionet before is actualy used by WinRT. Is it possible to use WinRT APIs in Windows console application ?

Evaldas B
  • 2,464
  • 3
  • 17
  • 25
  • A console application doesn't have a GUI, therefore a Toast is meaningless, no? – ManoDestra Jun 27 '16 at 19:54
  • A toast to what? It seems like you've answered your own question. – David L Jun 27 '16 at 19:55
  • Notifications has a lot of applications, and in this case I actualy realy need it – Evaldas B Jun 27 '16 at 19:55
  • Console application should be working in background and send notifications when an event occours – Evaldas B Jun 27 '16 at 19:57
  • http://stackoverflow.com/questions/12745703/how-can-i-use-the-windows-ui-namespace-from-a-regular-non-store-win32-net-app is probably what you're looking for – Warty Jun 27 '16 at 19:57
  • That one is for desktop apps, they have manifest files where I can declare that I will be using WinRT libraries. Console apps don't – Evaldas B Jun 27 '16 at 20:00
  • @Evaldas B: they can have manifests and they can have toasts. Plenty of use cases. Really not much use telling somebody that they don't need what the need, isn't it? :-) – Gábor Dec 30 '18 at 20:37

3 Answers3

20

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 yourProject(unavailable) and click Edit yourProject.csproj
  3. Add a new property group:<targetplatformversion>8.0</targetplatformversion>
  4. Reload project
  5. Add reference Windows from Windows > Core
    enter image description here

Now you need to add this code:

using Windows.UI.Notifications;

and you will be able to send notifications using this code:

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

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

yacc
  • 2,915
  • 4
  • 19
  • 33
Evaldas B
  • 2,464
  • 3
  • 17
  • 25
  • After that edit, my console application doesn't start with reference error: Could not load file or assembly 'System.Runtime, Version=4.0.20.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' – Jamby Jul 26 '16 at 15:01
  • It works and this is suggested by MS but this isn't the best way to do it. It results in a significant amount of DLLs copied with the app. If you just add the required five or six references manually, you can make it work without a single extra DLL, just using the system assemblies. – Gábor Dec 30 '18 at 20:33
7

I ran into some problems here with Evaldas B's Code I was missing a string. (Where It Says Need String Here)

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

warning I am kind of new to C# so my code probably sucks- but it does work and is pretty simplistic and that's more than I can say for most solutions I have found

Also I was having a hell of a time getting the xml document to read. I was fighting with System.xml (I think) and Windows.Data.Dom.Xml (also not completely sure). In the end I settled on making them hard coded strings for my example file and used a switch statement to switch between them. I have found a ton of people, looking for the solution that I have come up with, on stack overflow. It seems use of the toast notification system with console or background applications would be super useful, and the documentation that surrounds the toast notification system with windows applications all suggest that it needs to be used with an application. The Action Center is super useful for notifications vrs the NotificationTray/NotifyIcon route. I have not found a full solution anywhere else on the web. Here is example code.

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


        }
    }
}
probsJustin
  • 101
  • 1
  • 5
  • Does anybody have an answer to this? What should be the application id passed to CreateToastNotifier call? How to find that application id? I tried many possible variations looking at the universal app manifest but non of them worked. The toast is displayed, all right, but when I click on a button inside the universal app is not started. Actually in my case the universal app is desktop bridge universal app, WPF wrapped into UWP container. – Alex Jul 13 '17 at 17:09
3

1) For a toast notification to appear using a console or Desktop application, your application must have a shortcut on the start menu.

2) For an application to have a shortcut icon(not tile icon) in the start menu of Windows, your app must have an AppId. To create a short cut for you you application create a new class named ShellHelpers.cs and Paste this code in it.

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

Code for creating a shortcut(This code can be added to the same class where you will be showing the 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));
    }

Now you can create an show a 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);

The APP_ID can be any string. In my case it was "NotificationTest.KEY" Note: Dont modify the ShellHelper class. Edit : Follow Evaldas B's answer first then apply this solution.

Ahmad Qadri
  • 31
  • 1
  • 2