Как получить char ** с помощью С#?

Мне нужно передать аргумент небезопасной функции DllImported в виде:

[DllImport("third_party.dll")]
private static extern unsafe int start(int argc, char** argv);

Я предполагаю, что это массив строк. Однако, когда я пытаюсь сделать следующее, я получаю сообщение "Невозможно преобразовать из строки [] в char **". Как я могу заставить это работать? Спасибо.

string[] argv = new string[] { };
start(0, argv);

РЕДАКТИРОВАТЬ 1: Вопрос был отмечен как дубликат, но, глядя на возможный дублирующий вопрос, я до сих пор не вижу, как заставить это работать.

РЕДАКТИРОВАТЬ 2: Чтобы добавить clafiry вопрос и необходимые параметры. Он похож на ваши стандартные параметры argc/argv (количество параметров, а затем значения параметров). Точно так же вы можете запустить c-программу: int main(int argc, char** argv); Для этой конкретной проблемы я не хочу передавать какие-либо аргументы вообще (так что count равно 0).

ИЗМЕНИТЬ 3: Я получил дополнительную информацию от поставщика сторонней библиотеки. Вот он:

  • первым параметром является количество аргументов
  • второй параметр представляет собой массив строк с нулевым завершением
  • строки кодируются ANSI

EDIT 4: окончательное редактирование с рабочим решением (по крайней мере, в моем случае). Я бы ответил на этот вопрос, но не могу, потому что этот вопрос отмечен как дубликат. Здесь ссылка на вопрос, который мне очень помог. В конце функция dll ожидала массив указателей на буферы с цепочками ANSI. Итак, мой окончательный подход (основанный на связанном вопросе) был следующим. Создайте массив в памяти, чтобы удерживать указатели, затем выделяйте каждую строку в другом месте в памяти и указывайте указатели на те строки внутри первого массива указателей. Этот код работает в производстве:

[DllImport("third_party.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern int start(Int32 args, IntPtr argv);

public bool start(params string[] arguments)
{
    int result;

    if (arguments == null || arguments.Length == 0)
    {
        result = dll_system_startup(0, IntPtr.Zero);
    }
    else
    {
        List<IntPtr> allocatedMemory = new List<IntPtr>();

        int sizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr));
        IntPtr pointersToArguments = Marshal.AllocHGlobal(sizeOfIntPtr * arguments.Length);

        for (int i = 0; i < arguments.Length; ++i)
        {
            IntPtr pointerToArgument = Marshal.StringToHGlobalAnsi(arguments[i]);
            allocatedMemory.Add(pointerToArgument);
            Marshal.WriteIntPtr(pointersToArguments, i * sizeOfIntPtr, pointerToArgument);
        }

        result = start(arguments.Length, pointersToArguments);

        Marshal.FreeHGlobal(pointersToArguments);

        foreach (IntPtr pointer in allocatedMemory)
        {
            Marshal.FreeHGlobal(pointer);
        }
    }

    return result == 0;
}

Ответ 1

Я думаю, вам, возможно, понадобится использовать маршала.

var a = (char*)Marshal.StringToCoTaskMemAuto("myString");
char** = &a;

Это просто предположение, потому что у меня нет библиотеки, которая принимает char **, поэтому я не смог ее попробовать.

Ответ 2

Эквивалент C char** является полностью закрепленным byte[][] в С# (и полностью закрепленным я имею в виду внешний массив AND все внутренние массивы). Если вы хотите передать строки С#, вам придется преобразовать их в массивы байтов, например, с помощью Encoding.ASCII.GetBytes.

Ответ 3

Можете ли вы изменить тип параметра объявления DllImport на StringBuilder[]? Затем вы можете вызвать функцию:

StringBuilder[] args = new StringBuilder[] { };
start(args);