4

Hi everyone i had a issue with a code in C# .NET, I'm using a DLL for connect to OPC Servers the DLL that was used in a VB.NET project and works with no problem at all.

I'm trying to show a list of available Servers in a ListBox, the code used in VB.NET (and works) is this one:

Dim AllOPCServers As Object
AllOPCServers = AnOPCServer.GetOPCServers

' Load the list returned into the List box for user selection
Dim i As Short
For i = LBound(AllOPCServers) To UBound(AllOPCServers)
    AvailableOPCServerList.Items.Add(AllOPCServers(i))
Next i

and i wrote this to use in the C# application

try
{
    var _listOPCServer = _OPCServer.GetOPCServers();
    foreach(var i in _listOPCServer)
    {
        string serverName = (string)i;
        listServers.Items.Add(serverName);
    }             
}
catch (Exception exc)
{
    lstMsg.Items.Add(DateTime.Now + " Error al Obtener Lista de OPC's: " + exc.Message);
}

On Debug mode on Local tab shows this:

_listOPCServer | {string[1..2]} | dynamic {string[]} |

[1]        |  "Server01"    | string  
[2]        |  "Server02"    | string

UPDATE:

I get the error in line "foreach(var i in _listOPCServer)"

Unable To Cast object type 'System.String[*]' to type 'System.String[]'

That is the actually error.

I'm sure that I'm doing something wrong, can someone help me?

Manuel Espino
  • 273
  • 3
  • 14
  • 1
    Here is a minimal reproduction for a *similar* case: `(string[])(new string[0,0])`, although I do not know how to produce that exact message .. in any case, please post the *real error message* (copy and paste) to avoid possible errors. –  Sep 13 '12 at 23:50
  • On which line do you get the error? I don;t see where you're trying to cast `_listOPCServer` to anything. – D Stanley Sep 14 '12 at 00:02
  • @pst that is the error that i get Unable To Cast object type 'System.Sting[*]' to type 'System.String[]' – Manuel Espino Sep 14 '12 at 03:24
  • @DStanley i get the error in the line foreach(var i in _listOPCServer) – Manuel Espino Sep 14 '12 at 03:24
  • @DStanley haha sorry, bad spelling, is String, let me correct that – Manuel Espino Sep 14 '12 at 03:44

3 Answers3

5

Ok i found a way to work this out and it's only a mod of your advices

 Array _listOPCServer = (Array)(object)_OPCServer.GetOPCServers();               

            foreach(object i in _listOPCServer)
            {
                string serverName = (string)i;
                listServers.Items.Add(serverName);
            }             

I only added (object) in the declaration and works fine, now i can show the list of servers just the way i wanted it

or also do this

Array _listOPCServer = (Array)(object)_OPCServer.GetOPCServers();               

            foreach(object i in _listOPCServer)
            {
                listServers.Items.Add(i);
            }

Again, thanks a lot for your help and time!

Manuel Espino
  • 273
  • 3
  • 14
2

VB.NET is a lot more astute at dealing with non-conforming array types. Don't hesitate to use it, .NET make it easy to have languages inter-operate with each other.

At issue is that OPC is a COM based standard. The server you are using is returning a SAFEARRAY with a non-conforming lower bound, the first index is 1. Not 0. Not that unusual in COM, choosing between 0 and 1 as the first array index is like the endian problem or arguing whether a tomato is a fruit or a vegetable (It is a fruit. And little-endian is the right kind of vegetable).

However, 0 as the lower bound is what C# insists on when dealing with one-dimensional arrays. It wants a "vector", a distinct array type that always has one dimension with a lower bound of 0. Heavily optimized in the .NET runtime. What you get back doesn't match so is mapped to a multi-dimensional array with one dimension. Not an array type you can express in the C# language.

You can hack it in C#, but you'll have to use the Array type explicitly. Something like this, spelled out for clarity:

Array servers = (Array)_OPCServer.GetOPCServers();
int first = servers.GetLowerBound(0);
int last = servers.GetUpperBound(0);
for (int ix = first; ix <= last; ++ix) {
    var elem = (string)servers.GetValue(ix);
    // etc..
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Shouldn't `foreach` still work on a non-zero-based array? Where does the cast come in to play? – D Stanley Sep 14 '12 at 00:05
  • No idea actually. It never occurred to me to try it, the illusion that an array implements `IEnumerable` is bound to fall down on this kind of code. Just try it. The cast is obvious, Array.GetValue() returns an Object. – Hans Passant Sep 14 '12 at 00:14
  • I meant the cast from `System.Sting[*]` to `System.String[]` that the OP is getting the exception for. – D Stanley Sep 14 '12 at 00:18
  • Well, no, that cannot possible work. The tenure of my answer. A vector is not compatible with an multi-dimensional array with one dimension. – Hans Passant Sep 14 '12 at 00:27
  • I understand that; what I don't understand is _why_ the compiler is trying to cast to a `string[]`. I've added an answer with my guess. – D Stanley Sep 14 '12 at 01:12
  • Hi @HansPassant thanks for the time for answer my question, but after post the question here i found an explication about that you're commented here, i tried also declaring as Array type but i get the same error on the line Array servers= (Array)_OPCServer.GetOPCServer – Manuel Espino Sep 14 '12 at 03:38
  • Like @ManuelEspino says, the error still pops on the server variable creation. Casting to object before casting to array makes it work like in ManuelEspino describes in his answer. – Matthieu Aug 28 '17 at 18:11
1

While Hans is correct in distinguishing the difference between a zero-based array and a non-zero-based array, I still don't see why you're getting that exception.

My guess is that you're declaring _OPCServer as dynamic, which defers type-binding until run-time. Thus _listOPCServer is dynamic as well.

Since you're iterating over the array and extracting strings, the compiler may be trying to cast the object to a string[] which as Hans points out is invalid.

You should be able to cast _listOPCServer to an Array and use the foreach just as you are:

Array _listOPCServer = (Array)(_OPCServer.GetOPCServers());
foreach(var i in _listOPCServer)
{
    // etc.
John Saunders
  • 160,644
  • 26
  • 247
  • 397
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • Hi thanks for taking a time for answer this, but i tried as well the way that you recommended but still get the same error in the line Array _listOPCServer = (Array)(_OPCServer.GetOPCServers()); – Manuel Espino Sep 14 '12 at 03:41
  • I get the same error I'll update the post with some screenshots – Manuel Espino Sep 14 '12 at 19:00