当前位置: 代码迷 >> VB Dotnet >> VB.net 调用VB6编写的标准DLL字符串传入传出过程的有关问题
  详细解决方案

VB.net 调用VB6编写的标准DLL字符串传入传出过程的有关问题

热度:97   发布时间:2016-04-25 02:08:03.0
VB.net 调用VB6编写的标准DLL字符串传入传出过程的问题。
VB6SP6精简版,然后安装了“VB6编译标准DLL工具”,编写标准DLL。
VB6编写以下内容:
' * * *  ---== VB6.0 StandardCall DLL 模板 ==---  * * *
Option Explicit
' 在这后面写上你所需要的数据类型、变量、常量、API等声明

' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Private Sub Main(): End Sub   '请不要修改或删除这一行!!!
' - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
'在这后面写上你的其它 Sub/Function ,或者全部写到其它模块中

Function Test1(ByVal a As Long, ByVal b As Long) As Long
Test1 = (a + b)
End Function

Function Test2(ByVal s As String) As String
Test2 = s
End Function
并在生成时DLL选择了输出该2个函数。DLL生成成功!
需要在VB.net下调用该DLL。在VB.net 编写代码(x86编译模式):

Imports System.Runtime.InteropServices
Module Module1
    <DllImport("E:\vb\1\StdDLL.dll", CharSet:=CharSet.Auto, SetLastError:=False)> Private Function Test1(ByVal a As Integer, ByVal b As Integer) As Integer
    End Function
    <DllImport("E:\vb\1\StdDLL.dll", CharSet:=CharSet.Auto, SetLastError:=False)> Private Function Test2(ByVal s As String) As String
    End Function

    Sub Main()
        MsgBox(Test1(1, 2))
        MsgBox(Test2("1234567890"))
    End Sub
End Module

现在问题来了:Test1执行没问题,返回3。
Test2就出问题了。 调试运行提示vhost32停止工作。
如果把 CharSet:=CharSet.Auto 改为 CharSet:=CharSet.Ansi
调试就会报错:“System.AccessViolationException”类型的未经处理的异常在 DLL.exe 中发生 
其他信息: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏。

我发现只要是字符串【传出】就会有问题。
(传入的时候Len求传入字符串长度,通过Long返回,能取到长度值,但是长度不准确,似乎是1/2)。
只要Test2方法中没有任何行(即没有返回值)的时候,CharSet:=CharSet.Auto/Unicode 能执行通过。
我的目的是,传入一个字符串,在vb6代码中处理这个字符串然后返回新的字符串。

请问如何解决这个问题?请给一个能调试通过的代码(VB6 和 VB.net的)。

------解决思路----------------------
VB6肯定是用 CharSet:=CharSet.Unicode,这个应该能修正长度问题,还有千万不要忘了 CallingConvention:=CallingConvention.StdCall。
------解决思路----------------------

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)

Private Sub Form_Load()
Dim p As String
Dim n As Long
Dim r As Long
p = String(10, "1")
n = Len(p) * 2
r = Test2("123", StrPtr(p), n)
p = Left(p, r)
End Sub



Function Test2(ByVal s As String, ByVal retstrbuf As Long, ByRef bufsize) As Long
Dim s2 As String
Dim n As Long
Dim b() As Byte
    s2 = "新字符串" & s & Chr(0)
    n = LenB(s2)
    If n < bufsize Then
        b = StrConv(s2, vbFromUnicode)
        CopyMemory retstrbuf, StrPtr(s2), n
    End If
    bufsize = n
    Test2 = n / 2 - 1
End Function


<DllImport("E:\vb\1\StdDLL.dll", CharSet:=CharSet.unicode, SetLastError:=False)> Private Function Test2(ByVal s As String,byval buf as int32,byref n as int32) As int32
    End Function

        Dim p1 As IntPtr
        Dim s1 As String
        Dim n1 As Int32
        Dim r1 As Int32
        p1 = Runtime.InteropServices.Marshal.AllocHGlobal(20)
        s1 = "123"
        n1 = 20
        r1 = Test2(s1, p1, n1)
        s1 = Runtime.InteropServices.Marshal.PtrToStringUni(p1)
        Runtime.InteropServices.Marshal.FreeHGlobal(p1)
------解决思路----------------------
加 ExactSpelling:= False 试试,VB6 函数应该没有 Test2A/Test2W 这样的命名方式。
还有改回 CharSet.Ansi 也试试,暴露出来的“标准DLL”的接口可能不是 Unicode 的,在 Test2 中检查字符串长度验证一下。
------解决思路----------------------
传进来长度对了吗?

应该是字符串不能作为返回值,只能作为参数。
API 中也都是传入一个已经申请了空间的字符串指针来返回字符串内容的。
因为通用原则是调用者申请/释放内存。
VB函数返回字符串变成了被调用者申请/调用者释放,这块内存根本不受gc管理,不出错才怪了。
------解决思路----------------------

Function Test2(ByVal s As long, ByVal retstrbuf As Long, ByRef bufsize) As Long
Dim s2 As String
Dim n As Long
Dim b() As Byte
    n=lstrlenW(s)
    s2=string(n+1,0)
    copymemory strptr(s2),s,n*2
    s2=left(s2,n)
'之前传地址,后面正常操作string类型
    s2 = "新字符串" & s2 & Chr(0)
    n = LenB(s2)
    If n < bufsize Then
        b = StrConv(s2, vbFromUnicode)
        CopyMemory retstrbuf, StrPtr(s2), n
    End If
    bufsize = n
    Test2 = n / 2 - 1
End Function

Dim p2 As IntPtr = Marshal.StringToBSTR("abc")
上面vb代码不改,改下.net的第一个参数的声明,应该也不会出问题,内存非法访问可能是vb访问了字符串变量的前4个地址来求长度
<DllImport("E:\vb\1\StdDLL.dll", CharSet:=CharSet.unicode, SetLastError:=False)> Private Function Test2(ByVal s As intptr,byval buf as int32,byref n as int32) As int32
    End Function
------解决思路----------------------
stdcall 的函数,字符串参数是总能找出正确的调用方法的。
但是直接的字符串返回值是不行的。取返回值是调用方的语言自动完成的,根本没法干涉。
------解决思路----------------------
只有VB才能调用VB写的所谓“标准dll”

这只是对Linker的一个小Hack,让它为这些函数增加导出表,但是并不会影响编译器的行为。而VB之所以不支持“标准dll”是因为VB程序的一举一动都严重依赖msvbvm,需要复杂的初始化,而“标准dll”缺少这些初始化,所以根本没法执行,只有VB自己调用,因为已经初始化好了,所以才能工作。

试想,要是VB通过修改下链接器就能支持“标准dll”,微软为什么不提供这样的功能要故意藏起来呢?

总之,这就是骗骗菜鸟的把戏罢了。至于你要在VC++ VB.NET等调用VB的ActiveX DLL,有标准的方法,没必要做成“标准dll”
  相关解决方案