I Feel Your Pain

In an August 14th blog posting, Lisa Slater Nicholls bemoans the fact that the Vista Toolkit portion of Sedna doesn’t include any Vista User Account Control stuff. To that I say, “I couldn’t agree with you more Lisa!”. Now, as many of you know I am the developer that has been doing the work for Microsoft on the Vista Toolkit. While I would like you to try and remember that I didn’t set the priorities for the Vista Toolkit or make the final decisions about what was in or out of the toolkit, I do feel Lisa’s pain and the pain of any of the rest of you out there left wanting.

One Developer CAN Make a Difference
a community CAN make a future

I’ve said that the Community can move Visual FoxPro forward themselves. Without the core source? Yes, even without the source to Visual FoxPro’s runtime libraries or IDE. Microsoft or no Microsoft, I personally want to provide the Visual FoxPro community with some crazy-super-cool Vista bits. So, let’s start with this blog entry and maybe a few more right after it and see what we can do to open a few eyes to the possibilities and meet some of the needs that the Fox Community is going to have moving forward…

Whats in the Next Few Blog Entries?

  • A few things about the structure of a Visual FoxPro EXE or DLL.
  • How you can swap out the manifest in a Visual FoxPro EXE or DLL to facilitate Elevated Permissions
  • How you can swap out the manifest in a Visual FoxPro EXE or DLL to facilitate Reg-Free COM.
  • How to add or replace strings in the String Table resource of a Visual FoxPro EXE or DLL and why you would need or want to do that.
  • How to register VFP COM servers that require elevated permissions properly so that their OLEPublic classes can be instantiated from applications running under a Limited User Account (LUA).
  • How to instantiate a COM object that requires elevated permissions using the new COM Elevation Moniker, CoGetObject, and Sys(3096).

First, a Special Thanks

Before I get started on the meat of this blog entry and the others, I’d like to once again thank Bo Durban of Moxie Data, Inc. who popped in from time to time to help me out with some of what I’m going to show. He gets about as much sleep as I do and deserves a hearty thank you. That having been said, let’s start taking a look at some crazy-super-cool stuff.

Portable Executable Structure

I won’t be delving into this too deeply at this time. I’ll leave that for a future blog entry. For now, it’s important to know that the structure of a Portable Executable (PE) file plays a major part in what I’m doing and more importantly what I am able to do with Visual FoxPro executables. Understanding the PE format gives you an immense amount of power and allows you to do things you wouldn’t otherwise be able to do as a developer. The the PE format is used for all kinds of files. EXE, DLL, OCX, OBJ, SYS, and even control panel applets. They’re all in PE format and thus if you know how to read one of them, you know how to read them all.

While you might not think so, the structure of a PE file is laid out in a pretty straight-forward manner. Starting from the front of the PE you’ll run into a bunch of bytes constituting the DOS header (stub) and that will point you to the start of the executable code/NT_HEADER, which is followed by IMAGE_FILE_HEADER, then IMAGE_OPTIONAL_HEADER, IMAGE_OPTIONAL_HEADER_NT, etc. ad nauseum. In any event, if you dig far enough you’ll come to the IMAGE_SECTION_HEADER which provides base addresses and offsets so you can find some of the really cool stuff inside a PE file. This cool stuff would consist of things like the Imports, Exports, and Resources (Icons, String Tables, and Configurations) of the PE file.

All of these different pieces, and many I didn’t mention, inside of a PE file are either one right after the other in memory, or they are structures that contain members that show you where to find them, or they are relative to one another in some way. So, if you want something from a PE file you simply find its location and size then simply read it.

Poking Around in the PE

If you want to go poking around in a PE file to get a better idea of what I’m talking about, download a something like Resource Hacker and/or CFF Explorer. I’m intentionally leaving a lot of detail out of this blog entry, because for the most part we won’t need to read or manipulate our applications and DLLs using Low-level file functions. This is because thankfully Microsoft has provided a number of API calls that we can use to do this in an easier softer way. Well, you may call me a liar after you see some of the code in my next few blog entries. Perhaps “in a more elegant way” would have been a more accurate way to put it.

For those of you wanting to start poking around in a PE file using Visual FoxPro and Low-Level File Functions, run the code provided below. Simply run the prg and select a PE file (EXE, DLL, etc.). Look at the aPEFile array in the debug Locals window when you hit the Set Step On above the call to FClose, or step through the code byte by byte as it runs. Also, I haven’t provide a plethora of comments, but there should be enough structure names there that you can do some internet searches if you want further information on what it is and what it does.

Swapping Visual FoxPro’s Application Manifest

For any of you that have looked at a Visual FoxPro application in Notepad, you may have noticed that there is some XML near the end of it. That XML is actually an application manifest. Now, this manifest can be used to do some really cool things, and I’ll show some of them in my next blog entry.

Stepping through part of a PE file in Visual FoxPro
for informational purposes only

PUBLIC lnVirtualResourceAddress
LOCAL i
SET ESCAPE ON
m.lcFile = GETFILE()
IF FILE(m.lcFile)
    m.hFile = FOPEN(m.lcFile,0)
    m.lnFileSize = FSEEK(m.hFile, 0, 2)
    =FSEEK(m.hFile, 0, 0)
    
    DIMENSION aPEFile(5000)

    *!* Get DOS Header IMAGE_DOS_HEADER
    m.lnElementCounter = 1
    FOR i = 1 TO 30 skip 2
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
        m.lnElementCounter = m.lnElementCounter + 1
    ENDFOR
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && e_lfanew Pointer to where Windows executable starts
    
    *!* Goto NT Header
    =FSEEK(m.hFile, aPEFile(m.lnElementCounter), 0)
    
    *!* PE File Signature
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && IMAGE_NT_SIGNATURE from ImageSignatureTypes enum
    
    *!* IMAGE_FILE_HEADER
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    
    *!* IMAGE_OPTIONAL_HEADER
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,1),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,1),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    
    *!* IMAGE_OPTIONAL_HEADER_NT
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”)

    *!* IMAGE_DATA_DIRECTORY
     *!* IMAGE_DIRECTORY_ENTRY_EXPORT
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_IMPORT
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_RESOURCE
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_EXCEPTION
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_SECURITY
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_BASERELOC
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_DEBUG
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_ARCHITECTURE
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_GLOBALPTR
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_TLS
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_IAT
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* IMAGE_DIRECTORY_ENTRY_DOTNET_METADATA
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
     *!* ???
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
        
        *!* 248

        *!* IMAGE_SECTION_HEADER
        FOR i = 1 TO 8
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = FREAD(m.hFile,8) && Name
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Virtual Address
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Raw Size
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Raw Address
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Reloc Address
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Linenumbers
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”) && Relocations Number
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”) && Linenumbers Number
            m.lnElementCounter = m.lnElementCounter + 1
            aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Characteristics
        ENDFOR
        
        *!* 568
        m.lnFilePointer = 568
        m.lnVirtualResourceAddress = aPEFile(174)
        m.InitialResourceDirectoryAddress = aPEFile(176)
        GetImageResourceDirectory(m.InitialResourceDirectoryAddress, m.InitialResourceDirectoryAddress, hFile, m.lnFilePointer, @aPEFile, @lnElementCounter)                    
        SET STEP ON
        ?FCLOSE(m.hfile)
ENDIF

*************************
FUNCTION GetImageResourceDirectory(lnLocation, lnInitialLocation, hFile, lnFilePointer, aPEFile, lnElementCounter)
*************************
    *!* Private Type IMAGE_RESOURCE_DIRECTORY
    *!*     Characteristics As Long ‘\\Seems to be always zero?
    *!*     TimeDateStamp As Long
    *!*     MajorVersion As Integer
    *!*     MinorVersion As Integer
    *!*     NumberOfNamedEntries As Integer
    *!*     NumberOfIdEntries As Integer
    *!*    End Type
    m.lnFilePointer = lnLocation
    =FSEEK(m.hFile, m.lnFilePointer, 0)
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Characteristics
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && TimeDateStamp
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”) && MajorVersion
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”) && MinorVersion
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”) && NumberOfNamedEntries
    m.lnElementCounter = m.lnElementCounter + 1
    aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,2),“RS”) && NumberOfIdEntries
    m.lnFilePointer = m.lnFilePointer + 16
    GetImageResourceDirectoryEntries(aPEFile(m.lnElementCounter), m.lnInitialLocation, hFile, lnFilePointer, @aPEFile, @lnElementCounter)
ENDFUNC

*************************
FUNCTION GetImageResourceDirectoryEntries(lnNumberofEntries, lnInitialLocation, hFile, lnFilePointer, aPEFile, lnElementCounter)
*************************
    *!*    Private Type IMAGE_RESOURCE_DIRECTORY_ENTRY
    *!*     dwName As Long
    *!*     dwDataOffset As Long
    *!*     CodePage As Long
    *!*     Reserved As Long
    *!*    End Type
    LOCAL llHighBitSet, lnOffset, lcOffset, lnLocation, i
    FOR i = 1 TO lnNumberofEntries && NumberOfIdEntries
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && dwName (ID)
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && dwDataOffset
        m.lnFilePointer = m.lnFilePointer + 8
        m.lnOffset = aPEFile(m.lnElementCounter) + 2147483648
        m.llHighBitSet = m.lnOffset <= 2147483648
        IF m.llHighBitSet && Points to another Resource Directory
            m.lnLocation = m.lnOffset + m.lnInitialLocation
            GetImageResourceDirectory(m.lnLocation, m.lnInitialLocation, m.hFile, m.lnFilePointer, @aPEFile, @lnElementCounter)
        ELSE && Points to the data
            *!* dwName is offset, dwDataOffset is size
            m.lnLocation = aPEFile(m.lnElementCounter) + m.lnInitialLocation
            GetResourceDataEntry(m.lnLocation, aPEFile(m.lnElementCounter-2), m.hFile, m.lnInitialLocation, @aPEFile, @lnElementCounter)
        ENDIF
        =FSEEK(m.hFile, m.lnFilePointer, 0)
    ENDFOR
ENDFUNC

*************************
FUNCTION GetResourceDataEntry(lnLocation, lnSize, hFile, lnInitialLocation, aPEFile, lnElementCounter)
*************************
    IF lnLocation > 0 AND lnSize > 0
        =FSEEK(m.hFile, lnLocation, 0)
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && OffSet
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Size
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && CodePage
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = CTOBIN(FREAD(m.hFile,4),“RS”) && Reserved
        GetResourceData((aPEFile(m.lnElementCounter-3) m.lnVirtualResourceAddress) + lnInitialLocation, aPEFile(m.lnElementCounter-2), m.hFile, @aPEFile, @lnElementCounter)
    ELSE
        SET STEP ON
    ENDIF
ENDFUNC

*************************
FUNCTION GetResourceData(lnLocation, lnSize, hFile, aPEFile, lnElementCounter)
*************************
    IF lnLocation > 0 AND lnSize > 0
        =FSEEK(m.hFile, m.lnLocation, 0)
        m.lnElementCounter = m.lnElementCounter + 1
        aPEFile(m.lnElementCounter) = FREAD(m.hFile, m.lnSize)
    ELSE
        SET STEP ON
    ENDIF
ENDFUNC