zoukankan      html  css  js  c++  java
  • WPF 自己动手来做安装卸载程序

    前言

    说起安装程序,这也许是大家比较遗忘的部分,那么做C/S是小伙伴们,难道你们的程序真的不需要一个炫酷的安装程序么?

    声明在先

    本文旨在教大家以自己的方式实现安装,至于炫酷部分概不负责.

    剖析

    我们经常安装形形色色的程序,而碰到的安装步骤基本可概括为一下几种

    1,欢迎界面 - 无剖析可言

    2,选择路径页 - 无剖析可言

    3,安装页 - 包括解压、添加注册表

    4,完成页 - 创建快捷方式、开机启动、运行

    根据实际使用不同适当修改.

    经过剖析我们发现,其实一个高大上的安装程序真的没有技术可言,其实就是一个解压操作,写入注册表而已!!!

    那么根据剖析结果我们一步一步去实现安装

    解压

    这里使用第三方的ICSharpCode.SharpZipLib.Zip进行解压,

    具体用法大家可以自行搜索了解,这里贴一个现成的方法

    /// <summary>
            /// 解压缩文件
            /// </summary>
            /// <param name="sourceFile">源目录</param>
            /// <param name="destinationFile">目标目录</param>
            public void DeCompressFile(string sourceFile, string destinationFile, Action call)
            {
                try
                {
                    if (!System.IO.File.Exists(sourceFile))
                    {
                        return;
                    }
    
                    if (Directory.Exists(destinationFile))
                    {
                        Directory.Delete(destinationFile, true);
                    }
    
                    // 读取压缩文件(zip文件),准备解压缩
                    ZipInputStream s = new ZipInputStream(System.IO.File.OpenRead(sourceFile.Trim()));
                    ZipEntry theEntry;
                    string path = destinationFile;
    
                    // 解压出来的文件保存的路径
                    string rootDir = " ";
    
                    // 根目录下的第一个子文件夹的名称
                    while ((theEntry = s.GetNextEntry()) != null)
                    {
                        rootDir = System.IO.Path.GetDirectoryName(theEntry.Name);
    
                        // 得到根目录下的第一级子文件夹的名称
                        if (rootDir.IndexOf("\") >= 0)
                        {
                            rootDir = rootDir.Substring(0, rootDir.IndexOf("\") + 1);
                        }
                        string dir = System.IO.Path.GetDirectoryName(theEntry.Name);
    
                        // 根目录下的第一级子文件夹的下的文件夹的名称
                        string fileName = System.IO.Path.GetFileName(theEntry.Name);
    
                        // 根目录下的文件名称
                        if (dir != " ")
                        // 创建根目录下的子文件夹,不限制级别
                        {
                            if (!Directory.Exists(destinationFile + "\" + dir))
                            {
                                path = destinationFile + "\" + dir;
    
                                // 在指定的路径创建文件夹
                                Directory.CreateDirectory(path);
                            }
                        }
                        else if (dir == " " && fileName != "")
                        // 根目录下的文件
                        {
                            path = destinationFile;
                        }
                        else if (dir != " " && fileName != "")
                        // 根目录下的第一级子文件夹下的文件
                        {
                            // 指定文件保存的路径
                            if (dir.IndexOf("\") > 0)
                            {
                                path = destinationFile + "\" + dir;
                            }
                        }
    
                        // 判断是不是需要保存在根目录下的文件
                        if (dir == rootDir)
                        {
                            path = destinationFile + "\" + rootDir;
                        }
    
                        // 以下为解压缩zip文件的基本步骤
                        // 基本思路就是遍历压缩文件里的所有文件,创建一个相同的文件。
                        if (fileName != String.Empty)
                        {
                            FileStream streamWriter = System.IO.File.Create(path + "\" + fileName);
    
                            int size = 2048;
                            byte[] data = new byte[2048];
                            while (true)
                            {
                                size = s.Read(data, 0, data.Length);
                                if (size > 0)
                                {
                                    streamWriter.Write(data, 0, size);
                                }
                                else
                                {
                                    break;
                                }
                            }
    
                            streamWriter.Close();
                        }
                    }
                    s.Close();
                    call.Invoke();
                }
                catch (Exception ex)
                {
                    System.Windows.MessageBox.Show(ex.Message);
                }
            }

    注册表

    相信有些同学对安装注册表这块不太了解,你可以打开控制面板-程序和功能看到你所安装的程序都出现在这里,那么是怎么出现在这里的呢?

    其实就是在注册表SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall的位置添加了应用的信息,当然我们的程序也不例外,

    可以根据其它程序的注册信息研究发下,如下几个节点

    /// <summary>
            /// 注册应用信息
            /// </summary>
            /// <param name="setupPath">安装路径</param>
            public void AddRegedit(string setupPath)
            {
                try
                {
                    RegistryKey key = Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", true);
                    RegistryKey software = key.CreateSubKey(RegeditKey);
    
                    // 图标
                    software.SetValue("DisplayIcon", setupPath + "\" + AppExe);
    
                    // 显示名
                    software.SetValue("DisplayName", DisplayName);
    
                    // 版本
                    software.SetValue("DisplayVersion", Version);
    
                    // 程序发行公司
                    software.SetValue("Publisher", Publisher);
    
                    // 安装位置
                    software.SetValue("InstallLocation", setupPath);
    
                    // 安装源
                    software.SetValue("InstallSource", setupPath);
    
                    // 帮助电话
                    // software.SetValue("HelpTelephone", "123456789");
    
                    // 卸载路径
                    software.SetValue("UninstallString",setupPath + "/uninstall.exe");
    software.Close();
    key.Close();
    }
    catch (Exception) { }
    }

    可对应写入自己程序信息,这里重点说一下UninstallString,这个值为卸载程序的指向,即在控制面板-程序和功能里选择卸载时调用的程序

    原则上来说,我们有安装那么就需要对应的写一个卸载程序(用来删除注册表,程序目录,快捷等等)这里不做赘述

    创建快捷方式

    /// <summary>
            /// 创建快捷方式
            /// </summary>
            /// <param name="setupPath">安装目录</param>
            /// <param name="isDesktop">是否创建到桌面(默认创建到安装目录)</param>
            public void CreateLnk(string setupPath, bool isDesktop)
            {
                string path = setupPath;
    
                if (isDesktop)
                {
                    path = Environment.GetFolderPath(System.Environment.SpecialFolder.DesktopDirectory);
                }
    
    
                // 注:如果桌面有现准备创建的快捷键方式,当程序执行创建语句时会修改桌面已有快捷键方式,程序不会出现异常
    
                WshShell shell = new WshShell();
    
                // 快捷键方式创建的位置、名称
                IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(path + "\" + DisplayName + ".lnk");
    
                // 目标文件
                shortcut.TargetPath = setupPath + "\" + AppExe;
    
                // 该属性指定应用程序的工作目录,当用户没有指定一个具体的目录时,快捷方式的目标应用程序将使用该属性所指定的目录来装载或保存文件。
                shortcut.WorkingDirectory = setupPath;// System.Environment.CurrentDirectory;
    
                // 目标应用程序的窗口状态分为普通、最大化、最小化【1,3,7】
                // shortcut.WindowStyle = 1; 
    
                // 描述
                shortcut.Description = Description;
    
                // 快捷方式图标
                shortcut.IconLocation = setupPath + "\" + AppIco;
    
                shortcut.Arguments = "";
    
                // 快捷键
                //shortcut.Hotkey = "CTRL+ALT+F11"; 
    
                // 保存
                shortcut.Save();
            }

     这里需要引用COM里的Windows Script Host Object Model

    开机启动

    而开机启动无非也就是在在注册表的指定位置去添加启动

    我这里开机启动的时候调用的是快捷方式,而非exe程序

    /// <summary>
            /// 添加开机启动
            /// </summary>
            /// <param name="setupPath">安装目录</param>
            public void AddRun(string setupPath)
            {
                try
                {
                    RegistryKey runkey = Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
    
                    // 打开注册表子项
                    if (runkey == null)
    
                    // 如果该项不存在的话,则创建该子项
                    {
                        runkey = Registry.LocalMachine.CreateSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Run");
                    }
    
                    // 设置为开机启动
                    runkey.SetValue(RegeditKey, """ + setupPath + "\" + DisplayName + ".lnk"");
                    runkey.Close();
                }
                catch (Exception)
                {
    
                }
            }

    运行

    public void RunApp(string setupPath)
            {
                System.Diagnostics.Process.Start(setupPath + "\" + DisplayName + ".lnk");
            }

    做完安装后,你会想我最终其实只想得到一个安装的exe程序,而非安装程序的整个文件列表,对于这里我也没找到太好的解决方案,只能通过WinRar等程序把这个包压缩成一个可执行程序.

    完结

    至此我们炫酷的安装程序就结束了,怎么又提炫酷,好的好的,这篇文章根本就没提界面的事情~!

    需要注意一下,因涉及到注册表操作,所以我们的安装程序是需要以管理员身份运行的(设置程序以管理员身份运行的方法自行搜索).

    如有需要demo的同学可以留下邮箱,一星期左右发出(现在没有现成的demo).如果你觉得这篇文章写的可以不妨推荐一下.

    2021年9月28日 11:32:29

    这个账号绑定的邮箱不常用,这么多年过去没想到还有人来要这个demo

    其实这个方案当时存在一些问题,现在回头想想,这些点都可以去解决,最终可以呈现出来一个完美的解决方案

    其一:上面有提到过最终会生成多个文件,最后是通过winrar压缩成一个可执行程序,但并不完美

    其二:程序依赖framework,极端情况下(很少win电脑没有fw的情况)无法运行

    对于依赖,可以使用.net5,直接打包在项目中,但我有一个解决这些问题的新方法->python

    使用python写一个shell,主要实现检测fw或其他依赖,如vc++,安装后再去运行此方案的setup.exe

    最终使用pyinstaller生成一个exe

     

    注意生成的是32位还是64位,因为对应注册表的位置是不一样的

    关于这些有时间我会重新写一篇文章来说....上班学习作业多,可能会很久远...

  • 相关阅读:
    ListCtr 每一行都加上选择框
    VC中MFC check box的用法
    第二章 掌握C++(2)C++的特性(上)
    第二章 掌握C++(1)从结构到类
    道路横断面设计
    第一章 Windows程序内部运行机制(5)动手编写一个Windows程序
    第一章 Windows程序内部运行机制(4)WinMain函数(续)
    将div旋转任意角度
    地址栏图标修改
    script 错误
  • 原文地址:https://www.cnblogs.com/mcc7/p/5019341.html
Copyright © 2011-2022 走看看