我有一些客户使用使用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
使用VB6与OLE:
1日。注册DLL:C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ regsvcs.exe TestLib.dll /tlb:Test.tlb
第二。在项目中添加引用。程序运行,我得到一个字符串的响应,并在有结构时收到响应。
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
因此,使用COM可以很好地包装StructEchoData,但是如果我想使用Declare并通过入口点获取访问权限,则无法正常工作。有人可以提出任何建议吗?
答案 0 :(得分:3)
VB6 Declare Lib
仅适用于未管理的DLL导出函数。由于C#是托管代码,因此C#不会将其作为非托管函数公开。从C#导出类的唯一受支持的方法是使用COM。因此,您无法使用Declare Lib
从VB6访问C#方法。
有一个库应该从你的C#代码创建无人导出;罗伯特·吉塞克Unmanaged Exports。我个人从未使用它;我只是在Stack Overflow上看过它。
有一种支持的方法可以从.Net程序集中导出未管理的函数,并且使用C ++ / CLR,因为它允许混合托管代码和非托管代码。您可以创建一个C ++ / CLR包装器,用于导出调用C#DLL的非管理函数。这就是我要去的方式。
答案 1 :(得分:1)
您无法使用c#创建动态链接库。
但是,使用一点C ++,您可以通过利用CLR托管API为.Net dll创建一个引导程序。
您可以使用名为“LoadPlugins”的方法在C ++中创建动态链接库。
编写LoadPlugins以加载CLR(或CLR的特定版本),然后使用反射加载一些.net DLL。
使用相同的C ++代码,您可以将C ++ dll中的.net方法公开为c ++中的导出本机函数,这些函数将与VB6的声明一起使用...
c ++中的每个函数都必须检查以确保加载了CLR,并且加载了被调用的.net代码,然后使用反射来调用它。
答案 2 :(得分:0)
感谢您的回复,
如果代码中有入口点,则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)并编写exports指令来创建入口点,再次编译和生成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(使用Declare)或EchoSOle(COM)方法进行测试,两种情况都很好。当DLL在app中用作OLE时,结构可以看作Type并且运行正常,但是,在VB6中使用declare,我得到错误MarshalDirectiveException只有Structure返回,单个类型如字符串C#或整数,我不有问题。
我认为问题是如何通过静态方法EchoTestStructure对结构进行编组或如何在VB6中声明。也许可能是VB6中使用的错误方式(我不是任何专家)或静态方法'EchoTestStructure'中的任何编组参数,这是真正的问题和帮助。
PS:如果我无法解决,我会看到回复中的链接尝试其他方法。
再次感谢,还有其他想法吗?
答案 3 :(得分:0)
它已经解决了,但从另一个方面来说......这可能不是最先进的技术,但它确实有效。
首先,需要更改struct中的Marshaling类型,从BStr更改为LPStr。
我不知道究竟是什么动机,因为一些Microsoft帮助和其他链接,他们说:“System.String:转换为终止于空引用或BSTR的字符串”。
如果我将“Hello”设置为结构中的Str1,我会在DLL中收到“效汬㉯映潲䉖⸶⸮”。如果我返回一个固定的字符串“Hello”表单DLL,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'声明中,更改为使用引用并传递结构intead的地址返回结构。在代码之前:
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 error)和参数结构类型,方法改为:
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
感谢您的提示。如果您有任何建议,欢迎。