我有一个.NET 3.5程序集,它作为COM +服务器组件运行,我想从VBScript(经典ASP页面)调用此类中的方法。
这是方法大纲;
public bool FillArray(ref string[] arrayToFill)
{
...
}
我的VBScript如下;
Dim myComponent, result, myArray
Set myComponent = Server.CreateObject("MyComponentProgID")
result = myComponent.FillArray(myArray)
Response.Write("IsArray = " & IsArray(myArray) & "<br/>")
Response.Write("UBound = " & UBound(myArray) & "<br/>")
Response.Write("TypeName = " & TypeName(myArray) & "<br/>")
Response.Write("Element 1 = " & myArray(1))
这会导致以下错误(由我调用FillArray的行触发);
错误类型:Microsoft VBScript运行时 (0x800A0005)无效的过程调用或 参数:'FillArray'
启动OLEView,IDL看起来像这样;
HRESULT FillArray(
[in, out] SAFEARRAY(BSTR)* arrayToFill,
[out, retval] VARIANT_BOOL* pRetVal);
我尝试将方法签名更改为以下内容;
public bool FillArray(ref object[] arrayToFill)
导致以下IDL;
HRESULT FillArray(
[in, out] SAFEARRAY(VARIANT)* arrayToFill,
[out, retval] VARIANT_BOOL* pRetVal);
但仍然是相同的“无效的过程调用或参数'FillArray'”错误。
最后,我尝试将我的方法签名改为简单;
public bool FillArray(ref object arrayToFill)
其中给出了以下IDL;
HRESULT FillArray(
[in, out] VARIANT* arrayToFill,
[out, retval] VARIANT_BOOL* pRetVal);
现在出现了一个新错误;
Microsoft VBScript运行时 (0x800A000D)类型不匹配
此错误仅在最后一行启动,这是我尝试访问数组的元素时。如果我评论最后一行,那么我得到以下输出;
IsArray = True
UBound = 39
TypeName = String()
因此,显然该变体被识别为数组,并且具有正确的类型。此外,UBound返回正确数量的元素,但由于某些未知原因,我无法访问任何元素。
有谁知道可能导致这种情况的原因是什么?我自己做了一些研究,发现了以下链接;
http://connect.microsoft.com/VisualStudio/feedback/details/331632/marshaler-bug-with-vbscript-arrays
我不是100%肯定这是完全相同的问题,因为我没有在我的VBScript代码中以相同的方式声明我的数组。我真诚地希望这不是同一个问题,因为我无法升级到.NET 4.0。
答案 0 :(得分:3)
我设法自己解决这个问题。
事实证明,VBScript不处理非Variant类型的数组。所以,在我的C#代码中我尝试了这个;
public bool FillArray(ref object arrayToFill)
{
string[] tmpArrayToFill = (string[])arrayToFill;
...
arrayToFill = (object[])tmpArrayToFill
}
“...”指的是通过ref。
传递arrayToFill的其他调用不幸的是,这产生了完全相同的错误。
让我走上解决问题的道路是“TypeName()”VBScript函数STILL将类型视为“String()”。我很好奇.NET代码中发生了什么,所以写了一个小测试;
string[] stringArray = new string[1];
Console.WriteLine(stringArray.GetType());
object[] objectArray = (object[])stringArray;
Console.WriteLine(objectArray.GetType());
产生了以下内容;
System.String []
System.String []
这对我来说是新闻 - 我没有意识到情况就是如此。我认为期望Object []为第二种类型是合理的。
无论如何,无论如何,我写了一个小的扩展方法来生成一个新对象[]
public static Array ToObjectArray(this Array input)
{
if (input != null)
{
object[] objArray = new object[input.Length];
input.CopyTo(objArray, 0);
return objArray;
}
else
{
return null;
}
}
这只是一个粗略的第一步 - 将增加更强大的错误处理和对锯齿状阵列的支持。
所以我的代码现在看起来像;
public bool FillArray(ref object arrayToFill)
{
string[] tmpArrayToFill = (string[])arrayToFill;
...
arrayToFill = tmpArrayToFill.ToObjectArray();
}
现在,在VBScript中,TypeName返回 Variant(),我可以按预期访问数组。
希望这将有助于将来遇到此问题的其他人。
答案 1 :(得分:1)
很难理解为什么脚本解释器会对索引数组进行索引。这可能与数组的下限为零有关,VBScript中没有Option Base语句。在.NET中创建一个不从零开始的数组在技术上是可行的,通过Array.CreateInstance(),它的一个重载允许你创建一个具有非零下限的数组。我会提到VariantWrapper类,但不认为它是相关的。使数组成为返回值是另一种尝试作为解决方法。
答案 2 :(得分:0)
我有一个类似的问题,从VB 6.0程序传递一系列双打 用VB.net(2010)编写的COM服务器。
将VB.net函数声明为:
Public Function GetSpatialResults(ByRef Results() As Double) As Long
会给我一个类型不匹配错误,其中:
Public Function GetSpatialResults(ByRef Results As Object) As Long
Results(1) = 1
Results(2) = 2
Results(3) = 3
GetSpatialResults = 0
工作正常但是,我必须使用从1
而不是0
开始的数组索引。
答案 3 :(得分:0)
这是我根据C.McAtackney的回答所做的:
Private _lastErrors As List(Of Object)
''' <summary>
''' Expose error array to COM object consumer
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks>Type is Object() because VBScript requires a variant type.</remarks>
Public Property LastErrors As Object()
Get
If _lastErrors Is Nothing Then
_lastErrors = New List(Of Object)
End If
Return _lastErrors.ToArray
End Get
Private Set(value As Object())
If _lastErrors Is Nothing Then
_lastErrors = New List(Of Object)
End If
_lastErrors.AddRange(value)
End Set
End Property