繁体   English   中英

将结构从C#DLL传递到非托管代码到VB6

[英]Passing Structures to unmanaged code from C# DLL to VB6

我有一些使用VB6和其他语言的应用程序的客户。 该代码可以使用OLE(COM)正常运行,但是客户更喜欢使用本机DLL以避免注册库并将其部署在现场。

当我注册DLL并在VB6(OLE)中进行测试时,它工作正常。 当我调用返回Strutc的方法时,它可以很好地与OLE配合使用,但是,如果我使用Declare在VB6中进行访问,则该方法应返回致命错误,该错误应返回相同类型的结构(方法“ EchoTestData”,请参见下面的内容)。

该代码在C#中进行编译,以用于OLE或通过入口点在非托管代码中使用>我已使用VB6测试过。

namespace TestLib
{
  [ClassInterface(ClassInterfaceType.AutoDual)]
  [ProgId("TestClass")]
  public class TestClass : System.EnterpriseServices.ServicedComponent
  {

    /* 
     * NOTE:
     * ExportDllAttribut: a library that I have used to publish the Entry Points,
     * I had modified that project and it works fine. After complile, the libray
     * make the entry points...
     * http://www.codeproject.com/Articles/16310/How-to-Automate-Exporting-NET-Function-to-Unmanage 
     */

    /* 
     * System.String: Converts to a string terminating in a null 
     * reference or to a BSTR 
     */
    StructLayout(LayoutKind.Sequential)]
    public struct StructEchoData
    {
        [MarshalAs(UnmanagedType.BStr)]
        public string Str1;
        [MarshalAs(UnmanagedType.BStr)]
        public string Str2;
    }

    /*
     * Method static: when I use this method, the Vb6 CRASH and the EVENT VIEWER
     * show only: System.Runtime.InteropServices.MarshalDirectiveException
     * HERE IS THE PROBLEM in VB6 with declare...
     * Return: struct of StructEchoData type
     */
    [ExportDllAttribute.ExportDll("EchoTestStructure", CallingConvention.StdCall)]
    public static StructEchoData EchoTestStructure(string echo1, string echo2)
    {
        var ws = new StructEchoData
        {
            Str1 = String.Concat("[EchoTestData] Retorno String[1]: ", echo1),
            Str2 = String.Concat("[EchoTestData] Retorno String[1]: ", echo2)
        };
        return ws;
    }

    /*
     * Method NOT static: it is used as COM (OLE) in VB6
     * In VB6 it returns very nice using with COM.
     * Note that returns the StructEchoData without problems...
     * Return: struct of StructEchoData 
     */
    [ExportDllAttribute.ExportDll("EchoTestStructureOle", CallingConvention.StdCall)]
    public StructEchoData EchoTestStructureOle(string echo1, string echo2)
    {
        var ws = new StructEchoData
        {
            Str1 = String.Concat("[EchoOle] Return StringOle[1]: ", echo1),
            Str2 = String.Concat("[EchoOle] Return StringOle[2]: ", echo2),
        };
        return ws;
    }

    /*
     * Method static: It works very nice using 'Declare in VB6'
     * Return: single string
     */
    [ExportDllAttribute.ExportDll("EchoS", CallingConvention.StdCall)]
    // [return: MarshalAs(UnmanagedType.LPStr)]
    public static string EchoS(string echo)
    {
        return "[TestClass::EchoS from TestLib.dll]" + echo;
    }


    /*
     * Method NOT static: it is used as COM (OLE) in VB6 
     * In VB6 it returns very nice
     * Return: single string
     */
    [ExportDllAttribute.ExportDll("EchoSOle", CallingConvention.StdCall)]
    // [return: MarshalAs(UnmanagedType.LPStr)]
    public string EchoSOle(string echo)
    {
        return "[TestClass::EchoS from TestLib.dll]: " + echo;
    }
  }
}

现在,在VB6中,我无法使用Declare进行测试或将TestLib.Dll注册为COM

在VB6中使用DECLARE:

Private Declare Function EchoS Lib "C:\Temp\_run.dll\src.app.vb6\TestLib.dll"_
     (ByVal echo As String) As String

Private Type StructEchoData
    Str1 As String
    Str2 As String
End Type

Private Declare Function EchoTestStructure Lib  "C:\Temp\_run.dll\src.app.vb6\TestLib.dll"_
    (ByVal echo1 As String, ByVal echo2 As String) As StructEchoData

// ERROR - CRASH VB6
Private Sub EchoData_Click()
    Dim ret As StructEchoData
    ret = EchoTestStructure("echo1 Vb6", "echo2 vb6")
    TextBox.Text = ret.Str1
End Sub

// WORKS Fine, returns a string
Private Sub btRunEchoTestLib_Click()
    TextBox.Text = EchoS("{Run from VB6}")
End Sub

并使用带有OLE的VB6:

1st。 注册DLL:C:\\ Windows \\ Microsoft.NET \\ Framework \\ v4.0.30319 \\ regsvcs.exe TestLib.dll /tlb:Test.tlb

2号 在项目中添加参考。 程序运行后,我得到了一个字符串的响应,并且在具有结构时也收到了响应。

Private Sub Echo_Click()
    Dim ResStr As String
    Dim obj As TestLib.TestClass
    Set obj = New TestClass
    ResStr = obj.EchoSOle(" Test message")
    MsgBox "Msg Echo: " & ResStr, vbInformation, "ResStr"
    Beep
End Sub

Private Sub EchoDataOle_Click()
    Dim obj As TestLib.TestClass
    Set obj = New TestClass       
    // Here I define the struct and works fine!!
    Dim ret As TestLib.StructEchoData       
    ret = obj.EchoTestStructureOle("test msg1", "test msg2")       
    TextStr1.Text = ret.Str1
    TextStr2.Text = ret.Str2
    Debug.Print ret.Str1
    Debug.Print ret.Str2
   Beep
End Sub

因此,StructEchoData使用COM可以很好地包装,但是如果我想使用Declare并按入口点进行访问,则无法正常工作。 有人可以建议什么吗?

VB6 Declare Lib仅适用于未管理的DLL导出功能。 C#不会将其功能公开为非托管功能,因为它是托管代码。 从C#导出类的唯一受支持的方法是使用COM。 因此,您不能使用Declare Lib从VB6访问C#方法。

应该有一个库可以从您的C#代码创建未管理的导出。 罗伯特·吉塞克(Robert Giesecke)的不受管制的出口 我个人从未使用过它。 我只在堆栈溢出中看到它。

有一种受支持的方法可以从.Net程序集中导出非管理函数,该方法使用C ++ / CLR,因为它允许混合托管和非管理代码。 您可以创建一个C ++ / CLR包装器,该包装器可以导出调用C#DLL的未管理函数。 那就是我要走的路。

您不能使用c#创建动态链接库。

但是,使用少量C ++,您可以利用CLR托管API为.Net dll创建引导程序。

CLR托管API

您可以使用称为“ LoadPlugins”之类的方法在C ++中创建动态链接库。

编写LoadPlugins来加载CLR(或CLR的特定版本),然后使用反射来加载一些.net DLL。

同样使用相同的C ++代码,您可以将C ++ dll中的.net方法公开为c ++中的导出本机函数,这些函数可以与VB6的声明一起使用...

c ++中的每个函数都必须检查以确保已加载CLR,并且已加载了正在调用的.net代码,然后使用反射对其进行了调用。

感谢您的答复,

如果代码中有入口点,则C#仅适用于非托管DLL。 语句'ExportDllAttribut'在代码中使用的声明会生成非管理代码必须使用的各个入口点。

问题是将导出与“数据结构”一起使用,这是我的问题。 我已经毫无问题地使用了返回字符串或整数值的方法,例如本文中的EchoS。 我在VB6中使用了这个示例(TestLib.DLL),并且在VB6中与“ Declare”一起使用时效果很好:

Private Declare Function EchoS Lib "C:\Temp\_run.dll\src.app.vb6\TestLib.dll"_
 (ByVal echo As String) As String

// WORKS Fine, returns a string
Private Sub btRunEchoTestLib_Click()
    TextBox.Text = EchoS("{Run from VB6}")
End Sub

我在C#代码的开头写了一个便条,但是不清楚,抱歉。 解释了更多。 编译库后,在“项目属性”,“构建事件”中使用以下命令:

"$(ProjectDir)libs\ExportDll.exe" "$(TargetPath)" /Debug

该指令[ExportDllAttribute.ExportDll(“ NameOfEntryPoint”]取消了DLL(使用ilasm.exe和ildasm.exe)并编写了export指令来创建Entry Point,再次编译并生成DLL。

如果我在DLL中应用dumpbin命令,则结果是发布的入口点,因此,可以与非托管代码(如VB6)一起使用,但可以在Vb6中使用正确的编组类型或语句。 例:

dumpbin.exe /exports TestLib.dll
   ordinal hint RVA      name
     2    0 0000A70E EchoC
     5    1 0000A73E EchoSOle
     3    2 0000A71E EchoTestStructure
     6    3 0000A74E EchoTestStructureOle

使用方法EchoS(带有Delare)或EchoSOle(COM)测试了此代码,在两种情况下都可以。 当DLL在应用程序中用作OLE时,该结构可以视为Type且运行良好,但是在带有声明的VB6中,我仅在返回Structure的情况下遇到了MarshalDirectiveException错误,像字符串C#或整数之类的单打类型,我没有有问题。

我认为问题是如何通过静态方法EchoTestStructure来编排Structure,或者如何在VB6中声明它。 可能是VB6中使用的错误方法(我不是专家)或静态方法“ EchoTestStructure”中的任何编组参数,这才是真正的问题和帮助。

PS:如果无法解决,我会在答复中看到链接以尝试其他方法。

再次感谢,还有其他想法吗?

它已解决,但是通过另一种方式...这可能不是最新的技术,但它可以工作。

首先,需要将结构中的封送处理类型从BStr更改为LPStr。

我不知道动机是什么,因为有些Microsoft帮助和其他链接,他们说:“ System.String:转换为以空引用结尾的字符串或BSTR。”

如果在结构的Str1中设置“ Hello”,则会在DLL中收到“效汬㉯映潲䉖⸶⸮”。 如果我从DLL返回固定的字符串“ Hello”,则VB6仅显示第一个字符“ H”,因此,我更改为ANSI(LPStr)并解决了它。 使用OLE,BSTR可以正常工作,但本机DLL不能正常工作。 该代码已更改为:

StructLayout(LayoutKind.Sequential)]
public struct StructEchoData
{
    [MarshalAs(UnmanagedType.BStr)]
    public string Str1;
    [MarshalAs(UnmanagedType.BStr)]
    public string Str2;
}

在方法“ EchoTestStructureOle”的声明中,更改为使用引用并将传递结构的地址并返回一个结构。 在代码之前是:

public StructEchoData EchoTestStructure(string echo1, string echo2)
{   
    // It Works only wtih OLE the return of the type StructEchoData 
    var ws = new StructEchoData
    {
        Str1 = String.Concat("[EchoTestData] Return from DLL String[1]: ", echo1),
        Str2 = String.Concat("[EchoTestData] Return from DLL String[2]: ", echo2)
    };
    return ws;
}

现在我正在使用interger通过引用返回状态(1 OK,-1错误)和参数结构类型,方法已更改为:

public static int EchoTestStructure(ref StructEchoData inOutString)
{   
    // used to test the return of values only, data 'in' not used
    var ws = new StructEchoData
    {
        Str1 = String.Concat("[EchoTestData] Return from DLL String[1]: ", inOutString.Str1),
        Str2 = String.Concat("[EchoTestData] Return from DLL String[2]: ", inOutString.Str2)
    };
    inOutString = ws;
    return 1;
}

通过参考,我可以毫无问题地从VB6接收值并返回到VB6。 我认为必须有一种返回结构的方法,但是,这种方法现在解决了。

在VB6方面:代码已更改为:

Private Type StructEchoData // Same type, do not change...
    Str1 As String
    Str2 As String
End Type

Private Declare Function EchoTestData Lib "C:\Temp\_run.dll\src.app.vb6\TestLib.dll" (ByRef strcData As StructEchoData) As Long

Private Sub ShowMessage_Click()
    Dim res As Long
    Dim strcData As StructEchoData

    strcData.Str1 = "Str1 from VB6..."
    strcData.Str2 = "Str2 from VB6..."
    res = EchoTestData(strcData)
    /*
     strcData.Str1 --> Data Received from DLL: 
       [EchoTestData] Return from DLL String[1]: Str1 from VB6...
     strcData.Str2 --> Data Received from DLL
       [EchoTestData] Return from DLL String[2]: Str2 from VB6...
    */
    ...
End Sub

感谢您的提示。 如果您有任何建议,将非常欢迎。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM