|
-
Oct 24th, 2017, 01:34 PM
#1
FindResource and the IDE
Okay, I need a function that'll just tell me whether a file is in my resources or not.
I'd prefer not to use LoadResData with error trapping because some of my resource files are somewhat large. Therefore, my first idea was to use the FindResource API call. However, this only works once the program is compiled.
So, what I'd like as a ResourceExists(sFileName As String, sResourceType As String) As Boolean function that works the same in the IDE as compiled.
I'm going to do it with error trapping (and LoadResData) for now, but I'd sure like a better solution.
Thanks In Advance,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Oct 24th, 2017, 01:42 PM
#2
Re: FindResource and the IDE
Elroy, you know better than post a question, as its own thread, in the codebank 
But I'll add this while you consider reporting your own thread to be moved...
You can't check a file name. The file name is not preserved when you add something to a resource file. In short, what you are asking is very possible doing a byte-comparison between the file contents and res file content. However, one needs to be pretty familiar with how stuff is added there. Icons/Cusors are separated into separate resources (per image in the file), bitmaps have the file header stripped off for example.
-
Oct 24th, 2017, 02:51 PM
#3
Re: FindResource and the IDE
LaVolpe's got it. I bet you can add the same file multiple times if you want even.
-
Oct 24th, 2017, 03:54 PM
#4
Re: FindResource and the IDE
 Originally Posted by LaVolpe
Elroy, you know better than post a question, as its own thread, in the codebank
But I'll add this while you consider reporting your own thread to be moved...
You can't check a file name. The file name is not preserved when you add something to a resource file. In short, what you are asking is very possible doing a byte-comparison between the file contents and res file content. However, one needs to be pretty familiar with how stuff is added there. Icons/Cusors are separated into separate resources (per image in the file), bitmaps have the file header stripped off for example.
Ahhh, I obviously wasn't clear with my original question.
Here's the scenario. First, everything I'm talking about is a "Custom" resource. They're just files that I'll be adding. It's an ongoing project that will evolve over time, with more files completed as the project progresses.
What I wanted to do was to complete all the coding for all the files (i.e., the "Custom resources) before these files were actually completed. Others are working on the actual files. They're just word documents or PDF files.
So ... if the file wasn't found in the VB6 project's resources, I just wanted to give the user a nice message saying that this one isn't yet done, rather than an ugly error. I suppose I could rewrite my routines that fetch these files to include a bit of error trapping, but it just seems that I should be able to "ask" if the file is in the VB6 project's resources without actually loading the file.
Here's the code I've got now:
Code:
Public Function ResourceFileExists(sFileName As String, sResourceType As String) As Boolean
Dim bb() As Byte
'
On Error GoTo DidntFindIt
bb = LoadResData(Replace$(sFileName, " ", ""), sResourceType) ' Returns a Byte array.
ResourceFileExists = True
DidntFindIt:
End Function
I just thought it would be nice if I didn't have to load the file to answer this question. I can do it with FindResource once the project is compiled, but I do a great deal of testing in the IDE (as I suspect we all do), so I need it to work the same in the IDE as compiled.
p.s. I notified the moderators.
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Oct 24th, 2017, 04:23 PM
#5
Re: FindResource and the IDE
I can whip something up since I've got VB resource file parsers on-hand. Since icons/cursors/bitmaps aren't in play, makes it easier. However, you are comparing byte-by-byte. That would mean that if you changed one character in your Word doc outside of the resource, it wouldn't find it inside the resource. Is that what you're after?
What I will whip up is for uncompiled resources only. Adding for compiled exe resources isn't that difficult since I have those routines also. But gotta ask, why would you do this after resource is compiled? I'd think you may want to check the compiled exe from outside of the exe to see if you have the desired resource(s), but run from within the exe?
-
Oct 24th, 2017, 05:25 PM
#6
Re: FindResource and the IDE
LaVolpe,
Noooo, wait. It's simpler than that. All I want to know is, "is this file name, under this resource custom type in my resource file (or not)?" My error trapping routine above works, but it's slow because it reads the file to get it done. There's got to be a way to do it without reading the file.
Thanks for Helping,
Elroy
EDIT1: I'll manage updates to the files in a separate way. I don't care if it's an old version of the file in my resources. I just want to know if any version is there, or not.
EDIT2:
 Originally Posted by LaVolpe
But gotta ask, why would you do this after resource is compiled?
Yeah, I know. It doesn't make a great deal of sense, but it's about managing the phases of this project. In many cases, I know what the names of the files are, but I don't have them yet. I just want to get it coded, and then have a VB6 program that extracts the resource "if it's in the internal resources". If it's not, it reports that fact to the user.
Last edited by Elroy; Oct 24th, 2017 at 05:29 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Oct 24th, 2017, 06:16 PM
#7
Re: FindResource and the IDE
I think I got you now, after looking closer at your code. It appears you are using the file name as the resource ID.
In the following, it will create a collection of resources (custom only) in this format: ResourceType:ResourceID. You can use the collection to test for any 'filename' you are looking for, with error testing:
Code:
On Error Resume Next
bExists = (m_ResItems.Item(sResourceType & ":" & Replace$(sFileName, " ", "")) <> "")
If Error Then Err.Clear
To test this out, add this code in a form
Code:
Option Explicit
Private Declare Function EnumResourceNames Lib "kernel32.dll" Alias "EnumResourceNamesW" (ByVal hModule As Long, ByVal lpType As Long, ByVal lpEnumFunc As Long, ByRef lParam As Any) As Long
Private Declare Function EnumResourceTypes Lib "kernel32.dll" Alias "EnumResourceTypesW" (ByVal hModule As Long, ByVal lpEnumFunc As Long, ByRef lParam As Any) As Long
Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long
Private Declare Function LoadLibraryEx Lib "kernel32.dll" Alias "LoadLibraryExW" (ByVal lpLibFileName As Long, ByVal hFile As Long, ByVal dwFlags As Long) As Long
Private Declare Function GetFileSize Lib "kernel32.dll" (ByVal hFile As Long, ByRef lpFileSizeHigh As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function ReadFile Lib "kernel32.dll" (ByVal hFile As Long, ByRef lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, ByRef lpNumberOfBytesRead As Long, Optional ByVal lpOverlapped As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32.dll" (ByVal hFile As Long, ByVal lDistanceToMove As Long, ByRef lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function GetFileAttributesW Lib "kernel32.dll" (ByVal lpFileName As Long) As Long
Private Const FILE_BEGIN As Long = 0
Private Const FILE_CURRENT As Long = 1
Private Const FILE_END As Long = 2
Private Const INVALID_HANDLE_VALUE = -1&
Private m_ResItems As Collection
Private Function pvParseResEXE(FileName As String) As Boolean
Dim hMod As Long, n As Long, t As Long
Dim cResType As Collection, cResItems As Collection
Const LOAD_LIBRARY_AS_DATAFILE As Long = &H2
Set m_ResItems = New Collection
If Not FileName = "" Then ' else querying itself
hMod = LoadLibraryEx(StrPtr(FileName), 0&, LOAD_LIBRARY_AS_DATAFILE)
If hMod = 0 Then Exit Function
End If
Set cResType = New Collection
EnumResourceTypes hMod, AddressOf EnumResourceTypesProc, cResType
If cResType.Count Then
For t = 1 To cResType.Count
Set cResItems = New Collection
EnumResourceNames hMod, StrPtr(cResType(t)), AddressOf EnumResNameProc, cResItems
For n = 1 To cResItems.Count
m_ResItems.Add cResType(t) & ":" & cResItems(n)
Next
Next
End If
If hMod Then FreeLibrary hMod
End Function
Private Function pvParseResFile(hFile As Long) As Boolean
Dim lOffset As Long, lHdrSize As Long
Dim lDataSize As Long, iChar As Long
Dim vType As Variant, vName As Variant
Dim lEOF As Long, c As Long, aData() As Byte
Dim lRead As Long, sData As String
Set m_ResItems = New Collection
lEOF = GetFileSize(hFile, lRead)
If lRead > 0& Then Exit Function ' not prepared to handle 2+ GB resource files
SetFilePointer hFile, 0&, 0&, FILE_BEGIN
Do
ReadFile hFile, lDataSize, 4&, lRead
If lRead < 4& Then
' allow DWORD misaligned final resource; can be fixed when re-writing
If lEOF < lOffset Then Exit Do
Exit Function
End If ' FYI: lHdrSize should already be DWord sized (we'll allow this to be wrong)
ReadFile hFile, lHdrSize, 4&, lRead: If lRead < 4& Then Exit Function
If lHdrSize < ((lHdrSize + 3&) And Not 3&) Then lHdrSize = ((lHdrSize + 3&) And Not 3&)
If lHdrSize < 32 Or lDataSize < 0& Then Exit Function
If lOffset + lHdrSize + ((lDataSize + 3&) And Not 3&) > lEOF Then Exit Function
' get the resource type
ReadFile hFile, iChar, 2&, lRead: If lRead < 2& Then Exit Function
If iChar = &HFFFF& Then
ReadFile hFile, iChar, 2&, lRead: If lRead < 2& Then Exit Function
vType = iChar
Else
sData = vbNullString
For c = 2& To lHdrSize - 28& Step 2&
sData = sData & ChrW$(iChar)
ReadFile hFile, iChar, 2&, lRead: If lRead < 2& Then Exit Function
If iChar = 0 Then Exit For
Next
vType = sData
End If
If VarType(vType) = vbString Then
' get the resource name
ReadFile hFile, iChar, 2&, lRead: If lRead < 2& Then Exit Function
If iChar = &HFFFF& Then
ReadFile hFile, iChar, 2&, lRead: If lRead < 2& Then Exit Function
vName = iChar
Else
sData = vbNullString
c = SetFilePointer(hFile, 0&, 0&, FILE_CURRENT)
For c = 2& To (lOffset + lHdrSize) - c - 14& Step 2&
sData = sData & ChrW$(iChar)
ReadFile hFile, iChar, 2&, lRead: If lRead < 2& Then Exit Function
If iChar = 0& Then Exit For
Next
vName = sData
m_ResItems.Add vType & ":" & vName
End If
End If
lOffset = lOffset + lHdrSize + ((lDataSize + 3&) And Not 3&)
If lOffset >= lEOF Then Exit Do
SetFilePointer hFile, lOffset - SetFilePointer(hFile, 0&, 0&, FILE_CURRENT), 0&, FILE_CURRENT
Loop
ExitRoutine:
End Function
Private Function pvOpenFile(ByVal FileName As String) As Long
' Function uses APIs to read/create files with unicode support
Const GENERIC_READ As Long = &H80000000
Const OPEN_EXISTING = &H3
Const FILE_SHARE_READ = &H1
Const GENERIC_WRITE As Long = &H40000000
Const FILE_SHARE_WRITE As Long = &H2
Const CREATE_ALWAYS As Long = 2
Const FILE_ATTRIBUTE_ARCHIVE As Long = &H20
Const FILE_ATTRIBUTE_HIDDEN As Long = &H2
Const FILE_ATTRIBUTE_READONLY As Long = &H1
Const FILE_ATTRIBUTE_SYSTEM As Long = &H4
Const FILE_ATTRIBUTE_NORMAL = &H80&
Dim Flags As Long, Access As Long
Dim Disposition As Long, Share As Long
Access = GENERIC_READ Or GENERIC_WRITE
If Not ((GetFileAttributesW(StrPtr(FileName)) = INVALID_HANDLE_VALUE)) Then
Disposition = OPEN_EXISTING
Flags = FILE_ATTRIBUTE_ARCHIVE Or FILE_ATTRIBUTE_HIDDEN Or FILE_ATTRIBUTE_NORMAL _
Or FILE_ATTRIBUTE_READONLY Or FILE_ATTRIBUTE_SYSTEM
pvOpenFile = CreateFileW(StrPtr(FileName), Access, Share, ByVal 0&, Disposition, Flags, 0&)
End If
End Function
Add this in a module
Code:
Option Explicit
Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal length As Long)
Public Function EnumResNameProc(ByVal hModule As Long, ByVal lpszType As Long, _
ByVal lpszName As Long, ByRef lParam As Collection) As Long
Dim lLen As Long, sID As String
If (lpszName And &HFFFF0000) = 0& Then
lParam.Add "#" & lpszName
EnumResNameProc = 1&
Else
lLen = lstrlenW(lpszName)
If lLen Then
sID = Space$(lLen)
CopyMemory ByVal StrPtr(sID), ByVal lpszName, lLen + lLen
lParam.Add sID
EnumResNameProc = 1&
End If
End If
End Function
Public Function EnumResourceTypesProc(ByVal hModule As Long, ByVal lpszType As Long, ByRef lParam As Collection) As Long
Dim lLen As Long, sID As String
If (lpszType And &HFFFF0000) = 0& Then
'lParam.Add "#" & lpszType ' << do nothing; want "custom" types; therefore, string types
EnumResourceTypesProc = 1&
Else
lLen = lstrlenW(lpszType)
If lLen Then
sID = Space$(lLen)
CopyMemory ByVal StrPtr(sID), ByVal lpszType, lLen + lLen
lParam.Add sID
EnumResourceTypesProc = 1&
End If
End If
End Function
I've included a pvOpenFile routine that is unicode compatible; though that may not matter to you. However, included APIs must be used to read the file due to the code I provided.
If testing a resource file, call pvOpenFile passing it the resource path/file to retrieve a handle. Pass that handle to the pvParseResFile routine and close the handle after return. The m_resItems collection will be filled or have a count of zero
Code:
Dim hFile As Long
hFile = pvOpenFile(yourResourceFileName)
If hFile = 0 Or hFile = INVALID_HANDLE_VALUE Then
' error - file may be locked or bad file name passed. deal with it
Else
pvParseResFile hFile
CloseHandle hFile
End If
' now you can test any/all your file names against the collection
If testing a compiled executable, pass the executable file name or empty string if wanting to test against itself to pvParseResEXE
Code:
pvParseResEXE "" ' to test its own compiled resources
' now you can test any/all your file names against the collection
Edited: I tweaked the two parsing routines to only look for custom resources. Custom resources are defined as those having string names, i.e., "Custom". Non-custom are those with integer names, i.e., RT_BITMAP (Bitmaps in VB's resource editor).
Last but not least, an alternate way of comparing compiled resources of the exe itself would be to use FindResource API. This would negate the module and pvParseResEXE routine. The module is only needed if you want a collection to test against.
Code:
Private Declare Function FindResource Lib "kernel32.dll" Alias "FindResourceW" (ByVal hInstance As Long, ByVal lpName As Long, ByVal lpType As Long) As Long
example: bExists = (FindResource(0&, StrPtr(resourceType), StrPtr(fileName)) <> 0)
Offhand, I cannot see a simple way of replacing LoadResData() with a similar/quicker method while in the IDE. Parsing a resource file isn't ideal from within the project because you'd have to hard code its path/filename. Only other quickie I can think of is to create your own custom resource that only contains the files you've included, kinda like an index. Then you could use LoadResData() on it and extract the filenames. At least that resource should be relatively small in size. The code I posted above could be ideal for querying the resource file and compiled executable from outside -- as a quality control measure. Don't worry about the effort of putting together the sample code, just had to cobble it together from existing projects.
Last edited by LaVolpe; Oct 24th, 2017 at 07:14 PM.
-
Oct 25th, 2017, 12:50 PM
#8
Re: FindResource and the IDE
LaVolpe,
Thank you sooo much. I'm a bit distracted with other stuff, but I'll report back on how this goes as soon as I have a chance.
Again, Thank You!
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|