记录一下讲winform程序打包发布的经历吧,过程中也是遇到不少问题,不过关关难过慢慢过呗,最后能顺利做完也不错。
主要遇到的问题有如下几点:
1.安装包生成过程中报错,虽然教程上步骤很清晰,但自己操作的过程中还是会遇到各种各样的问题,所以接收的知识还是最好实践一下,得到的经验才是自己的。
2.修改目标机器的注册表,将安装程序的路径写入注册表中。
3.目标机器不带C++ runtime。
4.目标机器如果是win7,不支持TLS1.2及以上版本的协议。
--打包工具:通过NuGet安装,下载完毕后要关闭VS。
2.打包过程
--首先建立setup的工程,建好工程后右键->view->文件系统
--Application Folder就是安装程序在目标机器的文件夹下写入的文件内容。
--User's Desktop就是目标机器的桌面文件夹,这里主要会放一个应用程序的快捷方式。
-- Application Folder右键添加文件,把需要的文件放进来(不过我是直接拷贝了在它上右键粘贴的,通过添加文件总是缺几个,还得去找了一个个添加),主要保证项目主输出(一般发布都是release下)的文件是全的,然后加进来,最好提前把测试生成的日志等没必要的文件清理删除。
--Application Folder右键项目输出,把启动项设置为主输出,会在文件系统中多出一个主输出项。
--在上述的主输出项上右键,创建快捷方式(Create short cut),将创建的快捷方式项拉到User's Desktop文件夹,这样就会在目标机器桌面生成快捷方式。重命名成自己定义的名称,icon选择一个心仪的将来应用程序和快捷方式的桌面图标。
--从C盘的windows/system32文件夹下,找到msiexe.exe加入到Application Folder文件夹,并为它创建Short cut(主要用于卸载该程序),为卸载的应用程序命名(此处命名为Uninstaller.exe)
--点击setup工程,在它的属性中找到 ProductCode,拷贝出来,放到Uninstaller属性Arguments下 ,格式为/x {ProductCode},这样卸载程序就配置好了。
--为了执行一些安装过程中的步骤,比如修改目标机器注册表等,创建了一个CustomActions的项目,现在将该项目也在setup项目右键->add->项目输出中添加进来,此时会在Application Folder中多一个主输出项。
--setup项目右键->view->自定义操作打开,在installer(安装)右键->自定义操作将上述的主输出CustomActions加进来。将它的属性项CustomActionData 中输入一些自定义的参数,此处为/TargetDir="[TARGETDIR]\",这样在CustomeActions项目中通过
Context.Parameters["TargetDir"]即可获取用户安装项目的路径。
--setup项目右键->view也可以看到注册表和启动条件,但是不一定能满足项目要求,所以可以自定义一些操作。
--setup项目右键属性->Prerequisites,点击打开,检测一下安装必备组件是否勾选上,此处为Microsoft .net framework 4.7.2(x64) ,因为打包的项目是这个框架。
--最后检查seup项目->属性中的TargetPlatform项,和自己的发布程序还有目标机器一致(很重要,很多安装包生成过程中的错误就是因为这个),此处目标平台是x64机器,发布程序编译也是release x64。
--setup项目,右键重新生成。成功生成后在setup项目的生成路径中找到安装程序的msi和exe。
--自行安装测试。
--CustomActions代码如下,主要是安装时将安装信息写入注册表,卸载时清理注册表。然后测试阶段因为是动态编译,会检测一下目标机器是否有C++ runtime,没有会通知让用户点击确认自动安装一个。静态编译的话,应该就不用了。
public partial class InstallerActions : System.Configuration.Install.Installer
{
public InstallerActions()
{
//InitializeComponent();
this.AfterInstall += new InstallEventHandler(InstallerDemo_AfterInstall);
this.AfterUninstall += new InstallEventHandler(InstallerDemo_AfterUnInstall);
}
public void InstallerDemo_AfterInstall(object sender, InstallEventArgs e)
{
//修改注册表
try
{
// 获取安装路径
string installPath = Context.Parameters["TargetDir"];
string exeName = "ScannerClient.exe";
string fullPath = System.IO.Path.Combine(installPath, exeName);
// 写入注册表ClassRoot
using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(@"GctAnalyseScanClient"))
{
key.SetValue("", fullPath);
key.SetValue("URL Protocol", "");
RegistryKey defaulticon = key.CreateSubKey("DefaultIcon");
defaulticon.SetValue("", $"{fullPath},1");
RegistryKey shell = key.CreateSubKey("shell");
RegistryKey open = shell.CreateSubKey("open");
RegistryKey command = open.CreateSubKey("command");
command.SetValue("", $"\"{fullPath}\" \"%1\"");
}
// 写入注册表bendiji
using (RegistryKey key = Registry.LocalMachine.CreateSubKey(@"SOFTWARE"))
{
RegistryKey Wow6432Node = key.CreateSubKey("Wow6432Node");
RegistryKey Microsoft = Wow6432Node.CreateSubKey("Microsoft");
RegistryKey Windows = Microsoft.CreateSubKey("Windows");
RegistryKey CurrentVersion = Windows.CreateSubKey("CurrentVersion");
RegistryKey InternetSettings = CurrentVersion.CreateSubKey("Internet Settings");
RegistryKey WinHttp = InternetSettings.CreateSubKey("WinHttp");
WinHttp.SetValue("URL DefaultSecureProtocols", 0x00000800, RegistryValueKind.DWord);
}
}
catch (Exception ex)
{
// 捕获并处理异常
Console.WriteLine($"发生错误:{ex.Message}");
}
//
OnInstallComplete();
}
private void InstallerDemo_AfterUnInstall(object sender, InstallEventArgs e)
{
//清理注册表
try
{
using (RegistryKey rootKey = Registry.ClassesRoot)
{
if (rootKey.OpenSubKey("GctAnalyseScanClient") != null)
{
// 删除GctAnalyseScanClient键及其所有子项
rootKey.DeleteSubKeyTree("GctAnalyseScanClient");
Console.WriteLine("GctAnalyseScanClient键及其所有子项已被成功删除。");
}
else
{
Console.WriteLine("GctAnalyseScanClient键不存在。");
}
}
}
catch (Exception ex)
{
// 捕获并处理异常
Console.WriteLine($"发生错误:{ex.Message}");
}
}
private void OnInstallComplete()
{
bool install = IsVCRedist2015_2022Installed();
if (install)
{
string installPath = Context.Parameters["TargetDir"];
string exeName = "C++Runtime\\VC_redist.x64.exe";
string fullPath = System.IO.Path.Combine(installPath, exeName);
MsgBoxResult result = Interaction.MsgBox("Visual C++ 2015-2022运行时未安装,是否现在安装?", MsgBoxStyle.Question | MsgBoxStyle.YesNo, "安装Visual C++运行时");
if (result == MsgBoxResult.Yes)
{
InstallVCRedist(fullPath);
}
}
}
public static bool IsVCRedist2015_2022Installed()
{
string[] registryPaths = new string[]
{
@"SOFTWARE\Microsoft\VisualStudio\14.0\VC",
@"SOFTWARE\Microsoft\VisualStudio\15.0\VC ",
@"SOFTWARE\Microsoft\VisualStudio\16.0\VC",
@"SOFTWARE\Microsoft\VisualStudio\17.0\VC"
};
foreach (string path in registryPaths)
{
using (RegistryKey baseKey = Registry.LocalMachine)
{
using (RegistryKey key = baseKey.OpenSubKey(path))
{
if (key != null)
{
return true;
}
}
}
}
return false;
}
public static void InstallVCRedist(string installerPath)
{
try
{
// 创建一个ProcessStartInfo对象
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = installerPath,
Arguments = "/install /quiet /norestart", // 安装参数,静默安装
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
// 启动安装程序
using (Process process = Process.Start(startInfo))
{
process.WaitForExit(); // 等待安装完成
int exitCode = process.ExitCode;
if (exitCode == 0)
{
Console.WriteLine("Visual C++ 2015-2022运行时安装成功。");
}
else
{
Console.WriteLine($"Visual C++ 2015-2022运行时安装失败,退出代码:{exitCode}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"发生错误:{ex.Message}");
}
}
}
--最后说一下目标机器如果是win7,不支持TLS1.2及以上版本的协议的问题,本来通过代码尝试让目标机器兼容TLS1.0,1.1,1.2的协议,测试发现没什么变化,可能.net通过代码没法实现。最后就是尝试在目标的win7系统上安装微软发布的补丁包,让它能够支持TLS1.2协议,这样就可以通过http请求去访问服务器上的一些资源。