簡體   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