C# NET framework 4.5呼叫系統Toast通知

sssfffsssfff發表於2024-11-07

最近有一個工控程式,基於net4.5.2開發的,嘗試增加win10系統的Toast通知訊息,網路收集到如下結論:

1. Toast功能需要net4.8的高版本,呼叫Microsoft.Toolkit.Uwp.Notifications.dll —— 工控程式不可能升級的

2. 低版本net都是使用winform自己畫的窗體,然後自定義動畫 —— 成品效果不好

後續自己做了一些嘗試:

1. 新建一個高版本net Demo,將Toast功能輸出為dll,低版本去呼叫這個dll —— 環境報錯,不可行

2. 新建一個高版本net Demo,將Toast功能輸出為exe,低版本去呼叫這個exe —— 可行,但是釋出程式時要額外附帶一個exe,令客戶詫異

3. 新建一個高版本net Demo,將Toast功能輸出為exe,在低版本將exe新增為工程資源,然後從工程資源中呼叫exe —— 可行,釋出程式時也只有一個exe

其中方式3又經過多次最佳化

1. 低版本呼叫:工程資源/Toast,硬體本地/Microsoft.Toolkit.Uwp.Notifications.dll —— 釋出程式是要額外附帶一個dll

1. 低版本呼叫:工程資源/Toast,工程資源/Microsoft.Toolkit.Uwp.Notifications.dll —— 釋出程式時只有一個exe,但是開發操作時要呼叫兩個資源

1. 高版本呼叫:工程資源/Microsoft.Toolkit.Uwp.Notifications.dll ,低版本呼叫:工程資源/Toast —— 釋出程式時只有一個exe,開發操作時也只需要新增呼叫一個資源

首先是高版本Demo,建立為net4.8版本,在NuGet程式包中新增Microsoft.Toolkit.Uwp.Notifications功能,微軟官網有詳細介紹

using Microsoft.Toolkit.Uwp.Notifications;
using System;
using System.Threading.Tasks;

namespace TryToast
{
    public class TryToast
    {
        //參考https://developer.aliyun.com/article/1094575
        //.NET中如果使用Microsoft.Toolkit.Uwp.Notifications,必須指定Windows TFM,且要指定window版本,至少為net6.0-windows10.0.17763.0或更高。否則,將報錯找不到Show()方法。
        //右鍵專案,編輯專案檔案,將TargetFramework指定為如下:
        //<TargetFramework>net6.0-windows10.0.17763.0</TargetFramework>
        //通常,指定TFM後,啟動除錯會報錯沒有xxx的目標,確保已執行還原...等錯誤。
        //解決辦法是:清理專案,並重新生成一次即可。
        //有時候還會報錯 net6.0-windows10.0.17763.0與.Net框架版本不一致,
        //需要修改為 net5.0-windows10.0.17763.0。或者改為net5後生成無錯再改回net6;或者 直接清理專案並重新生成一次。
        public bool Show(string t1,string t2)
        {
            bool res = false;
            try
            {
                ToastNotificationManagerCompat.History.Clear();
                // Requires Microsoft.Toolkit.Uwp.Notifications NuGet package version 7.0 or greater
                new ToastContentBuilder()
                    //.AddArgument("action", "viewConversation")
                    //.AddArgument("conversationId", 9813)
                    //.AddHeader("6289", "Camping!!", "action=openConversation&id=6289")
                    .AddText(t1)
                    .AddText(t2)
                .Show(
                    toast => { toast.ExpirationTime = DateTime.Now.AddHours(3); }
                    );
                // Not seeing the Show() method? Make sure you have version 7.0, and if you're using .NET 6 (or later), then your TFM must be net6.0-windows10.0.17763.0 or greater
                res = true;
            }
            catch (Exception ex) 
            {
                Console.WriteLine($"在程式集TryToast中:{ex.Message}");
            }
            return res;
        }
    }
}

然後在高版本Demo的MAIN程式中編寫引數呼叫邏輯,需要將NuGet包自動下載的Microsoft.Toolkit.Uwp.Notifications.dll新增到工程資源中,形成如下工程結構

需要注意的是CurrentDomain_AssemblyResolve方法,網路上部落格都沒有考慮大陣列的記憶體溢位問題,這裡用一個靜態陣列規避。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TryToast
{
    internal static class Program
    {
        /// <summary>
        /// 應用程式的主入口點。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length == 0)
                return;

            //在InitializeComponent()之前呼叫 不需要設定為嵌入資源
            bb = new byte[Properties.Resources.Microsoft_Toolkit_Uwp_Notifications.Length];
            Array.Copy(Properties.Resources.Microsoft_Toolkit_Uwp_Notifications, bb, bb.Length);
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            if (args.Length == 1)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
            }
            else if (args.Length == 3)
            {
                var t1 = new TryToast();
                t1.Show(args[1], args[2]);
            }
        }
        static byte[] bb = null;
        static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            // byte[] bytes = Properties.Resources.Microsoft_Toolkit_Uwp_Notifications;這樣直接呼叫記憶體會溢位
            return System.Reflection.Assembly.Load(bb);
        }
    }
}

高版本編譯生成exe後,接下來在低版本net程式中就可以呼叫了

1.低版本net工程資源中新增剛編譯好的exe

2.新建一個BUTTON點選事件

        private void button1_Click(object sender, EventArgs e)
        {
            string[] the_args = { "1","hello","toast" };
            byte[] buffer = Properties.Resources.TryToast;//資源資料快取陣列
            Assembly asm = Assembly.Load(buffer); //載入資料
            MethodInfo pointInfo = asm.EntryPoint;//獲取程式入口點

            //pointInfo.Invoke(null, new string[][new string[3],new string [3]);//執行
            //invoke在你傳參的時候,如果是單引數當然是沒有問題,
            //如果是多引數就需要注意了,你需要將引數都裝到一個object陣列(object[])裡,
            //然後進棧的時候程式會將陣列內容push進去,而不是陣列object[] 這個變數,
            //而引數在出棧的時候根據委託的參數列一一對應棧中的每個元素安排出棧。
            pointInfo.Invoke(null, new object[] { the_args });//執行 注意資源屬性是否設定為嵌入式

        }

相關文章