Skip to content

Commit 582c80c

Browse files
Use Windows.UI.Notifications native toast notifications
1 parent a79447f commit 582c80c

8 files changed

Lines changed: 192 additions & 66 deletions

File tree

WireSockUI/Forms/MainForm.cs

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Microsoft.Toolkit.Uwp.Notifications;
2-
using System;
1+
using System;
32
using System.Collections.Generic;
43
using System.Drawing;
54
using System.IO;
@@ -30,26 +29,6 @@ public enum ConnectionState
3029
*/
3130
private readonly WireSockManager _wiresock;
3231

33-
private void InitializeToastNotifications()
34-
{
35-
// Write the icon to local appdata folder for toast notifications
36-
String toastIcon = $@"{Global.MainFolder}\WireSock.ico";
37-
38-
if (!File.Exists(toastIcon))
39-
{
40-
using (FileStream stream = new FileStream(toastIcon, FileMode.CreateNew))
41-
{
42-
this.Icon.Save(stream);
43-
}
44-
}
45-
46-
ToastNotificationManagerCompat.OnActivated += toastArgs =>
47-
{
48-
ToastArguments args = ToastArguments.Parse(toastArgs.Argument);
49-
50-
};
51-
}
52-
5332
/**
5433
* @brief Initializes a new instance of the Main class.
5534
*/
@@ -62,8 +41,6 @@ public frmMain()
6241
trayIcon.Icon = Resources.ico;
6342
cmiStatus.Image = BitmapExtensions.DrawCircle(16, 15, Brushes.DarkGray);
6443

65-
InitializeToastNotifications();
66-
6744
// Populate menu items with Windows supplied icons
6845
ddmAddTunnel.Image = WindowsIcons.GetWindowsIcon(WindowsIcons.Icons.Addtunnel, 16).ToBitmap();
6946
mniImportTunnel.Image = WindowsIcons.GetWindowsIcon(WindowsIcons.Icons.OpenTunnel, 16).ToBitmap();
@@ -112,18 +89,6 @@ protected override void OnLoad(EventArgs e)
11289
MessageBox.Show(Resources.LastProfileNotFound, Resources.DialogAutoConnect, MessageBoxButtons.OK, MessageBoxIcon.Error);
11390
}
11491

115-
116-
private void LaunchToastNotification(string title, string description)
117-
{
118-
Uri toastUri = new Uri($@"{Global.MainFolder}\WireSock.ico");
119-
120-
new ToastContentBuilder()
121-
.AddAppLogoOverride(toastUri)
122-
.AddText(title)
123-
.AddText(description)
124-
.SetToastScenario(ToastScenario.Default)
125-
.Show();
126-
}
12792
private void menuExit_Click(object sender, EventArgs e)
12893
{
12994
Application.Exit();
@@ -349,7 +314,7 @@ public void UpdateState(ConnectionState state)
349314
gbxState.Visible = true;
350315
tmrStats.Start();
351316

352-
LaunchToastNotification(Resources.ToastActiveTitle, String.Format(Resources.ToastActiveMessage, _wiresock.ProfileName));
317+
Notifications.Notify(Resources.ToastActiveTitle, String.Format(Resources.ToastActiveMessage, _wiresock.ProfileName));
353318
break;
354319
case ConnectionState.Disconnected:
355320
btnActivate.Text = Resources.ButtonInactive;
@@ -380,7 +345,7 @@ public void UpdateState(ConnectionState state)
380345
gbxState.Visible = false;
381346
tmrStats.Stop();
382347

383-
LaunchToastNotification(Resources.ToastInactiveTitle, String.Format(Resources.ToastInactiveMessage, _wiresock.ProfileName));
348+
Notifications.Notify(Resources.ToastInactiveTitle, String.Format(Resources.ToastInactiveMessage, _wiresock.ProfileName));
384349
break;
385350
}
386351
}

WireSockUI/Forms/SettingsForm.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.IO;
44
using System.Reflection;
55
using System.Windows.Forms;
6-
using WireSockUI.Notifications;
6+
using WireSockUI.Native;
77
using WireSockUI.Properties;
88

99
namespace WireSockUI.Forms

WireSockUI/Native/ShellLink.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
using System.Text;
88
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
99

10-
namespace WireSockUI.Notifications
10+
namespace WireSockUI.Native
1111
{
1212
// Modified from http://smdn.jp/programming/tips/createlnk/
1313
// Originally from http://www.vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Runtime.InteropServices;
5+
using System.Windows.Forms;
6+
7+
namespace WireSockUI.Native
8+
{
9+
internal class WindowsApplicationContext : ApplicationContext
10+
{
11+
private WindowsApplicationContext(string name, string appUserModelId)
12+
{
13+
Name = name;
14+
AppUserModelId = appUserModelId;
15+
}
16+
17+
/// <summary>
18+
/// </summary>
19+
public string Name { get; }
20+
21+
public string AppUserModelId { get; }
22+
23+
[DllImport("shell32.dll", SetLastError = true)]
24+
private static extern void SetCurrentProcessExplicitAppUserModelID(
25+
[MarshalAs(UnmanagedType.LPWStr)] string appId);
26+
27+
public static WindowsApplicationContext FromCurrentProcess(
28+
string customName = null,
29+
string appUserModelId = null)
30+
{
31+
ProcessModule mainModule = Process.GetCurrentProcess().MainModule;
32+
33+
if (mainModule?.FileName == null)
34+
{
35+
throw new InvalidOperationException("No valid process module found.");
36+
}
37+
38+
string appName = customName ?? Path.GetFileNameWithoutExtension(mainModule.FileName);
39+
string aumid = appUserModelId ?? appName; //TODO: Add seeded bits to avoid collisions?
40+
41+
SetCurrentProcessExplicitAppUserModelID(aumid);
42+
43+
using (ShellLink shortcut = new ShellLink
44+
{
45+
TargetPath = mainModule.FileName,
46+
Arguments = string.Empty,
47+
AppUserModelID = aumid
48+
})
49+
{
50+
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
51+
string startMenuPath = Path.Combine(appData, @"Microsoft\Windows\Start Menu\Programs");
52+
string shortcutFile = Path.Combine(startMenuPath, $"{appName}.lnk");
53+
54+
shortcut.Save(shortcutFile);
55+
}
56+
57+
return new WindowsApplicationContext(appName, aumid);
58+
}
59+
}
60+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Windows.Forms;
5+
using System.Xml.Linq;
6+
using Windows.UI.Notifications;
7+
using WireSockUI.Properties;
8+
9+
namespace WireSockUI.Native
10+
{
11+
internal static class Notifications
12+
{
13+
private static string icon;
14+
private static Windows.Data.Xml.Dom.XmlDocument GetXml(string title, string body)
15+
{
16+
XElement toast =
17+
new XElement("toast",
18+
new XElement("visual",
19+
new XElement("binding",
20+
new XAttribute("template", "ToastGeneric"),
21+
new XElement("text", title),
22+
new XElement("text", body),
23+
new XElement("image",
24+
new XAttribute("src", icon),
25+
new XAttribute("placement", "appLogoOverride"),
26+
new XAttribute("hint-crop", "circle")))));
27+
28+
Windows.Data.Xml.Dom.XmlDocument xml = new Windows.Data.Xml.Dom.XmlDocument();
29+
xml.LoadXml(toast.ToString());
30+
31+
return xml;
32+
}
33+
34+
private static void ToastNotificationOnDismissed(ToastNotification sender, ToastDismissedEventArgs args)
35+
{
36+
switch (args.Reason)
37+
{
38+
case ToastDismissalReason.ApplicationHidden:
39+
break;
40+
case ToastDismissalReason.TimedOut:
41+
break;
42+
case ToastDismissalReason.UserCanceled:
43+
break;
44+
}
45+
}
46+
47+
static Notifications()
48+
{
49+
// Write the icon to local appdata folder for toast notifications
50+
icon = $@"{Global.MainFolder}\WireSock.ico";
51+
52+
if (!File.Exists(icon))
53+
using (FileStream stream = new FileStream(icon, FileMode.CreateNew))
54+
Resources.ico.Save(stream);
55+
}
56+
57+
public static void Notify(string title, string body)
58+
{
59+
WindowsApplicationContext context = WindowsApplicationContext.FromCurrentProcess();
60+
ToastNotifier notifier = ToastNotificationManager.CreateToastNotifier(context.AppUserModelId);
61+
62+
ToastNotification notification = new ToastNotification(GetXml(title, body));
63+
64+
notification.Activated += Notification_Activated;
65+
notification.Dismissed += Notification_Dismissed;
66+
notification.Failed += Notification_Failed;
67+
68+
notifier.Show(notification);
69+
}
70+
71+
private static void Notification_Failed(ToastNotification sender, ToastFailedEventArgs args)
72+
{
73+
Debug.WriteLine($"Notification failed: {args.ErrorCode}");
74+
}
75+
76+
private static void Notification_Dismissed(ToastNotification sender, ToastDismissedEventArgs args)
77+
{
78+
switch (args.Reason)
79+
{
80+
case ToastDismissalReason.ApplicationHidden:
81+
Debug.WriteLine($"Notification dismissed: Application hidden");
82+
break;
83+
case ToastDismissalReason.UserCanceled:
84+
Debug.WriteLine($"Notification dismissed: User cancelled");
85+
break;
86+
case ToastDismissalReason.TimedOut:
87+
Debug.WriteLine($"Notification dismissed: Timed out");
88+
break;
89+
}
90+
}
91+
92+
private static void Notification_Activated(ToastNotification sender, object args)
93+
{
94+
95+
foreach (Form form in Application.OpenForms)
96+
{
97+
if (form.Name == "frmMain")
98+
{
99+
form.BeginInvoke((Action)(() => {
100+
101+
form.Show();
102+
form.WindowState = FormWindowState.Normal;
103+
}));
104+
}
105+
}
106+
107+
}
108+
}
109+
}

WireSockUI/Program.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Microsoft.Toolkit.Uwp.Notifications;
2-
using Microsoft.Win32;
1+
using Microsoft.Win32;
32
using System;
43
using System.Diagnostics;
54
using System.IO;
@@ -26,7 +25,7 @@ private static void Main()
2625
Application.ApplicationExit += (sender, eventArgs) =>
2726
{
2827
// Ensure we de-register from toast notifications on exit
29-
ToastNotificationManagerCompat.Uninstall();
28+
//ToastNotificationManagerCompat.Uninstall();
3029
};
3130

3231
if (!Directory.Exists(Global.MainFolder)) Directory.CreateDirectory(Global.MainFolder);

WireSockUI/WireSockUI.csproj

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
<ProjectGuid>{C309F359-DAFF-42D0-8379-70ECC9688943}</ProjectGuid>
88
<OutputType>WinExe</OutputType>
99
<RootNamespace>WireSockUI</RootNamespace>
10-
<AssemblyName>WiresSockUI</AssemblyName>
10+
<AssemblyName>WireSockUI</AssemblyName>
1111
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
12+
<TargetPlatformVersion>8.0</TargetPlatformVersion>
1213
<FileAlignment>512</FileAlignment>
1314
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
1415
<Deterministic>true</Deterministic>
@@ -29,6 +30,7 @@
2930
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
3031
<UseApplicationTrust>false</UseApplicationTrust>
3132
<BootstrapperEnabled>true</BootstrapperEnabled>
33+
<TargetFrameworkProfile />
3234
</PropertyGroup>
3335
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
3436
<PlatformTarget>x64</PlatformTarget>
@@ -61,9 +63,15 @@
6163
<ItemGroup>
6264
<Reference Include="System" />
6365
<Reference Include="System.Design" />
66+
<Reference Include="System.Xml" />
6467
<Reference Include="System.Xml.Linq" />
6568
<Reference Include="System.Drawing" />
69+
<Reference Include="System.Runtime" />
70+
<Reference Include="System.Runtime.InteropServices.WindowsRuntime" />
6671
<Reference Include="System.Windows.Forms" />
72+
<Reference Include="Windows.Data" />
73+
<Reference Include="Windows.Foundation" />
74+
<Reference Include="Windows.UI" />
6775
</ItemGroup>
6876
<ItemGroup>
6977
<Compile Include="Config\Curve25519.cs" />
@@ -98,6 +106,8 @@
98106
<DependentUpon>SettingsForm.cs</DependentUpon>
99107
</Compile>
100108
<Compile Include="Config\Profile.cs" />
109+
<Compile Include="Native\WindowsApplicationContext.cs" />
110+
<Compile Include="Notifications\Notifications.cs" />
101111
<Compile Include="Native\ProcessList.cs" />
102112
<Compile Include="Native\CommonControlExtensions.cs" />
103113
<Compile Include="Native\WindowsIcons.cs" />
@@ -150,25 +160,8 @@
150160
<ItemGroup>
151161
<None Include="Properties\App.config" />
152162
</ItemGroup>
153-
<ItemGroup>
154-
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
155-
<Visible>False</Visible>
156-
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
157-
<Install>true</Install>
158-
</BootstrapperPackage>
159-
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
160-
<Visible>False</Visible>
161-
<ProductName>.NET Framework 3.5 SP1</ProductName>
162-
<Install>false</Install>
163-
</BootstrapperPackage>
164-
</ItemGroup>
165163
<ItemGroup>
166164
<Content Include="Resources\wiresock.ico" />
167165
</ItemGroup>
168-
<ItemGroup>
169-
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
170-
<Version>7.1.3</Version>
171-
</PackageReference>
172-
</ItemGroup>
173166
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
174167
</Project>

WireSockUI/app.config

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
<?xml version="1.0" encoding="utf-8" ?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<configuration>
33
<configSections>
4-
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
5-
<section name="WireSockUI.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
4+
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
5+
<section name="WireSockUI.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
66
</sectionGroup>
77
</configSections>
88
<userSettings>
99
<WireSockUI.Properties.Settings>
1010
<setting name="LastProfile" serializeAs="String">
11-
<value />
11+
<value/>
1212
</setting>
1313
<setting name="AutoRun" serializeAs="String">
1414
<value>False</value>
@@ -27,4 +27,4 @@
2727
</setting>
2828
</WireSockUI.Properties.Settings>
2929
</userSettings>
30-
</configuration>
30+
</configuration>

0 commit comments

Comments
 (0)