I have recently gone through a bunch of standalone utility apps written in VB6 to make sure that registry virtualization is turned off for Windows Vista and above. I created a standalone manifest file for each exe, set the requestedExecutionLevel
appropriately (some of them need to modify HKEY_LOCAL_MACHINE
registry keys, others do not), and tested them. They all appear to work correctly.
I have only one small problem remaining. Since they are standalone utilities, people are used to just copying them around the network and running them manually. If anyone forgets to copy the manifest file as well as the exe, then the exe will silently write to the virtualized registry key instead of the real one and cause hard-to-debug problems.
The obvious solution is to embed the manifest into the exe as a resource. All the articles I have read on the net tell you to embed the resource like this:
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest"
This should work just fine, except that the VB compiler always creates the application icon with resource ID = 1. When I tried the above code, Windows refused to run the exe, complaining about a resource error (I'll update this post with the details later). I tried changing the resource ID to another number, after which Windows ran the program successfully but did not recognise the manifest contents.
Does anyone know of a way to get an embedded manifest to work in a VB6 exe, or should I just stick with an external file?
UPDATE 1
The text given above is the whole content of the .rc
file. I compile it to a .res
file like this:
"%ProgramFiles%\Microsoft Visual Studio\VB98\Wizards\rc.exe" /r /fo "Resources.res" "Resources.rc"
And embed it in the VB6 project file like this:
Type=Exe
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\..\..\..\..\WINDOWS\system32\stdole2.tlb#OLE Automation
Form=Main.frm
ResFile32="Resources.res"
IconForm="FMain"
Startup="FMain"
HelpFile=""
Title="Windows Vista Registry Test - VB6"
ExeName32="RegistryTestVB6.exe"
Path32=""
Command32=""
Name="RegistryTestVB6"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionComments="Windows Vista Registry Test - VB6"
VersionCompanyName=""
VersionFileDescription="Windows Vista Registry Test - VB6"
VersionLegalCopyright=""
VersionProductName="Windows Vista Registry Test - VB6"
CondComp=""
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
When I read the compiled exe into the VS2008 resource editor, it looks like this:
RegistryTestVB6.exe
Icon
1 [Neutral]
RT_MANIFEST
1 [English (United States)]
Version
1 [English (United States)]
When I construct an exact equivalent VB.NET test app in VS2008, then load that into the resource editor, it looks like this instead:
RegistryTestNET.exe
Icon
32512 [Neutral]
RT_MANIFEST
1 [Neutral]
Version
1 [Neutral]
UPDATE 2
Testing - the .NET exe runs fine on both Windows XP and Windows 7. However, the VB6 exe produces the following error on XP:
This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem.
and the following error on 7:
The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.
Looking in the event log I see the following entry:
Activation context generation failed for "RegistryTestVB6.exe". Error in manifest or policy file "RegistryTestVB6.exe" on line 10. Invalid Xml syntax.
Needless to say the XML isn't invalid, it's exactly the same file with the same encoding that I used for the .NET exe, and that one works.
RESOLUTION
The VB6 compiler does indeed require that an arbitrary text file included in a resource must be an exact multiple of 4 bytes. I simply added spaces to the XML until Notepad++ told me that the total file size including BOM was a multiple of 4.
Thanks to both Michael and Jim for pointing me in the correct direction. Just a pity I can't mark you both as the answer!
VB6 has a quirk in that any resource element must be an exact multiple of 4 in length. Try padding the manifest file out with spaces to ensure this, and see if that changes the behavior.
This quirk was documented in Microsoft article Q297112 (archive).
Also, you might add the resource using the VB6 IDE instead of editing the VBP. The effect may be the same, but the resource editor is the standard means for doing this.