首先说明一下本文的需求:利用Windows自带的API、DLL或命令行参数等任何手段,解压一个标准的zip压缩文件,并且不借助任何第三方程序。
一、前言——徒劳的探索
为什么会提出这种需求呢?因为我近期在编程中,需要在程序运行时解压一个zip文件,然后使用解压出来的文件。之所以要强调不借助第三方软件,是因为程序发布到客户电脑后,不能指望(或要求)客户电脑里必须有解压缩软件,因此利用Windows自带的解压缩功能是最保险的一个办法。我本来以为这是一个非常简单的需求,因为我在使用新装的Windows时,文件管理器(Explorer.exe)都能直接提取zip文档,因此我认为只要找到文件管理器里使用的解压方式或是命令行参数,直接应用到我的程序里就万事大吉了。
然而,事情并没有我想的那么简单。我搜遍了百度谷歌,各种中文英文论坛,网友们给出的方向无非两条路。第一条路,Windows自带的zip解压功能位于zipfldr.dll中,只要弄清楚怎么利用这个dll文件就行了。然而这个dll并没有提供公开的接口或命令行参数,唯一提供的还有点用的接口RouteTheCall只能显示压缩文件,并不能解压文件。关于为什么这个dll不对外提供服务,网上也是众说纷纭,有的说是这个dll文件功能还有缺陷,所以微软不想公开参数;也有的说因为现在解压缩zip文件的第三方软件、代码太多了,微软没必要再单独提供一个接口了等云云。真实的原因我们不去考证,但是总结起来就是一句话,这条路行不通。第二条路,包括在Microsoft官方论坛上,有很多人说直接安个7-zip或者Winrar就能用命令行解压了。或者有人没说的这么直白,他们说你用个开源且免费的zlib(或其他开源代码),然后就能很容易解压zip软件了。甚至更有很多自以为大师的小白说,打开cmd,输入如下参数,就能解压了:unzip.exe mydata.zip –XXXXXX(或是winrar.exe –x …….)。这些方式确实能解压,但是却与我的需求背道而驰,我的需求是不能借助任何第三方的程序便实现zip文档的解压。走这条路的一部分人也算实在,明确说这是利用第三方程序实现的,不利用第三方程序实现不了,另一部分“大师”级小白却信誓旦旦地说,这就是Windows自带的解压(难道仅仅因为是命令要打到cmd里就算是Windows自带的解压方式了?)。除了上述两条基本路线,还有一些比较冷门的说法,比如有人说Windows自带的解压程序是expand.exe,用这个可以解压zip。这个程序其实只能解压.cab文档,之所以有人说这个能解压zip是因为他们把一个文件的扩展名从.cab改成了.zip,然后用expand解压,这样当然能解压了。如果是一个标准的zip,用expand是无论如何也解压不了的(即使把文件扩展名改成.cab)。总之,经过数日的搜索与求助,结论是没有任何一种方法可以不借助第三方程序解压标准的zip。
二、进阶——思路的转变
既然网上没有答案,那就自己解决,就像我当年研究0xc000007b问题时一样(详见《运行游戏时出现0xc000007b错误的解决方法》),网上到处都是提问的,但是没有一个回答是有效的。这次也是,网上回答虽然多,但是没有能完全满足我的需求的。首先需要梳理一下我的思路。我原来计划的思路是:
1、得到一个zip压缩文件;2、用Windows自带的程序(或命令行等)解压这个zip文件;3、使用解压出来的文件(包括运行、复制、移动、重命名等)
在这个思路下,第2步就卡住了,后续自然也没有后续了。但是仔细回忆一下,我当时产生这个需求的时候为什么觉得这个需求应该很简单呢?因为我在使用原版Windows的时候,在文件管理器里只要双击就可以打开zip文档,并且在文件管理器里可以浏览压缩包里的所有文件,然后只要在要运行的程序上双击,就可以自动运行那个文件。整个过程就像打开一个普通文件夹,然后双击我要运行的程序一样那样自然,几乎感觉不到这是个压缩文档,中间还要有解压过程。那我的程序能不能也用这个思路解决呢?比如像这样:
1、得到一个zip压缩文件;2、打开这个zip压缩文档;3、运行我要运行的文件
在新思路的指引下我开始了我的尝试,然后我意外的发现,这个方式居然成功了,我完全不去考虑到底要怎么解压,我就把这个zip文档当成一个普通的文件夹在文件管理器中打开,然后运行我要运行的程序。当我运行这个程序时,Windows自己在后台便帮我把文件解压好了,程序运行起来就像已经解压好后再双击运行一样。
三、方法——代码的实现
下面我把实现的方法通过VB.NET代码的形式展现出来,大家可以根据上面的思路结合代码自行应用到其他语言上,或者直接在cmd命令行里运行也行。
第1步,首先找到一个示例的标准zip文档,里面有exe程序,也有txt文档,这里以我的DirectX修复工具在线修复版为例,如图1:
从上面的图标可以看出来,我的系统里没装任何第三方解压缩软件,只能通过Windows Explorer打开。
第2步,新建一个空程序,主窗体上画2个Button,然后打入代码(如图2)。Button1中的代码是用来打开非exe文件的,Button2中的代码是用来打开exe文件的。
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
'打开一个zip压缩文件中的非exe文件
Dim appID As Integer
appID = Shell("explorer ""C:\Users\zhangyue\Desktop\DirectXRepair_3.8_Online.zip\DirectXRepair_3.8_Online\使用说明.txt""", AppWinStyle.NormalFocus)
'appID = Shell("explorer ""C:\Users\zhangyue\Desktop\DirectXRepair_3.8_Online.zip\DirectXRepair_3.8_Online\Data\A\xinput1_1.dll""", AppWinStyle.NormalFocus)
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
'打开一个zip压缩文件中的exe文件
Dim appID As Integer
appID = Shell("explorer ""C:\Users\zhangyue\Desktop\DirectXRepair_3.8_Online.zip\DirectXRepair_3.8_Online\DirectX Repair.exe""", AppWinStyle.NormalFocus)
Threading.Thread.Sleep(1000) '需要一定的延迟,确保Explorer弹出对话框后再发送字母U
My.Computer.Keyboard.SendKeys("U")
End Sub
End Class
第3步,单击Button1,可以看到使用说明.txt就被打开了,没有一丝延迟,完全感受不到压缩文档的存在,就像文档已经解压好一样,直接打开(如图3)。Button1中的代码可以推广到打开所有的非exe文件上。
如果我们需要打开的是exe文件,假如也用Button1中的代码,只是文件名换成XXX.exe的话,文件管理器会弹出一个对话框,如图4。
这个提示的原因是因为,通常exe运行时,需要调用其文件夹中的dll文件或其他配置文件,方能正常运行,所以Windows会询问用户是否需要全部解压这个zip文档,还是只运行你选定的exe文件。由于DirectX Repair.exe不需要调用任何dll文件,所以上图中的对话框应该选择第二个按钮“运行”。关于这个选择,我们也可以自动化完成,通过添加2行代码(如图5),即可实现自动点击按钮并正常运行程序。之所以发送字母U,是因为如图4中,“运行”按钮的快捷键为U。
Threading.Thread.Sleep(1000) '需要一定的延迟,确保Explorer弹出对话框后再发送字母U
My.Computer.Keyboard.SendKeys("U")
第4步,单击Button2,可以看到DirectX修复工具主界面就被打开了。虽然代码上会有一点点延时,但是毕竟还是自动化操作,还算可以接受。如图6。
代码中的延时代码非常重要,如果没有延时,文件管理器还没弹出这个询问框时,可能程序就把按键发过去了,这样就会造成无法实现自动启动。关于具体延时需要多少毫秒,这个大家可以自行设置。
四、提高——疑问的解答
说到这大家可能会有几个疑问,我来一一解答。
问题1,有的时候我不只需要运行压缩包里的文件,我需要复制、重命名或是提供命令参数运行等操作,这个怎么实现?以刚才第4步打开的DirectX Repair.exe为例,一旦这个文件运行起来了,那么这个文件其实就已经被解压了存在于你的电脑上了,只要到系统文件夹的Temp文件夹里找到解压出来的exe文件(如图7),那么你想对它做任何事都随意了(图中示例路径为:C:\Users\zhangyue\AppData\Local\Temp\Temp1_DirectXRepair_3.8_Online.zip\DirectXRepair_3.8_Online\DirectX Repair.exe)。
问题2,我的exe程序还需要zip文档里dll文件等支持,怎们办?这时在运行你的程序之前,先按照之前的第3步,把dll文件打开,然后另存到你需要的目录下,将来运行exe时,到系统Temp文件夹中把exe也拷贝到你需要的目录下,这样就可以运行了。关于如何打开dll文件,我再给个提示,先到注册表里,把dll文件的默认打开方式设置为记事本(如图8),当按照第3步打开时,打开的就是一个记事本,只要把这个记事本保存并重命名为dll文件,即相当于对这个dll文件进行了解压。让dll文件通过记事本打开的注册表具体位置为:Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.dll\UserChoice\Progid,值为:Applications\NOTEPAD.EXE
问题3,我想后台操作,怎么办?这点我还没有实践,不过思路已经有了,就是首先后台通过explorer打开一个zip压缩文档,然后利用Windows API函数,把这个explorer中的文件默默拷贝出来,然后再关掉这个后台explorer,即相当于在后台把这个zip文档解压到了指定的文件夹。
五、结语——艰难的开端
关于我文中提到的方法,我毫不掩饰的说,并不完美,和我们的理想还有一定的差距。但是,这个方法是目前能满足我的需求的唯一方法。第三方软件固然方便,但是并不满足我的需求;zipfldr.dll也许可以实现我的需求,但是目前我(包括大家)并没有找到解决方法。万事开头难,从无到有是最困难的,虽然目前的方法不甚完美,但毕竟解决了有无问题,可以先用着,未来再不断研究新方法,改进实现的方式。就好比10年前我研究0xc000007b问题时,我最开始给大家提出的解决方案是安装DirectX9.0。虽然安装DirectX9.0不是100%有效,但是最起码解决了0xc000007b问题解决方案有无的问题,给大家指明了一个方向。后续针对0xc000007b问题发布的DirectX修复工具,再到后来的DirectX修复工具增强版,可以说已经可以解决现在市面上95%的问题了,已经接近完美了。
关于用Windows自带程序(或命令)解压zip文档的方案,我相信未来也可以变得更加高效、更加完美。如果各位对我的方案有任何意见或者建议,一定要及时告诉我,希望我们大家可以共同提高。