将VB6 Type转换为VB.net或C# Struct

4
我将一个最初使用vb6编写的应用程序转换为vb.net。这个应用程序做的一件事就是将“Type”对象发送到dll。我尝试将类型转换为结构,并使用p/invoking dll,但似乎不起作用。我已经卡了一个星期,任何帮助都将不胜感激。
以下是该类型的vb6代码:
'Define WICS Communications Control Block (CCB).
Type WicsCCBType ' Create user-defined type.
    CCBNum As String * 1
    CCBVer As String * 1
    Resp1  As String * 4
    Resp2  As String * 4
    PLUA   As String * 8
    LLUA   As String * 8
    Mode   As String * 8
    ReqMax As String * 5
    ResMax As String * 5
End Type      

以下是如何调用dll文件:

Private Declare Sub WICSRASP Lib "wicsrasp.dll" (MyWicsCCB As WicsCCBType)
WICSRASP MyWicsCCB

这是我用VB.NET尝试过的,但它没有起作用。

'Define WICS Communications Control Block (CCB).
    <System.Runtime.InteropServices.StructLayoutAttribute( _
            System.Runtime.InteropServices.LayoutKind.Sequential, _
            CharSet:=System.Runtime.InteropServices.CharSet.[Unicode])> _
    Structure WicsCCBType ' Create user-defined type.
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=1)> Dim CCBNum As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=1)> Dim CCBVer As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=4)> Dim Resp1 As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=4)> Dim Resp2 As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=8)> Dim PLUA As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=8)> Dim LLUA As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=8)> Dim Mode As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=5)> Dim ReqMax As String
        <MarshalAs(UnmanagedType.ByValTStr, sizeconst:=5)> Dim ResMax As String
    End Structure

这里是我尝试调用它的地方

<System.Runtime.InteropServices.DllImportAttribute("C:\windows\system32\wicsrasp.dll")> _
    Public Shared Sub WICSRASP(
                    ByRef CCB As WicsCCBType,
                    ByRef Request As DAWicsRequestType,
                    ByRef Response As DAWicsResponseType)
    End Sub

 Dim CCB As New modWICSDiary.WicsCCBType()
 CCB.CCBNum = "B"
            CCB.CCBVer = "2"
            CCB.LLUA = "        "
            CCB.Mode = "CICSMO2 "
            CCB.ReqMax = "2100 "
            CCB.ResMax = "2100 "
            CCB.Resp1 = "0   "
            CCB.Resp2 = "0   "
            CCB.PLUA = "WICSPLU "

  NativeMethods.WICSRASP(CCB)

关于数值,相同的数值适用于vb6类型。 再次感谢您的提前帮助。

你需要更加努力尝试,确切地提供它失败的原因。 - Security Hound
说实话,你不应该试图创建一个VB6结构的.NET版本,而是应该创建一个传递给WICSRASP的C数据结构的.NET版本。如果你有那个文档(比如头文件),那么这将会更容易。 - tcarvin
3个回答

1

我不知道原帖的问题是否已经解决,但这是我的看法。

在VB6中,UDT中的定长字符串不会作为指针进行编组,而是内联到结构中。此外,VB6在编组之前将Unicode转换为Ansi。并且VB6使用4字节对齐。

类型将在内存中以填充方式显示,每个连续的字段在图示中命名为A到I,_是由于对齐而产生的填充字节。

CCBNum As String * 1     
|
|+-CCBVer As String * 1     
||
||  Resp1  As String * 4
||  |
||  |   Resp2  As String * 4     
||  |   |
||  |   |   PLUA   As String * 8     
||  |   |   |
||  |   |   |       LLUA   As String * 8     
||  |   |   |       |
||  |   |   |       |       Mode   As String * 8     
||  |   |   |       |       |
||  |   |   |       |       |       ReqMax As String * 5     
||  |   |   |       |       |       |
||  |   |   |       |       |       |       ResMax As String * 5 
||  |   |   |       |       |       |       |
||  |   |   |       |       |       |       |
VV  V   V   V       V       V       V       V

AB__CCCCDDDDEEEEEEEEFFFFFFFFGGGGGGGGHHHHH___IIIII___

因此 CharSet 应该是 Ansi,StructAlignment 应该是 4。使用 ByValTString 是可以的,SizeConst 的使用也是可以的。

1

VB6将所有这些字符串元素“编组”为ANSI字符串。相应更改Vb.Net编组代码。

  • 在那些MarshalAs属性中尝试使用UnmanagedType.LPStr
  • 尝试将CharSet从Unicode更改为Ansi?
  • StructLayoutAttribute中尝试使用Pack=4

有用的链接,解释了VB6 Declare所做的假设。


当我尝试这样做时,我会得到一个异常(当我调用dll方法时):“尝试读取或写入受保护的内存。这通常是其他内存损坏的迹象。” - user889829
我想当你发表那个评论时,我正在编辑答案!你尝试过更改字符集吗? - MarkJ
尝试在你的StructLayoutAttribute中包含Pack=4 - MarkJ
我已经进行了所有三个更改,但仍然出现“尝试读取或写入受保护的内存。这通常是其他内存损坏的指示”的错误。 - user889829
@user889829:结构中的定长字符串不会被编组为指向缓冲区的指针,而是作为连续内存块的一部分进行内联。您的原始代码更接近实际情况,但 VB6 可能使用 Ansi 字符串,并且可能还存在 4 字节对齐。 - tcarvin

0

VB6使用BSTR,是吗?你不应该使用BSTR进行封送吗?


不,VB6在使用Declare调用DLL时会将Unicode转换为ANSI。http://vb.mvps.org/tips/vb5dll.asp - MarkJ
刚刚查了一下丹·阿普尔曼(Dan Appleman)的《移动到VB.NET》。他说:“在几乎所有调用Win32 API函数并带有字符串参数的情况下,你都会声明字符串ByVal As String... VB.NET在Declare语句上玩了个花招...换句话说,这是VB.NET与VB6保持语法兼容的地方之一。” - SSS
总结他更详细的解释,显然VB.NET在传递字符串之前会进行拷贝,即使你已经声明为ByVal。 - SSS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接