При вызове функций 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 они не делают. Это обязательно?

1 ответ

Решение

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

  • ExactSpelling=true это оптимизация, она избегает того, чтобы маршаллер pinvoke искал 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 всегда включает его.

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

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