VB6中如何调用DLL导出函数?含CallWindowProc参数限制及示例
如何在VB6环境中调用DLL导出函数(含CallWindowProc参数限制解决示例)
刚好之前折腾过VB6调用DLL的各种场景,我来一步步给你拆解清楚,包括你提到的CallWindowProc参数限制的问题:
一、常规DLL导出函数的调用方法
VB6里调用DLL导出函数有两种主流方式:静态声明调用和动态加载调用(就是你代码里用的LoadLibrary+GetProcAddress方式)。
1. 动态加载调用(适合需要灵活加载/卸载DLL的场景)
你的示例代码已经完成了加载DLL和获取函数地址的第一步,接下来需要把这个地址转换成VB6能调用的函数:
首先,要先声明几个基础的API函数(放在模块里更合适):
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
然后,声明你要调用的DLL函数的原型(必须和DLL里的导出签名完全匹配,这里以InternetOpenA为例):
Private Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" ( _ ByVal sAgent As String, _ ByVal lAccessType As Long, _ ByVal sProxyName As String, _ ByVal sProxyBypass As String, _ ByVal lFlags As Long _ ) As Long
接下来,定义一个函数指针类型,用CopyMemory把获取到的函数地址转换成可调用的函数:
' 定义和InternetOpen签名匹配的函数指针类型 Private Type InternetOpenProc fn As Long End Type Sub TestDynamicCall() Dim lb As Long, pa As Long lb = LoadLibrary("wininet.dll") If lb = 0 Then MsgBox "加载wininet.dll失败" Exit Sub End If pa = GetProcAddress(lb, "InternetOpenA") '注意要和DLL导出的名字一致,很多DLL会有A/W后缀区分ANSI和Unicode If pa = 0 Then MsgBox "获取InternetOpen地址失败" FreeLibrary lb Exit Sub End If ' 把函数地址转换成可调用的函数 Dim internetOpenFunc As InternetOpenProc CopyMemory internetOpenFunc, pa, 4 ' 现在可以正常调用了 Dim hInternet As Long hInternet = internetOpenFunc.fn("VB6测试代理", 1, vbNullString, vbNullString, 0) ' 记得用完后释放DLL If hInternet <> 0 Then ' 这里可以添加后续的Internet操作,比如InternetCloseHandle End If FreeLibrary lb End Sub
2. 静态声明调用(适合固定路径、固定函数的场景)
这种方式更简单,直接在模块里声明函数,然后直接调用即可:
' 模块里声明 Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" ( _ ByVal sAgent As String, _ ByVal lAccessType As Long, _ ByVal sProxyName As String, _ ByVal sProxyBypass As String, _ ByVal lFlags As Long _ ) As Long ' 直接调用 Sub TestStaticCall() Dim hInternet As Long hInternet = InternetOpen("VB6测试代理", 1, vbNullString, vbNullString, 0) ' 后续操作... End Sub
二、CallWindowProc的参数限制解决示例
CallWindowProc的原生参数只有wParam和lParam两个可以传递自定义数据,如果需要传递更多参数或者复杂数据,常用这两种方法:
方法1:用lParam传递结构体地址
把需要传递的所有数据打包成结构体,然后把结构体的地址放到lParam里,在目标窗口过程里解析:
首先定义结构体:
Private Type MyCustomData ID As Integer Content As String * 20 ' 注意VB6里字符串要固定长度,否则传递地址会有问题 Total As Long End Type
然后声明CallWindowProc和自定义窗口过程:
Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" ( _ ByVal lpPrevWndFunc As Long, _ ByVal hWnd As Long, _ ByVal Msg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long _ ) As Long ' 自定义窗口过程 Private Function MyCustomWndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Dim data As MyCustomData ' 从lParam里读取结构体数据 CopyMemory data, ByVal lParam, Len(data) ' 处理数据 Debug.Print "ID: " & data.ID Debug.Print "内容: " & Trim(data.Content) Debug.Print "总数: " & data.Total ' 调用原来的窗口过程(如果是子类化场景) MyCustomWndProc = CallWindowProc(lpOldWndProc, hWnd, Msg, wParam, lParam) End Function
调用时传递结构体地址:
Sub TestCallWindowProc() Dim myData As MyCustomData myData.ID = 1001 myData.Content = "测试VB6结构体传递" myData.Total = 999 ' 把结构体地址作为lParam传递 CallWindowProc(AddressOf MyCustomWndProc, Me.hWnd, &H1000, 0, VarPtr(myData)) End Sub
方法2:用全局变量存储额外数据
如果传递的数据比较复杂,或者不想处理结构体地址,也可以用全局变量来存储,在窗口过程里直接读取:
' 模块里定义全局变量 Global g_ExtraData As MyCustomData Private Function MyCustomWndProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long ' 直接读取全局变量里的数据 Debug.Print "全局变量ID: " & g_ExtraData.ID MyCustomWndProc = CallWindowProc(lpOldWndProc, hWnd, Msg, wParam, lParam) End Function ' 调用前设置全局变量 Sub TestGlobalData() g_ExtraData.ID = 2002 g_ExtraData.Content = "全局变量测试" g_ExtraData.Total = 888 ' 触发窗口消息,让自定义过程执行 CallWindowProc(AddressOf MyCustomWndProc, Me.hWnd, &H1000, 0, 0) End Sub
内容的提问来源于stack exchange,提问作者user2522767
相关产品推荐
相关产品推荐

