При вызове функций Windows API из C#, какому источнику подписей следует доверять: исходному коду .NET Framework или PInvoke?

Например, это из исходного файла .NET Framework UnsafeNativeMethods.cs:

[DllImport(ExternDll.User32, ExactSpelling=true, CharSet=CharSet.Auto)] 
public static extern bool GetWindowRect(HandleRef hWnd, 
    [In, Out] ref NativeMethods.RECT rect);

а это из PInvoke.Net:

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(HandleRef hwnd, out RECT lpRect);
  1. Какая правильная/лучшая подпись для этой функции? (только у одного из них есть [return: MarshalAs(UnmanagedType.Bool)] или [In, Out] ref и т. д.)

  2. Я заметил, что в исходных файлах .NET Framework многие/большинство подписей имеют ExactSpelling=true, CharSet=CharSet.Auto, но в PInvoke их нет. Это требуется?


person Alex Vang    schedule 29.11.2010    source источник


Ответы (1)


Они оба справятся с задачей. Просто есть несколько способов содрать шкуру с пинвок-кота. Конкретно для этого примера:

  • ExactSpelling=true — это оптимизация, она позволяет маршаллеру pinvoke искать версии GetWindowRectA и GetWindowRectW. Они не существуют для этой конкретной функции API, поскольку она не принимает строковый аргумент. Увидеть реальную разницу во времени выполнения было бы чудом.

  • CharSet=CharSet.Auto всегда хорошая идея, так как значение по умолчанию (Ansi) неэффективно. Просто так получилось, что здесь нет никакой разницы, поскольку функция не принимает никаких строковых аргументов.

  • [In, Out] не нужен, потому что это значение по умолчанию для преобразовываемого типа. Дорогое слово, означающее, что маршаллер pinvoke может напрямую передать указатель на управляемую память, никакого преобразования не требуется. Максимально эффективно. Та же идея, что и в CharSet, хотя явное описание помогает создавать самодокументирующийся код и помнить о том, что нужно иметь дело с необычным случаем. Возможность использовать только [In] или [Out] может быть значительной оптимизацией, но не здесь, поскольку она уже оптимизирована. Fwiw, [Out] был бы правильным выбором.

  • out против ref, та же идея, что и выше. Использование out более правильно, так как API фактически не использует переданные значения внутри RECT. Однако это не имеет никакого значения во время выполнения, поскольку JIT-компилятор все равно всегда инициализирует структуру.

  • [return: MarshalAs(UnmanagedType.Bool)] не нужен, это маршалинг по умолчанию для Windows BOOL. Не уверен, почему pinvoke.net всегда включает его.

Короче говоря, ни один из них не идеален, но оба они будут работать. Таковы опасности pinvoke.

person Hans Passant    schedule 29.11.2010
comment
FxCop будет жаловаться, если вы явно не укажете тип сортировки там, где он неоднозначен. Это относится к bool (поскольку есть Win32 BOOL, C++ bool и VARIANT_BOOL) или к строкам. - person Daniel Rose; 28.04.2011