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

Например, это из исходного файла .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 их нет. Это требуется?

1 Ответ

20 голосов
/ 29 ноября 2010

Они оба выполнят свою работу. Есть только несколько способов снять шкуру с кошки. Специально для этого примера:

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

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

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

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

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

Итак, в двух словах, ни один не совершенен, но они оба будут работать. Таковы опасности pinvoke.

...