При вызове CreateFile из WindowsAPI из ac #, что лучше всего делать: вызывать универсальную версию CreateFile, ANSI CreateFileA или Unicode CreateFileW?
Каждый из API имеет различную подпись для соответствующего CharSet:
// CreateFile generic
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern SafeFileHandle CreateFile (
[MarshalAs(UnmanagedType.LPTStr)] string lpFileName,
...
// CreateFileA ANSI
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern SafeFileHandle CreateFileA (
[MarshalAs(UnmanagedType.LPStr)] string lpFileName,
...
// CreateFileW Unicode
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle CreateFileW (
[MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
...
Согласно документации Microsoft 1 , для C # значением CharSet по умолчанию является Charset.ANSI.Это кажется действительно странным, поскольку строки в C # являются Unicode.Если документация верна, это означает, что CreateFile в конечном итоге будет вызывать CreateFileA во время выполнения (с соответствующими преобразованиями в ANSI туда-сюда).
Другой документ Microsoft 2 говорит: «КогдаCharSet - это Unicode, или аргумент явно помечен как [MarshalAs (UnmanagedType.LPWSTR)], и строка передается по значению (не ref или out), строка будет закреплена и использована непосредственно собственным кодом (а не скопирована)."Это прекрасно подходит для того, чтобы избежать копирования потенциально больших строк и обеспечения максимальной производительности.
Предположим, что я хочу вызвать разновидность CreateFile, которая оптимально работает со строками C #, имеет лучшую производительность, минимальное приведение / преобразование, работает в ОС Windows x64и во вторую очередь имеет максимальную переносимость.
Подход 1. Вызовите универсальный CreateFile, но измените подпись на CharSet.Unicode.
Это может быть проблемой, поскольку CreateFile маршализирует lpFileName как UnmanagedType.LPTStr, тогда как CreateFileW марширует его как UnmanagedType.LPWSTR.Кажется, что маршалинг должен был бы выполнить преобразования?чтобы получить правильный тип LP (более одного раза).Другая неэффективность заключается в том, что CreateFile должен вызывать CreateFileW внутри.Кроме того, я хочу убедиться, что «закрепление» происходит для максимальной производительности, и я не уверен, что это произойдет здесь.
Подход 2. Вызов универсального CreateFile с подписью CharSet.Auto. Это, кажется, обеспечивает максимальную переносимость.для целевой ОС, но вызовет внутренний вызов CreateFileA, что неприемлемо для строк C # (Unicode).
Подход 3: Вызовите CreateFileW напрямую.Это также кажется менее чем оптимальным, потому что, если я компилирую для другой целевой ОС, такой как Win x86 (которая использует только строки ANSI), программа не сможет работать вообще.
Кажется, что подход 1 будетбудь лучшим, но MarshalAs LPTStr мне не подходит (учитывая, что версия CreateFileW маршалирует как LPWStr).
Буду признателен за любую помощь, которую вы можете оказать в этом.Я копался в десятках конфликтующих веб-страниц и не могу найти однозначного ответа.
Ссылки:
1 DllImportAttribute.CharSet Field
2 Рекомендации по собственной совместимости
3 Копирование и закрепление