Passing array of sctruct to extern functions?

G

Guest

Hello I am developing a cross-platform solution for scheduled messages,
primary for SMS. The server parts are written in ASNI/ISO C to compile and
run cleanly on any POSIX compatible server.

For the client-side API a generic API in ANSI/ISO C is written as well, and
then wrappers for Perl, Objective-C, C++, COM, Java etc are written on top so
that all developers can use whatever they feel comfortable with (Or more
realistically what their projects already enforces upon them).

So the time has come to .NET, and I have decided to use C#. It is my first
try at C# so some questions I have or decisions I have made might have been
quite alien to you.

Anyway, here is a snippet of a simplefied but representive part of the
ANSI/ISO C header:

ANSI/ISO C
==========

typedef struct NUNuser {
int ID;
char fullname[48];
int manager;
}

typedef struct NUNmessage {
int ID;
int user;
char number[16];
int status;
char number[160];
}

typedef struct NUNmessageheader {
int ID;
int user;
char number[16];
}

NUNstatus NUNgetuserlist(int *userids, uint maxcnt);

NUNstatus NUNgetuser(int userid, NUNuser *user);

NUNstatus NUNgetmessagelist(NUNmessageheader *messageheaders, uint maxcnt);

NUNstatus NUNgetmessage(NUNmessageheader *messageheader, NUNmessage *message);

====

To clear up the examples, NUNstatus is an error code if negative, all
positive number (zero included) are OK.

So to write out all users' names one would do (No error checking done):
void printNames() {
int i;
// If NULL pointer passed for buffer then only count available records
NUNstatus cnt = NUNgetuserlist(NULL, 0);
int *userids = malloc(sizeof(int) * cnt);
// Pass last count AND store new count in case it changed.
cnt = NUNgetuserlist(userids, cnt);
for (i = 0; i < cnt; i++) {
NUNuser user;
NUNgetuser(userids, &user);
printf("%d: %s\n", user.ID, user.name);
}
free(userids);
}

As the messages are more complex, with a compound key of ID and the user
that owns the message we can not simply geta list of ints, instead we geta
list of NUNmessageheaders, with the number in it for laughs as it is used
ALLOT and another roundtrip to server is unnecessary.
But here we go:
void printMessages() {
int i;
NUNstatus cnt = NUNgetmessagelist(NULL, 0);
NUNmessageheader *messageheaders = malloc(sizeof(NUNmessageheader) * cnt);
cnt = NUNgetuserlist(messageheaders, cnt);
for (i = 0; i < cnt; i++) {
NUNmessage message;
NUNgetmessage(messageheaders, &message);
printf("'%s'\n", message.message);
}
free(messageheaders);
}


Anyway, this is what I have done in C#

C#
==

[StructLayout(LayoutKind.Sequential)]
internal struct NUNuser
{
public int ID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 48]
public byte[] fullname;
public int manager;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NUNmessage
{
public int ID;
public int user;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16]
public byte[] number;
public int status;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 160]
public byte[] message;
}

[StructLayout(LayoutKind.Sequential)]
internal struct NUNmessageheader
{
public int ID;
public int user;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16]
public byte[] number;
}

[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetuserlist(int[] userids, uint maxcnt);

[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetuser(int userid, ref NUNuser user);

[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetmessagelist(NUNmessageheader[]
messageheaders, uint maxcnt);

[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetmessage(ref NUNmessageheader
messageheader, ref NUNmessage message);

====

That means that the printNames() from the C example works fine. And gets
into something like this:

static void printNames() {
int i;
NUNstatus cnt = NUNgetuserlist(null, 0);
int[] userids = new int[(int)cnt];
cnt = NUNgetuserlist(userids, cnt);
for (i = 0; i < cnt; i++) {
NUNuser user;
NUNgetuser(userids, ref user);
Console.WriteLine(user.ID + ": " + user.name);
}
}

This works great, but now to the messages:
void printMessages() {
int i;
NUNstatus cnt = NUNgetmessagelist(null, 0);
NUNmessageheader[] messageheaders = new NUNmessageheader[(int)cnt]; // !!!
cnt = NUNgetuserlist(messageheaders, cnt);
for (i = 0; i < cnt; i++) {
NUNmessage message;
NUNgetmessage(ref messageheaders, ref message);
Console.WriteLine(message.message);
}
}

Now at the line marked with "// !!!" nothing happens. No error, it simply
does not fill in the messageheaders array at all.

What am I doing wrong?


Quite allot to read, but unless I define my problem well I can not get a
well defined solution :).

Regards
Fredrik Olsson.
 
M

Mattias Sjögren

Fredrik,
[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetmessagelist(NUNmessageheader[]
messageheaders, uint maxcnt); ....
Now at the line marked with "// !!!" nothing happens. No error, it simply
does not fill in the messageheaders array at all.

What am I doing wrong?

Try adding the Out attribute to the array parameter

[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetmessagelist([Out]
NUNmessageheader[] messageheaders, uint maxcnt);



Mattias
 
G

Guest

Mattias Sjögren said:
Fredrik,
[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetmessagelist(NUNmessageheader[]
messageheaders, uint maxcnt); ....
Now at the line marked with "// !!!" nothing happens. No error, it simply
does not fill in the messageheaders array at all.

What am I doing wrong?

Try adding the Out attribute to the array parameter

[DllImport("Nuntio.dll")]
public static extern NUNstatus NUNgetmessagelist([Out]
NUNmessageheader[] messageheaders, uint maxcnt);

It did the trick, thanks. Guess I should add [Out] to all extern
declarations, even those that work, just for safety? (And performance?)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top