描述:在使用C#编写winform程序后,进行打包exe并进行安装,在安装完成后进行自动运行当前安装程序以及自动自启功能(注册表中添加改程序即可)
环境:vs2010(这块主要满足Microsoft .NET Framework 4.0),Microsoft Visual Studio Installer Projects
如果使用的是vs2019 需要手动安装打包插件:Microsoft Visual Studio Installer Projects
打开vs2019,选择 工具 --> 扩展和更新 --> 联机,搜索Microsoft Visual Studio Installer Projects,进行安装。安装好以后,重启vs2019,这块就不在截图了。
话不多说,直接开始:
1、在解决方案右键,选择新建项目,选择其它项目类型
2、在应用程序文件夹右键
3、选择打包程序的log图标
4、生成快捷方式
5、把生成好的快捷方式拖到用户的程序菜单中
6、设置用户桌面(vs2010(不需要创建文件夹)和vs2019(创建单独的文件夹存放桌面快捷键)区别)
7、设置操作,.NET Framework版本,这块版本根据实际需求选择即可
如果.NET Framework版本有特殊指定,在安装的时候检查是否支持当前版本,否则就下载指定的版本,可以在这块设置
8、如果在安装中不支持.NET Framework版本,可以让用户下载联网或者离线的版本即可
9、设置打包程序
10、重点的来了,想在安装完成后自动运行该程序,和自动把该程序添加到注册表中随着电脑自启动
这块注意选择视图中的自定义操作,然后在安装,提交,回滚,卸载,中都添加自定义操作,选择应用主程序即可。
注意:CustomActionData 填上参数:/targetdir="[TARGETDIR]\" 后面会使用到,这块你可以在用户界面中添加自定义的文本框或按钮,通过这块来传递参数
11、这块自定义操作,根据实现Installer来重新方法来实现安装,卸载等操作。
代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Win32;
using System.IO;
using System.Windows.Forms;
namespace IDP_Agent_Geo
{
[RunInstaller(true)]
public partial class CustomeInstaller : Installer
{
public CustomeInstaller()
{
InitializeComponent();
}
/// <summary>
/// 安装完成之后的操作,可以保留安装路径到
/// 使用跨调用保留并传入 Install、Commit、Rollback和 Uninstall 方法的 IDictionary。
/// IDictionary savedState
/// </summary>
/// <param name="savedState"></param>
protected override void OnAfterInstall(IDictionary savedState)
{
//获取自定义安装用户界面上的端口值
//string portId = this.Context.Parameters["PortId"];
string path = this.Context.Parameters["targetdir"];
Logger(string.Format("OnAfterInstall添加 targetdir savedState:{0}", path));
//开机启动 1、硬编码,2设置Setup Projects的注册表编辑器
//1、安装完成以后可以把硬编码把该软件写到注册表中,这样可以设置开机启动,
//2、当然还有另外一种开机启动的方式是可以使用Setup Projects的注册表编辑器的来进行注册
savedState.Add("savedState", path);
Assembly asm = Assembly.GetExecutingAssembly();
string asmpath = asm.Location.Remove(asm.Location.LastIndexOf("\\")) + "\\";
Logger("OnAfterInstall asmpath:{asmpath}");
SetAutoStart(true, "IDP-Agent-Geo", asmpath + "IDP-Agent-Geo.exe");
//Process.Start(asmpath + "\\ServiceXStart.exe");//要执行的程序
Process.Start(asmpath + "IDP-Agent-Geo.exe");//要执行的程序
//Process.Start(path + "MyTestWinFrm.exe");//要执行的程序
base.OnAfterInstall(savedState);
}
protected override void OnBeforeUninstall(IDictionary savedState)
{
base.OnBeforeUninstall(savedState);
Trace.Listeners.Clear();
Trace.AutoFlush = true;
Trace.Listeners.Add(new TextWriterTraceListener(@"C:\IDP\OnBeforeUninstall.txt"));
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} OnBeforeUninstall");
//Process[] processes = Process.GetProcessesByName("ServiceXStart");
Process[] processes = Process.GetProcessesByName("IDP-Agent-Geo");
foreach (Process item in processes)
{
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} OnBeforeUninstall 进程:" + item);
item.Kill();
item.WaitForExit();
item.Close();
}
}
/// <summary>
/// 卸载软件的时候删除多余的文件
/// </summary>
/// <param name="savedState"></param>
protected override void OnAfterUninstall(IDictionary savedState)
{
//Install、Commit、Rollback和 Uninstall 方法并不总是在 Installer的同一实例上调用。
//例如,你可以使用 Installer 来安装和提交应用程序,然后释放对该 Installer的引用。
//稍后,卸载应用程序会创建对 Installer的新引用,这意味着 Uninstall 方法在 Installer的其他实例上调用。
//出于此原因,请不要在安装程序中保存计算机的状态。
//相反,请使用跨调用保留并传入 Install、Commit、Rollback和 Uninstall 方法的 IDictionary。
Trace.Listeners.Clear();
Trace.AutoFlush = true;
Trace.Listeners.Add(new TextWriterTraceListener(@"C:\IDP\OnBeforeUninstall.txt"));
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} OnBeforeUninstall");
var savedStateValue = savedState.Contains("savedState") ? savedState["savedState"] : "未获取到安装的目录";
Trace.WriteLine("OnAfterUninstall从OnAfterInstall获取 savedState,值为:{savedStateValue}");
string path = this.Context.Parameters["targetdir"];
Trace.WriteLine("targetdir:{path}");
Trace.WriteLine("开始删除目录:{path}");
if (Directory.Exists(path))
{
RemoveSubDirectory(new DirectoryInfo(path));
Trace.WriteLine(@"删除目录:{path} 成功");
}
Logger("OnAfterUninstall 进入。。。。");
Trace.WriteLine("OnAfterUninstall 完成了。。。。");
base.OnAfterUninstall(savedState);
savedState.Add("xizai", true);
}
protected override void OnCommitted(IDictionary savedState)
{
base.OnCommitted(savedState);
Trace.Listeners.Clear();
Trace.AutoFlush = true;
Trace.Listeners.Add(new TextWriterTraceListener(@"C:\IDP\OnCommitted.txt"));
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} OnCommitted");
if (savedState==null)
{
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} savedState={savedState}");
}
else
{
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} 111 1111111savedState={savedState}");
}
var isxizai = savedState.Contains("xizai") ? savedState["xizai"] : "";
var savedStateValue = savedState.Contains("savedState") ? savedState["savedState"] : "未获取到安装的目录";
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} isxizai:{isxizai}");
Trace.WriteLine("{DateTime.Now:yyyy-MM-dd HH:mm:ss} savedStateValue:{savedStateValue}");
}
/// <summary>
/// 卸载完成后删除多余的文件
/// </summary>
/// <param name="uper"></param>
private static void RemoveSubDirectory(DirectoryInfo directory)
{
Logger(string.Format("目录信息 directory:{0}", directory));
//foreach (FileInfo subFile in uper.GetFiles())
//{
// subFile.Delete();
//}
foreach (DirectoryInfo sub in directory.GetDirectories())
{
if (sub.GetFiles().Length > 0 || sub.GetDirectories().Length > 0)
RemoveSubDirectory(sub);
sub.Delete(true);
Logger(string.Format("要删除的目录信息 sub:{0}", sub));
}
Logger("目录成功");
}
/// <summary>
/// 将应用程序设为或不设为开机启动
/// </summary>
/// <param name="onOff">自启开关</param>
/// <param name="appName">应用程序名</param>
/// <param name="appPath">应用程序完全路径</param>
public static bool SetAutoStart(bool onOff, string appName, string appPath)
{
Logger(string.Format("注册表设置的开机启动项:{0},{1},{2}", onOff, appName, appPath));
#region MyRegion
bool isOk = false;
//如果从没有设为开机启动设置到要设为开机启动
if (!IsExistKey(appName)&&onOff)
{
Logger("------设置注册表自动启动----不存在开机启动项,即将添加开机启动项------");
isOk = SelfRunning(onOff, appName, @appPath);
}
//如果从设为开机启动设置到不要设为开机启动
else if (IsExistKey(appName)&&!onOff)
{
Logger("------设置注册表自动启动----存在开机启动项,但未开启,即将开启启动项------");
isOk = SelfRunning(onOff, appName, @appPath);
}
return isOk;
#endregion
}
/// <summary>
/// 判断注册键值对是否存在,即是否处于开机启动状态
/// </summary>
/// <param name="keyName">键值名</param>
/// <returns></returns>
private static bool IsExistKey(string keyName)
{
try
{
bool _exist = false;
RegistryKey local = Registry.LocalMachine;
RegistryKey runs = local.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
if (runs == null)
{
RegistryKey key2 = local.CreateSubKey("Software");
RegistryKey key3 = key2.CreateSubKey("Microsoft");
RegistryKey key4 = key3.CreateSubKey("Windows");
RegistryKey key5 = key4.CreateSubKey("CurrentVersion");
RegistryKey key6 = key5.CreateSubKey("Run");
runs = key6;
}
string[] runsName = runs.GetValueNames();
foreach (string strName in runsName)
{
if (strName.ToUpper() == keyName.ToUpper())
{
_exist = true;
return _exist;
}
}
return _exist;
}
catch
{
return false;
}
}
/// <summary>
/// 写入或删除注册表键值对,即设为开机启动或开机不启动
/// </summary>
/// <param name="isStart">是否开机启动</param>
/// <param name="exeName">应用程序名</param>
/// <param name="path">应用程序路径带程序名</param>
/// <returns></returns>
private static bool SelfRunning(bool isStart, string exeName, string path)
{
try
{
RegistryKey local = Registry.LocalMachine;
RegistryKey key = local.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
if (key == null)
{
local.CreateSubKey("Software//Microsoft//Windows//CurrentVersion//Run");
}
//若开机自启动则添加键值对
if (isStart)
{
key.SetValue(exeName, path);
key.Close();
Logger("------设置注册表自动启动----开启----成功------");
}
else//否则删除键值对
{
string[] keyNames = key.GetValueNames();
foreach (string keyName in keyNames)
{
if (keyName.ToUpper() == exeName.ToUpper())
{
key.DeleteValue(exeName);
key.Close();
Logger("------设置注册表自动启动----关闭----成功------");
}
}
}
}
catch (Exception ex)
{
Logger(string.Format("------设置注册表自动启动----异常----原因{0}------", ex));
return false;
}
return true;
}
/// <summary>
/// 记录日志
/// </summary>
/// <param name="content"></param>
public static void Logger(string content)
{
StreamWriter writer = null;
try
{
// 检查文件夹
string folderPath = @"C:\IDP-Logs\";
if (false == Directory.Exists(folderPath))
{
//创建文件夹
Directory.CreateDirectory(folderPath);
}
if (Directory.Exists(folderPath))
{
//存在/成功创建 文件夹
folderPath = folderPath + @"\";
}
else
{
//无则当前路径创建文件
folderPath = folderPath + @"-";
}
//写入日志
string filePath = string.Format(folderPath + @"logs{0}.txt", DateTime.Now.ToString("yyyy-MM-dd"));
FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write);
writer = new StreamWriter(fs);
writer.WriteLine(string.Format("日志开始时间:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
//foreach (var item in content)
//{
writer.WriteLine(content);
//}
writer.WriteLine("****************************************************************");
}
finally
{
if (writer != null)
{
writer.Close();
}
}
}
}
}
这块在操作注册表的时候,Registry.CurrentUser和Registry.LocalMachine的区别,在使用LocalMachine级别需要管理员才可以写入,不然会报异常。
参考网站:
https://www.cnblogs.com/java315/archive/2011/09/01/2397312.html
https://www.cnblogs.com/1175429393wljblog/p/13229438.html
https://blog.csdn.net/weixin_44790046/article/details/103016154
https://www.cnblogs.com/mingmingruyuedlut/archive/2011/01/21/1941225.html
https://www.cnblogs.com/langu/archive/2012/02/26/2368877.html