You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

字符串返回报错的原因

  1. 参数不匹配:VB6声明的MyStr接收1个字符串参数,但Nim定义的MyStr是无参过程,参数数量不匹配导致调用栈错误。
  2. 字符串类型不兼容: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 08:39:46