Невозможно упорядочить структуру, содержащую поле StringBuilder - PullRequest
3 голосов
/ 29 августа 2011

Мне нужно сделать вызовы взаимодействия с 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 и предварительной инициализации его строки, длина которой соответствует длине соответствующего буфера.

Это кажется немного грязным, чтобы предварительно инициализировать строку с фиктивными значениями. Есть ли другой / лучший способ сделать это?

...