简体   繁体   English

如何将.NET COM dll中的结构或类返回给消耗的VB6应用程序?

[英]How do you return an struct or class from a .NET COM dll to a consuming VB6 application?

This seems like something that would be easy to find on here, but if this has been asked before, I don't see where. 这看起来像是在这里很容易找到的东西,但如果之前有人问过,我看不到哪里。

Basically, I'm a.NET developer and am having to work with VB6 for a minute and learn about making a COM DLL. 基本上,我是一名.NET开发人员,我不得不使用VB6一分钟,并学习制作COM DLL。 I'm working in C# and am trying to get a COM DLL made in that language to return a custom class/struct to some VB6 code, and though the answer here was easy enough to use when returning a string or int from the COM method, I'm having trouble getting it to work with an actual object. 我正在使用C#,我正在尝试使用该语言编写的COM DLL将自定义类/结构返回到某些VB6代码,尽管这里的答案很容易在从COM方法返回stringint时使用,我无法让它与实际对象一起工作。

Example Code: 示例代码:

C# 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 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")

This approach works great when the return value is a string or an int (and when result is declared as a String ), at which point the value can be passed into MsgBox and displayed to the user just fine. 当返回值是stringint (并且result被声明为String )时,此方法很有效,此时该值可以传递到MsgBox并显示给用户。 But if either SomeClass or Test is returned, any attempt to pass result.[toString()/Test1/Test2] to MsgBox results in the messages "start" and "end" still being displayed to the user just fine, but in nothing showing at all in between (not even a blank message). 但是,如果任一 SomeClass Test返回,任何企图通过result.[toString()/Test1/Test2]MsgBox导致消息“开始”,仍然被显示给用户就好了“结束”,但没有什么表现在它们之间(甚至不是空白的消息)。

Of note is that by return an instance of Test and leaving result declared as a String , a call to MsgBox (result) will display "One.Two.Three.Test" - which shows that something is happening there. 值得注意的是,通过返回一个Test实例并将result声明为String ,对MsgBox (result)的调用将显示“One.Two.Three.Test” - 这表明发生了某些事情。

So...the question is: 所以......问题是:

What else would need to be done to get this object to be fairly accessible by the VB6 application? 还需要做些什么来使VB6应用程序能够公平地访问这个对象?

In particular, I'm going to need to return an array, List<T> , or something of some objects that each have several members. 特别是,我需要返回一个数组, List<T> ,或者某些对象,每个对象都有几个成员。 Again this seems like something that should be relatively easy to find on Google or SO, but it seems to get buried from other search results. 再次,这似乎应该是相对容易在谷歌或SO上找到的东西,但它似乎从其他搜索结果埋没。

PS The .NET Framework for the dll is 4.5. PS dll的.NET Framework是4.5。 (I used RegAsm out of the 4.0 folder, not the 2.0 specifically mentioned in the linked answer). (我在4.0文件夹中使用了RegAsm,而不是链接答案中特别提到的2.0)。 If that needs to be brought down to 4.0, I probably could, but it may not be possible to bring it all the way down to 2.0. 如果需要降低到4.0,我可能会,但可能无法将其一直降至2.0。

There are not only one way to make this work, but here is a simple one that matches how you're doing things: 不仅有一种方法可以使这项工作,但这里有一个简单的方法来匹配你的工作方式:

C# code can be very simple, you don't need to declare interfaces explicitely, you don't need to use ref keyword everywhere, etc, but you do need to add a ComVisible(true) attribute to your classes (string, int, etc. are implicitely ComVisible): C#代码可以很简单,你不需要明确地声明的接口,你不需要使用ref到处关键字等,但需要一个添加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 code: 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

But, if you want to get Intellisense from VB/VBA, you can also declare the classes like this: 但是,如果你想从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; }
}

I've just added ClassInterfaceType.AutoDual , and now I can reference a TLB (that I created using regasm /tlb), and use this code in VB/VBA: 我刚刚添加了ClassInterfaceType.AutoDual ,现在我可以引用一个TLB(我使用regasm / tlb创建),并在VB / VBA中使用此代码:

Dim c1 As New SomeClass1
Set c2 = c1.SomeFunction("hello world")
MsgBox c2.Text

ClassInterfaceType.AutoDual is not "recommended" because if you go that route, you'll have to make sure the binaries between the COM client (here VB/VBA) and the COM server (here C#) are 100% in sync or you may suffer hard VB/VBA crashes. ClassInterfaceType.AutoDual不是“推荐”的,因为如果你走这条路,你必须确保COM客户端(这里是VB / VBA)和COM服务器(这里是C#)之间的二进制文件是100%同步的,或者你可以遭受严重的VB / VBA崩溃。 However, it's a compromise, intellisense is a great productivity tool IMHO. 然而,这是一个妥协,intellisense是一个伟大的生产力工具恕我直言。

One last word on returning List<T> , you'll have troubles with that. 关于返回List<T>最后一句话,你会遇到麻烦。 If you face it, ask another question :-) 如果你面对它,问另一个问题:-)

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

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