.net按地址动态调用VC++DLL将非托管DLL中的函数地址转换为.NET可调用的委托

假如是findwindow(win as long,win2 as long,findclass as strnig,findtxt as strnig) as long 怎么包装

要理解这段代码的作用,以及如何包装类似 FindWindow 这样的函数,需要先搞清楚 非托管函数调用的核心逻辑 :.NET 托管代码无法直接调用非托管 DLL 的函数地址,必须通过 委托(Delegate) 作为 "桥梁",而 Marshal.GetDelegateForFunctionPointer 就是构建这座桥梁的关键方法。

一、先解析为什么需要那么长的代码

vbnet 复制代码
Dim lameInit As Func(Of IntPtr) = CType(Marshal.GetDelegateForFunctionPointer(lameInitAddr, GetType(Func(Of IntPtr))), Func(Of IntPtr))

这段代码的作用是 将非托管 DLL 中的函数地址(lameInitAddr)转换为 .NET 可调用的委托,拆解如下:

  1. lameInitAddr :非托管 DLL 中导出函数(如 lame_init)的内存地址(通过 GetProcAddress 获取)。这是一个纯内存地址(IntPtr 类型),.NET 无法直接调用,必须通过委托映射。

  2. Marshal.GetDelegateForFunctionPointer:.NET 提供的互操作 API,作用是

    • 第一个参数:非托管函数的内存地址(lameInitAddr)。
    • 第二个参数:委托类型GetType(Func(Of IntPtr))),用于描述非托管函数的 "签名"(参数类型、返回值类型)。它的返回值是一个 Delegate 基类对象,需要转换为具体的委托类型。
  3. CType(..., Func(Of IntPtr)) :将 GetDelegateForFunctionPointer 返回的基类 Delegate 转换为具体的委托类型(这里是 Func(Of IntPtr))。因为 GetDelegateForFunctionPointer 返回的是通用 Delegate,必须明确转换为匹配的具体类型才能调用。

  4. Func(Of IntPtr) :.NET 内置的泛型委托,代表 "无参数、返回 IntPtr 类型" 的函数,正好匹配 lame_init 函数的签名(lame_init 无参数,返回一个 IntPtr 类型的编码器句柄)。

简单说:非托管函数地址 → 通过委托类型映射 → 转换为 .NET 可调用的委托对象,这一步是托管 / 非托管互操作的必需流程。

二、如何包装类似 FindWindow 的函数

FindWindow 为例(注:实际 FindWindow 的签名是 (String lpClassName, String lpWindowName) As IntPtr,这里按你假设的签名 (win As Long, win2 As Long, findclass As String, findtxt As String) As Long 来演示)。

步骤 1:定义匹配函数签名的委托

委托的参数类型、返回值类型必须 严格匹配非托管函数的签名 (否则会导致内存错误)。假设非托管函数 FindWindow 的签名为:Long FindWindow(Long win, Long win2, String findclass, String findtxt)

对应的 VB.NET 委托定义:

vb

复制代码
' 委托类型必须与非托管函数签名完全匹配(参数顺序、类型,返回值类型)
Delegate Function FindWindowDelegate(win As Long, win2 As Long, findclass As String, findtxt As String) As Long
步骤 2:将函数地址转换为委托对象

假设已通过内存加载获取到 FindWindow 的地址 findWindowAddrIntPtr 类型),转换代码:

vb

复制代码
' 1. 将非托管函数地址转换为委托(使用上面定义的 FindWindowDelegate 类型)
Dim findWindow As FindWindowDelegate = CType(Marshal.GetDelegateForFunctionPointer(findWindowAddr, GetType(FindWindowDelegate)), FindWindowDelegate)

' 2. 调用委托(等价于调用非托管函数)
Dim result As Long = findWindow(123, 456, "Notepad", "无标题 - 记事本")

三、核心要点

  1. 委托签名必须严格匹配 :非托管函数的参数类型、顺序、返回值类型,必须与委托完全一致(例如:Long 对应非托管的 LONGString 对应 LPCSTRLPCWSTR,需注意字符串编码)。

  2. Marshal.GetDelegateForFunctionPointer 是关键:它负责将非托管的函数地址(纯内存指针)与托管的委托类型绑定,让 .NET 知道如何传递参数、接收返回值。

  3. 转换的必要性 :因为 GetDelegateForFunctionPointer 返回的是 Delegate 基类,而我们需要具体的委托类型才能调用(类似 "父类转子类" 的强制转换)。

总结

包装非托管函数的通用流程:

  1. 定义与非托管函数签名完全匹配的委托类型。
  2. Marshal.GetDelegateForFunctionPointer 将函数地址转换为该委托类型的实例。
  3. 调用委托实例,等价于调用非托管函数。

这种方式是 .NET 与非托管代码交互的标准方式,无论是否内存加载 DLL,只要调用非托管函数地址,都需要通过委托作为桥梁。

相关推荐
z落落2 小时前
C# WinForm Socket 转串口设备服务器+Modbus CRC16校验+Socket 客户端
服务器·开发语言·c#
小灰灰搞电子8 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
神仙别闹9 小时前
基于 C++ 实现两个有序链表序列的交集
java·c++·链表
传奇开心果编程10 小时前
【Rust入门知识点学与练】第21课:Trait 进阶 Advanced Traits
开发语言·学习·rust
新时代牛马10 小时前
字符设备驱动完整篇:从 cdev_add、file_operations 到chrdev_open 与排障
开发语言·python
swordbob10 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言
淡海水11 小时前
08-03-不可变-ImmutableDictionary-TKey-TValue-与ImmutableHashSet-T-持久化哈希树
数据结构·算法·c#·哈希算法·dictionary·immutable
白山编程大哥11 小时前
Java OutputStreamWriter 详解:从字符到字节的桥梁
java·开发语言·python
hansang_IR11 小时前
【题解】[APIO2023] 赛博乐园 / cyberland
c++·算法·图论
落羽的落羽11 小时前
【AI】快速理解AI应用的相关名词概念
linux·c++·人工智能·python·计算机网络·算法