A Resource File is a repository in executable file (exe/dll/ocx) where you can store images, sounds, videos, data, or whatever you like. VB provides addin to modify resource during design time but what if you want to modify it during runtime. Win32 API is the solution to this problem. There are 3 main APIs (BeginUpdateResource, UpdateResource and EndUpdateResource) which you can use to modify resource programatically which can be stored in any executable file. From this demo you will learn the following items
- Modify (Add,Update or Delete) string resource stored in any exe/dll/ocx
- Modify (Add,Update or Delete) bitmap resource stored in any exe/dll/ocx
- List all string resource items stored in any exe/dll/ocx
- Display a single string resource item using LoadString API
- Display Bitmap resource item using LoadImage and other GDI apis
Retriving resource item is comparatively easy in VB if you are going to retrive from the same Application file. You can use inbuilt VB functions like LoadResPicture, LoadResString or LoadResData to retrive resource item from the same application file but if you need to access resource from different executable file then you need Win32 APIs like LoadString, LoadImage etc...
This article will show you how to modify Bitmap and String resource from any executable file using API. Storing Bitmap is comparatively easy but to store string in to the resource file requires good understanding of string table format.
String table in Resource file
The smallest granularity of string resource that can be loaded/updated is a block. Each block is identified by an ID, starting with 1. You need to use the block ID when calling FindResource(), LoadResource(), UpdateResource(). A string with ID, nStringID, is in the block with ID, nBlockID, given by the following formula: |
Note integer division.
A block of string resource is laid out as follows:
Each block has NO_OF_STRINGS_PER_BLOCK (= 16) strings. Each string is represented as an ordered pair, (LENGTH, TEXT). The LENGTH is a WORD that specifies the size, in terms of number of characters, of the string that follows. TEXT follows the LENGTH and is a sequence of UNICODE characters, NOT terminated by a NULL character.Any TEXT may be of zero-length, in which case, LENGTH is zero. An executable does not have a string table block with ID, nBlockID, if it does not have any strings with IDs - ((nBlockID - 1) * 16) thru' ((nBlockID * 16) - 1). This format is the same for Windows NT, Windows 95 & Windows 98. Yes, strings in a resource are internally stored in UNICODE format even in Windows 95 & Windows 98.
Step-By-Step Example
- Create one standard exe project - Add one command button and one picturebox control on the form1 - Add the following code in form1 - Modify sResExeFilePath to any sample exe whose resource you want to modify - Modify sBMPFileToLoadPath to any existing BMP path. This BMP will be loaded into the exe resource as ID=101 so make sure that target exe/dll doesn't have Bitmap resource with 101 ID otherwise it will be overwritten |
Click here to copy the following block | Private Type BITMAP bmType As Long bmWidth As Long bmHeight As Long bmWidthBytes As Long bmPlanes As Integer bmBitsPixel As Integer bmBits As Long End Type
Private Type StringTable id As Long Text As String End Type
Private Const IMAGE_ARCHIVE_START_SIZE = 8 Private Const IMAGE_BITMAP = 0 Private Const LR_CREATEDIBSECTION = &H2000
Private Const DIFFERENCE = 11 Private Const RT_ACCELERATOR = 9& Private Const RT_ANICURSOR = (21) Private Const RT_ANIICON = (22) Private Const RT_BITMAP = 2& Private Const RT_CURSOR = 1& Private Const RT_DIALOG = 5& Private Const RT_DLGINCLUDE = 17 Private Const RT_ICON = 3& Private Const RT_FONT = 8& Private Const RT_FONTDIR = 7& Private Const RT_GROUP_CURSOR = (RT_CURSOR + DIFFERENCE) Private Const RT_GROUP_ICON = (RT_ICON + DIFFERENCE) Private Const RT_HTML = 23 Private Const RT_MENU = 4& Private Const RT_MESSAGETABLE = 11 Private Const RT_PLUGPLAY = 19 Private Const RT_RCDATA = 10& Private Const RT_STRING = 6& Private Const RT_VERSION = 16
Private Const RT_MYOWN_TYPE1 = 10001 Private Const RT_MYOWN_TYPE2 = 10002
Private Const ENGLISH_US = 1033
Private Declare Function BeginUpdateResource Lib "kernel32" Alias "BeginUpdateResourceA" ( _ ByVal pstrFileName As String, ByVal bDeleteExistingResources As Long) As Long
Private Declare Function UpdateResource Lib "kernel32" Alias "UpdateResourceA" ( _ ByVal hUpdate As Long, ByVal lpType As Long, ByVal lpName As Long, _ ByVal wLanguage As Integer, lpData As Any, ByVal cbData As Long) As Long
Private Declare Function EndUpdateResource Lib "kernel32" Alias "EndUpdateResourceA" ( _ ByVal hUpdate As Long, ByVal fDiscard As Long) As Long
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" ( _ ByVal lpLibstrFileName As String) As Long
Private Declare Function FindResourceEx Lib "kernel32" Alias "FindResourceExA" ( _ ByVal hModule As Long, ByVal lpType As Long, ByVal lpName As Long, _ ByVal wLanguage As Long) As Long
Private Declare Function LoadResource Lib "kernel32" ( _ ByVal hInstance As Long, ByVal hResourceInfo As Long) As Long
Private Declare Function LockResource Lib "kernel32" ( _ ByVal hResourceData As Long) As Long
Private Declare Function SizeofResource Lib "kernel32" ( _ ByVal hInstance As Long, ByVal hResourceInfo As Long) As Long
Private Declare Function FreeLibrary Lib "kernel32" ( _ ByVal hLibModule As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ Destination As Any, Source As Any, ByVal Length As Long)
Private Declare Function BitBlt Lib "gdi32.dll" ( _ ByVal hDestDC As Long, _ ByVal x As Long, _ ByVal y As Long, _ ByVal nWidth As Long, _ ByVal nHeight As Long, _ ByVal hSrcDC As Long, _ ByVal xSrc As Long, _ ByVal ySrc As Long, _ ByVal dwRop As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32.dll" ( _ ByVal hdc As Long) As Long
Private Declare Function DeleteDC Lib "gdi32.dll" ( _ ByVal hdc As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" ( _ ByVal hObject As Long) As Long
Private Declare Function LoadImage Lib "user32.dll" Alias "LoadImageA" ( _ ByVal hInst As Long, _ ByVal lpsz As String, _ ByVal un1 As Long, _ ByVal n1 As Long, _ ByVal n2 As Long, _ ByVal un2 As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" ( _ ByVal hdc As Long, _ ByVal hObject As Long) As Long
Private Declare Function GetObjectAPI Lib "gdi32" Alias "GetObjectA" (ByVal _ hObject As Long, ByVal nCount As Long, lpObject As Any) As Long
Private Declare Function LoadString Lib "user32.dll" Alias "LoadStringA" ( _ ByVal hInstance As Long, _ ByVal wID As Long, _ ByVal lpBuffer As String, _ ByVal nBufferMax As Long) As Long
Public Function StringResourceModify(strResExePath As String, id As Integer, _ ByVal strText As String, Optional LangID As Long = ENGLISH_US) As Boolean
Dim ret As Long Dim hResource As Long Dim x As Long Dim iGroup As Integer Dim hObject As Long Dim hResourceData As Long Dim lpResourceData As Long Dim cbResource As Long Dim hModule As Long Dim lpData() As Byte Dim lpReadData() As Byte Dim cbText As Integer
If id < 1 Then Exit Function
iGroup = Int(id / 16&) + 1 hModule = LoadLibrary(strResExePath) hObject = FindResourceEx(hModule, RT_STRING, iGroup, LangID) hResourceData = LoadResource(hModule, hObject) lpResourceData = LockResource(hResourceData) cbResource = SizeofResource(hModule, hObject)
If cbResource <= 32 Or hObject = 0 Then cbResource = 32& + Len(strText) * 2& ReDim lpData(cbResource - 1) cbText = Len(strText) CopyMemory lpData(2& * (id - (iGroup - 1) * 16&)), cbText, 2& If Len(strText) Then CopyMemory lpData(2& + 2& * (id - (iGroup - 1) * 16&)), ByVal StrPtr(strText), Len(strText) * 2 End If Else ReDim lpReadData(cbResource - 1) CopyMemory lpReadData(0), ByVal lpResourceData, cbResource
Dim countX As Long Dim CHECKLNG As Integer For x = 0 To (id - (iGroup - 1) * 16&) - 1 CopyMemory CHECKLNG, lpReadData(countX), 2& countX = countX + 2 + CHECKLNG * 2 Next CopyMemory CHECKLNG, lpReadData(countX), 2& ReDim lpData(countX + 2 + Len(strText) * 2 + (cbResource - (countX + 2 + CHECKLNG * 2)) - 1) CopyMemory lpData(0), lpReadData(0), countX cbText = Len(strText) CopyMemory lpData(countX), cbText, 2& If Len(strText) Then CopyMemory lpData(countX + 2), ByVal StrPtr(strText), Len(strText) * 2 End If If (cbResource - (countX + 2 + CHECKLNG * 2)) > 0 Then CopyMemory lpData(countX + 2 + Len(strText) * 2), lpReadData(countX + 2 + CHECKLNG * 2), (cbResource - (countX + 2 + CHECKLNG * 2)) End If End If
FreeLibrary hModule
hResource = BeginUpdateResource(strResExePath, ByVal 0&)
ret = UpdateResource(hResource, 6&, CLng(iGroup), CInt(LangID), lpData(0), UBound(lpData) + 1)
ret = EndUpdateResource(hResource, ByVal 0&)
StringResourceModify = ret End Function
Private Function StringResourceList(strFileName As String, Optional LangID As Long = ENGLISH_US) As StringTable() Dim ret As Long Dim hResource As Long Dim x As Long Dim iGroup As Integer Dim hObject As Long Dim hResourceData As Long Dim lpResourceData As Long Dim cbResource As Long Dim hModule As Long Dim lpData() As Byte Dim lpReadData() As Byte Dim Out() As StringTable Dim countX As Long Dim CHECKLNG As Integer Dim temp As String
ReDim Out(0) hModule = LoadLibrary(strFileName) For iGroup = 1 To 4096 hObject = FindResourceEx(hModule, 6&, iGroup, LangID) hResourceData = LoadResource(hModule, hObject) lpResourceData = LockResource(hResourceData) cbResource = SizeofResource(hModule, hObject) If cbResource >= 31 And hObject <> 0 Then ReDim lpReadData(cbResource - 1) CopyMemory lpReadData(0), ByVal lpResourceData, cbResource
countX = 0 x = 0 Do While countX < UBound(lpReadData) CopyMemory CHECKLNG, lpReadData(countX), 2& countX = countX + 2 If CHECKLNG Then ReDim Preserve Out(UBound(Out) + 1) Out(UBound(Out)).id = (iGroup - 1) * 16& + x temp = Space(CHECKLNG * 2) CopyMemory ByVal StrPtr(temp), lpReadData(countX), CHECKLNG * 2 Out(UBound(Out)).Text = temp countX = countX + CHECKLNG * 2 End If x = x + 1 Loop End If Next FreeLibrary hModule StringResourceList = Out End Function
Private Function BitmapResourceModify(strResExePath As String, strFileToLoad As String, _ Optional nResType As Long = RT_BITMAP, Optional nID As Long = 101, Optional bDelete As Boolean = False) As Boolean
Dim nFile As Long Dim Data() As Byte Dim arrSize As Long Dim ret As Long Dim Handle As Long
nFile = FreeFile
Open strFileToLoad For Binary As nFile arrSize = LOF(nFile) - 14 ReDim Data(0 To arrSize - 1) Get nFile, 15, Data Close nFile
Handle = BeginUpdateResource(strResExePath, 0) If Handle = 0 Then Exit Function
If bDelete = False Then ret = UpdateResource(Handle, ByVal nResType, ByVal nID, 1033, Data(0), arrSize) Else ret = UpdateResource(Handle, ByVal nResType, ByVal nID, 1033, ByVal 0&, 0) End If
ret = EndUpdateResource(Handle, 0) BitmapResourceModify = ret End Function
Sub DisplayBitmapRes(hDcDisplay As Long, sResFile As String, ResId As Long) Const IMAGE_BITMAP = 0& Const LR_CREATEDIBSECTION = &H2000
Dim hDLL As Long Dim sID As String Dim sIdent102 As String Dim hMemDC As Long Dim lret As Long Dim hBmp As Long Dim oldhBmp As Long Dim picwid As Long, pichgt As Long Dim BMP As BITMAP
sID = "#" & ResId
hDLL = LoadLibrary(sResFile)
hBmp = LoadImage(hDLL, sID, IMAGE_BITMAP, _ 0, 0, LR_CREATEDIBSECTION)
GetObjectAPI hBmp, Len(BMP), BMP
picwid = BMP.bmWidth pichgt = BMP.bmHeight
hMemDC = CreateCompatibleDC(hDcDisplay) oldhBmp = SelectObject(hMemDC, hBmp) lret = BitBlt(hDcDisplay, 0, 0, picwid, pichgt, _ hMemDC, 0, 0, vbSrcCopy)
DeleteObject hBmp
SelectObject hMemDC, oldhBmp DeleteDC hMemDC DeleteObject hBmp FreeLibrary hDLL End Sub
Sub DisplayStringRes(sResFile As String, ResId As Long) Dim c As Integer, s As String, id As Long, hDLL As Long c = 1024 s = String(c, 0) hDLL = LoadLibrary(sResFile) c = LoadString(hDLL, ResId, s, c) MsgBox s FreeLibrary hDLL End Sub
Private Sub Command1_Click() Dim sResExeFilePath As String Dim sBMPFileToLoadPath As String
sResExeFilePath = App.Path & "\test.exe" sBMPFileToLoadPath = App.Path & "\bmp_to_load.bmp"
MsgBox BitmapResourceModify(sResExeFilePath, sBMPFileToLoadPath, RT_BITMAP, 101), , "BitmapResourceModify"
MsgBox StringResourceModify(sResExeFilePath, 101, "This is test1"), , "StringResourceModify" MsgBox StringResourceModify(sResExeFilePath, 102, "This is test2"), , "StringResourceModify" MsgBox StringResourceModify(sResExeFilePath, 103, "This is test3"), , "StringResourceModify"
Dim st() As StringTable, sMsg As String, i As Long st = StringResourceList(sResExeFilePath) For i = 0 To UBound(st) sMsg = sMsg & st(i).id & " : " & st(i).Text & vbCrLf Next MsgBox sMsg, , "List of string items"
DisplayBitmapRes Picture1.hdc, sResExeFilePath, 101 DisplayStringRes sResExeFilePath, 101 Me.Caption = "BMP Resource from : " & sResExeFilePath End Sub
Private Sub Form_Load() Command1.Caption = "Modify Resource Demo" End Sub |
|