Получение информации о версии 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. Вы можете уведомить автора здесь.