You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动