方案一:使用Costura.Fody插件将自己写的程序打包成一个可以独立运行的EXE文件
第1步:安装Costura.Fody
首先用Visual Studio 2017打开你的解决方案,依次找到“工具”---“NuGet包管理” - “管理解决方案的NuGet程序包”,到了这一步会打开NuGet-解决方案页面,在浏览选项下面的搜索框内输入“ Costura.Fody ”,会自动搜索出Costura.Fody插件,鼠标左键单击一下Costura.Fody插件,在右边的位置会出现你的项目名称,选中你的项目名称,选择安装,到这一步Costura.Fody就成功按照到你的项目上了第2步:编译一下你的解决方案
直接按照你平常的习惯启动一下你的项目,这个时候,Costura.Fody就会完成打包,打包好的EXE文件在你的解决方案Debug根目录下,你现在可以把这个exe文件复制到任意一台电脑上去试试,完美运行
解决办法:手动添加FodyWeavers.xml文件 这样添加:将鼠标移动到你的解决方案上面,单击右键,依次选择“添加” - “新建项目” - “ XML文件 ”,注意在新建XML文件时将文件命名为“ FodyWeavers. xml “,然后将下面这段代码复制到 FodyWeavers.xml文件里面<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<Costura />
</Weavers>
方案二:WPF制作自己的安装程序(可定制)
安装程序原理:
1、将编译好的文件打包成zip的压缩文件,
2、然后将zip以资源的方式内嵌到安装程序中
3、在安装的时候使用ICSharpCode.SharpZipLib.dll将zip文件解压到相应的目录中
4、建立相应的快捷方式,启动主程序程序
第1步:WPF中将引用的ICSharpCode.SharpZipLib.dll文件打包到exe中
在做一个打包程序中,需要引用到一个ICSharpCode.SharpZipLib.dll的第三方库,编译之后dll需要生成到目录里面exe才能使用,但是只想给用户发送一个纯exe的安装文件,不想有关联的引用,怎么办呢?
在程序入口添加程序集解析事件(wpf的App.xaml.cs文件)
查看代码
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//添加程序集解析事件
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
var executingAssemblyName = executingAssembly.GetName();
var resName = executingAssemblyName.Name + ".resources";
AssemblyName assemblyName = new AssemblyName(args.Name); string path = "";
if (resName == assemblyName.Name)
{
path = executingAssemblyName.Name + ".g.resources"; ;
}
else
{
path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo != null && assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
}
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
更改.csproj的项目文件
在Import节点后面添加如下代码
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
原理:就是将dll文件以资源的方式嵌入包含到项目中,编译后目录里面仍然会编译出dll,我们将dll删除,发现程序仍然能运行,这是因为我们在入口注册了程序集解析事件
当程序集解析引用异常或有相关错误时,会进入事件,在事件中我们将嵌入的dll文件以流的方式映射加载,就相当于重新加载了删除的dll文件
第2步:解压核心代码
查看代码
/// <summary>
/// ZIP助手类
/// </summary>
public static class ZIPHelper
{
public static Action<double, double, string> ActionProgress;
/// <summary>
/// 解压缩zip文件
/// </summary>
/// <param name="zipFile">解压的zip文件流</param>
/// <param name="extractPath">解压到的文件夹路径</param>
/// <param name="bufferSize">读取文件的缓冲区大小</param>
public static void Extract(byte[] zipFile, string extractPath, int bufferSize)
{
extractPath = extractPath.TrimEnd('/') + "//";
byte[] data = new byte[bufferSize];
int size;//缓冲区的大小(字节)
double max = 0;//带待压文件的大小(字节)
double osize = 0;//每次解压读取数据的大小(字节)
using (ZipInputStream s = new ZipInputStream(new System.IO.MemoryStream(zipFile)))
{
ZipEntry entry;
while ((entry = s.GetNextEntry()) != null)
{
max += entry.Size;//获得待解压文件的大小
}
}
using (ZipInputStream s = new ZipInputStream(new System.IO.MemoryStream(zipFile)))
{
ZipEntry entry;
while ((entry = s.GetNextEntry()) != null)
{
string directoryName = Path.GetDirectoryName(entry.Name);
string fileName = Path.GetFileName(entry.Name);
//先创建目录
if (directoryName.Length > 0)
{
Directory.CreateDirectory(extractPath + directoryName);
}
if (fileName != String.Empty)
{
using (FileStream streamWriter = File.Create(extractPath + entry.Name.Replace("/", "//")))
{
while (true)
{
size = s.Read(data, 0, data.Length);
if (size > 0)
{
osize += size;
System.Windows.Forms.Application.DoEvents();
streamWriter.Write(data, 0, size);
string text = Math.Round((osize / max * 100), 0).ToString() + "%";
ActionProgress?.Invoke(max + 5, osize, text);
System.Windows.Forms.Application.DoEvents();
}
else
{
break;
}
}
}
}
}
}
}
}
创建快捷方式
查看代码
/// <summary>
/// 执行软件安装
/// </summary>
private void Setup()
{
try
{
IsFinished = false;
//获取用户选择路径中的最底层文件夹名称
string fileName = this.txtInstallationPath.Text.Split('\\')[this.txtInstallationPath.Text.Split('\\').Count() - 1];
//当用户选择的安装路径中最底层的文件夹名称不是“XthkDecryptionTool”时,自动在创建一个“XthkDecryptionTool”文件夹,防止在删除的时候误删别的文件
if (!fileName.Equals(InstallEntity.InstallFolderName))
{
this.txtInstallationPath.Text = this.txtInstallationPath.Text + @"\" + InstallEntity.InstallFolderName;
}
//安装路径
InstallPath = this.txtInstallationPath.Text;
//显示安装进度界面
//this.tcMain.SelectedIndex = 1;
this.grid_one.Visibility = Visibility.Collapsed;
this.grid_two.Visibility = Visibility.Visible;
this.grid_three.Visibility = Visibility.Collapsed;
//检测是否已经打开
Process[] procCoursewareDecryptionTool = Process.GetProcessesByName(InstallEntity.AppProcessName);
if (procCoursewareDecryptionTool.Any())
{
if (MessageBox.Show("“" + InstallEntity.DisplayName + "”正在运行中,是否强制覆盖程序?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes)
{
Common.IsAppKill(InstallEntity.AppProcessName);
}
else
{
Application.Current.Shutdown();
}
}
//创建用户指定的安装目录文件夹
Directory.CreateDirectory(InstallPath);
ZIPHelper.ActionProgress -= ActionProgressResult;
ZIPHelper.ActionProgress += ActionProgressResult;
this.pbSchedule.Value = 0;
this.txtSchedule.Text = "0%";
//将软件解压到用户指定目录
ZIPHelper.Extract(Install.SetupFiles.Setup, InstallPath, 1024 * 1204);
//将嵌入的资源释放到用户选择的安装目录下面(卸载程序)
string uninstallPath = this.txtInstallationPath.Text + @"\" + InstallEntity.UninstallName;
FileStream fsUninstall = System.IO.File.Open(uninstallPath, FileMode.Create);
fsUninstall.Write(Install.SetupFiles.Uninstall, 0, Install.SetupFiles.Uninstall.Length);
fsUninstall.Close();
//将嵌入的资源释放到用户选择的安装目录下面(快捷图标)
string InstallIcoPath = this.txtInstallationPath.Text + InstallEntity.IconDirectoryPath;
FileStream fsInstallIcoPath = System.IO.File.Open(InstallIcoPath, FileMode.Create);
var InstallIco = Install.SetupFiles.IcoInstall;
byte[] byInstall = Common.ImageToByteArray(InstallIco);
fsInstallIcoPath.Write(byInstall, 0, byInstall.Length);
fsInstallIcoPath.Close();
//将嵌入的资源释放到用户选择的安装目录下面(快捷卸载图标)
string UninstallIcoPath = this.txtInstallationPath.Text + InstallEntity.UninstallIconDirectoryPath;
FileStream fsUninStallIco = System.IO.File.Open(UninstallIcoPath, FileMode.Create);
var UnInstallIco = Install.SetupFiles.IcoUninstall;
byte[] byUnInstall = Common.ImageToByteArray(UnInstallIco);
fsUninStallIco.Write(byUnInstall, 0, byUnInstall.Length);
fsUninStallIco.Close();
//释放卸载程序完成,更新进度条
this.pbSchedule.Value = this.pbSchedule.Value + 1;
this.txtSchedule.Text = Math.Round((this.pbSchedule.Value / this.pbSchedule.Maximum * 100), 0).ToString() + "%";
//添加开始菜单快捷方式
RegistryKey HKEY_CURRENT_USER = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders");
string programsPath = HKEY_CURRENT_USER.GetValue("Programs").ToString();//获取开始菜单程序文件夹路径
Directory.CreateDirectory(programsPath + InstallEntity.MenuFolder);//在程序文件夹中创建快捷方式的文件夹
//更新进度条
this.pbSchedule.Value = this.pbSchedule.Value + 1;
this.txtSchedule.Text = Math.Round((this.pbSchedule.Value / this.pbSchedule.Maximum * 100), 0).ToString() + "%";
//快捷方式名称";
string IconPath = InstallPath + InstallEntity.IconDirectoryPath;
string UninstallIconPath = InstallPath + InstallEntity.UninstallIconDirectoryPath;
string InstallExePath = InstallPath + @"\" + InstallEntity.AppExeName;
string ExeUnInstallPath = InstallPath + @"\" + InstallEntity.UninstallName;
//开始菜单打开快捷方式
shortName = programsPath + InstallEntity.MenuFolder + InstallEntity.ShortcutName;
Common.CreateShortcut(shortName, InstallExePath, IconPath);//创建快捷方式
//更新进度条
this.pbSchedule.Value = this.pbSchedule.Value + 1;
this.txtSchedule.Text = Math.Round((this.pbSchedule.Value / this.pbSchedule.Maximum * 100), 0).ToString() + "%";
//开始菜单卸载快捷方式
Common.CreateShortcut(programsPath + InstallEntity.MenuFolder + InstallEntity.UninstallShortcutName, ExeUnInstallPath, UninstallIconPath);//创建卸载快捷方式
//更新进度条
this.pbSchedule.Value = this.pbSchedule.Value + 1;
this.txtSchedule.Text = Math.Round((this.pbSchedule.Value / this.pbSchedule.Maximum * 100), 0).ToString() + "%";
//添加桌面快捷方式
string desktopPath = HKEY_CURRENT_USER.GetValue("Desktop").ToString();//获取桌面文件夹路径
shortName = desktopPath + @"\" + InstallEntity.ShortcutName;
Common.CreateShortcut(shortName, InstallExePath, IconPath);//创建快捷方式
//常见控制面板“程序与功能”
//可以往root里面写,root需要管理员权限,如果使用了管理员权限,主程序也会以管理员打开,如需常规打开,需要在打开进程的时候做降权处理
RegistryKey CUKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32);
var currentVersion = CUKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall");
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("DisplayIcon", InstallExePath);//显示的图标的exe
dic.Add("DisplayName", InstallEntity.DisplayName);//名称
dic.Add("Publisher", InstallEntity.Publisher);//发布者
dic.Add("UninstallString", ExeUnInstallPath);//卸载的exe路径
dic.Add("DisplayVersion", InstallEntity.VersionNumber);
RegistryKey CurrentKey = CUKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\" + InstallEntity.DisplayName, true);
if (CurrentKey == null)
{
//说明这个路径不存在,需要创建
CUKey.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\" + InstallEntity.DisplayName);
CurrentKey = CUKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\" + InstallEntity.DisplayName, true);
}
foreach (var item in dic)
{
CurrentKey.SetValue(item.Key, item.Value);
}
CurrentKey.Close();
//更新进度条
this.pbSchedule.Value = this.pbSchedule.Value + 1;
this.txtSchedule.Text = Math.Round((this.pbSchedule.Value / this.pbSchedule.Maximum * 100), 0).ToString() + "%";
//安装完毕,显示结束界面
this.grid_one.Visibility = Visibility.Collapsed;
this.grid_two.Visibility = Visibility.Collapsed;
this.grid_three.Visibility = Visibility.Visible;
IsFinished = true;
}
catch (Exception)
{
//安装完毕,显示结束界面
this.grid_one.Visibility = Visibility.Visible;
this.grid_two.Visibility = Visibility.Collapsed;
this.grid_three.Visibility = Visibility.Collapsed;
throw;
}
}