Получение информации о версии DLL через Win32 - VerQueryValue(...) падает под Win7 x64

Уважаемая реализация оболочки с открытым исходным кодом.NET ( SharpBITS) служб Windows BITS не может определить базовую версию BITS под Win7 x64.

Вот исходный код, который терпит неудачу. NativeMethods - это нативные вызовы Win32, обернутые методами.NET и декорированные атрибутом DllImport.

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] - заполнен двоичными данными
  • block1 => 1033
  • block2 => 1200

Я посмотрел на целевой файл "C:\Windows\System32\qmgr.dll" (реализация BITS) через Windows. Это говорит о том, что версия продукта - 7.5.7600.16385. Вместо сбоя это значение должно возвращаться в строке verionInfo. Любой совет?

3 ответа

Решение

Ответ 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);

Я не знаю, если что-то не так с вашим кодом, но вы рассматривали возможность использования FileVersionInfo класс, а не вызовы API P/Invoke? Это проще в использовании и менее подвержено ошибкам...

Я посмотрел на SharpBITS около года назад. Я помню, что в то время я видел "Большую ошибку", которая делала бы ее непригодной для 64-битных операционных систем. Не могу вспомнить точную ошибку, возможно, плохое объявление P/Invoke. Этот фрагмент кода определенно неверен:

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

IntPtr не может быть приведен к int в режиме x64. Это должно выглядеть так:

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

Или намного лучше:

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

Я настоятельно рекомендую вам заставить ваше приложение работать в 32-битном режиме, если вы используете эту библиотеку, чтобы избежать этих проблем. Проект + Свойства, вкладка "Сборка", Цель платформы = x86. Вы можете уведомить автора здесь.

Другие вопросы по тегам