Как сделать маркер void * с вызовом платформы

Мне нужно вызвать функцию из C API, содержащегося в DLL. Функция-прототип выглядит следующим образом ....

int func( char* name, void* value );

где содержимое значения указателя может ссылаться на любой тип, зависящий от переданного имени. Я не уверен, как настроить вход в Dll, чтобы правильно упорядочить эту пустоту *. Я экспериментировал с IntPtr, который, кажется, работает, когда значение является целым, но я не могу получить значения правильно для чисел с плавающей запятой и т. Д.

Я пытаюсь импортировать функцию, как это ...

[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, ref IntPtr value );

Пожалуйста, обратите внимание, что значение является выходным. Указатель на значение любого типа, то есть адрес в глобальной области памяти значения известного типа (известного вызывающей стороне). В проге c ожидается, что вызывающая сторона применит этот void * к желаемому типу и разыменованию, чтобы получить фактическое значение, хранящееся там. Ответы, представленные до сих пор, основаны на предположении, что функция запишет результат в переданное местоположение указателя. Моя ошибка, поскольку я не был слишком конкретным. Сожалею. C # не моя сумка, и я даже не знаю, является ли IntPtr подходом ...

Ответы на вопрос(4)

[DllImport("dllname.dll", CharSet = CharSet.Ansi)] 
public static extern int func( string name, IntPtr value ); 

...

// n - number of bytes which is enough to keep any type used by function
IntPtr ptr = Marshal.AllocHGlobal(n);
func(name, ptr);

// Use Marshal.ReadByte, Marshal.ReadInt32 ... or Marshal.Copy
// to copy from ptr filled by func to managed variable. For example:
byte b = Marshal.ReadByte(ptr);

Marshal.FreeHGlobal(IntPtr);

Иногда может быть проще использовать этот подход.

Декларация:

[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func([MarshalAs(UnmanagedType.LPTStr)] string name, [MarshalAs(UnmanagedType.AsAny)] object value);

Пример использования для передачи значения:

func("some string", (int)12);
func("some string", (double)12);

Пример использования для передачи строки:

func("some string", "test");

Пример использования для передачи переменной:

int value = 12;
func("some string", value);

Вам не нужноref - IntPtr это способ пройтиvoid* в родной код.

[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, IntPtr value );

РЕДАКТИРОВАТЬ: код C может использовать вход для записи в необходимую память. Проблема, с которой вы сталкиваетесь, заключается в том, что управляемый код знает, сколько памяти выделяется для каждого возможного типа возвращаемого значения. Тогда блок соответствующего размера может быть выделен с помощьюMarshal.AllocHGlobal или жеMarshal.AllocCoTaskMem, освобожденный (в зависимости от того, какой метод размещения вы используете) использоватьFreeHGlobal или жеFreeCoTaskMemпосле того, как управляемый код сделан с выходным значением.

Смотрите ответ от @Alex Farber для примера.

 matt04 окт. 2010 г., 15:53
@steve - это указатель, который указывает на значение в области памяти.
 matt04 окт. 2010 г., 14:43
Я должен был быть более конкретным извините. Значение IntPtr является выходным. В случае этой функции это будет указатель на значение, которое может быть любого типа. Ожидается, что вызывающий абонент знает тип, который он пытается получить.
 Steve Townsend04 окт. 2010 г., 15:44
@matt - ваш код на C не может вернуть указатель через параметрvoid*, Это входной параметр. Ты имел ввидуvoid** ? Возможно, именно поэтому у вас есть проблемы с решением этой проблемы.
 Steve Townsend04 окт. 2010 г., 15:28
@matt - ознакомьтесь с ответом и комментариями Ганса - это ваш лучший выбор.
 matt04 окт. 2010 г., 15:36
Все еще не уверен, что я был достаточно конкретен ... Функция вернет указатель на значение, хранящееся в глобальной области памяти, которая затем может быть разыменована по типу, чтобы получить фактическое значение, хранящееся в этом месте ...
 Steve Townsend04 окт. 2010 г., 15:19
@matt - ответ все тот же - см. редактирование.

Лучший способ справиться с этим - обеспечить перегрузку функции, чтобы все было безупречно чисто на стороне C #. Как это:

[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func(string name, out int value);
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func(string name, out float value);
// etc, one each for each type
 Steve Townsend04 окт. 2010 г., 15:22
согласен, при условии, что код C может быть изменен, это лучшее решение.
 Steve Townsend04 окт. 2010 г., 15:28
тогда это лучшее решение и +1 еще более заслужен
 Hans Passant04 окт. 2010 г., 15:26
@Steve - никаких изменений кода C не требуется. Он просто видит указатель, который проходит маршаллер пинвока.

Ваш ответ на вопрос