C#安装项目:如何获取Windows Installer缓存的MSI文件路径并存储
获取Windows Installer缓存中的MSI路径并用于卸载
刚好我之前做过类似的需求,给你几个可行的方案,尤其是推荐用Windows Installer官方API的方式,比直接找缓存目录靠谱多了:
一、通过Windows Installer API获取缓存MSI路径(推荐)
Windows Installer本身提供了API可以直接查询已安装产品的缓存MSI路径,这是最稳定的方式——毕竟缓存里的文件名是系统随机生成的,直接遍历目录很容易出错。
步骤1:在Setup项目中添加自定义操作
你需要在Installer项目里添加一个自定义操作(Custom Action),用来在安装过程中执行获取路径的逻辑:
- 创建一个C#类库项目,继承
System.Configuration.Install.Installer; - 在这个类里重写
Commit方法(推荐选Commit阶段,确保安装完全完成后再获取缓存路径); - 将这个类库项目的输出添加到Setup项目的自定义操作中,指定在Commit阶段执行。
步骤2:调用Windows Installer API获取路径
在自定义操作的代码里,通过DllImport调用MsiGetProductInfo函数,传入你的产品代码,就能拿到缓存的MSI路径:
using System; using System.Configuration.Install; using System.IO; using System.Runtime.InteropServices; [System.ComponentModel.RunInstaller(true)] public class CustomInstaller : Installer { // 导入Windows Installer原生API [DllImport("msi.dll", CharSet = CharSet.Unicode)] static extern uint MsiGetProductInfo(string productCode, string property, StringBuilder valueBuf, ref uint len); public override void Commit(System.Collections.IDictionary savedState) { base.Commit(savedState); // 替换成你的产品代码(可以在Setup项目的属性面板里找到) string productCode = "{YOUR-PRODUCT-CODE-GUID}"; string property = "INSTALLPROPERTY_LOCALPACKAGE"; uint bufferSize = 1024; StringBuilder buffer = new StringBuilder((int)bufferSize); uint result = MsiGetProductInfo(productCode, property, buffer, ref bufferSize); if (result == 0) // API调用成功 { string msiCachePath = buffer.ToString(); // 将路径写入文本文件,比如存到应用程序的安装目录 string installDir = Context.Parameters["TargetDir"]; // 获取安装目录参数 string infoFilePath = Path.Combine(installDir, "uninstall_info.txt"); File.WriteAllText(infoFilePath, msiCachePath); } } }
步骤3:后续卸载时使用路径
在你的窗体程序里,读取这个uninstall_info.txt文件,然后执行卸载命令即可:
using System.Diagnostics; using System.IO; using System.Windows.Forms; string infoFilePath = Path.Combine(Application.StartupPath, "uninstall_info.txt"); if (File.Exists(infoFilePath)) { string msiPath = File.ReadAllText(infoFilePath); // 启动卸载程序,确保以管理员权限运行 Process.Start(new ProcessStartInfo { FileName = "msiexec.exe", Arguments = $"/x \"{msiPath}\"", UseShellExecute = true, Verb = "runas" }); }
二、直接遍历Windows Installer缓存目录(不推荐)
如果你一定要直接找缓存目录,C:\Windows\Installer下的MSI文件是系统重命名后的随机文件名,但可以通过WMI查询关联产品代码和缓存文件:
using System.Management; public string GetMsiCachePath(string productCode) { string query = $"SELECT LocalPackage FROM Win32_Product WHERE IdentifyingNumber = '{productCode}'"; ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); foreach (ManagementObject obj in searcher.Get()) { return obj["LocalPackage"]?.ToString(); } return null; }
不过这个方法依赖WMI,而且Win32_Product查询可能会触发MSI自检,导致性能问题,所以还是优先推荐第一个方案。
额外提示:用产品代码卸载更可靠
其实不需要缓存MSI路径也能卸载,直接用产品代码执行msiexec /x {YOUR-PRODUCT-CODE}即可——这样即使缓存MSI被用户清理了,也能正常卸载。你可以把产品代码存到文本文件里,比存MSI路径更稳定。
内容的提问来源于stack exchange,提问作者TmZn




