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

Уважаемая реализация оболочки .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 ]

1 голос
/ 17 февраля 2010

Я посмотрел 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. Вы можете сообщить автору здесь.

1 голос
/ 17 марта 2010

Ответ 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);
1 голос
/ 17 февраля 2010

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

...