分享

VB资源编辑器的奥秘

 hdzgx 2017-10-31
一、资源的概念
1.什么是资源
资源(Resource)可以视作一个程序携带的除了代码和窗体的额外数据,它像一个EXE的plugin和database,可以包含文本、图片、鼠标、光标、二进制数据等,几乎所有的Windows应用程序中都拥有资源,例如最常见的MFC程序,用eXeScope打开可以看到“资源段”,从这里也可以看出,资源和EXE代码段是分开的,EXE启动时并不一定需要加载它,而是在程序里通过一定语句读取处理,因此使用资源也能提高程序的效率。资源的文件载体通常以.RC(VC++)或.RES(VB)存在于存储介质上。

2.使用VB的资源编辑器
资源在VC编程里是很常见的了,可是在VB编程里,依然有很多人不知道这个功能,相不相信?其实它就藏在VB的“工具”菜单--->“Resource Editor”(图1),资源编辑器界面就出来了(图2),它就长这模样,记住没有?
如果你的VB的“工具”菜单里没有这个选项,请在“选项”里的“可连接的”属性页里把“工程资源管理器”选上(图3)。

3.添加资源
VB的资源编辑器可以放入5种资源:文本、光标、图标、图片、自定义资源,以自定义资源为例,点一下问号旁边最近的一个按钮,把一个EXE文件添加进去,资源编辑器就会出现一个“CUSTOM”目录树,添加进去的文件序号从101开始。(图4)
记得保存哦,保存下来的RES文件已经和工程文件Link了,接下来开始编程。

4.重要语句
这个过程的作用是把CUSTOM资源里的指定数据读取出来写成文件,想到什么没有?不知道?后面再公布迷底:P
============================================================================
Sub CreateFile(FileName As String, ResID As Integer, Size As Long)
'---------------------------------------------
'把资源读取出来保存为文件
'Author:小金(LK007)
'Site: www.s8s8.net
'E-MAIL: LK007@163.com
'Usage:CreateFile(文件名,资源序列号,文件大小)
'----------------------------------------------
On Error Resume Next
Dim File() As Byte 'Btye类型的数组
Dim Counter As Long
Dim FileNum As Integer

FileNum = FreeFile()

File = LoadResData(ResID, "CUSTOM") '将自定义资源中ResID号资源读入数组
Open FileName For Binary As #FileNum '以二进制方式写文件
For Counter = 0 To Size - 1 '注意因为从0 Byte开始因此以文件大小 - 1Byte 为终
Put #FileNum, , File(Counter)
Next Counter
Close #FileNum
End Sub
============================================================================
注意!这个是最基本也是最重要的代码!

二、开始编程
在这里我并不想教大家用资源给自己的程序添加图标、光标等,那些不是本文讨论的范围。

1.文件捆绑
现在网络上随便都能下载到一个EXE捆绑机,它们的原理是把两个EXE通过适当的代码连接在一起,最终产生的集合体为两个EXE之和,用任意一款Hex分析工具就能看到两个PE头部和各个EXE的数据,这样的组合是启动了就马上分离出两个文件一起运行的,有点心思的人都能感觉到启动的速度比较慢(如果你机器很快就不算-_-),因为WriteFile和启动是需要时间的,而且我们不能控制这个过程。
其实用VB的资源段,我们可以做得比EXE捆绑机好得多,最重要的是,它不是简单的两个PE组合,而且我们能控制它。
强烈建议编程之前先用EXE压缩工具把EXE压小一点,对大家都有好处,真的。

新建一个EXE工程,把一个EXE文件(WinShell、灰鸽子随便了)添加入自定义资源,预先需要记录资源里的文件原体积哦,VB是瞎子,不会帮你看的。
现在可以找一个事件或者按钮来触发它,例如我把一个5000字节的EXE文件放入了101号资源,并在窗体加载时释放它到程序运行的目录下面,就这样做:
===================================================
Private Sub Form_Load()
On Error Resume Next
Call CreateFile(App.Path & "/resApp.exe",101,5000) '忘记了这个代码?看前面~~
'Shell App.Path & "/resApp.exe",vbHide ''加上这句就马上偷偷运行了:)
End Sub
==================================================
编译成EXE,看看它的体积,发现没有?加入的资源是5000字节,出来的EXE是16KB,另外开个空EXE编译出来也是16KB,这个捆绑并不是简单的PE组合,因此两个文件总和始终小于简单的文件捆绑,经过多次测试,VB资源里捆绑的文件在编译出来后就等于被再次压缩了10%----45%,是不是很惊讶呢?
附:更详细的完整代码见附件。别怕,我捆绑的资源是一个Hello World程序,无害,也没什么用处。


2.ActiveX in Execute
VB程序的兼容性向来不是很好,一不小心就Can't create Object,归根结底是因为制作者用了ActiveX而又没有相关注册措施,例如一些VB编写的Hacker工具就普遍采用MSWINSCK.OCX作为WinSock介面(我写的Client也是这么偷懒的:P)
正是因为这样,VB程序经常危机重重,许多人不得不用M$提供的VB安装向导,一个程序才500KB,愣是做个2MB多的向导出来,资源极度浪费-_-而且你不可能想在肉鸡上用向导放你的宝贝程序吧?
用了VB的资源,你会发现原来一切都这么简单!
同样,建议你把需要的ActiveX文件用PECompact压缩一下……
制作这个不能像上面的EXE捆绑那样自由,因为你需要在需要的ActiveX没加载之前就把它交出来并DllRegister,否则你会发现错误依旧……所有窗体都没有加载前是最好的时机,别忘记VB的Sub Main()
同上,制作一个EXE工程,把你需要的ActiveX塞进资源,记录体积,现在来看这个代码:
==================================================
Sub Main()
Dim rc As Integer
Call CreateFile("C:/WINDOWS/SYSTEM/richtx32.ocx", 101, 260096) '释放~~~
rc = Register("C:/WINDOWS/SYSTEM/richtx32.ocx") '注册~~~
If eRegisterServerReturn_RegSuccess Then
Load Form1 '加载~~~~~~
Form1.Show
Else
MsgBox "注册ActiveX失败,程序不能启动:(", vbCritical, "LK007"
End
End If
End Sub
==================================================
完成一个ActiveX的注册就这么简单,呵呵,谁也不会想到你的EXE里居然还捆绑着ActiveX。
当然,别忘记了注册ActiveX的代码……
==================================================
Public Const E_ERR_BASE = 17980 + vbObjectError
Public Enum EErrRegisterServer
eErrRegisterServer_InvalidFileName = E_ERR_BASE + 1
eErrRegisterServer_ComponentFailure
End Enum
Public Const S_ERR_InvalidFileName = "无效的文件"
Public Const S_ERR_ComponentFailure = "注册失败"

Public Enum ERegisterServerReturn
eRegisterServerReturn_CannotBeLoaded = 1
eRegisterServerReturn_NotAValidComponent = 2
eRegisterServerReturn_RegFailed = 3
eRegisterServerReturn_RegSuccess = 4
eRegisterServerReturn_UnregSuccess = 5
eRegisterServerReturn_UnregFailed = 6
End Enum

Public Declare Function LoadLibraryRegister Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
Public Declare Function FreeLibraryRegister Lib "kernel32" Alias "FreeLibrary" (ByVal hLibModule As Long) As Long
Public Declare Function CreateThreadForRegister Lib "kernel32" Alias "CreateThread" (lpThreadAttributes As Any, ByVal dwStackSize As Long, ByVal lpStartAddress As Long, ByVal lpparameter As Long, ByVal dwCreationFlags As Long, lpThreadID As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Declare Function GetProcAddressRegister Lib "kernel32" Alias "GetProcAddress" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Public Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function GetExitCodeThread Lib "kernel32" (ByVal hThread As Long, lpExitCode As Long) As Long
Public Declare Sub ExitThread Lib "kernel32" (ByVal dwExitCode As Long)

Public Const STATUS_WAIT_0 = &H0
Public Const WAIT_OBJECT_0 = ((STATUS_WAIT_0) + 0)

Function Register(m_sFileName As String) As ERegisterServerReturn
On Error GoTo hComponentFailure
Dim dwExitCode As Long
Dim fSuccess As Long
Dim hThread As Long
Dim lLib As Long
Dim lProcAddress As Long
Dim lpThreadID As Long

If m_sFileName = "" Then Exit Function

lLib = LoadLibraryRegister(m_sFileName)
If lLib = 0 Then
Register = eRegisterServerReturn_CannotBeLoaded
Exit Function
End If

lProcAddress = GetProcAddressRegister(lLib, "DllRegisterServer")

If lProcAddress = 0 Then
Register = eRegisterServerReturn_NotAValidComponent
If lLib Then Call FreeLibraryRegister(lLib)
Exit Function
Else
hThread = CreateThreadForRegister(ByVal 0&, 0&, ByVal lProcAddress, ByVal 0&, 0&, lpThreadID)
If hThread Then
fSuccess = (WaitForSingleObject(hThread, 10000) = WAIT_OBJECT_0)
If Not fSuccess Then
Call GetExitCodeThread(hThread, dwExitCode)
Call ExitThread(dwExitCode)
Register = eRegisterServerReturn_RegFailed
If lLib Then Call FreeLibraryRegister(lLib)
Exit Function
Else
Register = eRegisterServerReturn_RegSuccess
End If
Call CloseHandle(hThread)
If lLib Then Call FreeLibraryRegister(lLib)
End If
End If
Exit Function

hComponentFailure:
Err.Raise eErrRegisterServer_ComponentFailure, App.EXEName & ".CRegisterServer", S_ERR_ComponentFailure

End Function
==================================================
附:更详细的完整代码见附件。程序释放并注册一个RichTextBox

三、EOF
牢记几个要点:
1.资源里的文件长度不要记错
2.太大的资源读取出来会很慢,如果你要做工具程序,建议在Load时使用Splash Window,别让用户以为你的程序死翘翘了
3.别忘记养成压缩EXE的习惯,无论是放进资源的还是最终编译的

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多