2

I have a .Net component that has a COM visible class with a method that returns a System.Array. Under the hood it returns a string array, however the return type is declared as System.Array. Don't ask me "why", I know I can declare the return type as string[] and it will be fine, but my question is for particularly when it returns System.Array. So for simplicity the .Net method is the following:

public Array GetData()
{
    return new string[] { };
}

Then in the VB6 project no matter how I try I cannot access and traverse through the array elements as strings. Below is my VB6 code snippet that doesn't work:

Dim objRetVal As Object
Dim resData As Variant
Dim strData As String

Set objRetVal = NetClassInstance.GetData()

For Each resData In objRetVal
    strData = CStr(resData)
    MsgBox "strData = " & strData
Next

The NetClassInstance above is an instance of the COM Visible .Net class in my .Net component. So, it fails all the time, no way it can marshal System.Array to string array for VB6 that I can loop and access strings in the array. Note, that the objRetVal is not Nothing and is not empty it has the data, just resData doesn't read a string value in the array.

I know if I return string array from my .Net method then it will most probably work at the VB6 end, but I want to know if there is a way to do proper marshaling and convert the returned System.Array into string() array on VB6 side.

Tiger Galo
  • 289
  • 4
  • 15
  • Why have you declared `objRetVal` as an `Object`? What's wrong with simply `Dim retVal() As String` and `strArr = NetClassInstance.GetData()`? – Cody Gray - on strike Sep 06 '16 at 14:28
  • http://stackoverflow.com/questions/31433552/can-on-marshal-a-string-to-a-safearray-variant – xxbbcc Sep 06 '16 at 14:29
  • 3
    VB6 requires a SafeArray, you will never get that from this declaration. Your method returns an *interface* reference to `_Array`. You normally also need a reference to C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb to get VB6 to understand what that interface looks like. Still doesn't buy you anything, the interface declaration is empty and it still doesn't let you copy the array. "it fails all the time" isn't specific enough to guess what goes wrong next. Best to stop hurting yourself, change the return type to string[] or object[]. – Hans Passant Sep 06 '16 at 15:01
  • Why are you trying to return `Array` instead of `string[]`? What do you hope to gain from doing so? – xxbbcc Sep 06 '16 at 15:13
  • I agree, if worse comes to worse I'll do that, since I own the .Net code, however, consider a case that we don't own the .Net code and we are forced to accept System.Array as a return type. Anyway, it seems like that's not the root cause of the issue. As I stated in my comment for Andre's answer, the same code runs and works fine on one PC not the other. It has something to do with VB runtime or whatsoever. – Tiger Galo Sep 06 '16 at 17:28

2 Answers2

2

All I needed to do is to follow Hans's and xxbbcc's comments which brought me to the hint what was required to make the code run. So, I adorned my method with the following return:MarshalAs tag and it worked well for me:

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
public Array GetData()
{
    return new string[] { };
}

Thank you all for supportive comments.

Tiger Galo
  • 289
  • 4
  • 15
  • That is pointless when you accomplish the **exact** same thing by returning string[]. Hmya, if only we could know why returning Array is so important. If you don't want to tell us then just delete this Q+A, it is *not* helpful to anybody else. – Hans Passant Sep 06 '16 at 21:20
  • As I mentioned earlier, the only reason for looking for a solution is the case if you don't own the .Net code and cannot change the return type. However, if the method is adorned with the MarshalAs and indicating the subtype as BSTR then at least on the VB6 side if objRetVal is not declared as an Object, but as a string array then it will convert. Of course, since I own the code, I will definitely change the return type to be directly string array and not a System.Array, the later I inherited from my co-worker. – Tiger Galo Sep 06 '16 at 21:48
  • But still I wonder, how come without that modification, the old code used to work on one PC not the other. How VB handles the _Array and converts it on one system and fails on the other? – Tiger Galo Sep 06 '16 at 21:54
0

Unless I missed something it seems that you have not set any value to "resdata" hence the empty returned string.

For Each resData In objRetVal ''Nothing to tie resdata to...
  strData = CStr(resData) ''resdata is already empty, returning nothing here...
  MsgBox "strData = " & strData
Next

Seeing that you are using an object "resdata" should be something like -

resData = objRetVal.StringReturned  ''Whatever the return name might be...  

For Each resData In objRetVal ''Now has a value...
  strData = CStr(resData) ''Returning your query...
  MsgBox "strData = " & strData
Next
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
AlwaysConfused
  • 450
  • 4
  • 13
  • OK, it's getting more ridiculous. So the same code works perfectly on other machine. Both have Win 7 64 bit with .Net 4.0 framework installed on it. However, the same code runs with errorless marshaling on one PC and not the other one (which is a virtual image not a physical machine). Now that gives me a hint that the VB related stuff is handling the case differently on different PCs. – Tiger Galo Sep 06 '16 at 17:24
  • Are you running both as administrator? – AlwaysConfused Sep 06 '16 at 18:37
  • No, just a regular user, although I just tried on one more physical PC and it crashes also with the same behavior. That PC's user has admin rights. I don't know what else to try. – Tiger Galo Sep 06 '16 at 19:48