• 空闲时间研究一个小功能:winform桌面程序如何实现动态更换桌面图标


      今天休息在家,由于天气热再加上疫情原因,就在家里呆着,空闲时想着,在很早以前(约3年前),产品人员跟我提了一个需求,那就是winform桌面程序的图标能否根据节日动态更换,这种需求在移动APP上还是比较常见,比如:淘宝、天猫、京东、360等,它们在逢节假日时除了APP内容有更新,APP ICON也是都更新了的,但PC端的应用程序(APP)则很少见到说有动态更新图标的,故当时我是直接回绝了的,明确表示做不了,但今天我仔细想了一下,其实也是可以实现的,虽然无法直接更新桌面图标,但我们可以更新替换掉桌面的快捷文件呀!(PC端桌面的图标本质都是一个LINK文件)想到这里我就开始设计,最终还是实现了无感知更新PC端桌面图标的功能。

    先看实现方案的流程图如下:

    其中:DynamicIconApp【原生真实程序】、AppLauncher【引导启动程序】 均是我演示的DEMO程序

     如上方案核心实现思路与步骤是:

    1.桌面快捷方式连接的程序是启动程序(即:前置程序),而非真实要打开的程序,目的是:如果要替换桌面快捷方式必需是另外进程来执行,如果快捷方式打开的是真实程序,而真实程序又来更新替换桌面快捷方式文件,会被该桌面快捷方式文件被占用; 【当然也可以不用单独搞一个启动程序,可以就是真实程序,但真实程序需支持传入参,根据入参的不同的,可以开启多个进程,也可以达到该目的,我之前就实现过类似功能:程序自己更新自己】

    2.桌面快捷方式本质只是一个软连接(LINUX中也有),故如果真实程序需要更新,只需通过独立的更新程序(程序更新实现原理有很多,在此就不展开说明)来更新真实的程序即可,而桌面的桌面快捷方式却不用动,仍然通过:桌面快捷方式-》启动程序-》最新的真实程序,用户无感知的。

    3.更新桌面图标准备工作与步骤:

    3.1.创建AppLauncher【引导启动程序】,在程序内部直接实现:执行启动DynamicIconApp.exe【原生真实程序】,启动时带上额外的参数(告之来自启动程序及自己的进程ID,如:fromlauncher:12345),然后关闭自己即可。(其实就是跳板的作用),示例代码如下:

    /// <summary>
    /// 引导启动程序
    /// author:zuowenjun
    /// date:2021-6-19
    /// </summary>
    namespace AppLauncher
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                ProcessStartInfo proc = new System.Diagnostics.ProcessStartInfo();
                proc.UseShellExecute = true;
                proc.FileName =Path.Combine(Application.StartupPath, "DynamicIconApp.exe");
                proc.Arguments = "fromlauncher:" + Process.GetCurrentProcess().Id;
                proc.CreateNoWindow = false;
                //启动进程
                Process.Start(proc);
                this.Close();
            }
        }
    }

    3.2.创建DynamicIconApp.exe【原生真实程序】,在程序内部实现:在程序启动界面前,通过参数判断是否来自启动引导程序,并判断AppLauncher【引导启动程序】进程是否已结束,若未结束,则先尝试直接KILL,若KILL失败则老实等待进程退出。若进程已结束,则再判断是否需要更新桌面快捷方式(这个看具体的情况,可以在DB表中或远程配置中心或API中增加可获取是否需要更新桌面快捷方式文件的逻辑),若需要更新,则将当前应用程序目录的指定的桌面快捷方式文件(如:DynamicIconApp.Lnk,如果不在,应该从CDN获取最新的桌面快捷方式文件)替换桌面上已有或不存的桌面快捷方式文件,替换OK后,再正常运行显示程序界面即可,这样就能实现桌面APP的ICON按需动态更换的效果。示例代码如下:

      1 /// <summary>
      2 /// 原生真实程序
      3 /// author:zuowenjun
      4 /// date:2021-6-19
      5 /// </summary>
      6 namespace DynamicIconApp
      7 {
      8     static class Program
      9     {
     10         /// <summary>
     11         ///  The main entry point for the application.
     12         /// </summary>
     13         [STAThread]
     14         static void Main(String[] args)
     15         {
     16             if (args != null && args.Length > 0)
     17             {
     18                 bool fromlauncher = args[0].StartsWith("fromlauncher:");
     19                 if (fromlauncher)
     20                 {
     21                     int launcherProcId = int.Parse(args[0].Substring(args[0].IndexOf(":") + 1));
     22                     //等待AppLauncher程序完全退出后,再正式运行
     23                     //MessageBox.Show("will starting..." + launcherProcId);
     24                     Process proc = null;
     25                     try
     26                     {
     27                         proc = Process.GetProcessById(launcherProcId);
     28                         MessageBox.Show(proc.Id + "," + proc.ProcessName + "," + proc.HasExited
     29                             + "," + proc.ExitTime);
     30                     }
     31                     catch (Exception e)
     32                     {
     33                         //MessageBox.Show("Process.GetProcessById error:" + e.ToString());
     34                         if (!e.Message.Contains("has exited"))
     35                         {
     36                             return;
     37                         }
     38                         proc = null;
     39                     }
     40 
     41 
     42                     bool waitExit = false;
     43                     if (null != proc)
     44                     {
     45                         try
     46                         {
     47                             Thread.Sleep(500);
     48                             proc.Kill();
     49                             waitExit = true;
     50                         }
     51                         catch (Exception e)
     52                         {
     53                             MessageBox.Show("kill Process error:" + e.ToString());
     54                             proc.WaitForExit();
     55                             waitExit = true;
     56                         }
     57                     }
     58 
     59                     //MessageBox.Show("start run after  launcher Process exit (waitExit = " + waitExit + ") !");
     60                 }
     61             }
     62             Application.SetHighDpiMode(HighDpiMode.SystemAware);
     63             Application.EnableVisualStyles();
     64             Application.SetCompatibleTextRenderingDefault(false);
     65             Application.Run(new Form1());
     66         }
     67     }
     68 }
     69 
     70 
     71 
     72 
     73 /// <summary>
     74 /// 原生真实程序
     75 /// author:zuowenjun
     76 /// date:2021-6-19
     77 /// </summary>
     78 namespace DynamicIconApp
     79 {
     80     public partial class Form1 : Form
     81     {
     82         public Form1()
     83         {
     84             InitializeComponent();
     85         }
     86 
     87         private void Form1_Load(object sender, EventArgs e)
     88         {
     89             //TODO:这里只是示例,判断是否需要更新桌面快捷方式文件(换图标)取决于远程动态配置
     90             bool needUpdateAppLink = true;
     91             //TODO:这里只是判断应用程序根目录有没有快捷方式文件,而实际的可能还要增加:
     92             //若本地没有,则去CDN下载到本地
     93             if (needUpdateAppLink && File.Exists("DynamicIconApp.lnk"))
     94             {
     95                 MessageBox.Show("will be copy DynamicIconApp.link to desktop dir!");
     96                 String desktopFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "DynamicIconApp.lnk");
     97                 if (File.Exists(desktopFilePath))
     98                 {
     99                     File.SetAttributes(desktopFilePath, FileAttributes.Normal);
    100                     File.Delete(desktopFilePath);
    101                 }
    102                 string linkFilePath = Path.Combine(Application.StartupPath, "DynamicIconApp.lnk");
    103                 File.Copy(linkFilePath, desktopFilePath);
    104             }
    105         }
    106     }
    107 }

    3.3.提前创建快捷方式文件,把图标及链接目标都设置好(当然也可以使用WshShell 组件通过C#来动态创建,这个看需要,我个人觉得没必要),放到CDN或像我示例的放到真实程序根目录即可,注意:DynamicIconApp.lnk 快捷方式的名字虽然叫原生真实程序名,但实际链接执行的是:引导启动程序,目的就是桌面的快捷方式必需是真实程序名,这样对于普通用户来说才是对的。

    联想一下,大家有没有发现,原来QQ也玩的是这一套,不信你看桌面的快捷方式及实际目标,截图为证:

    桌面快捷方式:

    QQ快捷方式的属性(目标链接的是:QQScLauncher.exe,这个就是QQ的引导程序,而本身的程序是QQ.exe)

     

    QQ真实应用:(它们的关系是:QQ快捷方式-》执行QQ引导程序-》QQ程序,与我们的设计是如出一辙呀!)

     QQ这样做,除了我说的那个目的(可以动态改快捷图标),也可以在启动QQ前做各种前置验证,比如:是否需要升级等。

     好了,回到我们的今天的主题上来,上面已讲了实现方案及具体步骤,现在是见证效果的时刻了。

    这是原始安装时的桌面快捷方式:(可以看到目标是指向的引导启动程序)

    然后我在应用程序根目录把快捷方式更新(更换图标),如下图示:【当然如果是真实的生产环境,应该是将快捷方式文件放到CDN,同时通过远程配置中心或API来返回是否需要更新快捷方式文件的逻辑】

    改后效果:

    好了,然后我们仍然模拟用户,是在桌面双击原快捷方式图标,最后运行后,桌面的快捷方式图标也自动更新了。如下图示:(原生真实程序运行起来了,桌面的ICON也同步更新了,当然想改名也是OK的,甚至改快捷链接目标也是可以的)

     文末说一下,这篇文章只是空闲时的小研究而矣,至于技术过不过时还是看需求吧,我最近工作重点是JAVA栈的SPRING微服务体系各种研究与实战,比如:最近我实现了基于自定义的Mybatis拦截器来实现SQL语句自动审计功能(即:自动发现SQL语句是否合规,是否存在性能问题,若审计不通过,则会报错,这样在开发阶段就能提前发现问题,及时止损),同时也研究了关于OAUTH2.0+OIDC相关内容,后面有机会再分享(为何今天不分享,因为家里的这个笔记本电脑太差,运行VS2019都比较卡,运行IEDA估计直接死机),近期工作真的很忙,上班没时间,下班又加班太晚没有精力,生活不易,但学习也不能止。

  • 相关阅读:
    Laravel 框架
    tp5
    jq关于对象类型的判断
    简易的 js 留言板
    学习任务
    实验报告:指针与地址
    C语言数据类型
    嗯,关于 nanxI 的50条~(算是自我介绍吧)
    初学C语言
    dropload.js
  • 原文地址:https://www.cnblogs.com/zuowj/p/14903456.html
Copyright © 2020-2023  润新知