Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使C#COM类支持VB6中的参数化属性_C#_.net_Dll_Com_Vb6 - Fatal编程技术网

如何使C#COM类支持VB6中的参数化属性

如何使C#COM类支持VB6中的参数化属性,c#,.net,dll,com,vb6,C#,.net,Dll,Com,Vb6,我对这个问题做了很多研究,虽然我发现了很多关于C#和参数化属性的知识(使用索引器是唯一的方法),但我还没有找到我的问题的实际答案 首先,我想做的是: 我有一个用VB6编写的现有COM DLL,我正在尝试创建一个使用类似接口的C#DLL。我之所以说类似,是因为VB6 DLL只用于后期绑定,因此调用不必使用相同的GUID(也就是说,它不必是“二进制兼容的”)。这个VB6 COM DLL在一些地方使用参数化属性,我知道C#不支持这些属性 当使用带有参数化属性的VB6 COM DLL时,C#中的引用将以

我对这个问题做了很多研究,虽然我发现了很多关于C#和参数化属性的知识(使用索引器是唯一的方法),但我还没有找到我的问题的实际答案

首先,我想做的是:

我有一个用VB6编写的现有COM DLL,我正在尝试创建一个使用类似接口的C#DLL。我之所以说类似,是因为VB6 DLL只用于后期绑定,因此调用不必使用相同的GUID(也就是说,它不必是“二进制兼容的”)。这个VB6 COM DLL在一些地方使用参数化属性,我知道C#不支持这些属性

当使用带有参数化属性的VB6 COM DLL时,C#中的引用将以“get_PropName”和“set_PropName”的形式作为方法访问它们。但是,我的方向正好相反:我不是试图访问C#中的VB6 DLL,而是试图使C#COM DLL与VB6 DLL兼容

因此,问题是:如何在C#COM DLL中生成getter和setter方法,这些方法在VB6使用时显示为单个参数化属性?

例如,假设VB6属性定义如下:

Public Property Get MyProperty(Param1 As String, Param2 as String) As String
End Property

Public Property Let MyProperty(Param1 As String, Param2 As String, NewValue As String)
End Property
C#中的等价物如下所示:

public string get_MyProperty(string Param1, string Param2)
{
}

public void set_MyProperty(string Param1, string Param2, ref string NewValue)
{
}
那么,当VB6使用这些C#方法时,我如何使它们看起来像(和函数一样)单个参数化属性呢

我尝试创建了两个方法,一个叫做“set_PropName”,另一个叫做“get_PropName”,希望它能发现当VB6使用它们时,它们应该是一个单一的参数化属性,但这不起作用;它们显示为来自VB6的两个不同的方法调用

我认为可能需要在C#中对它们应用一些属性,以便在COM和VB6中将它们视为单个参数化属性,但我找不到任何合适的属性

我还尝试过重载这些方法,删除“get_u3;”和“set_3;”,希望它能将它们视为一个单独的属性,但这也不起作用。其中一个在VB6中生成此错误:“未定义Property let过程,Property get过程未返回对象”

我几乎可以肯定的是,应该有办法做到这一点,但我就是找不到。有人知道怎么做吗

更新:

我采纳了Ben的建议,添加了一个访问器类,看看这是否能解决我的问题。然而,现在我遇到了另一个问题

首先,这里是我正在使用的COM接口:

[ComVisible(true),
 Guid("94EC4909-5C60-4DF8-99AD-FEBC9208CE76"),
 InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ISystem
{
    object get_RefInfo(string PropertyName, int index = 0, int subindex = 0);
    void set_RefInfo(string PropertyName, int index = 0, int subindex = 0, object theValue);

    RefInfoAccessor RefInfo { get; }

}
下面是访问器类:

public class RefInfoAccessor
{
    readonly ISystem mySys;
    public RefInfoAccessor(ISystem sys)
    {
        this.mySys = sys;
    }

    public object this[string PropertyName, int index = 0, int subindex = 0]
    {
        get
        {
            return mySys.get_RefInfo(PropertyName, index, subindex);
        }
        set
        {
            mySys.set_RefInfo(PropertyName, index, subindex, value);
        }
    }
}
以下是实现:

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid(MySystem.ClassId)]
[ProgId("MyApp.System")]
public class MySystem : ISystem
{
    internal const string ClassId = "60A84737-8E96-4DF3-A052-7CEB855EBEC8";

    public MySystem()
    {
        _RefInfo = new RefInfoAccessor(this);
    }


    public object get_RefInfo(string PropertyName, int index = 0, int subindex = 0)
    {
        // External code does the actual work
        return "Test";
    }
    public void set_RefInfo(string PropertyName, int index = 0, int subindex = 0, object theValue)
    {
        // External code does the actual work
    }

    private RefInfoAccessor _RefInfo;
    public RefInfoAccessor RefInfo
    {
        get
        {
            return _RefInfo;
        }
    }

}
下面是我在VB6中测试的步骤,但我得到一个错误:

Set sys = CreateObject("MyApp.System")

' The following statement gets this error:
' "Wrong number of arguments or invalid property assignment"
s = sys.RefInfo("MyTestProperty", 0, 0)
然而,这是可行的:

Set sys = CreateObject("MyApp.System")

Set obj = sys.RefInfo
s = obj("MyTestProperty", 0, 0)
它似乎试图使用属性本身的参数,但由于属性没有参数,因此出现错误。如果我在自己的对象变量中引用RefInfo属性,那么它将正确应用索引器属性

关于如何安排它,使它知道如何将参数应用于访问器的索引器,而不是试图将其应用于属性,有什么想法吗

还有,我怎么做a+1?这是我关于StackOverflow的第一个问题:-)

更新#2:

为了了解它的工作原理,我还尝试了默认值方法。下面是访问器现在的外观:

public class RefInfoAccessor
{
    readonly ISystem mySys;
    private int _index;
    private int _subindex;
    private string _propertyName;
    public RefInfoAccessor(ISystem sys, string propertyName, int index, int subindex)
    {
        this.mySys = sys;
        this._index = index;
        this._subindex = subindex;
        this._propertyName = propertyName;
    }
    [DispId(0)]
    public object Value
    {
        get
        {
            return mySys.get_RefInfo(_propertyName, _index, _subindex);
        }
        set
        {
            mySys.set_RefInfo(_propertyName, _index, _subindex, value);
        }
    }
}
这对于“获取”非常有效。但是,当我尝试设置该值时,.NET会出现以下错误:

托管调试助手“FatalExecutionEngineError”检测到 “blahblah.exe”中存在问题

其他信息:运行时遇到致命错误。这个 错误地址位于线程0x1694上的0x734a60f4。错误 代码为0xc0000005。此错误可能是CLR或中的错误 用户代码的不安全或不可验证部分。这方面的共同来源 错误包括COM互操作或PInvoke的用户封送错误 可能会损坏堆栈

我假设问题是.NET尝试将值设置为方法,而不是返回对象的默认属性或类似的内容。如果我在设置行中添加“.Value”,则效果良好

更新#3:成功

我终于成功了。然而,有一些东西需要寻找

首先,访问器的默认值必须返回一个定标器,而不是一个对象,如下所示:

public class RefInfoAccessor
{
    readonly ISystem mySys;
    private int _index;
    private int _subindex;
    private string _propertyName;
    public RefInfoAccessor(ISystem sys, string propertyName, int index, int subindex)
    {
        this.mySys = sys;
        this._index = index;
        this._subindex = subindex;
        this._propertyName = propertyName;
    }
    [DispId(0)]
    public string Value  // <== Can't be "object"
    {
        get
        {
            return mySys.get_RefInfo(_propertyName, _index, _subindex).ToString();
        }
        set
        {
            mySys.set_RefInfo(_propertyName, _index, _subindex, value);
        }
    }
}
这会让C#感到高兴,因为默认值是COM对象(dispid 0),而不是C#对象,所以C#希望返回refinfo访问器,而不是字符串。由于refinfo访问器可以强制为对象,因此没有编译器错误

在VB6中使用时,以下各项现在都可以工作:

s = sys.RefInfo("MyProperty", 0, 0)
Debug.Print s

sys.RefInfo("MyProperty", 0, 0) = "Test"  ' This now works!
s = sys.RefInfo("MyProperty", 0)
Debug.Print s
非常感谢本在这方面的帮助

C#可以创建索引属性,但这些属性必须使用具有索引器的帮助器类来实现。此方法适用于早期绑定的VB,但不适用于后期绑定的VB:

using System;


class MyClass {
    protected string get_MyProperty(string Param1, string Param2)
    {
        return "foo: " + Param1 + "; bar: " + Param2;
    }

    protected void set_MyProperty(string Param1, string Param2, string NewValue)
    {
        // nop
    }
    // Helper class
    public class MyPropertyAccessor {
        readonly MyClass myclass;
        internal MyPropertyAccessor(MyClass m){
            myclass = m;
        }
        public string this [string param1, string param2]{
             get {
                 return myclass.get_MyProperty(param1, param2);
             }
             set {
                 myclass.set_MyProperty(param1, param2, value);
             }
        }
    }
    public readonly MyPropertyAccessor MyProperty;
    public MyClass(){
        MyProperty = new MyPropertyAccessor(this);
    }
}


public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");

        var mc = new MyClass();
        Console.WriteLine(mc.MyProperty["a", "b"]);
    }

}
这里有一个教程:

晚绑定VB解决方案 这是一个变通方法,它利用了有关VB的两个事实。一个是数组中的索引运算符与函数调用运算符相同-圆括号(parens)。另一个是VB允许我们省略默认属性的名称

只读属性 如果属性是get only,则无需为此烦恼。只需使用一个函数,其行为与后期绑定代码的数组访问相同

读写属性 使用以上两个事实,我们可以看到它们在VB中是等价的

// VB Syntax: PropName could either be an indexed property or a function
varName = obj.PropName(index1).Value
obj.PropName(index1).Value = varName

// But if Value is the default property of obj.PropName(index1) 
// this is equivalent:
varName = obj.PropName(index1)
obj.PropName(index1) = varName
这意味着,与其这样做,不如:

//Property => Object with Indexer
// C# syntax
obj.PropName[index1];
我们可以这样做:

// C# syntax
obj.PropName(index1).Value
下面是示例代码,只有一个参数

class HasIndexedProperty {
    protected string get_PropertyName(int index1){
        // replace with your own implementation
        return string.Format("PropertyName: {0}", index1);
    }
    protected void set_PropertyName(int index1, string v){
        // this is an example - put your implementation here
    }
    // This line provides the indexed property name as a function.
    public string PropertyName(int index1){
        return new HasIndexedProperty_PropertyName(this, index1);
    }
    public class HasIndexedProperty_PropertyName{
        protected HasIndexedProperty _owner;
        protected int _index1;
        internal HasIndexedProperty_PropertyName(
            HasIndexedProperty owner, int index1){
            _owner = owner; _index1 = index1;
        }
        // This line makes the property Value the default
        [DispId(0)]
        public string Value{
            get {
                return _owner.get_PropertyName(_index1);
            }
            set {
                _owner.set_PropertyName(_index1, value);
            }
        }
    }
}
局限性 限制在于,要工作,这取决于在结果强制为非对象类型的上下文中进行的调用。比如说

varName = obj.PropName(99)
由于未使用
Set
关键字,VB知道它必须获取默认属性才能在此处使用

同样,当传递给以
varName = obj.PropName(99)
interface ISomething : IDispatch {
    [id(0x68030001), propget]
    HRESULT IndexedProp(
                    [in, out] BSTR* a,      // Index 1
                    [in, out] BSTR* b,      // Index 2
                    [out, retval] BSTR* );
    [id(0x68030001), propput]
    HRESULT IndexedProp(
                    [in, out] BSTR* a,      // Index 1
                    [in, out] BSTR* b,      // Index 2
                    [in, out] BSTR* );


    [id(0x68030000), propget]
    HRESULT PlainProp(
                    [out, retval] BSTR* );

    [id(0x68030000), propput]
    HRESULT PlainProp(
                    [in, out] BSTR* );
};