使用Nim创建返回字符串的VB6 DLL遇错误及@8别名疑问
环境信息
- Nim编译器版本:1.6.6 [Windows: i386]
- 编译时间:2022-05-05
- 激活启动开关:
-d:release
编译命令
nim c --cpu:i386 -d:release --app:lib --nomain mydll.nim
问题描述
已实现让DLL返回Long类型值,但返回字符串时VB6调用报错,参考示例后问题仍未解决。
VB6调用代码
Private Declare Function MyStr Lib "mydll.dll" (ByVal s As String) As String Private Declare Function return_multiply Lib "mydll.dll" Alias "return_multiply@8" (ByVal a As Long, ByVal b As Long) As Long Private Sub Form_Click() MsgBox MyStr("?") '执行此处报错 MsgBox return_multiply(5, 4) '执行正常 End Sub
原始Nim代码
import encodings const vbCodePage = "GB2312" vbTrue* = 1 vbFalse* = 0 type VBString* = cstring VBBoolean* = int32 proc MyStr*(): cstring {.stdcall, exportc, dynlib.} = result = $"teste" proc fromVBString*(a: VBString): string = return encodings.convert($a, "UTF-8", vbCodePage) proc toVBString*(a: string): VBString = return VBString(encodings.convert(a, vbCodePage, "UTF-8")) proc return_multiply*(a, b: int): int {.stdcall, exportc, dynlib.} = a * b
字符串返回报错的原因
- 参数不匹配:VB6声明的
MyStr接收1个字符串参数,但Nim定义的MyStr是无参过程,参数数量不匹配导致调用栈错误。 - 字符串类型不兼容:VB6使用BSTR(宽字符串),而Nim直接返回的
cstring是窄字符串,两者内存布局、编码格式完全不同。同时stdcall协议下,字符串返回的内存管理规则也未遵循VB6要求。
修复方案
1. 调整Nim代码适配VB6字符串规则
引入winim/com模块处理BSTR内存分配,同时匹配参数和类型:
import encodings, winim/com const vbCodePage = "GB2312" vbTrue* = 1 vbFalse* = 0 type VBString* = wstring # 用wstring对应VB6的BSTR宽字符串 VBBoolean* = int32 # 匹配VB6的参数列表,返回合法的BSTR proc MyStr*(input: VBString): VBString {.stdcall, exportc, dynlib.} = # 将VB传入的UTF-16宽字符串转码为GB2312 let nimInput = encodings.convert($input, "UTF-16", vbCodePage) # 生成返回内容,再转码为UTF-16宽字符串 let outputStr = $"输入内容: {nimInput} | 返回测试文本" # 调用SysAllocString分配BSTR内存,VB6会自动释放该内存 result = SysAllocString(wstring(outputStr)) proc fromVBString*(a: VBString): string = return encodings.convert($a, "UTF-16", vbCodePage) proc toVBString*(a: string): VBString = return VBString(encodings.convert(a, vbCodePage, "UTF-16")) proc return_multiply*(a, b: int32): int32 {.stdcall, exportc, dynlib.} = a * b
2. 简化VB6函数声明
修正参数匹配后,无需额外别名即可正常调用:
Private Declare Function MyStr Lib "mydll.dll" (ByVal s As String) As String Private Declare Function return_multiply Lib "mydll.dll" (ByVal a As Long, ByVal b As Long) As Long Private Sub Form_Click() MsgBox MyStr("测试输入") MsgBox return_multiply(5, 4) End Sub
关于return_multiply@8的别名说明
这是stdcall调用约定的名字修饰规则:
@8表示函数参数总字节数为8(两个Long类型,每个占4字节,4+4=8)。- Windows平台下,stdcall约定会自动给函数名添加
@参数总字节数后缀,用来确保调用栈的参数匹配。 - 后续可以直接用
return_multiply调用,是因为VB6在声明stdcall函数时会自动处理该名字修饰,只要函数名和参数列表一致即可。
内容的提问来源于stack exchange,提问作者huvdev4
相关产品推荐
相关产品推荐

