问题
I have a .NET 3.5 assembly which is being run as a COM+ server component, and I want to call a method in this class from VBScript (a Classic ASP page).
This is the method outline;
public bool FillArray(ref string[] arrayToFill)
{
...
}
My VBScript is as follows;
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))
This results in the following error (triggered by the line where I make the call to FillArray);
Error Type: Microsoft VBScript runtime (0x800A0005) Invalid procedure call or argument: 'FillArray'
Firing up OLEView, the IDL looks like this;
HRESULT FillArray(
[in, out] SAFEARRAY(BSTR)* arrayToFill,
[out, retval] VARIANT_BOOL* pRetVal);
I tried changing my method signature to the following;
public bool FillArray(ref object[] arrayToFill)
Which resulted in the following IDL;
HRESULT FillArray(
[in, out] SAFEARRAY(VARIANT)* arrayToFill,
[out, retval] VARIANT_BOOL* pRetVal);
But still the same "Invalid procedure call or argument 'FillArray'" error.
Finally, I tried changing my method signature to simply this;
public bool FillArray(ref object arrayToFill)
Which gave the following IDL;
HRESULT FillArray(
[in, out] VARIANT* arrayToFill,
[out, retval] VARIANT_BOOL* pRetVal);
This gives a new error now;
Microsoft VBScript runtime (0x800A000D) Type mismatch
This error is only fired off on the final line now, which is when I try to access an element of the array. If I comment the last line out, then I get the following output;
IsArray = True
UBound = 39
TypeName = String()
So, apparently the variant is being recognised as an array, and of the correct type. Also, the correct number of elements are returned by UBound, but I cannot access any of the elements for some unknown reason.
Does anyone have any idea what might be causing this? I've done some research myself, and came across the following link;
http://connect.microsoft.com/VisualStudio/feedback/details/331632/marshaler-bug-with-vbscript-arrays
I'm not 100% certain that it's the exact same issue, as I am not declaring my arrays in the same way in my VBScript code. I sincerely hope it's not the same issue, as I have no scope for upgrading to .NET 4.0.
回答1:
I managed to work this one out myself.
It turns out that VBScript does not handle arrays which are not of type Variant. So, in my C# code I tried this;
public bool FillArray(ref object arrayToFill)
{
string[] tmpArrayToFill = (string[])arrayToFill;
...
arrayToFill = (object[])tmpArrayToFill
}
The "..." refers to further calls which pass along arrayToFill by ref.
Unfortunately, this produced the exact same error.
What put me on the path to solving this was that the "TypeName()" VBScript function STILL saw the type as "String()". I was curious what was going on in the .NET code, so wrote a small test;
string[] stringArray = new string[1];
Console.WriteLine(stringArray.GetType());
object[] objectArray = (object[])stringArray;
Console.WriteLine(objectArray.GetType());
Which produced the following;
System.String[]
System.String[]
This was news to me - I didn't realise that this would be the case. I thought it reasonable to expect Object[] for the second type.
Anyway, regardless of that, I wrote up a small extension method to generate a new 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;
}
}
This is just a rough first go at it - more robust error handling and support for jagged arrays are going to be added.
So my code now looks like;
public bool FillArray(ref object arrayToFill)
{
string[] tmpArrayToFill = (string[])arrayToFill;
...
arrayToFill = tmpArrayToFill.ToObjectArray();
}
And now, in the VBScript, TypeName returns Variant() and I can access the array as expected.
Hopefully this will help anybody else who runs into this problem in the future.
回答2:
Hard to see why the script interpreter balks at indexing the array. This might have something to do with the lower-bound of the array being zero, there is no Option Base statement in VBScript. Creating an array that doesn't start at zero in .NET is technically possible through Array.CreateInstance(), one of its overloads lets you create an array that has non-zero lower bounds. I'll mention the VariantWrapper class but don't think it's relevant. Making the array the return value is something else to try as a workaround.
回答3:
This is what I did based on C.McAtackney's answer:
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
回答4:
I had a similar problem passing an array of doubles from a VB 6.0 program into a COM server written in VB.net (2010).
Declaring the VB.net function as:
Public Function GetSpatialResults(ByRef Results() As Double) As Long
would give me a type mismatch error where as :
Public Function GetSpatialResults(ByRef Results As Object) As Long
Results(1) = 1
Results(2) = 2
Results(3) = 3
GetSpatialResults = 0
Works fine, however, I had to use array indexes starting at 1
not 0
.
来源:https://stackoverflow.com/questions/4215957/type-mismatch-error-when-accessing-array-returned-by-ref-from-com-vbscript