为什么在我的.NET组件的索引并不总是从VBScript访问? [英] Why is the indexer on my .NET component not always accessible from VBScript?

查看:220
本文介绍了为什么在我的.NET组件的索引并不总是从VBScript访问?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有我从VBScript(传统的ASP),通过COM互操作访问.NET程序集。一类有一个索引(又名默认属性),我得到了从VBScript通过添加下面的属性添加到索引工作: [DISPID(0)] 。访问类作为另一对象的成员时它在大多数情况下,但不是

怎样才能得到它的语法如下工作: Parent.Member(钥匙),其中成员有索引(类似访问的默认属性内置的的Request.QueryString 的Request.QueryString(钥匙)

在我的情况下,有一个父类 TestRequest 查询字符串属性,它返回一个 IRequestDictionary ,其默认的索引。

VBScript示例:

 暗淡testRequest,testQueryString
设置testRequest =的Server.CreateObject(AspObjects.TestRequest)
设置testQueryString = testRequest.QueryString
testQueryString(钥匙)=值
 

下面的行会导致错误,而不是印刷的价值。这是语法我想获得工作:

 的Response.Write(testRequest.QueryString(钥匙))
 

  

Microsoft VBScript运行时(0x800A01C2)
  数量的参数或无效的属性赋值错误:查询字符串

但是,下面的行的别的没有错误并输出预期值的工作(请注意,第一行访问默认索引上的临时变量):

 的Response.Write(testQueryString(钥匙))
回复于(testRequest.QueryString.Item(钥匙))
 

下面是在C#2.0的简化接口和类。他们已经通过注册 RegAsm.exe /path/to/AspObjects.dll / codeBase的/ TLB

  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
公共接口IRequest {
    IRequestDictionary查询字符串{获取; }
}

[ClassInterface(ClassInterfaceType.None)
公共类TestRequest:IRequest {
    私人IRequestDictionary _queryString =新RequestDictionary();

    公共IRequestDictionary查询字符串{
        {返回_queryString; }
    }
}

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
公共接口IRequestDictionary:IEnumerable的{
    [DISPID(0)]
    反对此[对象键] {
        [DISPID(0)]获得;
        [DISPID(0)]设置;
    }
}

[ClassInterface(ClassInterfaceType.None)
公共类RequestDictionary:IRequestDictionary {
    私人Hashtable的_dictionary =新的Hashtable();

    公共对象本[对象键] {
        {返回_dictionary [关键]; }
        集合{_dictionary [关键] =价值; }
    }
}
 

我已经试过研究和各种选择尝试,但还没有找到一个解决办法。任何帮助将是pciated弄清楚为什么 testRequest.QueryString(钥匙)语法不工作,以及如何得到它的工作AP $ P $。

注:这是一个随访到<一个href="http://stackoverflow.com/questions/299251/exposing-the-indexer-default-property-via-com-interop">Exposing通过COM互操作索引/默认属性。

更新:下面是一些生成的IDL从类型库中(使用<一个href="http://www.microsoft.com/downloads/details.aspx?familyid=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en"相对=nofollow> OLEVIEW ):

  [
  UUID(C6EDF8BC-6C8B-3AB2-92AA-BBF4D29C376E)
  版本(1.0),
  定制(0F21F359-AB84-41E8-9A78-36D110E6D2F9,AspObjects.IRequest)

]
调度接口IRequest {
    特性:
    方法:
        [ID(0x60020000),propget]
        IRequestDictionary *查询字符串();
};

[
  UUID(8A494CF3-1D9E-35AE-AFA7-E7B200465426)
  版本(1.0),
  定制(0F21F359-AB84-41E8-9A78-36D110E6D2F9,AspObjects.IRequestDictionary)

]
调度接口IRequestDictionary {
    特性:
    方法:
        [ID(00000000),propget]
        VARIANT项目([IN]变密钥);
        [ID(00000000),propputref]
        无效项目(
                        [输入] VARIANT键,
                        [中] VARIANT右);
};
 

解决方案

我前几天偶然发现了这个确切的问题。我无法找到一个合理的解释,为什么它不工作。

花费很长时间尝试不同的解决方法后,我想我终于找到的东西,似乎工作,并且不那么脏。我所做的是落实,而不是一个属性访问器中的容器对象作为方法的集合。这个方法接收一个参数的关键。如果钥匙被丢失或为空,则该方法返回集合(此处理前pressions像testRequest.QueryString.Count在VBScript)。否则,该方法从集合返回特定项目。

在脏的部分用这种方法,该方法返回一个对象(因为有时返回引用的集合,有时候收集的项目),因此使用它从管理code需要铸件随处可见。为了避免这种情况,我创建在暴露该收集容器的另一个属性(这次的适当属性)。这个属性没有接触到COM。从C#/管理code我使用这个属性,并从COM / VBScript中/非托管code我使用的方法。

下面是上面的解决方法,使用该线程的例子的实现:

  [标记有​​ComVisible特性(真)
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
  公共接口IRequest
  {
IRequestDictionary ManagedQueryString {获得; } //物业使用的形式管理code
对象查询字符串(对象键); //属性从COM或非托管code使用
  }

  [标记有ComVisible特性(真)
  [ClassInterface(ClassInterfaceType.None)
  公共类TestRequest:IRequest
  {
私人IRequestDictionary _queryString =新RequestDictionary();

公共IRequestDictionary ManagedQueryString
{
{返回_queryString; }
}

公共对象查询字符串(对象键)
{
如果(键System.Reflection.Missing ||键== NULL)
返回_queryString;
	  其他
返回_queryString [关键];
}
  }

  [标记有ComVisible特性(真)
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
  公共接口IRequestDictionary:IEnumerable的
  {
[DISPID(0)]
反对此[对象键]
{
[DISPID(0)]
	  得到;
[DISPID(0)]
	  组;
}

诠释计数{获得; }
  }

  [标记有ComVisible特性(真)
  [ClassInterface(ClassInterfaceType.None)
  公共类RequestDictionary:IRequestDictionary
  {
私人Hashtable的_dictionary =新的Hashtable();

公共对象本[对象键]
{
{返回_dictionary [关键]; }
集合{_dictionary [关键] =价值; }
}

公众诠释计数{{返回_dictionary.Count; }}

#地区IEnumerable的成员

公众的IEnumerator的GetEnumerator()
{
抛出新的NotImplementedException();
}

#endregion
  }
 

I have a .NET assembly which I am accessing from VBScript (classic ASP) via COM interop. One class has an indexer (a.k.a. default property) which I got working from VBScript by adding the following attribute to the indexer: [DispId(0)]. It works in most cases, but not when accessing the class as a member of another object.

How can I get it to work with the following syntax: Parent.Member("key") where Member has the indexer (similar to accessing the default property of the built-in Request.QueryString: Request.QueryString("key"))?

In my case, there is a parent class TestRequest with a QueryString property which returns an IRequestDictionary, which has the default indexer.

VBScript example:

Dim testRequest, testQueryString
Set testRequest = Server.CreateObject("AspObjects.TestRequest")
Set testQueryString = testRequest.QueryString
testQueryString("key") = "value"

The following line causes an error instead of printing "value". This is the syntax I would like to get working:

Response.Write(testRequest.QueryString("key"))

Microsoft VBScript runtime (0x800A01C2)
Wrong number of arguments or invalid property assignment: 'QueryString'

However, the following lines do work without error and output the expected "value" (note that the first line accesses the default indexer on a temporary variable):

Response.Write(testQueryString("key"))
Response.Write(testRequest.QueryString.Item("key"))

Below are the simplified interfaces and classes in C# 2.0. They have been registered via RegAsm.exe /path/to/AspObjects.dll /codebase /tlb:

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IRequest {
    IRequestDictionary QueryString { get; }
}

[ClassInterface(ClassInterfaceType.None)]
public class TestRequest : IRequest {
    private IRequestDictionary _queryString = new RequestDictionary();

    public IRequestDictionary QueryString {
        get { return _queryString; }
    }
}

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IRequestDictionary : IEnumerable {
    [DispId(0)]
    object this[object key] {
        [DispId(0)] get;
        [DispId(0)] set;
    }
}

[ClassInterface(ClassInterfaceType.None)]
public class RequestDictionary : IRequestDictionary {
    private Hashtable _dictionary = new Hashtable();

    public object this[object key] {
        get { return _dictionary[key]; }
        set { _dictionary[key] = value; }
    }
}

I've tried researching and experimenting with various options but have not yet found a solution. Any help would be appreciated to figure out why the testRequest.QueryString("key") syntax is not working and how to get it working.

Note: This is a followup to Exposing the indexer / default property via COM Interop.

Update: Here is some the generated IDL from the type library (using oleview):

[
  uuid(C6EDF8BC-6C8B-3AB2-92AA-BBF4D29C376E),
  version(1.0),
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, AspObjects.IRequest)

]
dispinterface IRequest {
    properties:
    methods:
        [id(0x60020000), propget]
        IRequestDictionary* QueryString();
};

[
  uuid(8A494CF3-1D9E-35AE-AFA7-E7B200465426),
  version(1.0),
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, AspObjects.IRequestDictionary)

]
dispinterface IRequestDictionary {
    properties:
    methods:
        [id(00000000), propget]
        VARIANT Item([in] VARIANT key);
        [id(00000000), propputref]
        void Item(
                        [in] VARIANT key, 
                        [in] VARIANT rhs);
};

解决方案

I stumbled upon this exact problem a few days ago. I couldn't find a reasonable explanation as to why it doesn't work.

After spending long hours trying different workarounds, I think I finally found something that seems to work, and is not so dirty. What I did is implement the accessor to the collection in the container object as a method, instead of a property. This method receives one argument, the key. If the key is "missing" or null, then the method returns the collection (this handles expressions like "testRequest.QueryString.Count" in VbScript). Otherwise, the method returns the specific item from the collection.

The dirty part with this approach is that this method returns an object (because sometimes the return reference is the collection, and sometimes an item of the collection), so using it from managed code needs castings everywhere. To avoid this, I created another property (this time a proper property) in the container that exposes the collection. This property is NOT exposed to COM. From C#/managed code I use this property, and from COM/VbScript/unmanaged code I use the method.

Here is an implementation of the above workaround using the example of this thread:

  [ComVisible(true)]
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
  public interface IRequest
  {
	IRequestDictionary ManagedQueryString { get; } // Property to use form managed code
	object QueryString(object key); // Property to use from COM or unmanaged code
  }

  [ComVisible(true)]
  [ClassInterface(ClassInterfaceType.None)]
  public class TestRequest : IRequest
  {
	private IRequestDictionary _queryString = new RequestDictionary();

	public IRequestDictionary ManagedQueryString
	{
	  get { return _queryString; }
	}

	public object QueryString(object key)
	{
	  if (key is System.Reflection.Missing || key == null)
		return _queryString;
	  else
		return _queryString[key];
	}
  }

  [ComVisible(true)]
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
  public interface IRequestDictionary : IEnumerable
  {
	[DispId(0)]
	object this[object key]
	{
	  [DispId(0)]
	  get;
	  [DispId(0)]
	  set;
	}

	int Count { get; }
  }

  [ComVisible(true)]
  [ClassInterface(ClassInterfaceType.None)]
  public class RequestDictionary : IRequestDictionary
  {
	private Hashtable _dictionary = new Hashtable();

	public object this[object key]
	{
	  get { return _dictionary[key]; }
	  set { _dictionary[key] = value; }
	}

	public int Count { get { return _dictionary.Count; } }

	#region IEnumerable Members

	public IEnumerator GetEnumerator()
	{
	  throw new NotImplementedException();
	}

	#endregion
  }

这篇关于为什么在我的.NET组件的索引并不总是从VBScript访问?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆