我正在寻找使用VBScript从MSI外部安装或不安装(以更快者为准)的MSI中文件的路径。
我找到了一个使用 C# 的类似查询,Christpher 提供了一个解决方案,如下所示:如何在 C# 中解析 MSI 路径?
我现在正在经历同样的痛苦,但是无论如何要在 VBScript 中使用 WindowsInstaller 对象来实现这一点,而不是通过 MSI 的 SQL 表来回进行无休止的查询来实现同样的目的。尽管任何方向都会受到欢迎,因为我已经尝试过测试我所能做的一切,但成功非常有限。
我正在寻找使用VBScript从MSI外部安装或不安装(以更快者为准)的MSI中文件的路径。
我找到了一个使用 C# 的类似查询,Christpher 提供了一个解决方案,如下所示:如何在 C# 中解析 MSI 路径?
我现在正在经历同样的痛苦,但是无论如何要在 VBScript 中使用 WindowsInstaller 对象来实现这一点,而不是通过 MSI 的 SQL 表来回进行无休止的查询来实现同样的目的。尽管任何方向都会受到欢迎,因为我已经尝试过测试我所能做的一切,但成功非常有限。
是的,有一个解决方案,无需安装 msi 并使用 vbscript。Windows Installer SDK 中有一个很好的例子,叫做“WiFilVer.vbs”
使用该示例,我整理了一个快速示例脚本,它完全符合您的需要。
set installer = CreateObject("WindowsInstaller.Installer")
const READONLY = 0
set db = installer.OpenDataBase("<FULL PATH TO YOUR MSI>", READONLY)
set session = installer.OpenPackage(db, READONLY)
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")
set view = db.OpenView("SELECT File, Directory_, FileName, Component_, Component FROM File,Component WHERE Component=Component_ ORDER BY Directory_")
view.Execute
set record = view.Fetch
do until record is nothing
file = record.StringData(1)
directoryName = record.StringData(2)
fileName = record.StringData(3)
if instr(fileName, "|") then fileName = split(fileName, "|")(1)
wsh.echo(session.TargetPath(directoryName) & fileName)
set record = view.Fetch
loop
只需将路径添加到您的 MSI 文件。
告诉我您是否需要更详细的答案。今晚我将有更多时间详细回答这个问题。
编辑承诺的背景(以及为什么我需要调用 ConstFinalize)
naveen 实际上 MSDN 是唯一可以对此给出明确答案的资源,但是您需要知道在哪里以及如何查看,因为 windows 安装程序在恕我直言是一个非常复杂的主题。我真的建议混合使用 msdn安装程序功能参考、数据库参考和 windows 安装程序 SDK 中的示例(抱歉找不到下载链接,我认为它隐藏在 3GB windows SDK 中的某个地方)
首先,您需要 MSI 的一般知识:
MSI 实际上是一个关系数据库。一切都存储在相互关联的表中。(实际上不是一切,但我会尽量保持简单;))
此数据库由 Windows 安装程序解释,这会创建一个“会话”
还有一些部分是动态解析的,具体取决于您安装 msi 的系统,例如类似于环境变量的“特殊”文件夹。例如 msi 有一个“ProgramFilesFolder”,windows 通常有 %ProgramFiles%。
所有动态的东西只存在于安装程序会话中,而不是数据库本身。
在您的情况下,您需要查看 3 个表,处理关系并解决它们。
“文件”表包含所有文件,“组件”表告诉您哪个文件进入哪个目录,“目录”表包含有关文件系统结构的所有信息。
使用 SQL 查询,我可以链接组件和文件表以查找目录名称(或数据库术语中的主键)。
但是目录表本身就有关系,它的结构就像一棵树。看看这个示例目录表(取自 instEd MSI)
列是 Directory、Directory_Parent 和 DefaultDir
InstEdAllUseAppDat InstEdAppData InstEd
INSTALLDIR InstEdPF InstEd
CUBDIR INSTALLDIR hkyb3vcm|Validation
InstEdAppData CommonAppDataFolder instedit.com
CommonAppDataFolder TARGETDIR .
TARGETDIR SourceDir
InstEdPF ProgramFilesFolder instedit.com
ProgramFilesFolder TARGETDIR .
ProgramMenuFolder TARGETDIR .
SendToFolder TARGETDIR .
WindowsFolder_x86_VC.1DEE2A86_2F57_3629_8107_A71DBB4DBED2 TARGETDIR Win
SystemFolder_x86_VC.1DEE2A86_2F57_3629_8107_A71DBB4DBED2 WindowsFolder_x86_VC.1DEE2A86_2F57_3629_8107_A71DBB4DBED2 System
directory_parent 将其链接到一个目录。DefaultDir 包含实际名称。您现在可以自己解析树并替换所有特殊文件夹(在 vbscript 中会非常乏味)...
...或让 Windows 安装程序处理(就像安装 msi 时一样)。现在我必须介绍一个新事物:动作(和序列):在运行(安装、删除、修复)msi 时,会执行定义的动作列表。一些动作只是收集信息,一些改变实际的数据库。
对于 msi 可以执行的各种操作,有一系列操作(称为序列),例如一个用于安装的序列(称为 InstallExecuteSequence),一个用于从用户那里收集信息(MSI 的 UI:InstallUISequence)或一个用于管理点安装(AdminExecuteSequence )。
在我们的例子中,我们不想运行整个序列(这可能会改变系统或只需要很长时间),幸运的是,Windows 安装程序允许我们运行单个操作而不运行整个序列。阅读MSDN 上目录表的参考(备注部分),您可以看到您需要哪些操作:
Directory resolution is performed during the CostFinalize action
因此将所有这些放在一起脚本更易于阅读 * 打开 msi 文件 * '解析'它(提供会话) * 查询组件和文件表 * 运行 CostFinalize 操作以解析目录表(不运行整个 MSI) * 获取使用 targetPath 函数解析的路径
顺便说一句,我通过浏览 MSDN 上的安装程序参考找到了 targetPath 函数,我也注意到不需要 CostInitialize。仅当您想获取文件的源路径时才需要它。
我希望这能让一切更清楚,这很难解释,因为我自己花了半年时间才理解它;)
关于 PhilmEs 的回答:是的,对目录表的解析有更多影响,例如自定义操作。请记住,管理安装可能会导致不同的目录(例如,因为不同的序列可能包含不同的自定义操作)。组件有条件,所以可能根本没有安装文件。我很确定 InstEd 也不会考虑自定义操作。
所以是的,没有 100% 的解决方案。也许所有东西的混合都是必要的。
weberik 给出的脚本(源自 MS SDK VB 代码)非常好,因为它可以很容易地在没有自己的算法的情况下分析目录表(这是在循环或递归算法中进行的中等规模的工作) )。但它并不是为所有文件提供 100% 完美的视图,见下文。该脚本的方法是半动态的(可以通过其他操作进行扩展),但实际上它只提供静态目录结构,类似于默认的管理安装或高级 MSI 查看器。
通常这已经足够了,也是我们想要的。但请注意,这不是 100% 的解决方案(事后了解每个文件的确切路径)。这确实意味着,在某些情况下,这不会始终为您提供正确的路径:
实际上,如果没有真正安装,很难实现 100% 的解决方案。它几乎可以重新编程几乎整个 Windows 安装程序引擎。所以简化通常是足够的并且被接受。
但是您可以扩展该方法以涵盖自定义操作,例如为所需的每个附加操作添加一行“session.DoAction(..)”。或者包括命令行参数。
有时它可能很棘手。MSI 的结构越简单,您就越有可能在不付出更多努力的情况下取得成功。
编写自己的程序的替代方案:问题是,您真正想找出什么,以及是否真的有必要对其进行编程:如果您不想编写一个自动的日常 MSI 分析器,也许以下内容就足够了你:
第一个提示:使用 "msiexec /a mysetup.msi TARGETDIR="c:\mytestpath" 安装 MSI。(与上面 weberik 的脚本类似的限制)如果 MSI 没有使用自定义操作来更改路径,包括忘记添加到管理员序列(“忘记”应该被视为 99% 或现有设置的正常情况 :-),如果您安装“真正”并为 Windows 预定义文件夹提供一些特殊命名,您会得到文件结构,您会很容易找到。
如果管理安装缺少一些文件夹,则修复自定义操作(添加到管理序列)并将此场景用作主要测试用例通常是一个更好的主意。优点是,只有您限制管理员安装使用的动态。顺便说一句,您可以使用与实际安装相同的命令行参数或路径设置自定义操作。
第二个提示:Google for the InstEd tool ,转到文件或组件表,您将在调用 CostInitialize/CostFinalize 后以与提到的 VB 脚本相同的静态方式看到生成的 MSI 路径。对于人类视图,这样的编辑器视图可能更好。
对于自动测试和改进或准确性,您当然需要一个自己的程序。对于那些提到给定片段的人来说,这是一个很好的起点。:-)
你们其他人应该使用两种给定方法中的一种来更轻松地生活,而无需编程。