gpt4 book ai didi

c# - 调用采用指针引用的非托管库函数

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:40:24 25 4
gpt4 key购买 nike

假设我们有一个具有以下原型(prototype)的 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());

但同样,我不确定通过使用方法 .ToPointer() 发送 IntPtr 的地址是否正确。

我不想引入内存泄漏,你会建议做什么? (不使用不安全代码。)

编辑:我应该使用 ref 而不是 out,因为参数是对指针的引用吗?

最佳答案

你的第二种方法对我来说似乎是正确的。你在哪里:

FreePointerHandler(pointerToArray.ToPointer());

我认为你可以:

FreePointerHandler(pointerToArray);

我正在编写测试并将发布修改。

refout

refout 而言,这并不完全映射到 C++ 引用。 C#调用C#时,被调用函数需要设置一个out参数。对于互操作调用,这不能强制执行,为了文档值(value),在这种情况下我更喜欢 out

真正的问题

真正的问题是,如果您没有使用 .Net pinvoke 的经验,那么在调用没有任何直接、可见的副作用的函数时很难有信心。

@Hans Passant 建议“通过调用它一百万次来测试它”以使任何内存泄漏变得明显。虽然这种方法在这种情况下可能是最简单的,但它并没有推广到没有关联内存资源的情况,如果失败,它也不会提供任何附加信息。如果我有 .dll 的源代码,我更喜欢通过 pinvoke 调用进行调试,如果没有,我更喜欢编写模拟 API 实现。这样,当您尝试新的 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]);
}
}
}
}

调试版本打印状态消息。它们显示 myFunctionarray 值是传递给 freePointer 的值。

我还为 myFunction 创建了一个 C# 接口(interface)函数。当 pinvoke 调用非常重要时,您通常希望一次计算出细节并将其封装到一个函数中以供重用。

关于c# - 调用采用指针引用的非托管库函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14816970/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com