Можете ли вы вызвать многобайтовый ANSI в varargs? Что я делаю неправильно?

StackOverflow https://stackoverflow.com/questions/825844

Вопрос

** ОСНОВНОЕ ОБНОВЛЕНИЕ ** Я сделал небольшую ошибку, но мне все еще интересно, что именно происходит.

Функция, которую я вызываю, на самом деле является "fooV", функцией с такой подписью:

foo(const char *, const char *, EnumType, va_list)

Это очищает AccessViolationException, которые я получил, но не объясняет, почему параметры params работают для всех остальных типов .NET, за исключением строк, которые должны быть преобразованы в многобайтовые символы ANSI. Я собираюсь попросить разработчика DLL раскрыть версию, которая на самом деле использует ... в списке параметров, любые подсказки для PInvoking, если va_list является параметром?

** старая запись **

Это связано, но отличается от недавнего вопроса Я спросил .

Я должен использовать PInvoke для вызова функции библиотеки C, которая имеет следующую подпись:

foo(const char *, const char *, EnumType, ...)

Функция настраивает ресурс в зависимости от StructType; интересующий меня случай настраивает что-то, что ожидает, что varargs будет одной многобайтовой строкой ANSI. Я использовал эту подпись PInvoke для вызова функции:

    [DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
    public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3);

Строка params [] показалась мне странной, но предыдущие сигнатуры, которые вызывали эту функцию, использовали ее для других типов, таких как bool , поэтому я следовал шаблону.

Я обернул это более дружественным методом .NET:

public void Foo(string s1, string s2, string s3)
{
    int error = Dll.Foo(s1, s2, EnumType.Foo, s3);
    // handle errors
}

Недавно я изменил подпись, включив в нее " BestFitMapping = false, ThrowOnUnmappableChar = true " в атрибуте DLLImport, чтобы соответствовать рекомендациям FxCop. Это красная сельдь, как я опишу позже.

Чего я ожидаю от этого изменения, так это ограниченной поддержки Unicode. Например, на машине с английской кодовой страницей будет выдано исключение, если вы передадите строку, содержащую символ японского языка. На японском компьютере я ожидал, что смогу передать японскую строку в функцию. Тест по английскому языку сработал, как и ожидалось, но японский тест выдает исключение System.Runtime.InteropServices.COME с HRESULT 0x8007007A. Это происходит даже без настроек BestFitMapping и ThrowOnUnmappableChar.

Я немного осмотрелся и увидел несколько сайтов, которые предлагали PInvoke varargs, просто указав нормальные аргументы, поэтому я попробовал эту подпись:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, string s3);

Это создает исключение AccessViolationException, когда я использую его на английском или японском компьютере.

Я не могу использовать UTF-8 или другую кодировку Unicode, потому что эта библиотека C ожидает и обрабатывает только многобайтовую ANSI. Я обнаружил, что указание CharSet.Unicode для этой функции работает, но я беспокоюсь, что это только совпадение, а не то, на что я должен полагаться. Я думал о преобразовании строки в byte [] с использованием системной кодовой страницы, но не могу понять, как указать, что я хотел бы передать байтовый массив параметру varargs.

Что происходит? На английском компьютере английские символы работают нормально, а японские символы генерируют исключение ArgumentException, как и ожидалось. На японском компьютере английские символы работают нормально, а японские символы генерируют COMException. Что-то не так с моей подписью PInvoke? Я попытался использовать атрибут MarshalAs , чтобы указать тип LPArray и подтип LPStr, но это не удается аналогичным образом. UnmanagedType.LPStr указывает, что это однобайтовая строка ANSI; Есть ли способ указать многобайтовую строку ANSI?

** ОБНОВЛЕНИЕ ** Вот дополнение, учитывающее текущие комментарии.

Если я укажу соглашение о вызовах CDecl и приму обычные параметры, подобные этому:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

Когда я использую эту спецификацию, я получаю AccessViolationException. Поэтому я попробовал это:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.CDecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

Это работает для английского языка и вызывает исключение COMException при использовании японских символов.

Единственное, что я нашел, что работает последовательно, это:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int Foo(string s1, string s2, EnumType e1, params IntPtr[] s3)

Чтобы сделать это, я использую Marshal.Stri

Это было полезно?

Решение

Вероятно, вам также нужно указать CallingConvention = CallingConvention.Cdecl , поскольку функция varargs будет использовать соглашение о вызовах _cdecl (по умолчанию используется Winapi, в свою очередь по умолчанию используется StdCall) .

Вам может понадобиться что-то еще, но я уверен, что вам понадобится хотя бы это.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top