如何从 .NET COM dll return 结构或 class 到消费 VB6 应用程序?
How do you return an struct or class from a .NET COM dll to a consuming VB6 application?
这似乎很容易在这里找到,但如果以前有人问过,我不知道在哪里。
基本上,我是 a.NET 开发人员,我必须花一分钟时间使用 VB6 并学习如何制作 COM DLL。我在 C# 中工作,并试图让用该语言制作的 COM DLL return 自定义 class/struct 到一些 VB6 代码,虽然答案 here 很容易使用当从 COM 方法 returning string
或 int
时,我无法让它与实际对象一起工作。
示例代码:
C#
using System;
using System.Runtime.InteropServices;
namespace One.Two.Three
{
[Guid("<some GUID>"), ClassInterface(ClassInterfaceType.None)]
public class SomeClass : ISomeClass
{
public string Test1 { get; set; } = "Tesuto Ichiban";
public string Test2 { get; set; } = "Tesuto Niban";
public SomeClass SomeFunction(ref string str1, ref string str2, ref string str3,
ref bool someBool, string str4)
{
return new SomeClass();
}
}
[Guid("<another GUID>")]
public interface ISomeClass
{
SomeClass SomeFunction(ref string str1, ref string str2, ref string str3, ref bool
someBool, string str4);
}
public class Test
{
public string Test1 { get; set; } = "Tesuto Ichiban";
public string Test2 { get; set; } = "Tesuto Niban";
}
}
VB6
MsgBox ("start")
Dim result As Object
Dim someObj
Set someObj = CreateObject("One.Two.Three.SomeClass")
result = CallByName(someObj, "SomeFunction", VbMethod, "1", "2", "3", True, "4")
MsgBox (result)
'MsgBox (result.toString())
'MsgBox (result.Test1)
'MsgBox (result.Test2)
MsgBox ("end")
当 return 值为 string
或 int
时(以及当 result
声明为 String
时,这种方法非常有效),此时该值可以传递到 MsgBox
并显示给用户就好了。但是如果或者SomeClass
或者Test
被return编辑,任何试图通过result.[toString()/Test1/Test2]
到 MsgBox
导致消息 "start" 和 "end" 仍然很好地显示给用户,但两者之间没有任何显示(甚至没有空白消息)。
值得注意的是,通过 return Test
的实例并将 result
声明为 String
,对 MsgBox (result)
的调用将显示 "One.Two.Three.Test" - 这表明那里正在发生某些事情。
所以...问题是:
还需要做些什么才能让 VB6 应用程序公平地访问此对象?
特别是,我将需要 return 一个数组,List<T>
,或者一些每个都有多个成员的对象。同样,这似乎应该在 Google 或 SO 上相对容易找到,但它似乎被其他搜索结果掩盖了。
P.S。 dll 的 .NET Framework 是 4.5。 (我使用了 4.0 文件夹中的 RegAsm,而不是链接答案中特别提到的 2.0)。如果需要降到 4.0,我可能可以,但可能无法一直降到 2.0。
不仅有一种方法可以完成这项工作,还有一种与您的工作方式相匹配的简单方法:
C# 代码可以非常简单,你不需要显式声明接口,你不需要到处使用 ref
关键字等等,但是你 do 需要为你的 类 添加一个 ComVisible(true)
属性(字符串、整数等是隐含的 ComVisible):
[ComVisible(true)]
[ProgId("One.Two.Three.SomeClass")]
public class SomeClass1
{
public SomeClass2 SomeFunction(string text)
{
return new SomeClass2 { Text = text };
}
}
[ComVisible(true)]
public class SomeClass2
{
public string Text { get; set; }
}
VB/VBA代码:
Dim result As Object
Dim someObj
Set someObj = CreateObject("One.Two.Three.SomeClass")
' note the "Set" keyword, it's mandatory for COM objects...
Set result = CallByName(someObj, "SomeFunction", VbMethod, "hello world")
MsgBox result.Text
但是,如果您想从 VB/VBA 获取 Intellisense,您也可以像这样声明 类:
// progid is now optional because we won't need CreateObject anymore
[ProgId("One.Two.Three.SomeClass")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class SomeClass1
{
public SomeClass2 SomeFunction(string text)
{
return new SomeClass2 { Text = text };
}
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class SomeClass2
{
public string Text { get; set; }
}
我刚刚添加了 ClassInterfaceType.AutoDual
,现在我可以引用一个 TLB(我使用 regasm /tlb 创建),并在 VB/VBA:
中使用此代码
Dim c1 As New SomeClass1
Set c2 = c1.SomeFunction("hello world")
MsgBox c2.Text
ClassInterfaceType.AutoDual
不是 "recommended" 因为如果你走那条路,你必须确保 COM 客户端(这里是 VB/VBA)和 COM 服务器(这里的 C#) 是 100% 同步的,否则你可能会遇到严重的 VB/VBA 崩溃。然而,这是一个妥协,intellisense 是一个很棒的生产力工具恕我直言。
关于返回的最后一句话 List<T>
,你会遇到麻烦。如果你面对它,再问一个问题:-)
这似乎很容易在这里找到,但如果以前有人问过,我不知道在哪里。
基本上,我是 a.NET 开发人员,我必须花一分钟时间使用 VB6 并学习如何制作 COM DLL。我在 C# 中工作,并试图让用该语言制作的 COM DLL return 自定义 class/struct 到一些 VB6 代码,虽然答案 here 很容易使用当从 COM 方法 returning string
或 int
时,我无法让它与实际对象一起工作。
示例代码:
C#
using System;
using System.Runtime.InteropServices;
namespace One.Two.Three
{
[Guid("<some GUID>"), ClassInterface(ClassInterfaceType.None)]
public class SomeClass : ISomeClass
{
public string Test1 { get; set; } = "Tesuto Ichiban";
public string Test2 { get; set; } = "Tesuto Niban";
public SomeClass SomeFunction(ref string str1, ref string str2, ref string str3,
ref bool someBool, string str4)
{
return new SomeClass();
}
}
[Guid("<another GUID>")]
public interface ISomeClass
{
SomeClass SomeFunction(ref string str1, ref string str2, ref string str3, ref bool
someBool, string str4);
}
public class Test
{
public string Test1 { get; set; } = "Tesuto Ichiban";
public string Test2 { get; set; } = "Tesuto Niban";
}
}
VB6
MsgBox ("start")
Dim result As Object
Dim someObj
Set someObj = CreateObject("One.Two.Three.SomeClass")
result = CallByName(someObj, "SomeFunction", VbMethod, "1", "2", "3", True, "4")
MsgBox (result)
'MsgBox (result.toString())
'MsgBox (result.Test1)
'MsgBox (result.Test2)
MsgBox ("end")
当 return 值为 string
或 int
时(以及当 result
声明为 String
时,这种方法非常有效),此时该值可以传递到 MsgBox
并显示给用户就好了。但是如果或者SomeClass
或者Test
被return编辑,任何试图通过result.[toString()/Test1/Test2]
到 MsgBox
导致消息 "start" 和 "end" 仍然很好地显示给用户,但两者之间没有任何显示(甚至没有空白消息)。
值得注意的是,通过 return Test
的实例并将 result
声明为 String
,对 MsgBox (result)
的调用将显示 "One.Two.Three.Test" - 这表明那里正在发生某些事情。
所以...问题是:
还需要做些什么才能让 VB6 应用程序公平地访问此对象?
特别是,我将需要 return 一个数组,List<T>
,或者一些每个都有多个成员的对象。同样,这似乎应该在 Google 或 SO 上相对容易找到,但它似乎被其他搜索结果掩盖了。
P.S。 dll 的 .NET Framework 是 4.5。 (我使用了 4.0 文件夹中的 RegAsm,而不是链接答案中特别提到的 2.0)。如果需要降到 4.0,我可能可以,但可能无法一直降到 2.0。
不仅有一种方法可以完成这项工作,还有一种与您的工作方式相匹配的简单方法:
C# 代码可以非常简单,你不需要显式声明接口,你不需要到处使用 ref
关键字等等,但是你 do 需要为你的 类 添加一个 ComVisible(true)
属性(字符串、整数等是隐含的 ComVisible):
[ComVisible(true)]
[ProgId("One.Two.Three.SomeClass")]
public class SomeClass1
{
public SomeClass2 SomeFunction(string text)
{
return new SomeClass2 { Text = text };
}
}
[ComVisible(true)]
public class SomeClass2
{
public string Text { get; set; }
}
VB/VBA代码:
Dim result As Object
Dim someObj
Set someObj = CreateObject("One.Two.Three.SomeClass")
' note the "Set" keyword, it's mandatory for COM objects...
Set result = CallByName(someObj, "SomeFunction", VbMethod, "hello world")
MsgBox result.Text
但是,如果您想从 VB/VBA 获取 Intellisense,您也可以像这样声明 类:
// progid is now optional because we won't need CreateObject anymore
[ProgId("One.Two.Three.SomeClass")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class SomeClass1
{
public SomeClass2 SomeFunction(string text)
{
return new SomeClass2 { Text = text };
}
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class SomeClass2
{
public string Text { get; set; }
}
我刚刚添加了 ClassInterfaceType.AutoDual
,现在我可以引用一个 TLB(我使用 regasm /tlb 创建),并在 VB/VBA:
Dim c1 As New SomeClass1
Set c2 = c1.SomeFunction("hello world")
MsgBox c2.Text
ClassInterfaceType.AutoDual
不是 "recommended" 因为如果你走那条路,你必须确保 COM 客户端(这里是 VB/VBA)和 COM 服务器(这里的 C#) 是 100% 同步的,否则你可能会遇到严重的 VB/VBA 崩溃。然而,这是一个妥协,intellisense 是一个很棒的生产力工具恕我直言。
关于返回的最后一句话 List<T>
,你会遇到麻烦。如果你面对它,再问一个问题:-)