Как отправить строку по ссылке в неуправляемую библиотеку C, которая изменяет эту строку? - PullRequest
10 голосов
/ 29 июня 2011

Я новичок в мире взаимодействия с неуправляемыми библиотеками. У меня есть неуправляемая функция C, которая изменяет строку по ссылке внутри функции. У меня возникают проблемы при передаче строки из C # и ее модификации с помощью функции C.

Вот функция C:

__declspec(dllexport) void __stdcall Test(char* name)
{
    *name = "Bar";
}

Это код импорта DLL C #:

[DllImport(@"C:/blah/mylibrary.dll")]
public extern static string Test(string name);

Это код, который я использую для вызова функции:

string s = "foo";
Test(s);
//I want s to be "Bar" after the above line

Я пытался использовать «ref» и «out» для строкового параметра и пробовал Marshalling как LPStr. В зависимости от того, что я пытаюсь, я получаю сообщение об ошибке типа

«Указатель, переданный в виде строки, не должен находиться в нижних 64 КБ адресного пространства процесса».

или

«Попытка чтения или записи в защищенную память. Это часто свидетельствует о повреждении другой памяти».

Я уверен, что просто делаю что-то глупое с моими указателями. Может ли кто-нибудь помочь мне определить соответствующий код C #, чтобы "s" равнялся "bar"?

Спасибо

1 Ответ

12 голосов
/ 29 июня 2011

Ваша функция C Test не делает ничего, как вы сказали. Все, что он делает, это берет локальную переменную (name) и присваивает ее фиксированной строке. Чтобы сделать то, что вы сказали, нужно было бы выполнить операцию copy по адресу, указанному name:

__declspec(dllexport) void __stdcall Test(char* name)
{
    strcpy(name, "Bar");
}

Конечно, такая операция - это катастрофа в ожидании, поскольку у вас неверная сигнатура функции (длина буфера не указана).

Учитывая, что функция C такая же, как и выше, следует соблюдать правила, указанные в Маршалинг по умолчанию для строк :

В некоторых случаях фиксированная длина буфер символов должен быть передан в неуправляемый код для манипулирования. Простая передача строки не работает в этом случае, потому что вызываемый не может изменить содержимое переданного буфер. Даже если строка пройдена по ссылке, нет никакого способа инициализировать буфер до заданного размера.

Решение состоит в том, чтобы передать Буфер StringBuilder в качестве аргумента вместо строки. StringBuilder может быть разыменовано и изменено вызываемый, если это не так превышать возможности StringBuilder. Это также может быть инициализируется до фиксированной длины. За Например, если вы инициализируете StringBuilder буфер до емкости N, маршалер обеспечивает буфер размер (N + 1) символов. +1 аккаунты за то что неуправляемая строка имеет нулевой терминатор в то время как StringBuilder - нет.

Итак, ваша DLL должна быть такой:

[DllImport(@"C:/blah/mylibrary.dll")]
public extern static string Test(StringBuilder name);

и вызовите его, передав правильное значение StringBuilder:

StringBuilder foo = new StringBuilder(256);
Test(foo);

Если вы добавите параметр длины, к интерфейсу C будет добавлено некоторое здравомыслие.

...