通过 Win32 检索 dll 版本信息 - VerQueryValue(...) 在 Win7 x64 下崩溃

发布于 2024-08-21 12:29:53 字数 2584 浏览 11 评论 0 原文

SharpBITS)。 wikipedia.org/wiki/Background_Intelligent_Transfer_Service" rel="nofollow noreferrer">Windows BITS 服务 无法识别 Win7 x64 下的底层 BITS 版本。

这是失败的源代码。 NativeMethods 是由 .NET 方法包装并通过 DllImport 属性修饰的本机 Win32 调用。

private static BitsVersion GetBitsVersion()
    {
        try
        {
            string fileName = Path.Combine(
                     System.Environment.SystemDirectory, "qmgr.dll");
            int handle = 0;
            int size = NativeMethods.GetFileVersionInfoSize(fileName, 
                                                            out handle);
            if (size == 0) return BitsVersion.Bits0_0;
            byte[] buffer = new byte[size];
            if (!NativeMethods.GetFileVersionInfo(fileName, 
                                                  handle, 
                                                  size, 
                                                  buffer))
            {
                return BitsVersion.Bits0_0;
            }
            IntPtr subBlock = IntPtr.Zero;
            uint len = 0;
            if (!NativeMethods.VerQueryValue(buffer,
                              @"\VarFileInfo\Translation", 
                              out subBlock, 
                              out len))
            {
                return BitsVersion.Bits0_0;
            }

            int block1 = Marshal.ReadInt16(subBlock);
            int block2 = Marshal.ReadInt16((IntPtr)((int)subBlock + 2 ));
            string spv = string.Format(
                 @"\StringFileInfo\{0:X4}{1:X4}\ProductVersion", 
                 block1, 
                 block2);

            string versionInfo;
            if (!NativeMethods.VerQueryValue(buffer, 
                                             spv, 
                                             out versionInfo, 
                                             out len))
            {
                return BitsVersion.Bits0_0;
            }
...

该实现遵循 MSDN 说明 。仍然在第二次 VerQueryValue(...) 调用期间,应用程序崩溃并毫不犹豫地终止调试会话。 在崩溃之前再多一点调试信息:

  • spv => “\StringFileInfo\040904B0\ProductVersion”
  • 缓冲区=> byte[1900] - 充满二进制数据
  • 块1 =>第1033
  • 章1200

我通过Windows查看了目标“C:\Windows\System32\qmgr.dll”文件(BITS的实现)。它说产品版本是7.5.7600.16385。该值应该在 versionInfo 字符串中返回,而不是崩溃。有什么建议吗?

The respected open source .NET wrapper implementation (SharpBITS) of Windows BITS services fails identifying the underlying BITS version under Win7 x64.

Here is the source code that fails. NativeMethods are native Win32 calls wrapped by .NET methods and decorated via DllImport attribute.

private static BitsVersion GetBitsVersion()
    {
        try
        {
            string fileName = Path.Combine(
                     System.Environment.SystemDirectory, "qmgr.dll");
            int handle = 0;
            int size = NativeMethods.GetFileVersionInfoSize(fileName, 
                                                            out handle);
            if (size == 0) return BitsVersion.Bits0_0;
            byte[] buffer = new byte[size];
            if (!NativeMethods.GetFileVersionInfo(fileName, 
                                                  handle, 
                                                  size, 
                                                  buffer))
            {
                return BitsVersion.Bits0_0;
            }
            IntPtr subBlock = IntPtr.Zero;
            uint len = 0;
            if (!NativeMethods.VerQueryValue(buffer,
                              @"\VarFileInfo\Translation", 
                              out subBlock, 
                              out len))
            {
                return BitsVersion.Bits0_0;
            }

            int block1 = Marshal.ReadInt16(subBlock);
            int block2 = Marshal.ReadInt16((IntPtr)((int)subBlock + 2 ));
            string spv = string.Format(
                 @"\StringFileInfo\{0:X4}{1:X4}\ProductVersion", 
                 block1, 
                 block2);

            string versionInfo;
            if (!NativeMethods.VerQueryValue(buffer, 
                                             spv, 
                                             out versionInfo, 
                                             out len))
            {
                return BitsVersion.Bits0_0;
            }
...

The implementation follows the MSDN instructions by the letter. Still during the second VerQueryValue(...) call, the application crashes and kills the debug session without hesitation.
Just a little more debug info right before the crash:

  • spv =>
    "\StringFileInfo\040904B0\ProductVersion"
  • buffer => byte[1900] - full with binary data
  • block1 => 1033
  • block2 => 1200

I looked at the targeted "C:\Windows\System32\qmgr.dll" file (The implementation of BITS) via Windows. It says that the Product Version is 7.5.7600.16385. Instead of crashing, this value should return in the verionInfo string. Any advice?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

勿忘心安 2024-08-28 12:29:53

Nobugz 的答案确实指出了一个非常有效的问题(对此非常感谢!),但最终的杀手是 .NET 错误:在 x64 .NET 实现下,这种情况下的编组字符串失败。该错误在.NET4.0中已修复。 以下是报告的问题以及 Microsoft 的答案。

建议的解决方法是检索 IntPtr 而不是字符串作为输出并手动封送字符串。所以最终的代码(包括Nobugz的修复):

int block1 = Marshal.ReadInt16(subBlock);
int block2 = Marshal.ReadInt16(subBlock, 2);
string spv = string.Format(@"\StringFileInfo\{0:X4}{1:X4}\ProductVersion", 
                             block1, block2);

IntPtr versionInfoPtr;
if (!NativeMethods.VerQueryValue(buffer, spv, out versionInfoPtr, out len))
{
     return BitsVersion.Bits0_0;
}
string versionInfo = Marshal.PtrToStringAuto(versionInfoPtr);

The answer of Nobugz does point out a very valid issue (big thanx for that!), but the final killer was a .NET bug: marshaling strings in this scenario fails under the x64 .NET implementation. The bug is fixed in .NET4.0. Here is the issue reported, as well as Microsoft's answer.

The recommended workaround is retrieve an IntPtr instead of the string as output and marshaling the string manually. So the final code (including the fix of Nobugz):

int block1 = Marshal.ReadInt16(subBlock);
int block2 = Marshal.ReadInt16(subBlock, 2);
string spv = string.Format(@"\StringFileInfo\{0:X4}{1:X4}\ProductVersion", 
                             block1, block2);

IntPtr versionInfoPtr;
if (!NativeMethods.VerQueryValue(buffer, spv, out versionInfoPtr, out len))
{
     return BitsVersion.Bits0_0;
}
string versionInfo = Marshal.PtrToStringAuto(versionInfoPtr);
鸢与 2024-08-28 12:29:53

我不知道您的代码是否有问题,但是您是否考虑过使用 FileVersionInfo 类而不是 P/Invoke API 调用?它更容易使用并且更不容易出错......

I don't know if something's wrong with your code, but have you considered using the FileVersionInfo class rather than P/Invoke API calls ? It's easier to use and less error prone...

江南烟雨〆相思醉 2024-08-28 12:29:53

大约一年前我看过 SharpBITS。我记得当时看到一个大错误,这使得它不适合 64 位操作系统。不记得确切的错误,可能是一个错误的 P/Invoke 声明。这段代码肯定是错误的:

int block2 = Marshal.ReadInt16((IntPtr)((int)subBlock + 2 ));

IntPtr 不能在 x64 模式下转换为 int。它必须看起来像这样:

int block2 = Marshal.ReadInt16((IntPtr)((long)subBlock + 2 ));

或者更好:

int block2 = Marshal.ReadInt16(subBlock, 2); 

如果您使用此库来避免这些问题,我强烈建议您强制您的应用程序以 32 位模式运行。项目 + 属性,构建选项卡,平台目标 = x86。您可以在此处通知作者。

I looked at SharpBITS about a year ago. I remember seeing a Big Bug back then which would make it unsuitable on 64-bit operating systems. Can't remember the exact bug, probably a bad P/Invoke declaration. This code snippet is definitely wrong:

int block2 = Marshal.ReadInt16((IntPtr)((int)subBlock + 2 ));

An IntPtr cannot be cast to an int in x64 mode. It must look like this:

int block2 = Marshal.ReadInt16((IntPtr)((long)subBlock + 2 ));

Or much better:

int block2 = Marshal.ReadInt16(subBlock, 2); 

I'd strongly recommend you force your app to run in 32-bit mode if you use this library to avoid these problems. Project + Properties, Build tab, Platform Target = x86. You can notify the author here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文