При вызове функций 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);
Какая правильная / лучшая подпись для этой функции? (только один из них
[return: MarshalAs(UnmanagedType.Bool)]
, или же[In, Out] ref
, так далее.)Я заметил, что в исходных файлах.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)]
не требуется, это маршалинг по умолчанию для WindowsBOOL
, Не уверен, почему pinvoke.net всегда включает его.
Итак, в двух словах, ни один не идеален, но они оба будут работать. Таковы опасности пинвока.