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

时间:2010-02-17 19:00:26

标签: .net com dllimport microsoft-bits bits-service

SharpBITS受尊敬的开源.NET包装器实现(Windows BITS services)无法识别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 instructions。仍然在第二次VerQueryValue(...)调用期间,应用程序崩溃并且毫不犹豫地杀死调试会话。 在崩溃之前只需要一点调试信息:

  • spv => “\ StringFileInfo中\ 040904B0 \的ProductVersion”
  • buffer => byte [1900] - 完整的二进制数据
  • block1 => 1033
  • block2 => 1200

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

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

在x64模式下无法将IntPtr强制转换为int。它必须如下所示:

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

或者更好:

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

如果您使用此库来避免这些问题,我强烈建议您强制您的应用以32位模式运行。 Project + Properties,Build选项卡,Platform Target = x86。您可以通知作者here.

答案 2 :(得分:1)

Nobugz的答案确实指出了一个非常有效的问题(大于此问题!),但最终的杀手是一个.NET错误:在x64 .NET实现中,此方案中的编组字符串失败。该错误已在.NET4.0中修复。 Here is the issue reported, as well as Microsoft's answer.

建议的解决方法是检索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);