nsis是开源的安装包制作软件,可以各种定制,默认的模板也不错,以前我们的安装程序使用rar自解压,rar自解压的格式也挺好的,比较实用,就是美观性不够,另外协议的格式只能是纯文本的,不能加粗,不支持链接等,而nsis支持rtf格式的协议,相比之下好看多了。
我要制作的安装包所包含的文件很多,超过5G,将来指不定还会更大,如果直接用nsis默认方式制作安装包的话,安装包会很大,而FAT32格式的硬盘最大文件不能超过4G,所以必须分卷压缩。找到nsis7z插件,发现仅支持单个压缩包的解压,不支持多分卷解压。有一个变通的办法是使用7z的命令行版本,在nsis里调7za.exe解压,这个确实可行,缺点是进度条看不到具体解压的log。
于是把nsis7z的源代码下载下来研究了一下,用了7z920的源代码,注掉一些东西后就成功编译dll。7z是通过宏定义REGISTER_ARC来决定加载哪些压缩方式的,原插件只加载了7z这一种格式,而分卷解压的格式是Split,所以很简单,只要将SplitHandler.cpp加入插件所在的工程,SplitHandler.cpp有以下代码:
static CArcInfo g_ArcInfo =
{ L"Split", L"001", 0, 0xEA, { 0 }, 0, false, CreateArc, 0 };
REGISTER_ARC(Split)
原插件只支持显示百分比+已解压大小+总大小,更好的方式是显示当前正在解压的文件名及路径,找到ExtractCallbackConsole.h/cpp,在回调函数里增加文件名参数name,然后在
STDMETHODIMP CExtractCallbackConsole::PrepareOperation(const wchar_t *name, bool /* isFolder */, Int32 askExtractMode, const UInt64 *position)
{
if(!curFileName)
delete[] curFileName;
curFileName = new wchar_t[MAX_PATH];
wcsncpy(curFileName, name, MAX_PATH);
UpdateProgress();
return S_OK;
}
然后在显示的地方把这个文件名返回给nsis,即可在解压过程中显示当前正在解压的文件了。
问题本身不是很复杂,dll调试不太熟,用了最笨的办法,写log到文件,然后编译7z/cpp/7zip/Bundles/Alone,所幸Alone可以单步调试,逐步锁定问题出在SplitHandler.cpp没有编译进去。
另一个比较好玩的地方是nsis生成的exe可以直接用7z解压:)