我最近浏览了一堆用VB6编写的独立实用程序应用程序,以确保Windows Vista及更高版本的注册表虚拟化已关闭。我为每个exe创建了一个独立的清单文件,适当地设置requestedExecutionLevel
(其中一些需要修改HKEY_LOCAL_MACHINE
注册表项,其他人则不需要),并测试它们。它们似乎都能正常工作。
我只剩下一个小问题。由于它们是独立的实用程序,人们习惯于只在网络上复制它们并手动运行它们。如果有人忘记复制清单文件以及exe,那么exe将静默写入虚拟化注册表项而不是真实注册表项,并导致难以调试的问题。
显而易见的解决方案是将清单作为资源嵌入到exe中。我在网上看到的所有文章都告诉你要嵌入这样的资源:
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest"
这应该可以正常工作,除了VB编译器总是创建资源ID = 1的应用程序图标。当我尝试上面的代码时,Windows拒绝运行exe,抱怨资源错误(我会更新这个稍后发布详细信息)。我尝试将资源ID更改为另一个数字,之后Windows成功运行程序,但无法识别清单内容。
有没有人知道如何让嵌入式清单在VB6 exe中工作,或者我应该坚持使用外部文件?
更新1
上面给出的文字是.rc
文件的全部内容。我把它编译成.res
这样的文件:
"%ProgramFiles%\Microsoft Visual Studio\VB98\Wizards\rc.exe" /r /fo "Resources.res" "Resources.rc"
将它嵌入VB6项目文件中:
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
当我将已编译的exe读入VS2008资源编辑器时,它看起来像这样:
RegistryTestVB6.exe
Icon
1 [Neutral]
RT_MANIFEST
1 [English (United States)]
Version
1 [English (United States)]
当我在VS2008中构建一个完全等效的VB.NET测试应用程序,然后将其加载到资源编辑器中时,它看起来像这样:
RegistryTestNET.exe
Icon
32512 [Neutral]
RT_MANIFEST
1 [Neutral]
Version
1 [Neutral]
更新2
测试 - .NET exe在Windows XP和Windows 7上运行正常。但是,VB6 exe在XP上产生以下错误:
此应用程序无法启动,因为应用程序配置不正确。重新安装应用程序可能会解决此问题。
以及7上的以下错误:
应用程序无法启动,因为其并排配置不正确。请参阅应用程序事件日志或使用命令行sxstrace.exe工具获取更多详细信息。
查看事件日志,我看到以下条目:
“RegistryTestVB6.exe”的激活上下文生成失败。第10行的清单或策略文件“RegistryTestVB6.exe”出错。无效的Xml语法。
毋庸置疑,XML并非无效,它与我用于.NET exe的编码完全相同,并且可以正常运行。
解决
VB6编译器确实要求资源中包含的任意文本文件必须是4个字节的精确倍数。我只是在XML中添加了空格,直到Notepad ++告诉我包括BOM在内的总文件大小是4的倍数。
感谢Michael和Jim指出我正确的方向。可惜我无法将你们两个都标记为答案!
答案 0 :(得分:9)
VB6有一个怪癖,因为任何资源元素必须是4的长度的精确倍数。尝试使用空格填充清单文件以确保这一点,并查看是否会改变行为。
Microsoft article Q297112 (archive)记录了这个怪癖。
此外,您可以使用VB6 IDE添加资源,而不是编辑VBP。效果可能相同,但资源编辑器是执行此操作的标准方法。
答案 1 :(得分:9)
有趣的是,我最近必须做同样的事情。按照克里斯蒂安描述的步骤,我第一次通过工作。为了繁荣,这是我遵循的整个工作流程:
创建了一个app.manifest,保留了空格字符,这对于它来说非常重要。如前面的答案中所述,文件大小必须是4的倍数。
如针对rc生成.res文件的原始问题中所述,运行RC.EXE
ResFile32 = “Resources.res”
在标准vb6环境中构建EXE。部署在vista或win7计算机上时,屏蔽会显示,并提示用户以管理员身份运行。在工作室中打开EXE文件时,我验证了资源。
Notepad ++告诉我app.manifest文件的编码是ANSI。它没有在文件开头包含字节顺序标记。
如果您仍然遇到麻烦,请告诉我,我会尽可能地与您分享。除此之外,我不确定除了Works on my Machine!
之外还告诉你什么答案 2 :(得分:7)
使用Resource Compiler (rc.exe)还有很长的路要走。在可执行文件中嵌入应用程序清单有一个更简单的选项,无论是C ++还是VB6,还是几乎任何其他语言。 The Manifest Tool (mt.exe)专门用于在二进制文件中嵌入清单,并通过Windows SDK免费提供。使用mt.exe的另一个好处是它可以自动处理任何必要的填充。
在编译二进制文件后,只需运行以下命令行。我使用了Visual C ++ 2005编译器内部使用的命名约定,其中清单文件名包含附加了“.intermediate.manifest”的完整程序名。
mt.exe -nologo -manifest "program.exe.intermediate.manifest" -outputresource:"program.exe;#1
更新:我个人一直在使用VB6可执行文件的自动构建过程中使用它已超过两年了。它非常成功,我们从回归测试中消除了操作系统兼容性测试 - 特定于清单。