tags:

views:

235

answers:

2

I'm currently in the unfortunate position of having to call a VBA macro from C#, via the COM Interop. The macro returns an array of strings and looks something like:

Public Function Foo() As String()

which I'm then trying to cast to an array of strings in C# like this:

var result = (string[])xlApp.Run("Foo", ... missing args ...)

which then results in the runtime error:

Unable to cast object of type 'System.String[*]' to type 'System.String[]'.

Does anyone know what a String[*] is, and do you have any idea of how to cast it to a C# string[]? This cast works in a simple test case I've created. The only difference I can see, is that in the simple case, my array in VBA is of type string(0 to 5) whereas in the more complex case, it's of type string(1 to 6). Could this be the reason and if so, my VBA is rather rusty - how do I fix it?

Many thanks,
Jon

+5  A: 

.NET supports such arrays (that have lower bound different from 0), you can create one using such snippet:

Array arr = Array.CreateInstance(typeof(string), new int[] { 5 }, new int[] {1});

The simplest way to convert such array to string[] is (although, it's copying data, on the other hands, those are just references, so if your arrays aren't too long, you won't get huge perf hit):

string[] x = new string[5];
Array.Copy(arr, 1, x, 0, 5);

Edit: Or using more general approach:

string[] x = new string[arr.GetLength(0)];
Array.Copy(arr, arr.GetLowerBound(0), x, 0, arr.GetLength(0));

Edit2: It also looks like casting arrays with lowerbounds != 0 works for fine, if there are more than 1 dimension, you can read more here, where Jon Skeet seems to answer why that happens.

Edit3: To make response less ambiguous, following djna's comments. This is how you could obtain string[] out of object returned by a method, that can't be cast to string[] directly, because it has lowerBound != 0:

Array arr = (Array)xlApp.Run("Foo", ... missing args ...); //Here I assume that .Run() method is returning object, or something that has to be casted to Array.
string[] result = new string[arr.GetLength(0)];
Array.Copy(arr, arr.GetLowerBound(0), result, 0, arr.GetLength(0)); // Here data is copied, but only references in this scenario
Ravadre
Actually, it does, you can get another array, that has lower bound == 0, and is strongly typed, so how's that not an answer? The fact, that I was converting array(1..6) that is named "arr" instead of array(1..6) that is acquired by invoking xlApp.Run() doesn't change anything (well, it does if you are so lazy, that you can't change this code so it suites ur needs)
Ravadre
+, my solution gives you an string[], not an IEnumerable<string>, which isn't "worse" in my opinion.
Ravadre
+1, bit out of order that djna..
meandmycode
agree, misread the meaning of the answer. Apologies
djna
Apologies accepted ;-). Some of my responses tend to be too short (or I use thought-short cuts too often), and sometimes people misunderstand what I wanted to say, so it wasn't my first time.
Ravadre
Actually reading it all again, is there is one small step missing in the answer? Fixing that line of code: var result = (string[])xlApp.Run("Foo", ... missing args ...); The error is the case to (string[]) - it needs to be a cast to (Array) doesn't it?
djna
Yes, it does, just like you have supplied in your answer, as it of course did not work for Jon, I've assumed, that of course it must be cast to Array, and than I've showed how to get string[] of out these (and this is what I was saying about giving too short answers ;) )So if I was going to convert result of .Run(), I'd do: Array arr = (Array)foo.Run(), string[] result = new string[]; Array.Copy(.....);
Ravadre
Oh, and btw, I've found way of obtaining string[] using "mixed" approach:string[] result = arr.Cast<string>().ToArray();It gives string[], contains copying under the hood, but it looks almost as good as your solution, whereas my original looks pretty ugly (with all those .GetLowerBound(0) calls etc.).
Ravadre
All in all, a nice discussion. Very interesting. I'll go back to being a Java programmer now :-)
djna
+1  A: 

Your code was

var result = (string[])xlApp.Run("Foo", ... missing args ...);

As you have pointed out there is different behaviour with a 1-based array, with a zero-based array in VB your code does work.

You can get at the data like this

var result = (Array)xlApp.Run("Foo", ... missing args ...);

var stringsResult = result.Cast<string>[];

This gives you an IEnumerable of string

djna
That works nicely thank-you. I'm not entirely clear what the difference is between a System.Array of type string and a string[], but it gets me past this hurdle!
Jon Artus