2009-10-16 2 views
4

У меня есть C API с подписью:Как я могу передать указатель на целое число в C#

int GetBuffer(char* buffer, int* maxSize) 

В C, я буду называть его таким образом:

char buffer[4096]; 
int maxSize = 4096; 
GetBuffer(buffer, &maxSize); 

MaxSize установлен к размеру буфера и установить с заполненным фактическим размером.

Мне нужно позвонить ему из C#. Как это сделать в безопасном режиме?

+0

Под «безопасный режим», я предполагаю, что вы имеете в виду без использования «небезопасным {...}» –

+1

Если он хочет, чтобы код, чтобы быть «безопасными» - почему бы вы предположить, что он хотел бы использовать небезопасное ключевое слово. Таким образом, его код небезопасен? Это просто не имеет никакого смысла. –

+0

@Miky: «w/o» в комментарии Дэна означает «без». Попробуйте снова прочитать эту замену. –

ответ

2

Наличие ручка указателя не подходит для модели «безопасного режима». Если ресурс не управляется Framework, он небезопасен.

2

Вам необходимо использовать так называемое P\Invoke и сгенерировать объявление функции, которое ссылается на функцию C в Dll из C#.

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

[DllImport("Kernel32.dll", SetLastError=true)] 
static extern Int32 GetBuffer(byte[] buffer,ref Int32 maxSize); 

И использовать его:

byte[] myBuf = new myBuf[4096]; 
Int32 maxSize = myBuf.Length; 

GetBuffer(myBuf, ref maxSize); 
4

Одним из вариантов является просто использовать C типы # указатель - это требует unsafe блок (или модификатор метода/класса) и компиляции с /unsafe:

[DllImport(...)] 
static extern int GetBuffer(byte* buffer, ref int maxSize); 

Буфер можно выделить несколькими способами. Можно было бы использовать возлагали массив кучи:

fixed (byte* buffer = new byte[4096]) 
{ 
    int maxSize = buffer.Length; 
    GetBuffer(buffer, ref maxSize); 
} 

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

byte* buffer = stackalloc byte[4096]; 
int maxSize = 4096; 
GetBuffer(buffer, ref maxSize); 

Данный подход практически идентичен кода C в условия исполнения и схемы распределения.

Другой вариант - использовать маршалинг для массивов кучи и полностью избегать указателей.

[DllImport(...)] 
static extern int GetBuffer([Out] byte[] buffer, ref int maxSize); 

byte[] buffer = new byte[4096]; 
int maxSize = buffer.Length; 
GetBuffer(buffer, ref maxSize); 
+0

Вы также можете избежать небезопасного кода, выделив ваш массив, а затем с помощью GCHandle, чтобы получить указатель на первый элемент (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle.aspx). – plinth

+0

'char' in C * технически *' sbyte' в большинстве случаев, но обычно вы работаете с ними как 'byte'. Я изменил его на 'byte' в приведенном выше коде. –

+0

Спасибо, это была моя ошибка (и я не знаю ни одной платформы, на которой вы можете получить .NET, а C 'char' woudn't соответствует C#' byte'). –

3

Это должно работать без опасного кода.

extern int GetBuffer(IntPtr buffer, ref int bufSize); 

// ... 
byte[] buf = new byte[kBufSize]; 
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); // possibly expensive call 
IntPtr p = handle.AddrOfPinnedObject(); 
int size = buf.Length; 
int ret = GetBuffer(p, ref size); 
handle.Free(); 
Смежные вопросы