Resource callbacks


Most of the real work of finding resources is done by UpdateResources. It looks like this:

Private Sub UpdateResources(ByVal hMod As Long)
‘ Turn on hourglass, turn off redrawing
HourGlass Me
Call LockWindowUpdate(lstResource.hWnd)
lstResource.Clear

Call EnumResourceTypes(hMod, AddressOf ResTypeProc, Me)

Call LockWindowUpdate(hNull)
HourGlass Me
End Sub

The EnumResourceTypes API function sets up a callback function named Res­TypeProc. As described in “Limits of Procedure Pointers,” page 99, the callback procedure must reside in a BAS module, although it would be a lot handier to put it in WINWATCH.FRM with the UpdateResources procedure. The callback procedure will need access to the form so that it can fill the Resources list box and inspect the Filter Resources check box. How can we make these controls available to the standard module containing the callback? Well, we could always use global variables, but there is a better way. Like most callback functions, EnumResourceTypes has an extra parameter for user-defined data. We can use that parameter to pass the whole form. Here’s how it works:

Function ResTypeProc(ByVal hModule As Long, ByVal lpszType As Long, _
frm As Form) As Long
ResTypeProc = True ‘ Always return True
If lpszType <= 65535 Then
‘ Enumerate resources by ID
Call EnumResourceNamesID(hModule, lpszType, _
AddressOf ResNameProc, frm)
Else
‘ Enumerate resources by string name
Call EnumResourceNamesStr(hModule, PointerToString(lpszType), _
AddressOf ResNameProc, frm)
End If
End Function

First look at that ByRef Form parameter. Windows expects that parameter to be a ByVal Long, but Long is the size of a pointer, so we can pass anything we want here as long as we pass it by reference. We couldn’t be so fast and loose with types if ResTypeProc were going to be called by Visual Basic, but it’s not. Windows is the only caller and as long as it gets 32 bits, it doesn’t care what they point to. Notice that the only thing the procedure does with the frm parameter is pass it on to EnumResourceNames (which sets up ResNameProc as a callback).


An unusual thing about ResTypeProc is how it handles the string pointer lpsz­Type. Actually, this isn’t all that unusual. Windows often overloads string pointers to represent integers in some situations. For reasons that need not concern us, pointers always have a non-zero high word. That means that Windows can use a high word of zero to signal that a pointer isn’t really a pointer. With resources, Windows uses the low word for standard resource types. There are constants with values of less than 65,536 for common resource types such as icons, bit­maps, and cursors. Unusual and user-defined types have string names. When you’re enumerating resource types, you don’t know whether you’re going to get a real string pointer or one of these constants. The Windows API type library provides EnumResourceNamesID and EnumResourceNamesStr as aliases for the EnumResourceNames API function so you can enumerate the resource names for constants or strings. Here’s the callback function:

Function ResNameProc(ByVal hModule As Long, ByVal lpszType As Long, _
ByVal lpszName As Long, frm As Form) As Long
Dim sType As String, sName As String
ResNameProc = True ‘ Always return True
If lpszName <= 65535 Then
sName = Format$(lpszName, “00000”)
Else
sName = PointerToString(lpszName)
End If
If lpszType <= 65535 Then
sType = ResourceIdToStr(lpszType)
Else
sType = PointerToString(lpszType)
End If
If frm.chkFilter = vbChecked Then
If Not ValidateResource(hModule, sName, sType) Then Exit Function
End If
frm.lstResource.AddItem sName & “ “ & sType
End Function

Once again, you have to check the values of lpszType and lpszName to see if they are strings or integers. Windows uses integers to represent standard resources such as the stop sign icon or the arrow cursor. In this case, if we get an integer ID for the resource name, we format it as a zero-aligned string so that it will line up correctly in the list box. If we get an integer ID for the type, we look up the type name with the ResourceIdToStr function, which is just a Select Case block that returns string names for the standard type names.