脚本语言有一个技术限制,它们只能使用 coclass 的默认接口。他们根本没有接口的概念,也没有通过 IUnknown::QueryInterface() 获取另一个接口的后门。就像您可以在 C# 中通过转换为所需的接口类型一样。 ArrayList 的迭代器如下所示:
private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable {
// etc...
}
IEnumerator 是默认界面,您可以在 VBScript 中使用它。但是,Hashtable 的枚举器看起来像这样:
private class HashtableEnumerator : IDictionaryEnumerator, IEnumerable, ICloneable {
// etc..
}
IDictionaryEnumerator 是默认值,而不是 IEnumerable。因此 VBScript 找不到所需的 Current 和 MoveNext 成员。只有Entry、Key和Value,它们是无用的。 Keys and Values 集合也差不多:
public class KeysCollection : ICollection, IEnumerable {
// etc..
}
同样的问题,CopyTo、Count、IsSynchronized 和 SyncRoot 都没用。通过将 [ComDefaultInterface] 属性应用于这些类,Microsoft 可以很容易地解决此问题。但他们没有。
这可以解决。需要的是可以 QI 默认接口以获得 IEnumerable 接口的代码。你可以帮助一个小的 C# 类库项目:
using System;
using System.Collections;
using System.Runtime.InteropServices;
namespace VBScript
{
[ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMapper {
IEnumerable ToEnum(object itf);
}
[ComVisible(true), ProgId("VBScript.Mapper")]
public class Mapper : IMapper {
public IEnumerable ToEnum(object itf) {
return (IEnumerable)itf;
}
}
}
使用 32 位和 64 位版本的 Regasm 构建和注册程序集。现在你可以让这个脚本工作了:
Set table = CreateObject("System.Collections.Hashtable")
table.Add 1, "one"
table.Add 2, "two"
Set mapper = CreateObject("VBScript.Mapper")
For Each key in mapper.ToEnum(table.Keys)
WScript.Echo key & ": " & table(key)
Next
输出:
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.
1: one
2: two