方案一:使用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;
}
}