Мне нужно сделать вызовы взаимодействия с DLL, написанной на C ++. В коде C ++ есть различные функции, которые получают и возвращают строки. Все они используют обычно определенный тип (struct) в C ++, который содержит указатель на строку и целое число для ее размера следующим образом:
struct StringParam
{
int size; // 4 byte integer for the size of the string buffer
LPWSTR buff; // Pointer to a wide char buffer
}
Когда строка возвращается из DLL и когда строковый буфер слишком мал для хранения результата строки, возвращается код ошибки вместе с буфером правильного размера, который требуется в поле целого числа размер, чтобы вызывающая сторона могла предоставить буфер нужного размера.
Игнорируя структуру на данный момент, это можно легко сделать с помощью взаимодействия, используя комбинацию параметра StringBuilder вместе с параметром ref int для размера. Однако мы используем структуру, и поля StringBuilder недопустимы в структурах с маршализацией.
Учитывая функцию C ++, которая получает строку следующим образом:
int __stdcall DoSomethingWithString(StringParam *stringParam)
Можно объявить следующую структуру в C #:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct StringParam
{
public int Size;
[MarshalAs(UnmanagedType.LPWStr)]
public string Text;
public StringParam(string text)
{
this.Text = text;
this.Size = text.Length;
}
}
и может успешно инициализировать структуру и вызывать функцию C ++ со следующей подписью:
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int DoSomethignWithString(ref StringParam stringParam);
Однако, учитывая функцию, которая должна возвращать строку следующим образом, возникает проблема:
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetSomeString(ref StringParam stringParam);
При получении строки из вызова взаимодействия мы не знаем размер строки и должны выделить буфер, достаточный по размеру для хранения результата строки. StringBuilder был бы идеальным, но не может использоваться в структуре, которая маршалируется. Мы можем предварительно выделить строку с 2048 фиктивными символами, которые затем можно будет использовать для хранения результатов. Фактически это рекомендуется в сообщении об ошибке, которое появляется при попытке использовать тип StringBuilder:
Невозможно выполнить маршализацию поля 'Text' типа 'StringParam': поля структуры или класса не могут иметь тип StringBuilder. Тот же самый эффект обычно может быть достигнут при использовании поля String и предварительной инициализации его строки, длина которой соответствует длине соответствующего буфера.
Это кажется немного грязным, чтобы предварительно инициализировать строку с фиктивными значениями. Есть ли другой / лучший способ сделать это?