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?

5
  • 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. Commented Sep 13, 2012 at 23:50
  • On which line do you get the error? I don;t see where you're trying to cast _listOPCServer to anything. Commented Sep 14, 2012 at 0:02
  • @pst that is the error that i get Unable To Cast object type 'System.Sting[*]' to type 'System.String[]' Commented Sep 14, 2012 at 3:24
  • @DStanley i get the error in the line foreach(var i in _listOPCServer) Commented Sep 14, 2012 at 3:24
  • @DStanley haha sorry, bad spelling, is String, let me correct that Commented Sep 14, 2012 at 3:44

3 Answers 3

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!

Sign up to request clarification or add additional context in comments.

Comments

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..
}

7 Comments

Shouldn't foreach still work on a non-zero-based array? Where does the cast come in to play?
No idea actually. It never occurred to me to try it, the illusion that an array implements IEnumerable<T> is bound to fall down on this kind of code. Just try it. The cast is obvious, Array.GetValue() returns an Object.
I meant the cast from System.Sting[*] to System.String[] that the OP is getting the exception for.
Well, no, that cannot possible work. The tenure of my answer. A vector is not compatible with an multi-dimensional array with one dimension.
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.
|
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.

2 Comments

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());
I get the same error I'll update the post with some screenshots

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.