By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,042 Members | 1,036 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,042 IT Pros & Developers. It's quick & easy.

Passing Structures to Unmanaged Code

P: n/a
I am attempting to write a .NET wrapper in C# for an SDK that has been
supplied as a .LIB file and a .h header file. I have got most of the
functions to work but am really struggling with the functions that require a
structure to be passed to them. The function declaration in the .h file is
of the form:
SDCERR GetConfig(char *name, SDCConfig *cfg);
where SDCConfig is a structure defined in the .h file. I am not much of a C
(or C#) programmer but does the * in front of the cfg signify that the
structure has to be passed using the ref keyword (by reference)?
The other part I am having trouble with is the definition of the actual
structure itself. The .h definition is:
typedef struct _SDCConfig {
char configName[CONFIG_NAME_SZ];
char SSID[SSID_SZ];
int txPower;
....
....
CRYPT WEPKeys;
} SDCConfig;
(where CRYPT is another structure containing a char array). Whatever I seem
to try in the C# definition, I am getting a NotSupportedException when
calling the function. I am attempting to use the MarshalAs keyword in front
of the char array definitions, which I have done successfully when passing
strings, but to be honest I don't really understand what I am doing! I also
have another structure containing DWORDs as well as char arrays that I don't
know what to do with. The code I am using is:
[StructLayout.(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SDCConfig
{
MarshalAs[UnmanagedType.LPArray, SizeConst=CONFIG_NAME_SZ];
public byte[] configName;
...
...
}
I have tried playing about with the UnmanagedType.LPArray, and passing
char[], string, and StringBuilder instead of the byte array, but always get
the NotSupportedException. I have tried initialising the structure by
filling in the byte array. The call I am using is:
SDCConfig cfg = new SDCConfig(ConfigName);
SDCERR result = GetConfig(Name, ref cfg);
I am using the Compact Framework and VS.NET 2005 if that makes a
difference.
Any help would be appreciated. Thanks in advance.

Andy Baker
Jul 15 '08 #1
Share this Question
Share on Google+
13 Replies


P: n/a
On Jul 15, 1:20*pm, "Andy Baker" <aba...@NOSPAMvanputer.comwrote:
I am attempting to write a .NET wrapper in C# for an SDK that has been
supplied as a .LIB file and a .h header file. I have got most of the
functions to work but am really struggling with the functions that require a
structure to be passed to them. The function declaration in the .h file is
of the form:
* * SDCERR GetConfig(char *name, SDCConfig *cfg);
where SDCConfig is a structure defined in the .h file. I am not much of aC
(or C#) programmer but does the * in front of the cfg signify that the
structure has to be passed using the ref keyword (by reference)?
Yes.
* * The other part I am having trouble with is the definition of the actual
structure itself. The .h definition is:
* * typedef struct _SDCConfig {
* * * * char * *configName[CONFIG_NAME_SZ];
* * * * char * *SSID[SSID_SZ];
* * * * int * txPower;
* * * * ....
* * * * ....
* * * * CRYPT * *WEPKeys;} SDCConfig;

(where CRYPT is another structure containing a char array). Whatever I seem
to try in the C# definition, I am getting a NotSupportedException when
calling the function. *I am attempting to use the MarshalAs keyword in front
of the char array definitions, which I have done successfully when passing
strings, but to be honest I don't really understand what I am doing! I also
have another structure containing DWORDs as well as char arrays that I don't
know what to do with. The code I am using is:
* * [StructLayout.(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
* * public struct SDCConfig
* * {
* * * * MarshalAs[UnmanagedType.LPArray, SizeConst=CONFIG_NAME_SZ];
* * * * public byte[] configName;
* * * * ...
* * * * ...
* * }
Are you sure the NotSupportedException is because of [MarshalAs]?

Anyway, it would be helpful if you gave the entire definition of the
struct, both the original C, and your C# version. Also, C# DllImport
declaration for the function would be handy, too.
Jul 15 '08 #2

P: n/a
Thanks for getting back to me on this.
>Are you sure the NotSupportedException is because of [MarshalAs]?
No, it could be anything I guess!
>Anyway, it would be helpful if you gave the entire definition of the
struct, both the original C, and your C# version. Also, C# DllImport
declaration for the function would be handy, too.
They are as follows:
C .h header file - Structures

typedef struct _CRYPT {
DWORD size;
char buffer [120];
DWORD offset;
} CRYPT;

typedef struct _SDCConfig {
char configName[CONFIG_NAME_SZ];
char SSID[SSID_SZ];
char clientName[CLIENT_NAME_SZ];
int txPower;
AUTH authType;
EAPTYPE eapType;
POWERSAVE powerSave;
WEPTYPE wepType;
BITRATE bitRate;
RADIOMODE radioMode;
CRYPT userName;
CRYPT userPwd;
CRYPT PSK;
CRYPT WEPKeys;
} SDCConfig

And the declaration for one of the functions is
SDCERR GetConfig(char *name, SDCCONFIG *cfg);

My C# code - Structures

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPT
{
public uint size;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
public string buffer;
public uint offset;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SDCConfig
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CONFIG_NAME_SZ)]
public string configName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = SSID_SZ)]
public string SSID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CLIENT_NAME_SZ)]
public string clientName;
public Int32 txPower;
public AUTH authType;
public EAPTYPE eapType;
public POWERSAVE powerSave;
public WEPTYPE wepType;
public BITRATE bitRate;
public RADIOMODE radioMode;
public CRYPT userName;
public CRYPT userPwd;
public CRYPT PSK;
public CRYPT WEPKeys;
}

[DllImport("VPSummit.dll", CharSet = CharSet.Unicode)]
public static extern SDCERR GetConfig([MarshalAs(Unmanaged.Type.LPArray)]
byte [] name, ref SDCCONFIG cfg)];

and the function call is:
public bool ConfigTest
{
string configName = "VANPUTER";
byte [] Name = System.Text.ASCIIEncoding.ASCII.GetBytes(ConfigNam e +
'\0');
SDCConfig cfg = new SDCConfig();
SDCERR result = GetConfig(Name, cfg);
return (result = SDCERR_SUCCESS);
}

Actually this particular function does not give me a NotSupportedException,
and result contains SDCERR_SUCCESS, but cfg does not contain the
configuration values that it should, so I am obviously doing something
wrong. The other types SDCCERR, AUTH, EAPTYPE etc are defined as enums in
both C and C# and CONFIG_NAME_SZ etc are constant values. I am well out of
my depth here, so any advice will be very welcome! Thanks.

Andy Baker


Jul 15 '08 #3

P: n/a
On Jul 15, 7:33*pm, "Andy Baker" <aba...@NOSPAMvanputer.comwrote:
Thanks for getting back to me on this.
Are you sure the NotSupportedException is because of [MarshalAs]?

No, it could be anything I guess!
Anyway, it would be helpful if you gave the entire definition of the
struct, both the original C, and your C# version. Also, C# DllImport
declaration for the function would be handy, too.

They are as follows:
C .h header file - Structures

typedef struct _CRYPT {
* * DWORD * *size;
* * char * * * * * *buffer [120];
* * DWORD * *offset;

} CRYPT;

typedef struct _SDCConfig {
* * char * * * * * * * * * * configName[CONFIG_NAME_SZ];
* * char * * * * * * * * * * SSID[SSID_SZ];
* * char * * * * * * * * * * clientName[CLIENT_NAME_SZ];
* * int * * * * * * * * * * * *txPower;
* * AUTH * * * * * * * * authType;
* * EAPTYPE * * * * * eapType;
* * POWERSAVE * *powerSave;
* * WEPTYPE * * * * *wepType;
* * BITRATE * * * * * *bitRate;
* * RADIOMODE * *radioMode;
* * CRYPT * * * * * * * userName;
* * CRYPT * * * * * * * userPwd;
* * CRYPT * * * * * * * PSK;
* * CRYPT * * * * * * * WEPKeys;

} SDCConfig

And the declaration for one of the functions is
SDCERR GetConfig(char *name, SDCCONFIG *cfg);

My C# code - Structures

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPT
{
* * public uint size;
* * [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
* * public string buffer;
* * public uint offset;

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SDCConfig
{
* * [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CONFIG_NAME_SZ)]
* * public string configName;
* * [MarshalAs(UnmanagedType.ByValTStr, SizeConst = SSID_SZ)]
* * public string SSID;
* * [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CLIENT_NAME_SZ)]
* * public string clientName;
* * public Int32 txPower;
* * public AUTH authType;
* * public EAPTYPE eapType;
* * public POWERSAVE powerSave;
* * public WEPTYPE wepType;
* * public BITRATE bitRate;
* * public RADIOMODE radioMode;
* * public CRYPT userName;
* * public CRYPT userPwd;
* * public CRYPT PSK;
* * public CRYPT WEPKeys;

}

[DllImport("VPSummit.dll", CharSet = CharSet.Unicode)]
public static extern SDCERR GetConfig([MarshalAs(Unmanaged.Type.LPArray)]
byte [] name, ref SDCCONFIG cfg)];

and the function call is:
public bool ConfigTest
{
* * string configName = "VANPUTER";
* * byte [] Name = System.Text.ASCIIEncoding.ASCII.GetBytes(ConfigNam e +
'\0');
* * SDCConfig cfg = new SDCConfig();
* * SDCERR result = GetConfig(Name, cfg);
* * return (result = SDCERR_SUCCESS);

}
One thing that immediately looks wrong here is that all your C structs
use char strings (that is, non-Unicode), but you use CharSet.Unicode
combined with UnmanagedType.ByValTStr - that would make them all
Unicode for marshalling, and skew the layout of following fields
(since now they suddenly are twice as far as they ought to be). Try
using CharSet.Ansi instead and see if that helps.
Jul 15 '08 #4

P: n/a
>One thing that immediately looks wrong here is that all your C structs
>use char strings (that is, non-Unicode), but you use CharSet.Unicode
combined with UnmanagedType.ByValTStr - that would make them all
Unicode for marshalling, and skew the layout of following fields
(since now they suddenly are twice as far as they ought to be). Try
using CharSet.Ansi instead and see if that helps.
I tried that already - but I am using the compact framework, and don't
appear to have the option of Ansi - I though CE used Unicode anyway. The
only options I get are CharSet.Unicode and CharSet.Auto, and have tried both
with the same result. What is the alternative to ByValTStr - that was the
only option I tried that didn't give me the NotSupportedException, but still
didn't do what I wanted it to.
Jul 16 '08 #5

P: n/a
I seem to have got the strings working now - I declared everything as a byte
array instead of a string in the structure definition, and used
MarshalAs[UnmanagedType.ByValArray)], and now when I make my function call
and convert between string and byte array and back, I get the results I am
expecting.
However, I still have a problem with the rest of the structure(s),
defined in the C header file as follows:

typedef struct _CRYPT {
DWORD size;
char buffer[120];
DWORD offset;
} CRYPT;

typedef struct _SDCConfig {
char configName[CONFIG_NAME_SZ];
char SSID[SSID_SZ];
char clientName[CLIENT_NAME_SZ];
int txPower;
AUTH authType;
EAPTYPE eapType;
POWERSAVE powerSave;
WEPTYPE wepType;
BITRATE bitRate;
RADIOMODE radioMode;
CRYPT userName;
CRYPT userPwd;
CRYPT PSK;
CRYPT WEPKeys;
} SDCConfig

My C# structure definitions are now defined as follows:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CRYPT
{
public Uint32 size;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 120)]
public byte[] buffer;
public Uint32 offset;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SDCConfig
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CONFIG_NAME_SZ)]
public byte[] configName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = SSID_SZ)]
public byte[] SSID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CLIENT_NAME_SZ)]
public byte[] clientName;
public Int32 txPower;
public AUTH authType;
public EAPTYPE eapType;
public POWERSAVE powerSave;
public WEPTYPE wepType;
public BITRATE bitRate;
public RADIOMODE radioMode;
public CRYPT userName;
public CRYPT userPwd;
public CRYPT PSK;
public CRYPT WEPKeys;
}

When I make the function call, the arrays configName, SSID and clientName
all now contain the correct values - unfortunately the rest of the fields
don't. I have replaced int in the C definition by Int32 in C# - as far as I
can see this is correct, but I am unsure how to deal with the others. In C,
they are all defined as enumerations i.e.:

typedef enum _AUTH {
AUTH_OPEN = 0,
AUTH_SHARED,
AUTH_NETWORK_EAP,
} AUTH;

which I have defined in C# as:

public enum AUTH { AUTH_OPEN = 0, AUTH_SHARED, AUTH_NETWORK_EAP };

Do I have to define the data type as well somewhere? I see that if no
underlying type is specifically declared, then Int32 is used, but is this
the same as in C++?. If the data type is wrong and the C# definition is
taking up more (or less) bytes than it should be then it would explain the
results that I am getting - instead of enumerations such as AUTH_OPEN,
AUTH_SHARED etc, the value AUTH contains a large (random) number, and so do
all the other enumerations. Thanks for any advice.

Andy Baker
Jul 16 '08 #6

P: n/a
On Jul 16, 5:40*pm, "Andy Baker" <aba...@NOSPAMvanputer.comwrote:
When I make the function call, the arrays configName, SSID and clientName
all now contain the correct values - unfortunately the rest of the fields
don't. I have replaced int in the C definition by Int32 in C# - as far asI
can see this is correct, but I am unsure how to deal with the others. In C,
they are all defined as enumerations i.e.:

typedef enum _AUTH {
* AUTH_OPEN = 0,
* AUTH_SHARED,
* AUTH_NETWORK_EAP,

} AUTH;

which I have defined in C# as:

public enum AUTH { AUTH_OPEN = 0, AUTH_SHARED, AUTH_NETWORK_EAP };

Do I have to define the data type as well somewhere? I see that if no
underlying type is specifically declared, then Int32 is used, but is this
the same as in C++?.
Unfortunately, this is essentially implementation-defined in C++.
Visual C++ defaults to int on the desktop, but this can be overridden
with compiler flags to use the smallest fitting type instead, and I've
no idea what the default is for the WinCE compiler. One way to know
for sure is to write a small C++ program that just prints sizeof on
all members of the struct.
Jul 17 '08 #7

P: n/a
>Unfortunately, this is essentially implementation-defined in C++.
>Visual C++ defaults to int on the desktop, but this can be overridden
with compiler flags to use the smallest fitting type instead, and I've
no idea what the default is for the WinCE compiler. One way to know
for sure is to write a small C++ program that just prints sizeof on
all members of the struct.
I have done that, and the C++ enumerations are all 4-byte integers, so it
looks like they should be the same in C#.
One thing that I have found is that if I did sizeof on the whole structure
in C++, it returns 523, which is what I would expect:
configName 33
SSID 33
clientName 17
txPower 4
authType 4
eapType 4
powerSave 4
wepType 4
bitRate 4
radioMode 4
userName 128
userPwd 128
PSK 128
WEPkeys 128
The last 128 byte elements are all of type CRYPT which consists of a char
array of size 120 and 2 integers = 128. The first 3 are all char arrays in
C++ which I have converted to byte arrays from a string in C#, and added a
'\0' on the end - assuming the extra byte (33 instead of 32 and 17 instead
of 16 is for the '\0'). This bit seems to work anyway. The thing that struck
me as strange is that if I tried getting the size of the structure I am
passing from C# using MarshalAs.SizeOf(cfg) where cfg is the a variable of
the structure type, it gives me 524, although when I look at the individual
elements they are all the right size - I am passing byte arrays of the
correct size, the enumerations are all Int32 and the CRYPTs are all 128. I
also have a second structure in this application, and doing the same
exercise with that gives me a C# size of 128 against a C++ size of 127,
again an extra byte. I can't understand where this is coming from, or if it
makes a difference. If it is adding a byte at the end, maybe it doesn't
matter, but for some reason it appears to be going wrong after clientName as
all the rest of the elements contains nonsense values.


Jul 17 '08 #8

P: n/a
On Jul 17, 3:14*pm, "Andy Baker" <aba...@NOSPAMvanputer.comwrote:
Unfortunately, this is essentially implementation-defined in C++.
Visual C++ defaults to int on the desktop, but this can be overridden
with compiler flags to use the smallest fitting type instead, and I've
no idea what the default is for the WinCE compiler. One way to know
for sure is to write a small C++ program that just prints sizeof on
all members of the struct.

I have done that, and the C++ enumerations are all 4-byte integers, so it
looks like they should be the same in C#.
One thing that I have found is that if I did sizeof on the whole structure
in C++, it returns 523, which is what I would expect:
* * configName * *33
* * SSID * * * * * * *33
* * clientName * * 17
* * txPower * * * * *4
* * authType * * * * 4
* * eapType * * * * *4
* * powerSave * * *4
* * wepType * * * * 4
* * bitRate * * * * * *4
* * radioMode * * *4
* * userName * * * 128
* * userPwd * * * * 128
* * PSK * * * * * * * 128
* * WEPkeys * * * 128
The last 128 byte elements are all of type CRYPT which consists of *a char
array of size 120 and 2 integers = 128. The first 3 are all char arraysin
C++ which I have converted to byte arrays from a string in C#, and added a
'\0' on the end - assuming the extra byte (33 instead of 32 and 17 instead
of 16 is for the '\0'). This bit seems to work anyway. The thing that struck
me as strange is that if I tried getting the size of the structure I am
passing from C# using MarshalAs.SizeOf(cfg) where cfg is the a variable of
the structure type, it gives me 524, although when I look at the individual
elements they are all the right size - I am passing byte arrays of the
correct size, the enumerations are all Int32 and the CRYPTs are all 128. I
also have a second structure in this application, and doing the same
exercise with that gives me a C# size of 128 against a C++ size of 127,
again an extra byte. I can't understand where this is coming from, or if it
makes a difference. If it is adding a byte at the end, maybe it doesn't
matter, but for some reason it appears to be going wrong after clientNameas
all the rest of the elements contains nonsense values.
Ah, that explains things now. It seems that the C++ code is compiled
with struct padding disabled. Normally, in C++, struct fields are
padded so that every field's alignment is the same as its size (i.e.
bytes are not aligned at all, Int16 are aligned on 2-byte boundaries,
Int32 are aligned on 4-byte boundaries, etc). C# uses the same logic
by default, IIRC, but it can be changed via StructLayoutAttribute.Pack
property - try setting it to 1.

In your case, the first 3 fields are not aligned since they are byte
arrays. Their total size is 33+33+17=83. However, since the next field
is Int32, it expects to be aligned at 4-byte boundary, and so a byte
of padding is added before it so that it will start at byte #84 (which
is divisible by 4).
Jul 17 '08 #9

P: n/a
On Jul 17, 4:14*am, "Andy Baker" <aba...@NOSPAMvanputer.comwrote:

[problems with structures in C#]

I'm just a newbie, but I once read about a guy having problems with
structures, and he solved the problem by simply declaring the
structures as a public class. That simple. Apparently classes, which
live on the heap, are different from structures that live on the
stack. Or so I believe. If it's easy you might want to make this
change and see what happens.

Ever since I read this, I don't use structures in CLI .NET managed
code.

RL
Jul 17 '08 #10

P: n/a
On Jul 17, 4:50*pm, raylopez99 <raylope...@yahoo.comwrote:
I'm just a newbie, but I once read about a guy having problems with
structures, and he solved the problem by simply declaring the
structures as a public class. *That simple.
I doubt that would make any difference, since field layout in both is
exactly the same (when LayoutKind.Sequential or Explicit is used, and
you can't marshal with Auto anyway), and that's all that matters.
>*Apparently classes, which live on the heap, are different from structures that live on the
stack.
Not in any way that is relevant to the issue at hand.
Ever since I read this, I don't use structures in CLI .NET managed
code.
While there are valid philosophical and practical reasons for avoiding
structs in .NET, this one isn't either. It's better to fully
comprehend the issue before making such broad decisions, and, from
what you say, it is clear that you do not understand them at all.
Jul 17 '08 #11

P: n/a
but it can be changed via StructLayoutAttribute.Pack property - try
setting it to 1.
In your case, the first 3 fields are not aligned since they are byte
arrays. Their total size is 33+33+17=83. However, since the next field
is Int32, it expects to be aligned at 4-byte boundary, and so a byte
of padding is added before it so that it will start at byte #84 (which
is divisible by 4).
That makes alot of sense. If I make the same function call in C++ it returns
sensible values so it is definitely working with a structure size of 623. In
the .h header file there is a line that says #pragma pack(1) - does this do
what you are saying and disable struct padding? However, the compact
framework 2.0 does not appear to support the Pack property - there is not an
option. I tried setting it to LayoutKind.Explicit and using FieldOffset in
the structure definition, but this just gives me a TypeLoadException. Is
there another way to do this? I am nearly there - thanks for all your help
so far.

Andy Baker
Jul 17 '08 #12

P: n/a
On Jul 17, 6:13*pm, "Andy Baker" <aba...@NOSPAMvanputer.comwrote:
That makes alot of sense. If I make the same function call in C++ it returns
sensible values so it is definitely working with a structure size of 623.In
the .h header file there is a line that says #pragma pack(1) - does this do
what you are saying and disable struct padding?
Yes, it is precisely that.
However, the compact
framework 2.0 does not appear to support the Pack property - there is notan
option. I tried setting it to LayoutKind.Explicit and using FieldOffset in
the structure definition, but this just gives me a TypeLoadException. Is
there another way to do this? I am nearly there - thanks for all your help
so far.
It would seem that there is no general workaround in that case - .NET
CE does not indeed support Pack, and Explicit alone is not enough
(it'll still pad the fields by default). The only way to work around
this limitation in general is to marshal the entire struct as byte[],
and use BitConverter.GetBytes + Buffer.BlockCopy to put data into it,
and BitConverter.ToXXX to extract it.

For your case, however, it might just be easier to cheat and add a
dummy byte field between clientName and txPower to account for the
padding. You do not need to care about its value, either - whatever
goes there is fine.
Jul 17 '08 #13

P: n/a
I only have 2 structures and I've done the first one successfully like that.
The second is a little more complicated but shouldn't be a problem. Thanks
again for your help.
Jul 17 '08 #14

This discussion thread is closed

Replies have been disabled for this discussion.