2016-03-16 10 views
0

у меня есть этот родной интерфейс:Как связать родной DLL в C# класс с данными полукокса *

void CLASS_Version(char *Version); 

Я пытался импортировать его:

[DllImport("class.dll", EntryPoint = "CLASS")] 
private static extern void CLASS_Version(ref string[] Version); 

или

[DllImport("class.dll", EntryPoint = "CLASS")] 
private static extern void CLASS_Version(ref char[] Version); 

[DllImport("class.dll", EntryPoint = "CLASS")] 
private static extern void CLASS_Version(out string[] Version); 

[DllImport("class.dll", EntryPoint = "CLASS")] 
private static extern void CLASS_Version(out char[] Version); 

Но я всегда получаю ошибку «AccessViolation», Единственный хороший прогон был сделан с

[DllImport("class.dll", EntryPoint = "CLASS")] 
private static extern void CLASS_Version(ref char Version); 

но таким образом я получил только первый символ строк ... как получить всю строку?

+0

Почему не просто 'string'? –

+3

Потому что строка не изменена. Вы должны передать StringBuilder с достаточной емкостью. Нет * ref * или * out *. И произнесите небольшую молитву о том, что она не испортит кучу GC, не поставит ее в цель. –

+0

Для начала я хотел бы указать, что 'EntryPoint' должен' = "CLASS_Version", если это имя метода. Если вы используете одно и то же имя метода при его объявлении, вам даже не нужно определять свойство «EntryPoint», это необходимо только в том случае, если вы переименовываете функцию из своего выбора или из-за нарушения имени в собственном коде, вы не хотите реплицировать управляемый код. – Pharap

ответ

4

char * неоднозначно, но это определенно не массив строк. Скорее всего, это указатель на строку, поэтому вы будете использовать просто StringBuilder (нет ref или out).

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

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

+1

Я верю, что если вы получите неправильное соглашение о вызове, CLR предоставит вам исключение, объясняющее именно это. Хотя я мог ошибаться. –

+2

@ TheodorosChatzigiannakis Только, если вам очень повезло. Если вам не повезло, один случайный день будет раздавлен случайным образом. Или, может быть, вы перейдете от 32 до 64 бит, и все начнет бум. – xanatos

+1

@ TheodorosChatzigiannakis Невозможно узнать с родными конечными точками. Нет метаданных, и нет способа «вывести», если ваше соглашение о вызове «сработало». Это всегда было проблемой с собственным кодом - избегание подобных проблем было одной из причин, лежащих в основе разработки JVM и .NET CLR. Представьте себе, как это будет работать: вызывающая сторона ожидает параметры в стеке, а вызывающая сторона отправляет их в регистры. Вызывающий вызывает параметры из стека (всегда есть что-то поп: P), и оба видят действительные (ish) состояния. В режиме отладки .NET предупреждает вас о дисбалансе стека, но все. – Luaan

0

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

попробовать:

[DllImport("class.dll", EntryPoint = "CLASS_Version")] 
private static extern void CLASS_Version(IntPtr Version); 
0

попробовать это:

[DllImport("class.dll", EntryPoint = "CLASS")] 
private static extern void CLASS_Version([MarshalAs(UnmanagedType.VBByRefStr)] ref string Version); 

И когда вы собираетесь назвать свой метод:

Version = Space(14);// first declare memory space requiered ¿14 bytes? ¿more? 
CLASS_Version(Version); 
0

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

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

Учитывая, что это обработка строк, вам также нужно беспокоиться о кодировании. Ради простоты я собираюсь предположить (и надеюсь), что ваша строка находится в ASCII/Windows-1252, а не UTF8, UTF7 или какой-либо другой формат.

Учитывая эти предположения, у вас есть несколько вариантов, но я буду представлять только предпочтительный способ обработки этого на основе предоставленной информации.(Это может быть, что ваша ситуация требует что-то другое, но это лучшее решение, которое я могу предложить на основе asumptions выведенного из информации в вашем вопросе.)

[DllImport("class.dll", EntryPoint = "CLASS_Version", , CharSet = CharSet.Ansi)] // CharSet is important 
private static extern void CLASS_Version(StringBuilder Version); 

Это «правильный» способ справиться с ситуацией - полагайтесь на компилятор, чтобы обработать маршаллинг для вас. Однако одно небольшое предостережение заключается в том, что вы должны вручную установить емкость StringBuilder, прежде чем передавать его методу.

// be sure to allocate a correct size, 
// there will be no error if it overflows because it's too small 
StringBuilder buffer = new StringBuilder(size); 
// automagically marshalled to and from unmanaged code byt the compiler 
CLASS_Version(buffer); 
string version = buffer.ToString(); 

Я хотел бы воспользоваться этой возможностью, чтобы указать на то, что CLASS_Version не должно быть private. Все ваши собственные методы должны быть сделаны public и сгруппированы вместе в одном internal static class.

Некоторые дополнительные ресурсы о струнной сортировочных, которые вы можете найти под рукой: https://limbioliong.wordpress.com/2011/11/01/using-the-stringbuilder-in-unmanaged-api-calls/