Docs on marshalling fix length strings in structs

  • Thread starter Thread starter Patrick Long
  • Start date Start date
P

Patrick Long

Can anyone point me at some good resources for marshalling fix length
strings in complex types using P/Invoke.

The Advanced P/Invoke is superb for the discussion of string pointers but
not so good on fix length strings.

TIA

Pat Long
 
The easiest way of handling structures with fixed length strings is to
marshal the entire struct as a byte array. Then write property getters and
setters to read write the sections of the array as standard .NET types.
What can then be done is to to write operators for the class to support
implicit casting to and from a byte array. Here is an example (a fairly
simple struct but illustrates the above points):-

/// <summary>

/// SMS Addressing information

/// </summary>

/// <remarks>Equivalent to native <b>sms_address_tag</b> structure</remarks>

public class SmsAddress

{

// Length of fixed length address string

internal const int SmsMaxLength = 256;

/// <summary>

/// Length, in Bytes, of SmsAddress structure.

/// </summary>

public const int Length = 260;

// Byte array containing structure data

private byte[] m_data;

/// <summary>

/// Create a new instance of SmsAddress.

/// </summary>

public SmsAddress()

{

m_data = new byte[Length];

}

/// <summary>

/// Create a new instance of SmsAddress from a Byte array.

/// </summary>

/// <param name="data">SmsAddress data in a Byte array.</param>

public SmsAddress(byte[] data)

{

if(data.Length > Length)

{

m_data = data;

}

}

/// <summary>

/// Create a new instance of SmsAddress with a specified address.

/// </summary>

/// <param name="address">Address e.g. +447890123456</param>

public SmsAddress(string address) : this()

{

this.Address = address;

}

/// <summary>

/// Create a new instance of SmsAddress with a specified address and type.

/// </summary>

/// <param name="address">Address e.g. +447890123456</param>

/// <param name="type">A member of the SmsAddressType Enumeration</param>

public SmsAddress(string address, SmsAddressType type) : this(address)

{

this.Type = type;

}

/// <summary>

/// Returns a flat Byte Array of the Sms Address data

/// </summary>

/// <returns>Byte array containing SmsAddress data.</returns>

public byte[] ToByteArray()

{

return m_data;

}

/// <summary>

/// Cast SmsAddress object to a byte array

/// </summary>

/// <param name="sa">SmsAddress object</param>

/// <returns>Byte array containing SmsAddress data.</returns>

public static implicit operator byte[](SmsAddress sa)

{

return sa.ToByteArray();

}

/// <summary>

/// Cast byte array to SmsAddress object.

/// </summary>

/// <param name="b">Byte array containing SmsAddress data</param>

/// <returns>SmsAddress version of the data.</returns>

public static implicit operator SmsAddress(byte[] b)

{

return new SmsAddress(b);

}

#region Type Property

/// <summary>

/// The address type.

/// </summary>

public SmsAddressType Type

{

get

{

//return address type identifier

return (SmsAddressType)BitConverter.ToInt32(m_data, 0);

}

set

{

//copy value to data array

BitConverter.GetBytes((int)value).CopyTo(m_data, 0);

}

}

#endregion

#region Address Property

/// <summary>

/// The address in string format. For example, "127.0.0.1" or
"+1.800.123.4567".

/// </summary>

public string Address

{

get

{

//return the number portion of the address minus any trailing nulls

return System.Text.Encoding.Unicode.GetString(m_data, 4,
SmsMaxLength).TrimEnd('\0');

}

set

{

System.Text.Encoding.Unicode.GetBytes(value).CopyTo(m_data, 4);

}

}

#endregion

}



Peter
 
Could you show the C++ declares of the types/methods
you're trying to marshal?
 
Hi Patrick,

The lazy, but acceptable way to do it is to explictly put the bytes in the
struct. I actually do this in the following example because the string is
only 32 characters. Keep in mind that for the code below, the string is a
TCHAR array not a char array.

Admittedly, this is not pretty but it works:

Original C/C++ declaration:
typedef struct {
WORD wMid;
WORD wPid;
MMVERSION vDriverVersion;
CHAR szPname[MAXPNAMELEN];
DWORD dwFormats;
WORD wChannels;
WORD wReserved1;
DWORD dwSupport;
} WAVEOUTCAPS;

C# Inplementation:
protected class WAVEOUTCAPS
{
public ushort wMid = 0;
public ushort wPid = 0;
public uint vDriverVersion = 0;

// Begin TCHAR szPname[MAXPNAMELEN]
public short b0 = 0;
public short b1 = 0;
public short b2 = 0;
public short b3 = 0;
public short b4 = 0;
public short b5 = 0;
public short b6 = 0;
public short b7 = 0;
public short b8 = 0;
public short b9 = 0;
public short b10 = 0;
public short b11 = 0;
public short b12 = 0;
public short b13 = 0;
public short b14 = 0;
public short b15 = 0;
public short b16 = 0;
public short b17 = 0;
public short b18 = 0;
public short b19 = 0;
public short b20 = 0;
public short b21 = 0;
public short b22 = 0;
public short b23 = 0;
public short b24 = 0;
public short b25 = 0;
public short b26 = 0;
public short b27 = 0;
public short b28 = 0;
public short b29 = 0;
public short b30 = 0;
public short b31 = 0;
// End TCHAR szPname[MAXPNAMELEN]

public uint dwFormats = 0;
public ushort wChannels = 0;
public ushort wReserved1 = 0;
public uint dwSupport = 0;

public string GetProductName()
{
char[] bytes =
{
(char)b0, (char)b1, (char)b2, (char)b3, (char)b4,
(char)b5, (char)b6, (char)b7, (char)b8, (char)b9,
(char)b10, (char)b11, (char)b12, (char)b13, (char)b14,
(char)b15, (char)b16, (char)b17, (char)b18, (char)b19,
(char)b20, (char)b21, (char)b22, (char)b23, (char)b24,
(char)b25, (char)b26, (char)b27, (char)b28, (char)b29,
(char)b30, (char)b31
};

return new string(bytes);
}


--
Geoff Schwab
Program Manager
Excell Data Corporation
http://msdn.com/mobility
http://msdn.microsoft.com/mobility/prodtechinfo/devtools/netcf/FAQ/default.aspx

This posting is provided "AS IS" with no warranties, and confers no rights.
 
Eeww! Why not just pass a byte array, then use a class parse the array into
something usable? That way you could just use a string.

--
Chris Tacke, eMVP
Co-Founder and Advisory Board Member
www.OpenNETCF.org
---
Windows CE Product Manager
Applied Data Systems
www.applieddata.net


Geoff Schwab said:
Hi Patrick,

The lazy, but acceptable way to do it is to explictly put the bytes in the
struct. I actually do this in the following example because the string is
only 32 characters. Keep in mind that for the code below, the string is a
TCHAR array not a char array.

Admittedly, this is not pretty but it works:

Original C/C++ declaration:
typedef struct {
WORD wMid;
WORD wPid;
MMVERSION vDriverVersion;
CHAR szPname[MAXPNAMELEN];
DWORD dwFormats;
WORD wChannels;
WORD wReserved1;
DWORD dwSupport;
} WAVEOUTCAPS;

C# Inplementation:
protected class WAVEOUTCAPS
{
public ushort wMid = 0;
public ushort wPid = 0;
public uint vDriverVersion = 0;

// Begin TCHAR szPname[MAXPNAMELEN]
public short b0 = 0;
public short b1 = 0;
public short b2 = 0;
public short b3 = 0;
public short b4 = 0;
public short b5 = 0;
public short b6 = 0;
public short b7 = 0;
public short b8 = 0;
public short b9 = 0;
public short b10 = 0;
public short b11 = 0;
public short b12 = 0;
public short b13 = 0;
public short b14 = 0;
public short b15 = 0;
public short b16 = 0;
public short b17 = 0;
public short b18 = 0;
public short b19 = 0;
public short b20 = 0;
public short b21 = 0;
public short b22 = 0;
public short b23 = 0;
public short b24 = 0;
public short b25 = 0;
public short b26 = 0;
public short b27 = 0;
public short b28 = 0;
public short b29 = 0;
public short b30 = 0;
public short b31 = 0;
// End TCHAR szPname[MAXPNAMELEN]

public uint dwFormats = 0;
public ushort wChannels = 0;
public ushort wReserved1 = 0;
public uint dwSupport = 0;

public string GetProductName()
{
char[] bytes =
{
(char)b0, (char)b1, (char)b2, (char)b3, (char)b4,
(char)b5, (char)b6, (char)b7, (char)b8, (char)b9,
(char)b10, (char)b11, (char)b12, (char)b13, (char)b14,
(char)b15, (char)b16, (char)b17, (char)b18, (char)b19,
(char)b20, (char)b21, (char)b22, (char)b23, (char)b24,
(char)b25, (char)b26, (char)b27, (char)b28, (char)b29,
(char)b30, (char)b31
};

return new string(bytes);
}


--
Geoff Schwab
Program Manager
Excell Data Corporation
http://msdn.com/mobility
http://msdn.microsoft.com/mobility/prodtechinfo/devtools/netcf/FAQ/default.aspx

This posting is provided "AS IS" with no warranties, and confers no rights.
Patrick Long said:
Can anyone point me at some good resources for marshalling fix length
strings in complex types using P/Invoke.

The Advanced P/Invoke is superb for the discussion of string pointers but
not so good on fix length strings.

TIA

Pat Long
 
Hi Chris,

Like I said, it is ugly and the byte array is the better solution. I
typically just use this method for testing because I can dump the code in
quickly. Once I know the P/Invoke works properly, I can convert the class
to a byte array.

--
Geoff Schwab
Program Manager
Excell Data Corporation
http://msdn.com/mobility
http://msdn.microsoft.com/mobility/prodtechinfo/devtools/netcf/FAQ/default.aspx

This posting is provided "AS IS" with no warranties, and confers no rights.
Chris Tacke said:
Eeww! Why not just pass a byte array, then use a class parse the array into
something usable? That way you could just use a string.

--
Chris Tacke, eMVP
Co-Founder and Advisory Board Member
www.OpenNETCF.org
---
Windows CE Product Manager
Applied Data Systems
www.applieddata.net


Geoff Schwab said:
Hi Patrick,

The lazy, but acceptable way to do it is to explictly put the bytes in the
struct. I actually do this in the following example because the string is
only 32 characters. Keep in mind that for the code below, the string is a
TCHAR array not a char array.

Admittedly, this is not pretty but it works:

Original C/C++ declaration:
typedef struct {
WORD wMid;
WORD wPid;
MMVERSION vDriverVersion;
CHAR szPname[MAXPNAMELEN];
DWORD dwFormats;
WORD wChannels;
WORD wReserved1;
DWORD dwSupport;
} WAVEOUTCAPS;

C# Inplementation:
protected class WAVEOUTCAPS
{
public ushort wMid = 0;
public ushort wPid = 0;
public uint vDriverVersion = 0;

// Begin TCHAR szPname[MAXPNAMELEN]
public short b0 = 0;
public short b1 = 0;
public short b2 = 0;
public short b3 = 0;
public short b4 = 0;
public short b5 = 0;
public short b6 = 0;
public short b7 = 0;
public short b8 = 0;
public short b9 = 0;
public short b10 = 0;
public short b11 = 0;
public short b12 = 0;
public short b13 = 0;
public short b14 = 0;
public short b15 = 0;
public short b16 = 0;
public short b17 = 0;
public short b18 = 0;
public short b19 = 0;
public short b20 = 0;
public short b21 = 0;
public short b22 = 0;
public short b23 = 0;
public short b24 = 0;
public short b25 = 0;
public short b26 = 0;
public short b27 = 0;
public short b28 = 0;
public short b29 = 0;
public short b30 = 0;
public short b31 = 0;
// End TCHAR szPname[MAXPNAMELEN]

public uint dwFormats = 0;
public ushort wChannels = 0;
public ushort wReserved1 = 0;
public uint dwSupport = 0;

public string GetProductName()
{
char[] bytes =
{
(char)b0, (char)b1, (char)b2, (char)b3, (char)b4,
(char)b5, (char)b6, (char)b7, (char)b8, (char)b9,
(char)b10, (char)b11, (char)b12, (char)b13, (char)b14,
(char)b15, (char)b16, (char)b17, (char)b18, (char)b19,
(char)b20, (char)b21, (char)b22, (char)b23, (char)b24,
(char)b25, (char)b26, (char)b27, (char)b28, (char)b29,
(char)b30, (char)b31
};

return new string(bytes);
}


--
Geoff Schwab
Program Manager
Excell Data Corporation
http://msdn.com/mobility
http://msdn.microsoft.com/mobility/prodtechinfo/devtools/netcf/FAQ/default.aspx
This posting is provided "AS IS" with no warranties, and confers no rights.
 
The call I was looking at making was SimWritePhonebookEntry. It's args are
this

HRESULT SimWritePhonebookEntry (
HSIM hSim,
DWORD dwLocation,
DWORD dwIndex,
LPSIMPHONEBOOKENTRY lpPhonebookEntry
);with the LPSIMPHONEBOOKENTRY struct looking like this

typedef struct simphonebookentry_tag {
DWORD cbSize;
DWORD dwParams;
TCHAR lpszAddress[MAX_LENGTH_ADDRESS];
DWORD dwAddressType;
DWORD dwNumPlan;
TCHAR lpszText[MAX_LENGTH_PHONEBOOKENTRYTEXT];
}


TIA

Pat
 
Take a look at this article:

http://msdn.microsoft.com/library/d.../dnnetcomp/html/ProcessManager.asp?frame=true

--
Alex Yakhnin, NET CF MVP
IntelliProg, Inc.
http://www.intelliprog.com

Patrick Long said:
The call I was looking at making was SimWritePhonebookEntry. It's args are
this

HRESULT SimWritePhonebookEntry (
HSIM hSim,
DWORD dwLocation,
DWORD dwIndex,
LPSIMPHONEBOOKENTRY lpPhonebookEntry
);with the LPSIMPHONEBOOKENTRY struct looking like this

typedef struct simphonebookentry_tag {
DWORD cbSize;
DWORD dwParams;
TCHAR lpszAddress[MAX_LENGTH_ADDRESS];
DWORD dwAddressType;
DWORD dwNumPlan;
TCHAR lpszText[MAX_LENGTH_PHONEBOOKENTRYTEXT];
}


TIA

Pat


Alex Yakhnin said:
Could you show the C++ declares of the types/methods
you're trying to marshal?
 
Back
Top