在 C# 中执行 msi 安装
有时候我们需要在程序中执行另一个程序的安装,这就需要我们去自定义 msi 安装包的执行过程。
需求
比如我要做一个安装管理程序,可以根据用户的选择安装不同的子产品。当用户选择了三个产品时,如果分别显示这三个产品的安装交互 UI 显然是不恰当的。我们期望用一个统一的自定义 UI 去取代每个产品各自的 UI。
实现思路
平时使用 msiexec.exe 习惯了,所以最直接的想法就是在一个子进程中执行:
msiexec.exe /qn
这样固然是能够完成任务,但是不是太简陋了? 安装开始后我们想取消这次安装怎么办? 或者我们还想要拿到一些安装进度的信息。
其实可以通过调用三个 windows API 轻松搞定这个事儿!下面的 C# demo 用一个自定义 Form 来指示多个 MSI 文件的安装过程。Form 上放的是一个滚动条,并且配合一个不断更新的 label。先看看 demo 长什么样子。
下面是安装过程中的 UI:
aaarticlea/png;base64,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" alt="" />
点击 Cancel 按钮取消安装后的 UI:
aaarticlea/png;base64,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9bJYLJWVlZu77dy5c968ef/1X/+1ffv2LVu2sGdGRkbev3/fGonIyEg7O7snH6IVFhZOnjz5vffeO378+MyZM//2t7+tW7duxYoVS5cudXNz8/LyioqKYhgmPT3d09Pz9OnTZ86cOX78OMMwS5cuXbx4McMwJ06cOH36dHx8vLe3t5ubm/mYwZsAABE1SURBVLu7+6JFi/793//9d7/73aJFi9zd3d3c3JYtW5aVlXX58uUVK1a89dZbR44cOXnypLu7+8KFC3/zm9989NFHCxcudHd3T09Pb29v7/d/FQAA6N2gHTlMTEwcN27cY2XKycmxxmPChAm/+tWv2EhYRUREsI+T3nvvvdDQ0DFjxnA4nLq6urFjx7733nu7du0aPXq0p6cne23p6elxcXFCodDDw+P06dPr1q1bvXr12bNnPTw8hELh0qVLP/zww927d2/atCkiIuLQoUO7du3atGnT5s2bd+/e/dvf/vaLL75gGEYikfzjH/84cuRIcnIy2933339/7ty5O3fu3Lx5s1KpbGlp6fd/FQAA6N2AvmqDIAiz2cw+xXXx4kU7OzvroUKSJFtaWoqKirZu3RoREeHk5PTHP/7x+++/37x5865du7799tstW7Zs3br1woULDMPs27fv008/PX78uJub24IFCxiG2blzp5eXF8MwX3/9tZ+fX0xMzMGDB9nDhgzDFBQUFBUVbdy4ce3atSUlJfn5+QzDXL9+XSwWs527cOFCcnJyz44mJSWxhwfPnz//7rvv7tu3z/ohV1fXM2fOsKfNZjOOHAIADLwBrVdra6tUKv3qq68Yhrl48WLPx16dnZ0Gg4FtG8Mwly5dGjVqFHtaJpNZn9liDy0ePHjw3XffDQwMtLOz8/b2Zhjmq6++8vDwYBjmyy+/nDdvnkwmmzdv3oEDB9ra2q5cueLn57d06dK33nrr97///dKlS/38/C5evNizVQsWLFi9evWTRyl37tzJ4XDGjBnz3XffMQyze/fuefPm/cd//MeHH37o6emZmpqKw4YAAINiQOvFPuT63//9X4Zh4uPj//u//9vd3d3FxcXFxWXjxo2tra0URbW1tWVnZ69YseL69etsqw4fPjx+/Hg2XQRBsI+9Pv744z179syaNWvRokVsvZYvX37jxg0/P7/09HSGYbKzsy9dusQwDIfDGTFiRGRk5KlTp06d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" alt="" />
主要接口介绍
我们先来了解一下主要用到的几个 win32 API。
首先是 MsiSetInternalUI 方法:
[DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern int MsiSetInternalUI(int dwUILevel, IntPtr phWnd);
在调用 msiexec.exe 时,我们通过指定 /q 参数让安装过程显示不同的 UI。如果不显示UI的话就要使用参数 /qn 。MsiSetInternalUI 方法就是干这个事儿的。通过下面的调用就可以去掉 msi 中自带的 UI:
NativeMethods.MsiSetInternalUI(, IntPtr.Zero)
接下来是 MsiSetExternalUI 方法:
[DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern MsiInstallUIHandler MsiSetExternalUI([MarshalAs(UnmanagedType.FunctionPtr)] MsiInstallUIHandler puiHandler, NativeMethods.InstallLogMode dwMessageFilter, IntPtr pvContext);
MsiSetExternalUI 函数允许指定一个用户定义的外部 UI handler 用来处理安装过程中产生的消息。这个外部的 UI handler 会在内部的 UI handler 被调用前调用。 如果在外部的 UI handler 中返回非 0 的值,就说明这个消息已经被处理。
这个外部的 UI handler 就是 MsiSetExternalUI 方法的第一个参数,我们通过实现这个 handler 来处理自己感兴趣的消息, 比如当安装进度变化后去更新进度条。或者通过它传递我们的消息给 msi,比如说告诉 msi,停止安装,执行 cancel 操作。使用这个方法需要注意的是,当你完成安装后一定要把原来的 handler 设回去。否则以后执行 msi 安装包可能会出问题。
MSDN 上有一个 MsiInstallUIHandler 的 demo,感兴趣的同学可以去看看。
下面是 MsiInstallProduct 方法:
[DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern uint MsiInstallProduct([MarshalAs(UnmanagedType.LPWStr)] string szPackagePath, [MarshalAs(UnmanagedType.LPWStr)] string szCommandLine);
正如其名,这个是真正干活儿的方法。
实在忍不住要介绍第四个方法,虽然它对实现当前的功能来说是可选的,但对一个产品来说,它却是用来救命的。
[DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern uint MsiEnableLog(GcMsiUtil.NativeMethods.InstallLogMode dwLogMode, [MarshalAs(UnmanagedType.LPWStr)] string szLogFile, uint dwLogAttributes);
这个方法会把安装 log 保存到你传递给它的文件路径。有了它生活就会 happy 很多,很多… 否则当用户告诉你安装失败时,你一定会抓狂的。
主要代码
好了,下面是 MyInstaller demo 的主要代码:
InstallProcessForm.cs
public partial class InstallProcessForm : Form
{
private MyInstaller _installer = null;
private BackgroundWorker _installerBGWorker = new BackgroundWorker();
internal InstallProcessForm()
{
InitializeComponent(); _installer = new MyInstaller(); _installerBGWorker.WorkerReportsProgress = true;
_installerBGWorker.WorkerSupportsCancellation = true; _installerBGWorker.DoWork += _installerBGWorker_DoWork;
_installerBGWorker.RunWorkerCompleted += _installerBGWorker_RunWorkerCompleted;
_installerBGWorker.ProgressChanged += _installerBGWorker_ProgressChanged; this.Shown += InstallProcessForm_Shown;
} private void InstallProcessForm_Shown(object sender, EventArgs e)
{
// 当窗口打开后就开始后台的安装
_installerBGWorker.RunWorkerAsync();
} private void _installerBGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// 消息通过 e.UserState 传回,并通过label显示在窗口上
string message = e.UserState.ToString();
this.label1.Text = message;
if (message == "正在取消安装 ...")
{
this.CancelButton.Enabled = false;
}
} private void _installerBGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// 安装过程结束
} private void _installerBGWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgWorker = sender as BackgroundWorker; // 开始执行安装方法
_installer = new MyInstaller();
string msiFilePath = "xxx.msi"; // msi file path
_installer.Install(bgWorker, msiFilePath);
} private void CancelButton_Click(object sender, EventArgs e)
{
_installer.Canceled = true;
_installerBGWorker.CancelAsync();
}
}
MyInstaller.cs
internal class MyInstaller
{
private BackgroundWorker _bgWorker = null; public bool Canceled { get; set; } public void Install(BackgroundWorker bgWorker, string msiFileName)
{
_bgWorker = bgWorker; NativeMethods.MyMsiInstallUIHandler oldHandler = null;
try
{
string logPath = "test.log";
NativeMethods.MsiEnableLog(NativeMethods.LogMode.Verbose, logPath, 0u);
NativeMethods.MsiSetInternalUI(, IntPtr.Zero); oldHandler = NativeMethods.MsiSetExternalUI(new NativeMethods.MyMsiInstallUIHandler(MsiProgressHandler),
NativeMethods.LogMode.ExternalUI,
IntPtr.Zero);
string param = "ACTION=INSTALL";
_bgWorker.ReportProgress(, "正在安装 xxx ...");
NativeMethods.MsiInstallProduct(msiFileName, param);
}
catch(Exception e)
{
// todo
}
finally
{
// 一定要把默认的handler设回去。
if(oldHandler != null)
{
NativeMethods.MsiSetExternalUI(oldHandler, NativeMethods.LogMode.None, IntPtr.Zero);
}
}
} //最重要的就是这个方法了,这里仅演示了如何cancel一个安装,更多详情请参考MSDN文档
private int MsiProgressHandler(IntPtr context, int messageType, string message)
{
if (this.Canceled)
{
if (_bgWorker != null)
{
_bgWorker.ReportProgress(, "正在取消安装 ...");
}
// 这个返回值会告诉msi, cancel当前的安装
return ;
}
return ;
}
} internal static class NativeMethods
{
[DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern int MsiSetInternalUI(int dwUILevel, IntPtr phWnd); [DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern MyMsiInstallUIHandler MsiSetExternalUI([MarshalAs(UnmanagedType.FunctionPtr)] MyMsiInstallUIHandler puiHandler, NativeMethods.LogMode dwMessageFilter, IntPtr pvContext); [DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern uint MsiInstallProduct([MarshalAs(UnmanagedType.LPWStr)] string szPackagePath, [MarshalAs(UnmanagedType.LPWStr)] string szCommandLine); [DllImport("msi.dll", CharSet = CharSet.Auto)]
internal static extern uint MsiEnableLog(NativeMethods.LogMode dwLogMode, [MarshalAs(UnmanagedType.LPWStr)] string szLogFile, uint dwLogAttributes); internal delegate int MyMsiInstallUIHandler(IntPtr context, int messageType, [MarshalAs(UnmanagedType.LPWStr)] string message); [Flags]
internal enum LogMode : uint
{
None = 0u,
Verbose = 4096u,
ExternalUI = 20239u
}
}
简单说明一下,用户定义的 UI 运行在主线程中,使用 BackgroundWorker 执行安装任务。在安装进行的过程中可以把 cancel 信息传递给 MsiProgressHandler,当MsiProgressHandler 检测到 cancel 信息后通过返回值告诉 msi 的执行引擎,执行 cancel 操作(msi的安装过程是相当严谨的,可不能简单的杀掉安装进程了事!)。
这样,一个支持 cancel 的自定义 UI 的安装控制程序就 OK了(demo哈)。如果要安装多个 msi 只需在 Install 方法中循环就可以了。
总结
通过调用几个 windows API,我们可以实现对 msi 安装过程的控制。这比调用 msiexec.exe 更灵活,也为程序日后添加新的功能打下了基础。
在 C# 中执行 msi 安装的更多相关文章
- [转]MSI安装程序中的文件替换
原文链接:http://teach.hanzify.org/article/652-1233562028.html 前言 最近有汉化朋友问起如何不重新制作MSI文件,而直接用汉化好的文件替换MSI安装 ...
- 在Win10中,在安装msi安装包的时候常常会出现代码为2502、2503的错误。
前言:在Win10中,在安装msi安装包的时候常常会出现代码为2502.2503的错误.其实这种错误是由于安装权限不足造成的,可以这种msi的安装包不像其他exe的安装程序,在安装包上点击“右键”之后 ...
- pyv8的安装和使用:python中执行js代码
pyv8 的作用是在python中执行js代码,然后可以使用js里的变量等内容.python取得javascript里面的值.javascript取得python里面的值.python和javascr ...
- Vbox中Ubuntu的安装和共享文件夹设置
1. 选择版本 1.1 Ubuntu桌面版与服务器版的区别 桌面版与服务器版,只要发布版本号一致,这两者从核心来说也就是相同的,唯一的差别在于它们的预期用途.桌面版面向个人电脑使用者,可以进行文字处理 ...
- Windows 10 安装ElasticSearch(2)- MSI安装ElasticSearch和安装Kibana
翻阅上篇文章:Windows 10 安装 ElasticSearch 上次写的是下载Zip包安装的,在下载页面 发现有 MSI (BETA) 的下载可选项.了解之后发现MSI安装也值得尝试. MSI安 ...
- mac中使用brew安装软件,下载太慢怎么办?
mac中使用brew安装软件,下载太慢怎么办? 本文所说的软件是指较大的软件,如果软件较小,例如软件只有几M,那么使用此方法后,提升会非常小. 了解brew原理: 1: 从网络下载安装包 2: 执行一 ...
- [linux]执行pip安装的程序:command not found
执行pip安装的程序:command not found 问题描述: 我有一台阿里云服务器,上面装的是centos系统,我用pip安装好vituralenv,都没办法直接启动.同样 我今天在部署我的t ...
- Ubuntu 中软件的安装、卸载以及查看的方法总结
Ubuntu 中软件的安装.卸载以及查看的方法总结 博客分类: Linux UbuntuDebian配置管理CacheF# 说明:由于图形化界面方法(如Add/Remove... 和Synaptic ...
- 黄聪:WebBrowser执行和安装jQuery脚本(IEBrowse)
上一文章说明了如何让 js 脚本访问 .NET 中的类, 这次希望给大家说明一下在任意页面中安装 jQuery 脚本, 并操作页面上的元素. 其实在第一篇关于 IEBrowser 的文章当中, 已经有 ...
随机推荐
- Third Day(上班第四天):Android开发环境配置问题相关
换公司新电脑了,重新安装Android开发环境,并配置,具体流程如下:1.百度JDK,访问Oracle官网:http://www.oracle.com/technetwork/java/javase/ ...
- CYQ.Data V5 MDataTable 专属篇介绍
前言 以前一两个月才出一篇,这三天有点变态地连续1天1篇(其实都是上周末两天写好的存货). 短期应该没有新的和此框架相关的文章要写了,这应该是最后一篇,大伙且看且珍惜. 前两篇讲数据库读写分离和分布式 ...
- 前端神器avalonJS入门(三)
本章将介绍如何使用avalon来实现前端路由功能. 我们需要用到两个avalon路由配套模块—— mmHistory.js 和 mmRouter.js .其中mmHistory是用于历史管理,它会劫持 ...
- 【腾讯Bugly干货分享】Android ImageView 正确使用姿势
本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5832602d7196970d65901d76 导语 本文主要介绍了ImageV ...
- Nova PhoneGap框架 第二章 理解index.html
跟绝大多数PhoneGap程序一样,Index.html是程序的入口.这个页面应该完成应用程序的初始化工作. 首先,让我们来看看这个页面通常都长什么样子: 下面我将一一解释这个页面都做了哪些初始化工作 ...
- java中文乱码解决之道(四)-----java编码转换过程
前面三篇博客侧重介绍字符.编码问题,通过这三篇博客各位博友对各种字符编码有了一个初步的了解,要了解java的中文问题这是必须要了解的.但是了解这些仅仅只是一个开始,以下博客将侧重介绍java乱码是如何 ...
- SQL Server 数据库备份还原和数据恢复
认识数据库备份和事务日志备份 数据库备份与日志备份是数据库维护的日常工作,备份的目的是在于当数据库出现故障或者遭到破坏时可以根据备份的数据库及事务日志文件还原到最近的时间点将损失降到最低点. 数据 ...
- 用python实现的百度音乐下载器-python-pyqt-改进版
之前写过一个用python实现的百度新歌榜.热歌榜下载器的博文,实现了百度新歌.热门歌曲的爬取与下载.但那个采用的是单线程,网络状况一般的情况下,扫描前100首歌的时间大概得到40来秒.而且用Pyqt ...
- Mysql 备份
MySQL数据库备份命令 备份MySQL数据库的命令 mysqldump -hhostname -uusername -ppassword databasename > backupfile ...
- JQuery图片切换动画效果
由于博主我懒,所以页面画的比较粗糙,但是没关系,因为我主要讲的是如何实现图片动画切换. 思路:想必大家都逛过淘宝或者其他的一些网站,一般都会有图片动画切换的效果,那是怎样实现的呢?博主我呢,技术不是很 ...