
'打印出复合文件的内部目录结构 PrivateSub Test_PrintDirs() Dim fAs CFile Set f= New CFile() '打开文件,以字节方式打开 f.OpenFile "D:\VBA\隐藏模块测试.swp" Dim cf As CCompoundFile Set cf=New CCompoundFile Dim ret As String ret= cf.Parse(f) If VBA.Len(ret) Then Debug.Print ret EndIf Dim fs() As String fs= cf.DirsName() Dim i As Long For i=0 To UBound(fs) Debug.Print i, fs(i) Next Set cf=Nothing Set f=Nothing End Sub
上面这段代码用于打印“D:\VBA\隐藏代码测试.swp”这个“复合文件”的内部结构,这个文件在solidworks里打开的结果如下图所示,代码主要由Main和Hide这两个模块组成,还有一个UserForm1窗体。
运行上面的代码段后,得到下面这样的输出结果:

0 Root Entry1 Root Entry\apc2 Root Entry\apc\The VBA Project3 Root Entry\apc\The VBA Project\_VBA_Project4 Root Entry\apc\The VBA Project\Host Project Item Names5 Root Entry\apc\The VBA Project\VBA Project Data6 Root Entry\apc\The VBA Project\Host Project Items7 Root Entry\apc\The VBA Project\VBA Project Signature8 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary9 Root Entry\apc\The VBA Project\Host Project Item Names\Project_Data_CurVer10 Root Entry\apc\The VBA Project\Host Project Item Names\Host Project Item List Data11 Root Entry\apc\The VBA Project\_VBA_Project\VBA12 Root Entry\apc\The VBA Project\_VBA_Project\PROJECTwm13 Root Entry\apc\The VBA Project\_VBA_Project\PROJECT14 Root Entry\apc\The VBA Project\_VBA_Project\VBA\ThisLibrary15 Root Entry\apc\The VBA Project\_VBA_Project\VBA\Main16 Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_217 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Project Item Data18 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Control List Data19 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\ReqControl List Data20 Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_321 Root Entry\apc\The VBA Project\_VBA_Project\VBA\UserForm122 Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\f23 Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\o24 Root Entry\apc\The VBA Project\_VBA_Project\UserForm125 Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\[1]CompObj26 Root Entry\apc\The VBA Project\_VBA_Project\UserForm1\[3]VBFrame27 Root Entry\apc\The VBA Project\_VBA_Project\VBA\Hide28 Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_429 Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_530 Root Entry\apc\The VBA Project\_VBA_Project\VBA\_VBA_PROJECT31 Root Entry\apc\The VBA Project\_VBA_Project\VBA\dir32 Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_033 Root Entry\apc\The VBA Project\_VBA_Project\VBA\__SRP_134 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\ReqControl List Data\ReqControl List Data35 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Control List Data\Control_Data_CurVer36 Root Entry\apc\The VBA Project\Host Project Item Names\ThisLibrary\Control List Data\Control List Data
在这个“复合文件”内部,有许多组成文件,而VBA模块的定义就放在“Root Entry\apc\The VBA Project\_VBA_Project\PROJECT”这个文件里,我们只需要改写这个文件,就可以达到隐藏其中某个模块的目的。为了改写这个文件,首先要了解它有哪些内容,可以用下面这段代码把它的内容显示出来:

Private Sub Test_GetStream() Dim f As CFile Set f = NewCFile() f.OpenFile"D:\VBA\隐藏模块测试.swp" Dim cf As CCompoundFile Set cf=New CCompoundFile Dim ret As String ret= cf.Parse(f) Dim b() As Byte ret= cf.GetStream("Root Entry\apc\The VBA Project\_VBA_Project\PROJECT", b) If VBA.Len(ret) Then Debug.Print ret End If Debug.Print VBA.StrConv(b, vbUnicode) Set cf=Nothing Set f=Nothing End Sub
运行上面这段代码后,得到下面的输出结果:

ID="{6366A273-ED0F-4463-BE89-B6DD8ECD755B}" Document=ThisLibrary/&H00000000 Module=Main Module=Hide Package={AC9F2F90-E877-11CE-9F68-00AA00574A4F} BaseClass=UserForm1 Name="Macro4" HelpContextID="0" VersionCompatible32="393222000" CMG="888A859389938993899389" DPB="C4C6C9EF392A3A2A3A2A" GC="00020D0E0E0E0EF1"[Host Extender Info] &H00000001={3832D640-CF90-11CF-8E43-00A0C911005A};VBE;&H00000000[Workspace] ThisLibrary=0, 0, 0, 0, C Main=534, 155, 1812, 802, Z UserForm1=49, 97, 1327, 744, , 756, 68, 2034, 715, Hide=224, 224, 1187, 708,
从上面的输出结果可以看到,程序文件一共有2个模块:Main和Hide,如果要隐藏Hide模块,那么只需要把Module=Hide这行删除即可。下面再用一段VBA代码来改写上面的“复合文件”,使Hide模块隐藏。

'改写对应的文件 Private Sub Test_ReWriteStream() Dim f As CFile Set f= New CFile() f.OpenFile "D:\VBA\隐藏模块测试.swp" Dim cf As CCompoundFile Set cf=New CCompoundFile Dim ret As String ret= cf.Parse(f) If VBA.Len(ret)Then Debug.Print ret End If Dim b()As Byte ret= cf.GetStream("Root Entry\apc\The VBA Project\_VBA_Project\PROJECT", b) If VBA.Len(ret)Then Debug.Print ret End If Dim strSrc As String strSrc= VBA.StrConv(b, vbUnicode) '替换后模块将被隐藏 'strSrc = VBA.Replace(strSrc, "Module=MMain" & vbNewLine, "") strSrc= VBA.Replace(strSrc,"Module=Hide"& vbNewLine,"") Debug.Print"更改后的文本:"& Chr(13)& strSrc b= VBA.StrConv(strSrc, vbFromUnicode) ret= b Debug.Print"转换后的文本"& Chr(13)& ret ret= cf.ReWriteStream("Root Entry\apc\The VBA Project\_VBA_Project\PROJECT", b) If VBA.Len(ret) Then Debug.Print ret EndIf Set cf=Nothing Set f=Nothing EndSub
经过改写文件后,再用solidworks打开swp文件,发现Hide模块不见了,但是程序还能正常运行。学会了隐藏模块的方法,那么恢复模块可见就很简单了,只需要给文件重新添加上对应的模块名即可。其实上面这种方法不仅可以隐藏模块,也可以用于解除VBA程序设置的密码,下次我们再详细说说如何解除程序密码。
上面的代码段用到了一些未公开的方法和代码,如果你想要深入学习和了解,请关注微信公众号“全栈开发的码农”,欢迎私信或留言探讨。