如何将结构从C#发送到VB6,以及从VB6发送到C#?

如何将结构从C#发送到VB6,以及从VB6发送到C#?,c#,vb6,struct,C#,Vb6,Struct,我需要将结构从C#发送到VB6应用程序,修改VB6中的数据,然后通过windows消息将结果发送回。我该怎么做 我可以使用PostMessage来回发送基本整数(使用C#中的DllImport并使用windows messaging注册vb6应用程序),但需要发送更多结构化数据,包括字符串、整数和小数 寻找实现来回传递结构数据的最简单解决方案 VB6类型的基本示例 Public Type udtSessionData SessionID As Integer SessionNam

我需要将结构从C#发送到VB6应用程序,修改VB6中的数据,然后通过windows消息将结果发送回。我该怎么做

我可以使用PostMessage来回发送基本整数(使用C#中的DllImport并使用windows messaging注册vb6应用程序),但需要发送更多结构化数据,包括字符串、整数和小数

寻找实现来回传递结构数据的最简单解决方案

VB6类型的基本示例

Public Type udtSessionData
    SessionID As Integer
    SessionName As String
    MinVal As Currency
    PctComplete As Double
    NVal As Integer
    ProcessedFlag As Boolean
    ProcessedDate As Date
    Length As Integer
End Type

您必须分配guid并使用marshallas属性。NET COM互操作处理转换。和一门课没有太大区别。文章的数量说明了您需要做什么。

您必须分配guid并使用marshallas属性。NET COM互操作处理转换。和一门课没有太大区别。文章的数量说明了您需要做什么。

通过在.NET上使用p/Invoke并在VB6中导入CopyMemory,您可以完成这项工作,但这是一个维护灾难,我建议您从类似的情况下运行。

通过在.NET上使用p/Invoke并在VB6中导入CopyMemory,您可以完成这项工作,但这是一项维护工作灾难我建议你不要这样做。

警告:

在我开始之前,人们可能有兴趣注意一下你的另一个问题

正如在这里和你的另一篇文章中提到的,你真的应该重新考虑如何使这项工作成功。特别是,即使您对这里的技术很在行,您的同事或其他可能需要维护您的代码的人也将处于非常糟糕的时期。请注意,在VB6中调试过程也非常困难-经常保存并使用大量断点!如果代码中有错误,可能会发生大量崩溃

此外,PostMessage不应与此技术一起使用。这将需要更多的清理工作

解决方案:

我附上了一个示例,它可以传回一个只包含字符串和整数类型的结构。做这项工作需要很多活动部件。我们将深入讨论从C#到VB的过程,因为这更为棘手。一旦你知道怎么做,反过来就不难了

首先,在C#端,您应该声明您的结构。在C#中打包结构实际上并不坏。下面是一个COM可见的C#示例类,它演示了如何包装结构。关键是在调用的对立面使用Marshal.StructureToPtr和Marshal.destrostructure。根据所涉及的类型,您甚至可能不必编写代码来映射类型。使用MarshalAs属性标记VB6的正确映射。MarshalAs中使用的枚举中的大多数项对应于变体和COM自动化中使用的不同变量类型

这里使用的缓冲区由HGlobal支持,需要在调用结束后释放该缓冲区。这里也可以使用GCHandle,这也需要类似的清理




在VB6端,您需要设置一种拦截消息的机制。由于细节在您的其他问题和其他地方都有介绍,我将跳过子类化的主题

要在VB6端展开一个结构,您需要对每个字段执行此操作,因为没有现成的机制来解除对指针值的引用并将其强制转换为结构。幸运的是,如果没有在C#中另行指定,那么在VB6中可以预期字段成员在4字节边界上对齐。这使我们能够逐个字段地工作,将项目从一个表示映射到另一个表示

首先,一些模块代码来完成所有的支持工作。以下是功能和注意事项

TestInfo类型-两侧使用的结构的镜像定义。
CopyMemory—可用于复制字节的win32函数。
ZeroMemory—将内存重置为零字节值的win32函数

除了这些项之外,我们还使用VB6中未记录的VarPtr()函数来获取项的地址。我们可以使用它来索引VB6端的结构。有关此功能的详细信息,请参阅以下链接

请注意,此解决方案需要发送方和接收方的合作。因为我们不希望两次释放字符串字段,所以在返回控件之前,我们会清空在VB6端创建的副本。如果您尝试为字段成员分配新值,则此处将发生什么情况尚未定义,因此请避免编辑结构中的字段

在映射字段中,C#中的UnmanagedType.BStr与VB6中的字符串直接类似。
在VB6中,UnmanagedType.I4映射为整数和Long。您在UDT中指定的其他字段也有等效字段,尽管我不确定VB6中的DateTime

VB6应用程序(表单源代码)的其余部分很简单

Dim CSharpClient As New HostLibrary.TestSender

Private Sub Command1_Click()
    CSharpClient.DoTest ("Hello World from VB!")
End Sub

Private Sub Form_Load()
    CSharpClient.hostwindow = Form1.hWnd
    Module1.SubclassWindow (Form1.hWnd)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    CSharpClient.hostwindow = 0
    Module1.UnsubclassWindow (Form1.hWnd)
End Sub
现在,在将结构从VB6发送到C#时,需要执行相反的操作。对于一些简单的结构,您甚至可以只发送结构本身的地址。如果需要memberwise控件,可以使用GlobalAlloc获得合适的缓冲区内存,然后使用GlobalFree释放它。对于每个字段,成员复制的执行方式可能与从C#解包参数的方式相同。但是,调用后清理更简单。如果使用了缓冲区,则只需在将其移交给GlobalFree之前清空缓冲区中的内存即可


当消息到达C端时,使用Marshal.PtrToStructure()将IntPtr映射到.NET结构中


注意事项:

在我开始之前,人们可能有兴趣注意一下你的另一个问题

正如在这里和你的另一篇文章中提到的,你真的应该重新考虑如何使这项工作成功。特别是,即使您对这里的技术很在行,您的同事或其他可能需要维护您的代码的人也会在fo中
Public Const WM_APP As Long = 32768
Private Const GWL_WNDPROC = (-4)
Private procOld As Long

Type TestInfo
    label As String
    count As Integer
End Type

Private Declare Function CallWindowProc Lib "USER32.DLL" Alias "CallWindowProcA" _
    (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal uMsg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long

Private Declare Function SetWindowLong Lib "USER32.DLL" Alias "SetWindowLongA" _
    (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Private Declare Sub CopyMemory Lib "KERNEL32.DLL" Alias "RtlMoveMemory" _
    (ByVal pDst As Long, ByVal pSrc As Long, ByVal ByteLen As Integer)

Private Declare Sub ZeroMemory Lib "KERNEL32.DLL" Alias "RtlZeroMemory" _
    (ByVal pDst As Long, ByVal ByteLen As Integer)

Public Sub SubclassWindow(ByVal hWnd As Long)
    procOld = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf SubWndProc)
End Sub

Public Sub UnsubclassWindow(ByVal hWnd As Long)
    procOld = SetWindowLong(hWnd, GWL_WNDPROC, procOld)
End Sub

Private Function SubWndProc( _
        ByVal hWnd As Long, _
        ByVal iMsg As Long, _
        ByVal wParam As Long, _
        ByVal lParam As Long) As Long

    If hWnd = Form1.hWnd Then
        If iMsg = WM_APP Then

            Dim inf As TestInfo
            ' Copy First Field (label)
            Call CopyMemory(VarPtr(inf), lParam, 4)
            ' Copy Second Field (count)
            Call CopyMemory(VarPtr(inf) + 4, lParam + 4, 4)

            Dim strInfo As String
            strInfo = "label: " & inf.label & vbCrLf & "count: " & CStr(inf.count)

            Call MsgBox(strInfo, vbOKOnly, "WM_APP Received!")

            ' Clear the First Field (label) because it is a string
            Call ZeroMemory(VarPtr(inf), 4)
            ' Do not have to clear the 2nd field because it is an integer

            SubWndProc = True
            Exit Function
        End If
    End If

    SubWndProc = CallWindowProc(procOld, hWnd, iMsg, wParam, lParam)
End Function
Dim CSharpClient As New HostLibrary.TestSender

Private Sub Command1_Click()
    CSharpClient.DoTest ("Hello World from VB!")
End Sub

Private Sub Form_Load()
    CSharpClient.hostwindow = Form1.hWnd
    Module1.SubclassWindow (Form1.hWnd)
End Sub

Private Sub Form_Unload(Cancel As Integer)
    CSharpClient.hostwindow = 0
    Module1.UnsubclassWindow (Form1.hWnd)
End Sub