2016-06-09 18 views
11

мне нужно передать аргумент в небезопасное DllImported функции в виде:Как получить символ ** с помощью C#?

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

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

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

EDIT 1: вопрос был отмечен как дубликат, но, глядя на возможный дубликат вопрос, я до сих пор не понимаю, как получить эту работу.

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

EDIT 3: Я получил дополнительную информацию от поставщика сторонней библиотеки. Здесь:

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

РЕДАКТИРОВАТЬ 4: Заключительное редактирование с рабочим решением (по крайней мере, в моем случае). Я бы ответил на этот вопрос, но не могу, потому что этот вопрос отмечен как дубликат. Вот link на вопрос, который мне очень помог. В конце функция 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; 
} 
+0

'char *' Я знаю, как это сделать, но 'char **' ...? Argh. – code4life

+2

Согласен - вопрос на самом деле краткий.Я не сделал этого, поэтому я не могу ответить :) –

+0

Я думаю, вам нужно использовать StringBuilder [] вместо строки [] – RomCoo

ответ

0

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

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

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

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

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

+1

Он скомпилировался без меня, чтобы изменить подпись DllImport, но я все еще получаю исключение PInvokeStackImbalance. Похоже, что вызов работает частично, потому что я вижу, что ведение журнала DLL для сторонних разработчиков выводит окно. Мне нужно проверить, что что-то еще не вызывает его, прежде чем я приму свой ответ. +1 на данный момент. – Eternal21

+0

@ Eternal21 Учитывая ошибку PInvokeStackImbalance, я собираюсь угадать, что вы были правы, поскольку это «массив строк», хотя было бы очень полезно увидеть более подробную информацию о сторонней библиотеке. Стив на правильном пути, что вы должны использовать маршала, чтобы преобразовать строки в небезопасные указатели, но я думаю, вам нужно выяснить, что эти строки должны быть, а затем построить свой массив из ваших строк. –

2

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

+0

Я решил вручную «Маршал» данных в виде одного байтового [] буфера. Ive отредактировал мой вопрос с новым кодом, который я придумал. Похоже, что он должен работать, но библиотеке это не нравится, в итоге я получаю доступ к нему. Не уверен, что это потому, что я использовал IntPtr вместо байта **. – Eternal21

+0

Ваша подпись dllimport, вероятно, неверна. Маловероятно, что функция ожидает C# 'char **', которая будет похожа на 'wchar_t **' на C. Вероятно, она должна быть просто 'string []' и позволить P/Invoke обрабатывать маршаллинг, как в связанные дублирующиеся вопросы. – Asik

+0

Функция не ожидает C# char **, поэтому я использовал байт, как вы предлагали. – Eternal21