转自https://blog.csdn.net/davidhsing/article/details/9962377

※说明:
目前可以用于MSI编辑的软件很多,但是有些软件在保存时会在MSI文件中写入一些自己的表或内容,有些会另外嵌入一个CAB文件,使得MSI文件增大。而这里提供的方法保证不会写入任何不必要的内容和文件。

※关键点:
1、CAB中的文件顺序要和MSI的文件表中的文件顺序保持一致。
2、有些CAB中的文件使用了Windows不支持的文件名。

软件准备

1、Orca,微软提供的MSI编辑器。汉化版下载地址(版本为 4.5.6001.22192):
http://www.hanzify.org/index.php?Go=Show::List&ID=11122 或
http://www.wanfutrade.com/software/hanhua/Orca45chs.msi

2、MsiDb,微软提供的MSI中CAB文件提取和替换工具。本人的汉化版下载地址(版本为4.5.6001.22192):
http://www.wanfutrade.com/software/hanhua/MsiDb.exe(链接不可用了)

3、IExpress,微软提供的CAB文件打包用户界面工具。本人的汉化版下载地址(版本为6.0.3790.0):
http://www.wanfutrade.com/software/hanhua/iexpress20.exe(链接不可用了)

4、Excel,相信这个大家都有吧。什么版本都可以。

基础知识

1、MSI文件的格式
MSI文件实际上一个安装数据库,里面有很多表,这些表都是由 TAB 格式写成的文本,就像EXCEL一样由列和行组成。可以从MSI中导出每个表。导出的表可以用文本编辑器和EXCEL打开。至于每个表和表中每个字段的作用就不在这里讲述了。请参考微软提供的相关资料。

2、MSI安装文件存在形式
MSI 安装程序中,需要安装的文件的存在形式许多种,常见的有以下几种:
(1)安装文件被打包为CAB文件,并嵌入在MSI文件中
(2)安装文件被打包为CAB文件,但CAB独立于MSI存在
(3)安装文件未被打包为CAB文件,而是以原来的状态存在于各个文件夹中
对于以(1)、(2)形式存在的CAB文件,可以采用本文介绍的方法替换,而对于以(3)形式存在的文件文件直接替换即可。

3、MSI安装文件列表
在MSI文件中有二个表是用来表达CAB中的文件列表和CAB文件的名称和文件数以及其他信息的,这就是 File 表和 Media 表,其中File表保存了CAB中存在的文件名以及每个文件所属的组件、安装后的实际文件名、文件大小、版本、语言、属性和文件顺序等信息,Media表保存了CAB文件的磁盘ID、文件数、磁盘描述、CAB文件名以及卷标等信息。
先来看一下MSI文件中这二个表:
(1)File表
 

上面就是File表,对涉及到本主题的列说明如下:
File列,表示 CAB中的文件名列表,有的是我们常见的文件名格式,有的则在常见文件名后加上许多像类 ID那样的编号,有的干脆就是一个编号。对为什么不用常见的文件名,我想是由于有的MSI制作软件为了某种目的故意这样命名的。特别是InstallShield制作的MSI文件。
Compoent_ 列,表示该文件属于哪个组件。
FileName 列,表示安装后的文件名。其中 | 前面的为短文件名,后面的为长文件名。
FileSize 列,表示文件的大小。一般不需要更改它,即使替换后的文件与其不一致。
Language 列,表示文件所用的语言。一般也不需要更改它,即使替换后的文件语言与其不一致。
Attributes 列,表示文件的属性,比如隐藏、系统、存档等等。一般也不需要更改它,除非您有特别的用途,如想在安装后隐藏某个文件。
Sequenec 列,表示文件的序号,这里的需要不允许重复,并且始终是从1开始的。

(2)Media表

Media表中,DiskID、DiskPrompt、VolumeLable、Source列一般不需要去考虑,这里主要介绍以下其他二个列。
LastSequence 列,表示CAB中的文件数,也就是上述File表中Sequenec 列中的最大数,这二个数一定要一致。
Cabinet 列,表示 CAB文件名。注意:文件名前的"#"不是真正文件名的一部分。

开始实践

1、首先提取嵌入在MSI中的CAB文件。如果是CAB文件独立于MSI,跳过该操作。
  在DOS下或MsiDb.exe快捷方式下,使用"MsiDb.exe –x CAB文件名 -d MSI文件名"命令提取。
其中:(1)CAB文件名就是Media表中的Cabinet 列中的CAB文件名。注意忽略前置的"#"。
(2)CAB 和 MSI 文件名均要带后缀扩展名。

2、直接用Windows 资源管理器或RAR或其他压缩软件提取CAB文件中的文件到一个单独的文件夹。

3、使用CMD命令,转入DOS界面,在释放CAB中的文件的单独文件夹下,使用"dir /b > CabFileList.xls"命令提取该文件夹中的文件名列表。这里的CabFileList.xls文件名您可以用其他文件名代替,自由命名。

4、用MsiDb.exe或Orca打开MSI文件,导出MSI文件中的File表。导出后的文件名为File.idt。

5、用EXCEL打开File.idt文件,并按 Sequenec 列对整个表进行排序备用。如下图。

6、比较从CAB中提取的文件数量是否与MSI文件中的File表中所列的文件数量相等。可以直接在MSI文件和解开的文件夹中判断,也可以用EXCEL分别打开前面生成的CabFileList.xls和File.idt进行判断。

(1)如果相等,一般情况下说明文件名都是相同的。
(2)如果不等,说明CAB中的某些文件有Windows 系统不认可的文件名存在,比如CAB中有XXX.(注意XXX后面的 . )这样的文件名。
在不相等的情况下,用EXCEL分别打开前面生成的CabFileList.xls和File.idt二个文件,分别对文件名列进行排序,并将File.idt文件的文件名所在列整列复制到CabFileList.xls中的一个新列中,并用IF函数判断二者是否有差异。如图所示(注意Msi_FileTable_FileList列中后面带 . 的文件)。

7、在提取的文件夹中,将在有差异的文件的文件名后添加一个数字或其他什么,将其改名,然后再从CAB文件中提取有差异的文件的另一个文件。

8、在CabFileList.xls中插入一个工作表,将第5步中已排序的File.idt 文件的File列和Sequenec 列分别复制到新的工作表中,并在其他列中添加"FILE"、"""、"="等列,然后用CONCATENATE文本合并函数,将这些列中的数据合并成IExpress所需的脚本格式。如图所示。

9、用汉化好的文件替换已解开的文件(注意:第6、7步中有差异的文件)。
※推荐直接汉化已解开的文件,以免重新命名等麻烦。

10、运行IExpress,选择"仅创建压缩文件"选项(如图),将已用汉化替换的文件全部或部分添加到要打包的文件中,然后选择"在软件包中使用长文件名保存文件"选项,最后保存好.SED扩展名的IExpress脚本文件。

11、修改IExpress脚本文件。用文本编辑器打开刚才保存的IExpress脚本文件(.sed),并从第8步的EXCEL表中复制SED[Strings] 列到IExpress脚本文件的 [Strings] 节中,同理,复制SED[SourceFiles0] 列到 [SourceFiles0] 节。
※重要提示:
如果第6、7步中有差异文件存在,请在IExpress脚本文件的 [Strings] 节中,将有差异的文件按MSI文件中的文件名命名(在第8步中已经采用了MSI中的文件名列表,所以这里一般不需要修改)。并将IExpress脚本文件的 [SourceFiles0] 节中的文件名全部改为在资源管理器中存在的实际文件名。
※备注:
(1)在一般的IExpress脚本文件(.sed)的[SourceFiles0] 节中的"="号后是没有文件名的。因为有差异文件名存在,所以这里需要特别处理。
(2)创建CAB时,程序会自动删除[SourceFiles0]节中的所有文件名。请注意保存备份,并在下次创建时复制回去。

12、再次运行IExpress,打开刚才修改好的IExpress脚本文件,一路下一步,一个已用汉化后文件替换的符合MSI文件中的File表的文件顺序的新CAB文件生成了。

13、将新的CAB文件插入到MSI文件中。如果是CAB文件独立于MSI,跳过该操作。
  首先,在DOS下或MsiDb.exe快捷方式下,使用"MsiDb.exe -k CAB文件名 -d MSI文件名"命令删除MSI文件中的CAB文件(其实只是清除关联)。然后使用"MsiDb.exe -a CAB文件名 -d MSI文件名"命令插入新的CAB文件到MSI文件中。
其中:(1)CAB文件名就是Media表中的Cabinet 列中的CAB文件名。注意忽略前置的"#"。
(2)CAB 和 MSI 文件名均要带后缀扩展名。

如果提示不成功,请先用"MsiDb.exe -k CAB文件名 -d MSI文件名"命令清除流,然后再用"MsiDb.exe -a CAB文件名 -d MSI文件名"添加新的CAB文件。
注意:MsiDb.exe 对某些长文件夹名不支持,会提示错误。建议尽量用短文件夹名。

14、安装测试。运行一下安装程序,看看是否会在安装过程中出现类似"文件不存在"等错误,如果没有,则大功告成。

备注

1、谢谢您阅读,如果本文对您有些帮助,将十分荣幸。
2、本文版权属wanfu所有,欢迎在保持完整和不修改的条件下转载本文。
3、联系邮件:z_shangyi@163.com

关于压缩率

1、默认情况下,用IEPress制作的CAB文件压缩率相对较低。经初步测试,IEpress的脚本支持Makecab.exe的所有参数,只要在[Options]节中添加CompressionType=lzx一行,就可以实现 lzx 压缩率。
2、根据 zhfi 网友的提示,查了一下有关资料,用 cabarc.exe -m LZX:21 -r N CAB文件名 "文件所在目录\*" 命令确实可以获得比 IEpress 更高的压缩率,而且不存在顺序问题。但是如果有MSI 中有 XXX. 这样的文件,而实际解压后没有了 XXX. 这样的文件,就无法打包成和原始CAB一样的新CAB文件,只能通过修改MSI文件中的File表中的File列中的相关文件名以及在其他表中所关联的文件名才能解决。
3、根据汉化好友"鱼"的方法,可以在要压缩的文件所在的文件夹下,用以下命令行实现:
makecab /f <完整路径>MsiFileList.txt /d compressiontype=lzx /d compressionmemory=21 /d maxdisksize=1024000000 /d diskdirectorytemplate=data* /d cabinetnametemplate=data*.cab
这里:MsiFileList.txt 是指MSI文件中File表中的File列的文件名列表(请按Sequenec 列从小到大排列),
diskdirectorytemplate=data* 中的 data*,即分卷压缩的情况下,在要压缩的文件所在的文件夹下生成 data1/data2/data3……文件夹,不分卷压缩的情况下,只生成一个文件夹。
cabinetnametemplate=data*.cab 中的 data*.cab,即分卷压缩的情况下,data1/data2/data3……文件夹中生成data1.cab/data2.cab/data3.cab......等CAB文件名。
但是,如果有原CAB中有XXX. 和XXX二个解压后文件名相同的文件,那么该方法就不适用了。
4、如果你已经删除了CAB中的一些文件,并对相关的MSI表进行了修改(如不修改会出现安装错误!),需要缩小MSI文件的话,请使用Orca打开清流后的MSI,然后另存为一个MSI文件,然后用MsiDb.exe插入新的CAB即可。

注:本文转载自:http://teach.hanzify.org/article/652-1233562028.html

补充:压缩cab文件时,文件顺序要按file.idt表中的顺序,否则会出现找不到文件的错误信息。

替换文件时,文件大小应该会变化,需要使用Orca修改file表中的文件大小。

基本流程:从msi中提取cab文件→使用Orca另存msi文件备用(里面的cab文件应该自行分离,msi文件变小)→解压cab文件→替换文件→压缩cab文件(需要创建sed文件)→修改sed文件中的文件顺序→重新压缩cab文件→将cab文件加入到msi文件

Msi中文件替换的更多相关文章

  1. [转]MSI安装程序中的文件替换

    原文链接:http://teach.hanzify.org/article/652-1233562028.html 前言 最近有汉化朋友问起如何不重新制作MSI文件,而直接用汉化好的文件替换MSI安装 ...

  2. 【原创】DOTNET动态调试破解Spoon,及MSI安装包文件替换技术

    提到Spoon可能大家还会感到陌生,但是如果提及XenoCode那么研究过DOTNET的人应该都知道吧.Spoon的前身就是XenoCode,虽然没有了PostBuild这个混淆软件,但是虚拟化技术仍 ...

  3. Visual Studio 2010 实用功能:使用web.config发布文件替换功能

    当建立ASP.NET Web应用程序项目后,默认除了生成web.config外,还生成了web.debug.config与Web.Release.config.顾名思义,根据它们的命名我可以推测到他们 ...

  4. 怎样修复“Windows/System32/Config/System中文件丢失或损坏”故障

    怎样修复“Windows/System32/Config/System中文件丢失或损坏”故障 英文原文引自 http://xphelpandsupport.mvps.org/how_do_i_repa ...

  5. OC中文件的操作

    OC中文件操作,在之前的文章中,已经接触到了文件的创建了,但是那不是很具体和详细,这篇文章我们就来仔细看一下OC中是如何操作文件的: 第一.首先来看一下本身NSString类给我们提供了哪些可以操作文 ...

  6. ASP.NET:MVC中文件上传与地址变化处理

    目录 文件的上传和路径处理必须解决下面列出的实际问题: 1.重复文件处理 2.单独文件上传 3.编辑器中文件上传 4.处理文章中的图片路径 5.处理上传地址的变化 一.上传文件和重复文件处理 文件处理 ...

  7. Win8 Metro中文件读写删除与复制操作

    Win8Metro中,我们不能在向以前那样调用WIN32的API函数来进行文件操作,因此,下面就来介绍一下Win8 Metro中文件的读写操作. 1 Windows 8 Metro Style App ...

  8. python中文件操作的六种模式及对文件某一行进行修改的方法

    一.python中文件操作的六种模式分为:r,w,a,r+,w+,a+ r叫做只读模式,只可以读取,不可以写入 w叫做写入模式,只可以写入,不可以读取 a叫做追加写入模式,只可以在末尾追加内容,不可以 ...

  9. Servlet接口的实现类,路径配置映射,ServletConfig对象,ServletContext对象及web工程中文件的读取

    一,Servlet接口实现类:sun公司为Servlet接口定义了两个默认的实现类,分别为:GenericServlet和HttpServlet. HttpServlet:指能够处理HTTP请求的se ...

随机推荐

  1. cocos creator主程入门教程(十)—— A*寻路

    摘要: 五邑隐侠,本名关健昌,10年游戏生涯,现隐居五邑.本系列文章以TypeScript为介绍语言. 这一篇介绍A*寻路算法.在RPG.SLG.模拟经营类游戏,有需要给角色寻路的需求,一般寻路我们采 ...

  2. sql 脚本编写之路 常用语句(一) 1.用一个表中的某一列更新另外一个表的某些列:

    for ACCESS 数据库: update a, b set a.name=b.name1 where a.id=b.id for SQL Server 数据库: update a set a.na ...

  3. Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器

    概要 之前的两篇文章,讲述了Spring Security 结合 OAuth2 .JWT 的使用,这一节要求对 OAuth2.JWT 有了解,若不清楚,先移步到下面两篇提前了解下. Spring Bo ...

  4. JavaScript常用代码书写规范

    javascript 代码规范 代码规范我们应该遵循古老的原则:“能做并不意味着应该做”. 全局命名空间污染 总是将代码包裹在一个立即的函数表达式里面,形成一个独立的模块. 不推荐 , y = ; c ...

  5. 003. 什么是 正向代理 & 反向代理

    正向代理: 客户端的代理: 反向代理: 服务端的代理:

  6. MySQL字段操作与数据处理

    一,对字段的操作 1.拼接字段:Concat()函数 多数DBMS使用 + 或者 || 来实现拼接,而MySQL使用 Concat() 函数来实现拼接. 实例: Concat()函数拼接时加上的字符需 ...

  7. JS 的继承

    1:原生链:prototype 儿子能够继承父亲的属性,也可以觉得遗传基因不好自己改属性,(但是不能改变老爸的属性). 看例子:             function farther(){     ...

  8. Python 基于python操纵zookeeper介绍

    基于python操纵zookeeper介绍 by:授客  QQ:1033553122 测试环境 Win7 64位 Python 3.3.4 kazoo-2.6.1-py2.py3-none-any.w ...

  9. HandlerThread原理分析

    HandlerThread是一个内部拥有Handler和Looper的特殊Thread,可以方便地在子线程中处理消息. 简单使用 HandlerThread的使用比较简单. mHandlerThrea ...

  10. d3js scales深入理解

    转自:https://www.cnblogs.com/kidsitcn/p/7182274.html 比例尺函数是这样的javascript函数: 接收通常是数字,日期,类别等data输入并且: 返回 ...