假设我们有一个带有以下原型的C ++函数:
int myFunction(int someNumber, int &arraySize, signed char *&array)
// Extra function to free allocated memory:
int freePointer(void* myPointer)
此函数需要一些数字,并根据该数字创建一个数组。所以我们传递一个数字并得到一个数组。用C#调用它的最佳方法是什么?
我的第一个方法:
[DllImport(...)]
internal static int myFunction(int someNumber, out int arraySize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out byte[] array)
// ...
byte[] myArray;
int discard;
int myNumber = 100;
myFunction(myNumber, discard, myArray);
但问题是,我需要释放非托管库分配的内存,而我不知道如何将myArray
的地址传递给freePointer()
。而且,我不知道myArray
的地址是否等于非托管库创建的数组的地址。
然后我的第二种方法是:
[DllImport(...)]
internal static int myFunction(int someNumber, out int arraySize, out IntPtr array)
// ...
byte[] myArray;
int myArraySize
IntPtr pointerToArray;
int myNumber = 100;
myFunction(myNumber, myArraySize, pointerToArray);
Marshal.Copy(pointerToArray, myArray, 0, myArraySize);
FreePointerHandler(pointerToArray.ToPointer());
但同样,我不确定我是否通过使用方法IntPtr
发送.ToPointer()
的地址来做到这一点。
我不想引入内存泄漏,你建议做什么? (不使用不安全的代码。)
修改:我应该使用ref
而不是out
,因为参数是对指针的引用吗?
答案 0 :(得分:2)
你的第二种方法对我来说是正确的。你在哪里:
FreePointerHandler(pointerToArray.ToPointer());
我认为你可以:
FreePointerHandler(pointerToArray);
我正在编写测试并发布编辑。
ref
与out
就ref
与out
而言,这并不完全映射到C ++引用。当C#调用C#时,被调用函数需要设置out
参数。如果没有强制执行互操作,对于文档值,在这种情况下我更喜欢out
。
真正的问题是,如果你没有.Net pinvoke的经验,那么在调用函数时很难有信心而没有任何直接的,可见的副作用。
@Hans Passant建议“通过调用它一百万次来测试它”,以使任何内存泄漏明显。虽然这种方法在这种情况下可能是最简单的,但它并没有推广到没有相关内存资源的情况,如果失败则不会提供任何其他信息。如果我有.dll的源代码,或者如果不这样做,我更喜欢通过pinvoke调用进行调试。这样,当您尝试新的pinvoke功能时,您可以确切地看到发生了什么。如果出现问题,在调试器中进行跟踪可以帮助您查看出错的地方。
以下是基于原始问题的完整示例。没有记录返回值,也没有记录myFunction的详细信息。该示例仅使用(0,1,2)填充长度为3的已分配数组。
这是DLL的头文件:
// testlib.h
#ifdef TESTLIB_EXPORTS
#define TESTLIB_API __declspec(dllexport)
#else
#define TESTLIB_API __declspec(dllimport)
#endif
extern "C" {
TESTLIB_API int myFunction(int someNumber, int &arraySize, signed char *&array);
TESTLIB_API int freePointer(void* myPointer);
}
.DLL的.cpp源文件:
// testlib.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "testlib.h"
#include <iostream>
using namespace std;
TESTLIB_API int myFunction(int someNumber, int &arraySize, signed char *&array)
{
#ifdef _DEBUG
cout << "myFunction enter " << someNumber << ", " << arraySize << ", 0x" << &array << endl;
#endif
arraySize = 3;
array = (signed char*)malloc(arraySize);
#ifdef _DEBUG
cout << "myFunction array: 0x" << (void *)array << endl;
#endif
for (int i = 0; i < arraySize; ++i)
{
array[i] = i;
}
#ifdef _DEBUG
cout << "myFunction exit " << someNumber << ", " << arraySize << ", 0x" << &array << endl;
#endif
return 0;
}
TESTLIB_API int freePointer(void* myPointer)
{
#ifdef _DEBUG
cout << "freePointer: 0x" << myPointer << endl;
#endif
free(myPointer);
return 0;
}
C#来源:
using System;
using System.Runtime.InteropServices;
namespace InteropTest
{
class InteropTest
{
[DllImport("testlib.dll")]
private static extern int myFunction(int someNumber, out int arraySize, out IntPtr array);
[DllImport("testlib.dll")]
public static extern int freePointer(IntPtr pointer);
public static byte[] myFunction(int myNumber)
{
int myArraySize;
IntPtr pointerToArray;
int iRet = myFunction(myNumber, out myArraySize, out pointerToArray);
// should check iRet and if needed throw exception
#if (DEBUG)
Console.WriteLine();
Console.WriteLine("InteropTest.myFunction myArraySize: {0}", myArraySize);
Console.WriteLine();
#endif
byte[] myArray = new byte[myArraySize];
Marshal.Copy(pointerToArray, myArray, 0, myArraySize);
freePointer(pointerToArray);
#if (DEBUG)
Console.WriteLine();
#endif
return myArray;
}
static void Main(string[] args)
{
#if (DEBUG)
Console.WriteLine("Start InteropTest");
Console.WriteLine();
#endif
int myNumber = 123;
byte[] myArray = myFunction(myNumber);
for (int i = 0; i < myArray.Length; ++i)
{
Console.WriteLine("InteropTest myArray[{0}] = {1}", i, myArray[i]);
}
}
}
}
调试版本打印状态消息。他们显示myFunction
的{{1}}值是传递给array
的值。
我还为freePointer
创建了一个C#接口函数。当pinvoke调用非常简单时,您通常需要计算一次详细信息并将其封装到函数中以供重用。