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

Marshalling Question: Array of Complex Structs?

P: n/a
Hi all,

I'm taking my first steps into C# <--C++ DLL Interop and
unfortunately I've run into (what seems to be) a very complicated case
as my first task. Perhaps someone here can help me.

I need to pass an array of RADIO_INFO2 structures to be filled by a
function in the DLL. This is how the structure is defined in the C++
example that comes with the DLL:

#pragma pack(1) // set byte packing
typedef struct {
unsigned __int32 bLength;
char szSerNum[9];
char szProdName[9];
unsigned __int64 MinFreq;
unsigned __int64 MaxFreq;
struct {
unsigned __int32 ExtRef:1;
unsigned __int32 FMWEnabled:1;
unsigned __int32 Reserved:30;
} Features;
} RADIO_INFO2;
#pragma pack() // set back the default packing

As you can see, there are a few obvious difficulties. First, the
nested Features struct. I think I've solved this by just defining
Features as a UInt32 and I'll handle the bit masking manually if I
need to. The second are the two char arrays. The DLL wants two 9
byte buffers to fill with character data while the marshalling code
wants to pass references. This brings me to the third problem, the
size of the structure. In C++ it's 42 bytes long: 32bits + 9bytes +
9bytes + 64bits + 64bits + 32bits = 42bytes. However, due to the
reference vs. value array problem the Marshalling code is coming up
with 32bytes: 32bits + 32bit (ref) + 32bit (ref) + 64bits + 64bits +
32bits = 32 bytes. The following is my best shot at describing this
structure in C#.

[StructLayout(LayoutKind.Sequential, Pack = 1,
CharSet = CharSet.Ansi)]
public struct RADIO_INFO2
{
public UInt32 bLength;
public Byte[] szSerNum;
public Byte[] szProdName;
public UInt64 MinFreq;
public UInt64 MaxFreq;
public UInt32 Features;
};

As stated above, this is the struct that gives me a
Marshalling.SizeOf() of 32 bytes. If, instead of the two Byte arrays,
I provide 18 individual Bytes, I get a matching size of 42 bytes, but
that seems like a heavy-handed work-around. Surely there's a Right
Way to do this.

Additionally, if instead of Byte arrays I define them as Char arrays,
I get an error from the Marshalling code complaining that the array
types don't match.

Just for reference, the actual call that consumes this struct looks
like:

[DllImport("WRG315API.DLL")]
public static extern UInt32 GetRadioList(ref RADIO_INFO2[]
lpRadioInfo,
UInt32 BufferSize,
ref UInt32 InfoSize);

Thanks in advance for any help that is offered.
Todd Pafford
ca******@gmail.com
Jan 14 '08 #1
Share this Question
Share on Google+
2 Replies


P: n/a
Todd,

You can just tell the marshalling layer to embed the string into the
struct by value:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct RADIO_INFO2
{
public UInt32 bLength;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=9)]
public string szSerNum;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst=9)]
public string szProdName;
public UInt64 MinFreq;
public UInt64 MaxFreq;
public UInt32 Features;
};

If you are using unsafe code, then you could use the fixed keyword as
well to fix the character array to a certain size, but what's above should
work just fine.

--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com
<ca******@gmail.comwrote in message
news:27**********************************@j78g2000 hsd.googlegroups.com...
Hi all,

I'm taking my first steps into C# <--C++ DLL Interop and
unfortunately I've run into (what seems to be) a very complicated case
as my first task. Perhaps someone here can help me.

I need to pass an array of RADIO_INFO2 structures to be filled by a
function in the DLL. This is how the structure is defined in the C++
example that comes with the DLL:

#pragma pack(1) // set byte packing
typedef struct {
unsigned __int32 bLength;
char szSerNum[9];
char szProdName[9];
unsigned __int64 MinFreq;
unsigned __int64 MaxFreq;
struct {
unsigned __int32 ExtRef:1;
unsigned __int32 FMWEnabled:1;
unsigned __int32 Reserved:30;
} Features;
} RADIO_INFO2;
#pragma pack() // set back the default packing

As you can see, there are a few obvious difficulties. First, the
nested Features struct. I think I've solved this by just defining
Features as a UInt32 and I'll handle the bit masking manually if I
need to. The second are the two char arrays. The DLL wants two 9
byte buffers to fill with character data while the marshalling code
wants to pass references. This brings me to the third problem, the
size of the structure. In C++ it's 42 bytes long: 32bits + 9bytes +
9bytes + 64bits + 64bits + 32bits = 42bytes. However, due to the
reference vs. value array problem the Marshalling code is coming up
with 32bytes: 32bits + 32bit (ref) + 32bit (ref) + 64bits + 64bits +
32bits = 32 bytes. The following is my best shot at describing this
structure in C#.

[StructLayout(LayoutKind.Sequential, Pack = 1,
CharSet = CharSet.Ansi)]
public struct RADIO_INFO2
{
public UInt32 bLength;
public Byte[] szSerNum;
public Byte[] szProdName;
public UInt64 MinFreq;
public UInt64 MaxFreq;
public UInt32 Features;
};

As stated above, this is the struct that gives me a
Marshalling.SizeOf() of 32 bytes. If, instead of the two Byte arrays,
I provide 18 individual Bytes, I get a matching size of 42 bytes, but
that seems like a heavy-handed work-around. Surely there's a Right
Way to do this.

Additionally, if instead of Byte arrays I define them as Char arrays,
I get an error from the Marshalling code complaining that the array
types don't match.

Just for reference, the actual call that consumes this struct looks
like:

[DllImport("WRG315API.DLL")]
public static extern UInt32 GetRadioList(ref RADIO_INFO2[]
lpRadioInfo,
UInt32 BufferSize,
ref UInt32 InfoSize);

Thanks in advance for any help that is offered.
Todd Pafford
ca******@gmail.com

Jan 14 '08 #2

P: n/a
Thanks for the quick response, Nicholas. Indeed, the MarshalAs()
directive fixes the string sizing and typing issue. This is a great
help.

Oddly, when I pass an array of ten of these structs as refs to the
DLL, I get back an array of length 1 (I've only got one radio so this
is ok), but that one struct contains only the initialization values I
assigned all the elements in my original array. It's as if the array
was passed by reference and so could be modified, but each element of
the array cannot be modified. Does this make sense according to the
marshalling rules? Certainly, the problem could be in the DLL in
which case I can take it up with the manufacturers, but could this
behavior be a result of a problem with my definitions?

Thanks,
Todd
On Jan 14, 2:06 pm, "Nicholas Paldino [.NET/C# MVP]"
<m...@spam.guard.caspershouse.comwrote:
Todd,

You can just tell the marshalling layer to embed the string into the
struct by value:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct RADIO_INFO2
{
public UInt32 bLength;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=9)]
public string szSerNum;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst=9)]
public string szProdName;
public UInt64 MinFreq;
public UInt64 MaxFreq;
public UInt32 Features;

};

If you are using unsafe code, then you could use the fixed keyword as
well to fix the character array to a certain size, but what's above should
work just fine.

--
- Nicholas Paldino [.NET/C# MVP]
- m...@spam.guard.caspershouse.com

<calen...@gmail.comwrote in message

news:27**********************************@j78g2000 hsd.googlegroups.com...
Hi all,
I'm taking my first steps into C# <--C++ DLL Interop and
unfortunately I've run into (what seems to be) a very complicated case
as my first task. Perhaps someone here can help me.
I need to pass an array of RADIO_INFO2 structures to be filled by a
function in the DLL. This is how the structure is defined in the C++
example that comes with the DLL:
#pragma pack(1) // set byte packing
typedef struct {
unsigned __int32 bLength;
char szSerNum[9];
char szProdName[9];
unsigned __int64 MinFreq;
unsigned __int64 MaxFreq;
struct {
unsigned __int32 ExtRef:1;
unsigned __int32 FMWEnabled:1;
unsigned __int32 Reserved:30;
} Features;
} RADIO_INFO2;
#pragma pack() // set back the default packing
As you can see, there are a few obvious difficulties. First, the
nested Features struct. I think I've solved this by just defining
Features as a UInt32 and I'll handle the bit masking manually if I
need to. The second are the two char arrays. The DLL wants two 9
byte buffers to fill with character data while the marshalling code
wants to pass references. This brings me to the third problem, the
size of the structure. In C++ it's 42 bytes long: 32bits + 9bytes +
9bytes + 64bits + 64bits + 32bits = 42bytes. However, due to the
reference vs. value array problem the Marshalling code is coming up
with 32bytes: 32bits + 32bit (ref) + 32bit (ref) + 64bits + 64bits +
32bits = 32 bytes. The following is my best shot at describing this
structure in C#.
[StructLayout(LayoutKind.Sequential, Pack = 1,
CharSet = CharSet.Ansi)]
public struct RADIO_INFO2
{
public UInt32 bLength;
public Byte[] szSerNum;
public Byte[] szProdName;
public UInt64 MinFreq;
public UInt64 MaxFreq;
public UInt32 Features;
};
As stated above, this is the struct that gives me a
Marshalling.SizeOf() of 32 bytes. If, instead of the two Byte arrays,
I provide 18 individual Bytes, I get a matching size of 42 bytes, but
that seems like a heavy-handed work-around. Surely there's a Right
Way to do this.
Additionally, if instead of Byte arrays I define them as Char arrays,
I get an error from the Marshalling code complaining that the array
types don't match.
Just for reference, the actual call that consumes this struct looks
like:
[DllImport("WRG315API.DLL")]
public static extern UInt32 GetRadioList(ref RADIO_INFO2[]
lpRadioInfo,
UInt32 BufferSize,
ref UInt32 InfoSize);
Thanks in advance for any help that is offered.
Todd Pafford
calen...@gmail.com
Jan 14 '08 #3

This discussion thread is closed

Replies have been disabled for this discussion.