Last Time

In part 1 of this series I covered generally what I am going to show and also gave some brief information regarding the PE format. At the end I provided some code that would allow a Visual FoxPro developer to step through some of the PE structures and data. In this blog entry I’m going to show you how you can edit the application manifest in a PE File and how that can facilitate elevated execution permissions in Vista UAC and Reg-Free COM.

Application Manifests in Visual FoxPro

Some time ago the Visual FoxPro Team started adding configuration information as a resource to Visual FoxPro DLLs and EXEs. This configuration information is actually an Application Manifest. Let’s take a quick look at one. Run the following code and when prompted select one of you VFP EXEs or DLLs.

Declare Long LoadLibrary In WIN32API String
Declare Long FindResource In WIN32API Long, Long, Long
Declare Long LoadResource In WIN32API Long, Long
Declare Long SizeofResource In WIN32API Long, Long
Declare Long FreeLibrary In WIN32API Long
Declare Long FreeResource In WIN32API Long

Local lcModule, hModule
m.lcModule = Getfile(“EXE|DLL”)
m.hModule = LoadLibrary(m.lcModule)
m.lnRsrc = FindResource(m.hModule, 1, 24)
m.lnMem = LoadResource(m.hModule, m.lnRsrc)
m.lnSize = SizeofResource(m.hModule, m.lnRsrc)
m.lcManifest = Sys(2600, m.lnMem, m.lnSize)
FreeResource(m.lnMem)
FreeLibrary(m.hModule)
?m.lcManifest

You should be seeing something similar to the following…

<?xml version=“1.0” encoding=“UTF-8” standalone=“yes”?>
<assembly xmlns=“urn:schemas-microsoft-com:asm.v1” manifestVersion=“1.0”>
<assemblyIdentity
    version=“1.0.0.0”
    type=“win32”
    name=“Microsoft.VisualFoxPro”
    processorArchitecture=“x86”
/>
<description>Visual FoxPro</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=“win32”
name=“Microsoft.Windows.Common-Controls”
version=“6.0.0.0”
language=“*”
processorArchitecture=“x86”
publicKeyToken=“6595b64144ccf1df”
/>
</dependentAssembly>
</dependency>
</assembly>

Visual FoxPro 9.0 SP2

Now, for those of you that selected an EXE or DLL that was built with VFP9 SP2 you will note that your manifest xml is not exactly the same as what I posted above. In VFP9 SP2 the manifest has an additional trustinfo node that is similar to…

<trustInfo xmlns=“urn:schemas-microsoft-com:asm.v2”>
<security>
<requestedPrivileges>
<requestedExecutionLevel
level=“asInvoker”
uiAccess=“false”/>
</requestedPrivileges>
</security>

This trustinfo node is used for security related stuff, and it effects the way User Account Control on Vista reacts to the start of your application and what it allows your application to do when it is runnning. Note the requestedExecutionLevel node and more specifically the level attribute that it contains. “asInvoker” means to basically run your application with the least priveleges possible (it was actually called leastPrivelege at one time if I remember right). So, unless your users right-click on your application and choose Run as Administrator, or they create a shortcut to your application that tells Vista to always run your application as Administrator, then your application is going to be very limited in what it can do on Vista. That’s actually a good thing, but more on that in a moment. For the most part this is viewed as a royal pain in the butt by most and one of the chief complaints that developers and users alike have about Vista.

Can We Change the Application Manifest?

So, surely Microsoft provided Visual FoxPro’s Project Info or Project Build dialogs with a way to change this execution level setting, right? Wrong. As of the last SP2 Beta I saw there was nothing for this. Now, that’s not necessarily because they weren’t looking out for us (although I agree that it would have been nice to have). Probably the reason they didn’t do it is because Microsoft has decreed that Certified for Windows Vista requires that applications are marked asInvoker. Should you need to accomplish tasks that require a higher privelege level, then you would do so by requesting those priveleges as needed – such as instantiating a class with elevated permissions from a VFP COM server you’ve created. If you want more details on what all of the requirements are for UAC you can download a Word document provided by Microsoft.

OK, so does this mean that we can’t elevate the execution level by changing the Application Manifest? No. You can do it, it is just that it isn’t the way Microsoft wants you to do it and you won’t have a prayer of getting one of those nice Vista Logos for your application if you care about that sort of thing. Well, not a prayer unless you are one of the lucky few that are provided with a special waiver by Microsoft because you’ve been able to argue your case for doing it well enough. I digress…

If you want to change the level then you have two other choices: highestAvailable and requireAdministrator. Now, the problem is that even if you know these there is currently no way in Visual FoxPro natively to change these. Let’s look at a solution that was first explored by Calvin Hsia in one of his blog entries.

Changing the Application Manifest

If you read Calvin’s blog you would have noted that among other things he is basically using the UpdateResource API call and doing some weird machinations with the end of the Visual FoxPro application. The UpdateResource API does exactly what its name implies. It updates a resource inside a PE file, and since the application manifest is a resource inside of a Visual FoxPro 9.0 application it can be used to update the application manifest. So what’s with that stuff Calvin is doing where he goes to the end of the file and strips off a couple 14 byte structures? Well, near as Bo Durban and I can make out those two structures at the end of a Visual FoxPro PE file are used to point to a compiled VFP application and some typelib/registration information within the executable. You might have heard at one time or another that a VFP PE is different from a normal PE in that it is really just an executable that uses the VFP runtimes to run an internal FXP or APP. There seems to be some truth to that given Calvin’s code. In any event, he strips out that VFP proprietary stuff to protect it, then when UpdateResource is done doing its thing he puts them back (though I believe he makes a tiny mistake and puts the stuff back in there backwards). Pretty slick huh?

So, by using the technique that Calvin outlined we can replace the Application Manifest inside a Visual FoxPro application and thus change the way our application behaves under UAC in Vista. This technique works for VFP9 SP1 as well as SP2, and for that matter it would work for previous versions of Visual FoxPro as well, but it’s best if you just upgrade to VFP9 and come along with the rest of us for the ride.

Above: Screenshot of a Visual FoxPro Executable on Vista Ultimate that is marked with the default “asInvoker” execution level.

Above: Screenshot of a Visual FoxPro Executable on Vista Ultimate that is marked with the “requireAdministrator” execution level by modifying the application manifest that the exe contains. Note the “shield overlay” that Vista places on the exe’s icon to denote that it requires admin priveleges.

Above: Automatic dialog that Vista Ultimate displays when the exe is marked with the “requireAdministrator” execution level. The user no longer needs to right-click on the exe in order to Run as Administrator. The elevated permissions are requested as soon as the exe is executed.

Reg-Free COM

In addition to changing the execution level of your application for Vista UAC, you can also use this technique to facilitate Reg-Free COM in Windows XP and Vista. Reg-Free COM is designed to alleviate DLL-Hell, specifically versioning and side-by-side execution. Also, if you are going to be using ClickOnce deployment in the future it helps since ClickOnce doesn’t register COM components. Speaking of which, I’m giving a session on ClickOnce at Southwest Fox this year. I’d be more than happy to spend some time with anyone who attends going over this stuff in more detail (blog doesn’t always do it justice), so if you’re going to be free come October 18th, jump on over and register for the conference and I’ll see you there. OK, as I was saying before I shamelessly plugged the super-awesome-not-to-be-missed Southwest Fox conference, we can actually use the application manifest to house the information in our Visual FoxPro applications that would usually need to be written into the registry by registering our COM components with regsvr32. What does this look like? Well, just like the security stuff was facilitated by adding a “trustInfo” node, the Reg-Free COM stuff is made possible by adding a “file” node. Let’s see what it looks like…

<file name=“regfree.dll”>
        <comClass description=“regfree.adder”
clsid=“{99A4ADD8-5A6A-40C1-89FF-50B5E8267B69}”
progid=“regfree.adder”
threadingModel=“Both”/>
</file>

As you can see from the XML above there is a name attribute of the file node, in this case its value is “regfree.dll”, then there is a nested (child) node called comClass where the description, clsid, progid, and threadingModel information for the OLEPublic class is contained. That’s all there is to it. So, when this is added with the previous security modifications you should get an application manifest that looks somewhat like the following…

<?xml version=“1.0” standalone=“yes”?>
<assembly xmlns=“urn:schemas-microsoft-com:asm.v1” manifestVersion=“1.0”>
    <assemblyIdentity version=“1.0.0.0” type=“win32” name=“Microsoft.VisualFoxPro” processorArchitecture=“x86”/>
    <description>Visual FoxPro</description>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type=“win32” name=“Microsoft.Windows.Common-Controls” version=“6.0.0.0” language=“*” processorArchitecture=“x86” publicKeyToken=“6595b64144ccf1df”/>
        </dependentAssembly>
    </dependency>
    <trustInfo xmlns=“urn:schemas-microsoft-com:asm.v3”>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level=“requireAdministrator” uiAccess=“false”/>
            </requestedPrivileges>
        </security>
    </trustInfo><file name=“regfreedll.dll”>
        <comClass description=“regfreedll.adder” clsid=“{99A4ADD8-5A6A-40C1-89FF-50B5E8267B69}” progid=“regfreedll.adder” threadingModel=“Both”/></file></assembly>

A little on the messy side, but you get the idea and I save a little time not having to format that XML that I pulled from one of my VFP Exes. Now, with that application manifest in place, I wouldn’t have to register the regfree.dll on any XP or Vista systems. Only if, god forbid, the user was running Windows 2000 or Windows 98 would my dll need to be registered. Regfree.dll only contains one class, the Adder class, but if it contained more then I would put in additional comClass nodes for each of the classes that I was using in my Exe, and if I have more than one DLL or OCX then I would add more file nodes. So there you have Reg-Free COM.

An Application Manifest Modifying Class

Since we don’t have a way to do all of this natively in Visual FoxPro, I thought I would create a class to handle this for us. Presumably the class could be expanded on and could be used in conjunction with project hooks and a slick interface to really give us a professional way of handling this within the Visual FoxPro IDE. Also, this class will work on other PE Files, it has some VFP specific code in it, but that will only execute if the PE file you are modifying was compiled with VFP. For instance, you could run it against the vfp9.exe to make it always run in administrator mode while developing on Vista… that was just a for instance, it is completely against the EULA to do something like that, so please don’t do that. I’m just saying, if it wasn’t against the EULA, you could do something like that. 🙂

Next Up

In my next blog entry I’ll take you through how to manipulate the String Table resource inside a PE File using Visual FoxPro and also show you how you can use the new COM Elevation Moniker and CoGetObject to facilitate instancing classes that require elevated permissions from a Visual FoxPro application running asInvoker. This will allow Visual FoxPro developers to run applications as they should on Windows Vista and only elevate the permissions as needed. More Vista compatibility and power to the developer.

Heads Up

Soon I’m going to debut a mini-filter I wrote in C and a user mode DLL that I wrote in C++ that will allow Visual FoxPro developers to fully encrypt and decrypt Visual FoxPro data on the fly. This will be somewhat like Cryptor does it, however I believe it to be more secure and, of course, it will be free. After that we’ll take a look at rendering ActiveX controls, like MSChart directly to a report using an FLL I wrote with Bo Durban that one of my clients is being nice enough to let me share, and then we’ll do some other crazy stuff. But for right now I am going to go nuts for awhile on Vista stuff. We’ve got a lot of other stuff to cover in this UAC stuff before I’ve shown you all the pieces, how they connect, and the code we need to use them. OK, I’ll shut-up now and give you the code to the Application Manifest modifier class (just cut-n-paste the code into a prg, run it, and go select a VFP application you want to modify the manifest on – if you are going to be adding a COM server then be sure to change the path and name in the call to AddCOMModule method to your actual file).

Application Manifest Modification Class And Sample Code

*!* Sample Usage
LOCAL loModuleResourceEditor
m.loModuleResourceEditor = CREATEOBJECT(“ModuleResourceEditor”)
m.loModuleResourceEditor.Module = GETFILE(“EXE|DLL|OCX|CPL”)
m.loModuleResourceEditor.ExecutionLevel = 3 && Require Administrator
?m.loModuleResourceEditor.AddCOMModule(“D:\Documents and Settings\Craig Boyd\Desktop\RegFreeComTest\regfreedll.dll”)
?m.loModuleResourceEditor.CreateNewModuleManifest()
?m.loModuleResourceEditor.ApplyNewManifest()
?m.loModuleResourceEditor.AddStringToStringTable(“RegFreeDll Adder”,101)

DEFINE CLASS ModuleResourceEditor AS CUSTOM
    DIMENSION aCOMFiles(1) && array holding the COM modules to be added to manifest for REG-Free COM
    COMFileCount = 0 && Used internally to dimension aCOMFiles
    ExecutionLevel = 1 && asInvoker default
    Module = “” && EXE/OCX/DLL/etc. that will have its manifest modified
    Manifest = “” && New manifest
    DefaultManifest = “” && Manifest to be used if a manifest is not found in module
    LastError = 0 && Error number returned from GetLastError()
    
*******************************
    FUNCTION Init()
*******************************
    this.aCOMFiles(1) = .F.
ENDFUNC

*******************************
    FUNCTION AddCOMModule(tcCOMModuleName)
*******************************
        IF FILE(m.tcCOMModuleName)
            this.COMFileCount = this.COMFileCount + 1
            DIMENSION this.aCOMFiles(this.COMFileCount)
            this.aCOMFiles(this.COMFileCount) = m.tcCOMModuleName
            =ASORT(This.aCOMFiles,1,-1,0,1)
            RETURN .T.
        ELSE
            RETURN .F.
        ENDIF
    ENDFUNC

*******************************
    FUNCTION RemoveCOMModule(tcCOMModuleName)
*******************************
        LOCAL lnFoundAt
        m.lnFoundAt = ASCAN(this.aCOMFiles, m.tcCOMModuleName)
        IF m.lnFoundAt > 0
            this.aCOMFiles(m.lnFoundAt) = .F.
        ENDIF
    ENDFUNC

*******************************
    FUNCTION ApplyNewManifest()
*******************************
        #DEFINE RT_MANIFEST 24
        LOCAL hFile, lnPosition, lnSize, hModule, lcStruct, lnCounter, ;
        llVFPModule, lnResult, llReturn, lcManifestXML
        LOCAL ARRAY laVFPSections[2]
        m.llReturn = .F.
        IF !FILE(This.Module)
            *!* The Module property does not appear to be a file or has not been set.
            RETURN m.llReturn
        endif
        IF Empty(This.Manifest)
            *!* The Manifest property does not appear to have been set.
            RETURN m.llReturn
        endif
SET STEP ON
        DECLARE INTEGER BeginUpdateResource IN WIN32API STRING, INTEGER
        DECLARE INTEGER EndUpdateResource IN WIN32API INTEGER, INTEGER
        DECLARE INTEGER UpdateResource IN WIN32API INTEGER, INTEGER, INTEGER, INTEGER, STRING @, INTEGER
        DECLARE INTEGER GetLastError IN WIN32API

        m.llVFPModule = .T. && Assume that the module is a VFPModule
*!* Preserve APP stub and Typelib/Registration info
        m.hFile = FOPEN(This.Module)
        m.lnPosition = FSEEK(m.hFile,0,2) && Go EOF and Get Size
        FOR m.lnCounter = 1 TO 2
            =FSEEK(m.hFile, m.lnPosition 14, 0)
            m.lcStruct = FREAD(m.hFile, 14)
            m.lnSize = CTOBIN(SUBSTR(m.lcStruct, 11, 4), “4sr”) && last 4 bytes holds the size
            IF m.lnSize > m.lnPosition OR m.lnSize < 0 OR (m.lnCounter = 1 AND m.lnSize = 0)
                m.llVFPModule = .F.
                EXIT
            ENDIF
            IF m.lnSize != 0
                =FSEEK(m.hFile, m.lnPosition m.lnSize, 0)
                m.laVFPSections[m.lnCounter] = FREAD(m.hFile, m.lnSize)
                IF m.lnCounter = 1 AND LEFT(m.laVFPSections[1],3) != 0hFEF2FF
                    m.llVFPModule = .F.
                    EXIT
                ENDIF
            ELSE
                m.laVFPSections[m.lnCounter] = m.lcStruct
            ENDIF
            m.lnPosition = m.lnPosition m.lnSize
        ENDFOR
        =FCLOSE(m.hFile)

*!* Update Manifest in module
        m.hModule = BeginUpdateResource(This.Module, 0)
        m.lcManifestXML = This.Manifest
        m.lnResult = UpdateResource(m.hModule, RT_MANIFEST, 1, 1033, @m.lcManifestXML, LEN(m.lcManifestXML))
        IF EndUpdateResource(m.hModule, 0) = 0
            this.LastError = TRANSFORM(GetLastError())
        ELSE
            m.llReturn = .T.
        ENDIF

        IF m.llVFPModule
*!*     Restore APP stub and the Typelib
            m.hFile = FOPEN(This.Module, 2)
            m.lnPosition = FSEEK(m.hFile, 0, 2)
            FOR m.lnCounter = 2 TO 1 STEP -1
                =FWRITE(m.hFile, m.laVFPSections[m.lnCounter])
            ENDFOR
            =FCLOSE(m.hFile)
        ENDIF
        RETURN m.llReturn
    ENDFUNC

***********************
    FUNCTION AddStringToStringTable(tcString, tnStringIndex) && pass -1 for tnStringIndex to add to end of string table
***********************
        #DEFINE RT_STRING 6
        LOCAL hFile, lnPosition, lnSize, hModule, lcStruct, ;
        lnCounter, llVFPModule, lnResult, lnReturn, lcBaseStringTable, lcNewStringTable
        LOCAL ARRAY laVFPSections[2]
        
        m.lnReturn = -1
        
        IF !FILE(This.Module)
            *!* The Module property does not appear to be a file or has not been set.
            RETURN m.lnReturn
        endif
        IF Empty(m.tcString)
            *!* The parameter was not set.
            RETURN m.lnReturn
        endif

        DECLARE INTEGER BeginUpdateResource IN WIN32API STRING, INTEGER
        DECLARE INTEGER EndUpdateResource IN WIN32API INTEGER, INTEGER
        DECLARE INTEGER UpdateResource IN WIN32API INTEGER, INTEGER, INTEGER, INTEGER, STRING @, INTEGER
        DECLARE INTEGER GetLastError IN WIN32API

        m.llVFPModule = .T. && Assume that the module is a VFPModule
*!* Preserve APP stub and the Typelib
        m.hFile = FOPEN(This.Module)
        m.lnPosition = FSEEK(m.hFile,0,2) && Go EOF and Get Size
        FOR m.lnCounter = 1 TO 2
            =FSEEK(m.hFile, m.lnPosition 14, 0)
            m.lcStruct = FREAD(m.hFile, 14)
            m.lnSize = CTOBIN(SUBSTR(m.lcStruct, 11, 4), “4sr”) && last 4 bytes holds the size
            IF m.lnSize > m.lnPosition OR m.lnSize < 0 OR (m.lnCounter = 1 AND m.lnSize = 0)
                m.llVFPModule = .F.
                EXIT
            ENDIF
            IF m.lnSize != 0
                =FSEEK(m.hFile, m.lnPosition m.lnSize, 0)
                m.laVFPSections[m.lnCounter] = FREAD(m.hFile, m.lnSize)
                IF m.lnCounter = 1 AND LEFT(m.laVFPSections[1],3) != 0hFEF2FF
                    m.llVFPModule = .F.
                    EXIT
                ENDIF
            ELSE
                m.laVFPSections[m.lnCounter] = m.lcStruct
            ENDIF
            m.lnPosition = m.lnPosition m.lnSize
        ENDFOR
        =FCLOSE(m.hFile)
        SET STEP ON
        m.lcBaseStringTable = This.GetStringResource(1, RT_STRING)
        m.lnLastStringIndex = This.GetStringTableCount(m.lcBaseStringTable)
        DO case
        CASE m.tnStringIndex < 0
            m.tcString = This.GetFormattedStringForStringTable(m.tcString)
            m.lcNewStringTable = m.lcBaseStringTable + m.tcString
        CASE m.tnStringIndex > m.lnLastStringIndex
            m.tcString = This.GetFormattedStringForStringTable(m.tcString)
            m.lcNewStringTable = m.lcBaseStringTable + REPLICATE(0h0000, m.tnStringIndex m.lnLastStringIndex) + m.tcString
        OTHERWISE
            m.lcNewStringTable = This.ReplaceStringTableString(m.lcBaseStringTable, m.tnStringIndex, m.tcString)
        ENDCASE
*!* Update String Table in module
        m.hModule = BeginUpdateResource(This.Module, 0)
        m.lnResult = UpdateResource(m.hModule, RT_STRING, 1, 1033, @m.lcNewStringTable, LEN(m.lcNewStringTable))
        IF EndUpdateResource(m.hModule, 0) = 0
            this.LastError = TRANSFORM(GetLastError())
        ELSE
            m.lnReturn = IIF(m.tnStringIndex < 0, m.lnLastStringIndex + 1, m.tnStringIndex)
        ENDIF

        IF m.llVFPModule
*!*     Restore APP stub and the Typelib
            m.hFile = FOPEN(This.Module, 2)
            m.lnPosition = FSEEK(m.hFile, 0, 2)
            FOR m.lnCounter = 2 TO 1 STEP -1
                =FWRITE(m.hFile, m.laVFPSections[m.lnCounter])
            ENDFOR
            =FCLOSE(m.hFile)
        ENDIF
        RETURN m.lnReturn
    ENDFUNC

***********************
    FUNCTION CreateNewModuleManifest()
***********************
        #DEFINE RT_MANIFEST 24
        LOCAL lcBaseManifest, lcLevelXML, lcModuleName, lnCounter, ;
            lcProgID, lcNameSpace, lcFileXML, lccomClassXML, lnClassCounter, ;
            loDOM AS MSXML2.DOMDocument, loParentNode AS MSXML2.IXMLDOMELEMENT, ;
            loNode AS MSXML2.IXMLDOMELEMENT, loNewLevelNode AS MSXML2.IXMLDOMELEMENT, ;
            loTempNode AS MSXML2.IXMLDOMELEMENT, loTypeLib AS TLI.TypeLibInfo
        IF !FILE(This.Module)
            *!* The Module property does not appear to be a file or has not been set.
            RETURN .F.
        ENDIF
        This.Manifest = “”
*!* Get Manifest from Module or Create one
        m.lcBaseManifest = This.GetStringResource(1, RT_MANIFEST)
        IF EMPTY(m.lcBaseManifest)
            IF EMPTY(this.DefaultManifest)
                m.lcBaseManifest = This.GetDefaultManifest()
            ELSE
                m.lcBaseManifest = this.DefaultManifest
            ENDIF
        ENDIF

        m.loDOM = CREATEOBJECT(“MSXML2.DOMDocument.4.0”)

        IF m.loDOM.LOADXML(m.lcBaseManifest)
            m.lcNameSpace = m.loDOM.firstChild.NEXTSIBLING.getAttribute(“xmlns”)
*!* Append or Replace Execution Level
            IF TYPE(“This.ExecutionLevel”) = “N”
                m.loNode = m.loDOM.selectSingleNode(‘//*[local-name()=”trustInfo”]’)
                IF !ISNULL(m.loNode)
                    m.loParentNode = m.loNode.parentNode
                    m.loParentNode.removeChild(m.loNode)
                ENDIF
                m.lcLevelXML = This.GetExecutionLevelXML()
                m.loNewLevelNode = This.GetNodeFromXML(m.lcLevelXML, m.lcNameSpace)
                IF !ISNULL(m.loNewLevelNode)
                    m.loDOM.selectSingleNode(‘//*[local-name()=”assembly”]’).appendChild(m.loNewLevelNode)
                ENDIF
            ENDIF

            m.loDOM.setProperty(“SelectionNamespaces”, “xmlns:pe='” + m.lcNameSpace + “‘”)
*!* Append or Replace Reg-Free COM
            IF TYPE(“This.aCOMFiles”,1) = “A”
                m.loTypeLib = CREATEOBJECT(“TLI.TypeLibInfo”)
                IF TYPE(“m.loTypeLib”) = “O”
                    FOR m.lnCounter = 1 TO ALEN(This.aCOMFiles,1)
                        IF TYPE(“This.aCOMFiles(m.lnCounter)”) = “C” AND FILE(This.aCOMFiles(m.lnCounter))
                            m.loTypeLib.ContainingFile = This.aCOMFiles(m.lnCounter)
                            FOR m.lnClassCounter = 1 TO m.loTypeLib.TypeInfoCount
                                m.loTypeInfo = m.loTypeLib.TypeInfos(m.lnClassCounter)
                                IF m.loTypeInfo.TypeKind = 5
                                    m.lcModuleName = JUSTFNAME(m.loTypeLib.ContainingFile)
                                    m.lcProgID = m.loTypeLib.NAME + “.” + m.loTypeLib.TypeInfos(m.lnClassCounter).NAME
                                    m.loNode = m.loDOM.selectSingleNode(‘//*[local-name()=”file”][translate(@name,”ABCDEFGHIJKLMNOPQRSTUVWXYZ”,”abcdefghijklmnopqrstuvwxyz”)=”‘ + LOWER(m.lcModuleName) + ‘”]’)
                                    IF ISNULL(m.loNode)
                                        m.lcFileXML = This.GetCOMFileXML(m.lcModuleName)
                                        m.loNewLevelNode = This.GetNodeFromXML(m.lcFileXML, m.lcNameSpace)
                                        IF !ISNULL(m.loNewLevelNode)
                                            m.loNode = m.loDOM.selectSingleNode(‘//*[local-name()=”assembly”]’).appendChild(m.loNewLevelNode)
                                        ENDIF
                                    ELSE
                                        FOR EACH loTempNode IN m.loNode.childNodes
                                            IF m.loTempNode.nodeName = “comClass”
                                                IF LOWER(m.loTempNode.getAttribute(“progid”)) = LOWER(m.lcProgID)
                                                    m.loTempNode.parentNode.removeChild(m.loTempNode)
                                                    EXIT
                                                ENDIF
                                            ENDIF
                                        NEXT
                                    ENDIF
                                    m.lccomClassXML = This.GetcomClassXML(m.lcModuleName, m.lcProgID, m.loTypeInfo.GUID, m.loTypeInfo.HELPSTRING)
                                    m.loNewLevelNode = This.GetNodeFromXML(m.lccomClassXML, m.lcNameSpace)
                                    IF !ISNULL(m.loNewLevelNode)
                                        m.loNode.appendChild(m.loNewLevelNode)
                                    ENDIF
                                ENDIF
                            ENDFOR
                        ENDIF
                    ENDFOR
                ENDIF
            ENDIF
            This.Manifest = m.loDOM.XML
        ENDIF
        RETURN .T.
    ENDFUNC

***********************
    FUNCTION GetDefaultManifest()
***********************
        LOCAL lcReturn
        TEXT TO m.lcReturn textmerge noshow
<?xml version=“1.0” encoding=“UTF-8” standalone=“yes”?>
<assembly xmlns=“urn:schemas-microsoft-com:asm.v1” manifestVersion=“1.0”>
<assemblyIdentity
    version=“1.0.0.0”
    type=“win32”
    name=“<<tcModuleName>>”
    processorArchitecture=“x86”/>
<description><<tcModuleName>></description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=“win32”
name=“Microsoft.Windows.Common-Controls”
version=“6.0.0.0”
language=“*”
processorArchitecture=“x86”
publicKeyToken=“6595b64144ccf1df”/>
</dependentAssembly>
</dependency>
</assembly>
        ENDTEXT
        RETURN m.lcReturn
    ENDFUNC

***********************
    FUNCTION GetStringTableCount(tcStringTable)
***********************
LOCAL lnReturn, lnCounter, lnStringSize
m.lnReturn = 0
FOR m.lnCounter = 1 TO LEN(m.tcStringTable) STEP 2
    m.lnStringSize = CTOBIN(SUBSTR(m.tcStringTable,m.lnCounter,2),“2RS”) * 2
    m.lnReturn = m.lnReturn + 1
    m.lnCounter = m.lnCounter + m.lnStringSize
ENDFOR
RETURN m.lnReturn
ENDFUNC

***********************
    FUNCTION GetStringTableString(tcStringTable, tnIndex)
***********************
LOCAL lnIndex, lnCounter, lnStringSize, lcReturn
m.lnIndex = 0
m.lcReturn = “”
FOR m.lnCounter = 1 TO LEN(tcStringTable) STEP 2
    m.lnStringSize = CTOBIN(SUBSTR(tcStringTable,m.lnCounter,2),“2RS”) * 2
    m.lnIndex = m.lnIndex + 1
    IF m.lnIndex = m.tnIndex
        m.lcReturn = STRCONV(SUBSTR(m.tcStringTable, m.lnCounter + 2, m.lnStringSize),6)
        EXIT
    ENDIF
    m.lnCounter = m.lnCounter + m.lnStringSize
ENDFOR
RETURN m.lcReturn
ENDFUNC

***********************
FUNCTION GetFormattedStringForStringTable(tcString)
***********************
LOCAL lcReturn, lnLen
m.lnLen = LEN(m.tcString)
m.lcReturn = BINTOC(m.lnLen,“2RS”) + STRCONV(m.tcString,5) && + IIF(MOD(m.lnLen,2) = 1,0h0000,“”)
RETURN m.lcReturn
ENDFUNC

***********************
    FUNCTION ReplaceStringTableString(tcStringTable, tnIndex, tcReplacementString)
***********************
LOCAL lnIndex, lnCounter, lnStringSize, lcReturn
m.lnIndex = 0
m.lcReturn = “”
m.tnIndex = m.tnIndex + 1
FOR m.lnCounter = 1 TO LEN(tcStringTable) STEP 2
    m.lnStringSize = CTOBIN(SUBSTR(tcStringTable,m.lnCounter,2),“2RS”) * 2
    m.lnIndex = m.lnIndex + 1
    IF m.lnIndex = m.tnIndex
        m.lcReturn = STUFF(m.tcStringTable, m.lnCounter, m.lnStringSize + 2, This.GetFormattedStringForStringTable(tcReplacementString))        
        EXIT
    ENDIF
    m.lnCounter = m.lnCounter + m.lnStringSize
ENDFOR
RETURN m.lcReturn
ENDFUNC

***********************
    FUNCTION GetStringResource(tnName, tnType)
***********************
        LOCAL lcModule, hModule, lcReturn, lnResource, lnSize, hGlobal
        DECLARE LONG LoadLibrary IN WIN32API STRING
        DECLARE LONG FindResource IN WIN32API LONG, LONG, LONG
        DECLARE LONG LoadResource IN WIN32API LONG, LONG
*!*            DECLARE LONG LockResource IN WIN32API LONG
        DECLARE LONG SizeofResource IN WIN32API LONG, LONG
        DECLARE LONG FreeLibrary IN WIN32API LONG
        DECLARE LONG FreeResource IN WIN32API LONG

        m.hModule = LoadLibrary(This.Module)
        m.lnResource = FindResource(m.hModule, tnName, tnType)
        m.hGlobal = LoadResource(m.hModule, m.lnResource)
        m.lnSize = SizeofResource(m.hModule, m.lnResource)
*!*    LockResource(m.lnMem)
        m.lcReturn = SYS(2600, m.hGlobal, m.lnSize)
        FreeResource(m.hGlobal)
        FreeLibrary(m.hModule)
        RETURN m.lcReturn
    ENDFUNC

***********************
    FUNCTION GetNodeFromXML(tcXML, tcNameSpace)
***********************
        LOCAL loDOMTemp AS MSXML2.DOMDocument, loNode AS MSXML2.IXMLDOMELEMENT
        m.loNode = NULL
        m.loDOMTemp = CREATEOBJECT(“MSXML2.DOMDocument.4.0”)
        IF m.loDOMTemp.LOADXML([<dummy xmlns=“] + m.tcNameSpace + [“>] + m.tcXML + [</dummy>])
            m.loNode = m.loDOMTemp.firstChild.firstChild
        ENDIF
        RETURN m.loNode
    ENDFUNC

*****************************
    FUNCTION GetExecutionLevelXML()
*****************************
*!*
*!* This.ExecutionLevel = 1 asInvoker, 2 highestAvailable, 3 requireAdministrator
*!*
*!*    asInvoker — module executes with the same permissions as its parent process (formerly known as leastPrivilege)
*!* highestAvailable — module executes with the highest privileges the current user can obtain
*!* requireAdministrator — module executes only for administrators (includes shield overlay for module icon)
*!*
*****************************
        LOCAL lcLevel, lcReturn
        m.lcLevel = ICASE(This.ExecutionLevel = 2, “highestAvailable”, This.ExecutionLevel = 3, “requireAdministrator”, “asInvoker”)
        TEXT TO m.lcReturn TEXTMERGE NOSHOW
    <trustInfo xmlns=“urn:schemas-microsoft-com:asm.v3”>
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level=“<<m.lcLevel>>” uiAccess=“false”/>
            </requestedPrivileges>
        </security>
    </trustInfo>
        ENDTEXT
        RETURN m.lcReturn
    ENDFUNC

*****************************
    FUNCTION GetCOMFileXML(tcModuleName)
*****************************
        LOCAL lcReturn
        TEXT TO m.lcReturn TEXTMERGE NOSHOW
<file name=“<<LOWER(JUSTFNAME(tcModuleName))>>”>
</file>
        ENDTEXT
        RETURN m.lcReturn
    ENDFUNC

*****************************
    FUNCTION GetcomClassXML(tcModuleName, tcProgID, tcCLSID, tcDescription)
*****************************
        LOCAL lcReturn && tcCLSID
*!*    m.lcCLSID = CLSIDFromProgIDEx(m.tcProgID)
        IF PCOUNT() < 4
            m.tcDescription = m.tcProgID
        ENDIF
        TEXT TO m.lcReturn TEXTMERGE NOSHOW
<comClass description=“<<m.tcDescription>>”
clsid=“<<m.tcCLSID>>”
progid=“<<m.tcProgID>>”
threadingModel=“Both”/>
        ENDTEXT
        RETURN m.lcReturn
    ENDFUNC

*****************************
FUNCTION GetLastErrorMessage(tnError)
*****************************
#DEFINE FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
LOCAL lcBuffer
DECLARE INTEGER GetLastError IN win32api
DECLARE INTEGER FormatMessage IN kernel32.DLL ;
    INTEGER dwFlags, ;
    STRING @lpSource, ;
    INTEGER dwMessageId, ;
    INTEGER dwLanguageId, ;
    STRING @lpBuffer, ;
    INTEGER nSize, ;
    INTEGER Arguments
    m.lcBuffer = SPACE(128)
    =FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, ‘WINERROR.H’, m.tnError, 0, @m.lcBuffer, 128 , 0)
    RETURN m.lcBuffer
ENDFUNC
ENDDEFINE