0

Windows BITS 服务的受人尊敬的开源 .NET 包装器实现 ( SharpBITS )无法识别 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"
  • buffer => byte[1900] - 充满二进制数据
  • 块1 => 1033
  • 块2 => 1200

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

4

3 回答 3

2

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

推荐的解决方法是检索 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);
于 2010-03-17T07:54:08.960 回答
1

大约一年前,我查看了 SharpBITS。我记得当时看到一个大错误,这将使它不适用于 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 位模式下运行。项目 + 属性,构建选项卡,平台目标 = x86。您可以在此处通知作者

于 2010-02-17T19:49:34.583 回答
1

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

于 2010-02-17T19:16:35.317 回答