.NET使用System.Runtime.InteropServices.Marshal实现委托与函数指针互转

本文介绍了.NET Framework中如何使用Marshal类的GetDelegateForFunctionPointer和GetFunctionPointerForDelegate方法实现非托管函数指针与托管委托之间的转换。包括转换过程中的参数说明、返回值、异常处理及使用限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数介绍

摘自MSDN

命名空间: System.Runtime.InteropServices

函数:Marshal.GetDelegateForFunctionPointer

(将非托管函数指针转换为委托)

publicstatic Delegate GetDelegateForFunctionPointer(
    IntPtr ptr,
    Type t
)
参数
ptr

类型:System.IntPtr

要转换的非托管函数指针。
t
类型: System.Type
要返回的委托的类型。
返回值
类型: System.Delegate
委托实例,可强制转换为适当的委托类型。
异常
异常条件
ArgumentException

t 参数不是委托或泛型。

ArgumentNullException

ptr 参数为 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing)

- 或 -

t 参数为 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing)

备注

在 .NET Framework 1.0 或 1.1 版中,可以将表示托管方法的委托作为函数指针传递给非托管代码,因此,非托管代码可以通过函数指针调用托管方法。非托管代码也可以将该函数指针传递回托管代码,指针可以正确解析为基础托管方法。

在 .NET Framework 2.0 及更高版本中,可以使用 GetDelegateForFunctionPointer 方法和GetFunctionPointerForDelegate 方法双向封送委托。 用 GetDelegateForFunctionPointerptr 将导入为System.IntPtr。 通过调用 GetFunctionPointerForDelegate 可以为托管委托获得 System.IntPtr,然后作为参数传递;此后可以从非托管方法中调用。 注意,在 .NET Framework 2.0 及更高版本中,参数封送拆收器还可将函数指针封送到委托。

GetDelegateForFunctionPointer 方法具有下列限制:

  • 在互操作方案中不支持泛型。

  • 不能将无效的函数指针传递给此方法。

  • 此方法只能用于纯非托管函数指针。

  • 不能将此方法用于通过 C++ 或从 GetFunctionPointer 方法获取的函数指针。

  • 此方法不能用于由指向另一托管委托的函数指针创建委托。

.NET Framework 安全性
  • SecurityCriticalAttribute 

    需要完全信任直接调用方。此成员不能由部分受信任或透明的代码使用。

函数:Marshal.GetFunctionPointerForDelegate

(将委托转换为可从非托管代码调用的函数指针)

publicstatic IntPtr GetFunctionPointerForDelegate(
    Delegate d
)
参数
d
类型: System.Delegate
要传递给非托管代码的委托。
返回值
类型: System.IntPtr
一个可传递给非托管代码的值,非托管代码使用该值来调用基础托管委托。
异常
异常条件
ArgumentException

d 参数是泛型类型。

ArgumentNullException

d 参数为 nullNothingnullptrnull 引用(在 Visual Basic 中为 Nothing)

备注

委托 d 转换为可使用 __stdcall 调用约定传递给非托管代码的函数指针。

必须以手动方式阻止垃圾回收器从托管代码中回收该委托。垃圾回收器不跟踪对非托管代码的引用。

例如一个函数指针定义为:

typedef int (*FUN)(int iType,void *pvArgs);

而一个委托定义为:

public delegate int TEST_DLGT(int iType, IntPtr pvArgs);

则可以通过如下方法完成他们之间的互相转换
        public delegate int TEST_DLGT(int iType, IntPtr pvArgs);

        public Form1()
        {
            TEST_DLGT funCallBack1 = new TEST_DLGT(this.testfun);
            IntPtr pvFun = Marshal.GetFunctionPointerForDelegate(funCallBack1);//可将pvFun强制转化为void*,再强制转化为FUN类型
            TEST_DLGT funCallBack12 = (TEST_DLGT)Marshal.GetDelegateForFunctionPointer(pvFun, typeof(TEST_DLGT));
        }

在托管C++中,则应写为:

int ClassName::Start(TEST_DLGT ^funCallBack1)//委托通常在托管代码(C#)中创建,传入由托管C++封装的类库。这里假设ClassName是一个CLR类库的接口类。
{
	FUN pfun1=(FUN)(void*)Marshal::GetFunctionPointerForDelegate(funCallBack1);
	TEST_DLGT ^funCallBack2=(TEST_DLGT^)Marshal::GetDelegateForFunctionPointer((IntPtr)(void*)pfun1, TEST_DLGT::typeid);
	return 0;
}

----------------------------EOB--------------------------------

在Kylin系统中使用.NET Core框架动态加载`.so`(shared object)后缀的库,通常涉及跨平台运行时(比如Mono)和使用第三方工具(如P/Invoke)。由于.NET Core并不直接支持Linux上的`.so`库,以下是几个步骤来实现这一目标: 1. **使用Mono**:如果你的应用是在Linux上,或者需要支持.NET Core在非Windows平台上运行,你需要使用 Mono 运行时,它是 .NET的一个开源实现。安装相应的NuGet包,如`Mono.Posix`,它提供了对POSIX API的支持,包括动态链接。 ```dotnet dotnet add package Mono.Posix ``` 2. **LoadLibrary函数**:在.NET Core中,你需要通过`DllImport`特性来引用`LoadLibrary`或`dlopen`这样的函数,它们允许你在运行时动态加载共享库。 ```csharp [DllImport("libdl", SetLastError = true)] private static extern IntPtr dlopen(string fileName, int flags); ``` 3. **GetProcAddress函数**:接着,你可以使用`GetProcAddress`函数来获取库中的函数地址,然后就可以像调本地函数一样调用它了。 ```csharp [DllImport("libdl", SetLastError = true)] private static extern IntPtr dlsym(IntPtr handle, string symbolName); ``` 4. **示例代码**: ```csharp using System; using System.Runtime.InteropServices; class Program { [DllImport("libdl", SetLastError = true)] private static extern IntPtr dlopen(string fileName, int flags); [DllImport("libdl", SetLastError = true)] private static extern IntPtr dlsym(IntPtr handle, string symbolName); static void Main(string[] args) { string libFilePath = "/path/to/your/so/library.so"; IntPtr handle = dlopen(libFilePath, 0); // RTLD_NOW | RTLD_LAZY if (handle == IntPtr.Zero) { Console.WriteLine("Failed to load the shared library."); return; } try { IntPtr funcPtr = dlsym(handle, "YourFunctionName"); // 替换为你想要调用的函数名 if (funcPtr == IntPtr.Zero) { Console.WriteLine("Failed to find function in the library."); } else { // 现在你可以使用Marshal.PtrToStructure将函数指针转换为所需类型的函数 Func<int> yourFunction = Marshal.GetDelegateForFunctionPointer(funcPtr, typeof(Func<int>)); int result = yourFunction(); // 调用你的函数 } } finally { dlclose(handle); // 关闭动态链接库 } } } ``` 请注意,实际过程可能因库的具体结构和函数签名而异。对于复杂的库,你可能还需要处理异常、线程安全性和错误检查等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值