Хобрук: Ваш путь к мастерству в программировании

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

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

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

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

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

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

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

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

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

РЕДАКТИРОВАНИЕ 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;
}

  • char* Я знаю, как это сделать, но char**...? Арх. 09.06.2016
  • Согласен - вопрос на самом деле краткий. Я этого не делал, поэтому не могу ответить :) 09.06.2016
  • Я думаю, вам нужно использовать StringBuilder[] вместо string[] 09.06.2016
  • Возможно, но проблема с подписью все еще остается — char ** не является StringBuilder. 09.06.2016
  • @LukStorms Я не контролирую функцию. Это сторонняя dll. 09.06.2016
  • использование ссылки на char * может сделать это. 09.06.2016
  • Как насчет этой ссылки: stackoverflow. ком/вопросы/11572631/ 09.06.2016
  • Как насчет использования &args в контексте unsafe (где args — это string)? 09.06.2016
  • @MichaelDorgan В ответе на этот вопрос есть, казалось бы, полезная ссылка (Marshaling char**) mono -project.com/docs/advanced/pinvoke и, вероятно, я начну искать его, если более простое решение не появится. 09.06.2016
  • Обратите внимание, что если это char в C, это не будет char в C#; C char — 1 байт, а C# char — 2 байта. 09.06.2016
  • Понижающие голоса совершенно действительны. Спрашивать, как вызвать неуправляемую функцию без указания этой неуправляемой функции, бессмысленно. Тип char**. Отлично. Но какова семантика? 09.06.2016
  • Я чувствую, что это недостающая информация; сколько строк вы ожидаете добавить в массив? Являются ли они нулевым завершением или строками фиксированной длины? 09.06.2016
  • @DavidHeffernan Я обновил вопрос соответствующей информацией. 10.06.2016
  • @MeirionHughes Вопрос обновлен. 10.06.2016
  • Нет, ты этого не сделал. Мы до сих пор понятия не имеем, что такое неуправляемый интерфейс. В любом случае, вы приняли ответ, и вопрос закрыт. Я поражен интересом к этому вопросу. Я не уверен, почему так много активности, но, к сожалению, вы получили огромное количество плохих советов. Ответов быть не должно, потому что ваш вопрос не совсем точно сформулирован. Пока вы не сможете указать интерфейс, которому вы пытаетесь соответствовать, писать код бессмысленно. То, что вы делаете все эти пробы и ошибки, — просто огромная трата времени. 10.06.2016
  • @DavidHeffernan Так много активности, потому что main(int argc, char** argv) является довольно распространенной записью в собственных библиотеках c dll, и нет ни одного вопроса о переполнении стека, который бы отвечал на вопрос, как маршалировать данные через него. Конечно, не предполагаемые повторяющиеся вопросы (один говорит о char*, другой о const char**). 11.06.2016
  • Вы все еще не определили интерфейс. Обычно argv имеет длину argc + 1 с последней записью, равной NULL. Так ли это здесь? Вы плохо задали вопрос и получили много плохих советов. 11.06.2016
  • @DavidHeffernan Все, что я получаю от поставщика, это то, что они ожидают, что первым аргументом будет имя исполняемого файла, без упоминания последнего аргумента NULL. Я проверю еще раз, чтобы быть уверенным. В любом случае, я, наконец, заработал (см. мое последнее редактирование). Функция ожидала массив указателей на буферы строк, а не конкатенированные буферы строк, как мне сказали изначально. Как только я внес это изменение, приложение начало работать, как и ожидалось. Ваше замечание по поводу аргумента NULL является хорошим, так как теперь оно может сработать случайно, если память за пределами моих указателей просто обнулится. 13.06.2016
  • Только что проверено, они используют argc для выхода из цикла синтаксического анализа. 13.06.2016

Ответы:


1

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

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

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

09.06.2016
  • Он скомпилирован, и мне не пришлось менять подпись DllImport, но я все равно получаю исключение PInvokeStackImbalance. Однако вызов, похоже, работает частично, потому что я вижу, как сторонние dll регистрируются в окне вывода. Мне нужно убедиться, что что-то еще не вызывает этого, прежде чем я приму ваш ответ. +1 пока. 09.06.2016
  • @ Eternal21 Учитывая ошибку PInvokeStackImbalance, я предполагаю, что вы были правы в том, что это массив строк, хотя было бы очень полезно увидеть более подробную информацию о сторонней библиотеке. Стив находится на правильном пути, что вы должны использовать Marshal для преобразования строк в небезопасные указатели, но я думаю, вам нужно выяснить, какими должны быть эти строки, а затем построить свой массив из ваших строк. 09.06.2016

  • 2

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

    09.06.2016
  • Я решил вручную «упорядочить» данные в виде однобайтового [] буфера. Я отредактировал свой вопрос с новым кодом, который я придумал. Вроде должно работать, но библиотеке это не нравится, получаю от нее Access Violation. Не уверен, что это потому, что я использовал IntPtr вместо byte**. 10.06.2016
  • Ваша подпись dllimport, вероятно, неверна. Маловероятно, что функция ожидает C# char**, который был бы подобен wchar_t** в C. Вместо этого, вероятно, следует просто string[] и позволить P/Invoke заниматься сортировкой, как в связанных повторяющихся вопросах. 11.06.2016
  • Функция не ожидает символ С# **, поэтому я использовал байт, как вы предложили. 11.06.2016
  • Но тогда подпись C# DllImport неверна, потому что она использует C# char. 11.06.2016
  • Все исправлено теперь в моем окончательном редактировании. Ваш байтовый комментарий был полезен. 13.06.2016

  • 3

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

    StringBuilder[] args = new StringBuilder[] { };
    start(args);
    
    09.06.2016
    Новые материалы

    Правильное тестирование машинного обучения: обнаружение ошибок в данных.
    Эта статья была первоначально размещена на нашем сайте компании . Платформа для разработчиков Lakera позволяет командам машинного обучения создавать отказоустойчивые модели компьютерного..

    Обучение SAP FICO в NOIDA.
    Лучший провайдер SAP Training с целевым размещением SAP-коучинга в Нойде. Наш курс SAP концентрируется от коучинга базового уровня до продвинутого уровня и охватывает как функциональные, так и..

    Sinkhorn Knopp: поиск оптимального транспорта для выравнивания данных
    В области выравнивания данных и оптимального транспорта алгоритм Синкхорна-Кноппа стал мощным инструментом для решения задач оптимизации транспорта. С приложениями, варьирующимися от сопоставления..

    Мой начальный путь к микросервисам с Spring Boot
    Почему мы используем микросервисы? Микросервисы — это шаблон проектирования программного обеспечения, который включает создание большого приложения в виде набора небольших независимых..

    Уменьшите количество шаблонов при запуске тестов Kotlin
    Используя изящный трюк Kotlin, вы можете сделать свои тесты чистыми и простыми для понимания и обслуживания. Тестирование должно быть легким. Если ваши тесты слишком сложны и сложны в..

    Понимание React.js: гармоничная симфония компонентов и модульность в стиле LEGO
    Понимание React.js: гармоничная симфония компонентов и модульность в стиле LEGO Представляем искусство и науку, лежащую в основе строительных блоков React.js, React Components, которые помогают..

    Почему я, журналист, в отчаянии создал сетевое приложение B2B
    Почему я, журналист, в отчаянии создал сетевое приложение B2B Итак, вот верхняя линия. Я построил OnGreentech, сеть для индустрии возобновляемых источников энергии. Если вам интересно,..