I am currently developing an application to work with WAV files. I want to be able to display the information in the struct with its native type, but C# thinks of char as a 16 bit value.
The four bytes ChunkID0...3 are supposed to contain 'R' 'I' 'F' 'F'
[StructLayout(LayoutKind.Explicit, Size = 12, Pack = 1)]
public unsafe struct RiffDescriptor
{
[FieldOffset(0)]
public byte ChunkID_0;
[FieldOffset(1)]
public byte ChunkID_1;
...
}
I want the debugger to show the ChunkID as 'R' instead of 122.
Any thoughts?
public class RiffDescriptor
{
public RiffDescriptor(BinaryReader b)
{
// Read the ChunkID - Should be RIFF
ChunkID = b.ReadBytes(4);
// Read the ChunkSize
ChunkSize = b.ReadUInt32();
// Read the Format - Should be WAVE
Format = b.ReadBytes(4);
}
[DebuggerDisplay("ChunkID = {System.Text.Encoding.Default.GetString(ChunkID)}")]
public byte[] ChunkID;
public UInt32 ChunkSize;
[DebuggerDisplay("Format = {System.Text.Encoding.Default.GetString(Format)}")]
public byte[] Format;
}
Related
I have a float value of 128.523 which needs to be expressed as 24 bit 2s Complement signed number.
The 23rd Bit is the sign bit.
Bits 22 to fifteen are the integer part.
Bits 14 to 0 are the floating part.
I need to send this data to a protocol which only accepts data in this format.
I understand that i need to give my code and ask answers but unfortunately i am totally lost as how to proceed forward.
Any help would be greatly appreciated.
Update with the help of the community
I would be getting tracking data from a external device and send it to a application which uses freeD protocol.I have
There is no 24Bit float that is the problem , i have found out how the bits would be arranged if we make it 24 bit( user Class).
I will send it via UDP
I have no idea in what order the bits need to be sent
Update
It is Bigendian
Based on the other response I gave you, the only problem then is to trucate the Int32 to a Int24. The simplest thing would be to use the BitConverter.GetBytes(int) that returns a byte[4] and then remove the last byte... Or we can use some special struct, like:
[StructLayout(LayoutKind.Explicit)]
struct Int32ToBytes
{
[FieldOffset(0)]
public int Int32;
[FieldOffset(0)]
public uint Uint32;
[FieldOffset(0)]
public byte Byte0;
[FieldOffset(1)]
public byte Byte1;
[FieldOffset(2)]
public byte Byte2;
[FieldOffset(3)]
public byte Byte3;
}
public struct Int24
{
public byte Byte0;
public byte Byte1;
public byte Byte2;
}
And then:
private const int minSrc = 0x5A0000;
private const int maxSrc = 0xA60000;
private const double minDest = -180.0;
private const double maxDest = 180.0;
private const double rangeDest = maxDest - minDest;
public static Int24 ConvertToInt24(double num)
{
int res = (int)(((num - minDest) * rangeSrc / rangeDest) + minSrc);
var conv = new Int32ToBytes { Int32 = res };
var res2 = new Int24
{
Byte0 = conv.Byte0,
Byte1 = conv.Byte1,
Byte2 = conv.Byte2,
};
return res2;
}
Now, the Int24 struct has 3 bytes, Byte0, Byte1 and Byte2.
I'm trying to pass an array of object or struct from a C# library (.net 4.5) called by a C++ MFC application but I can't get correct data. An additional difficulty is that C++ application don't know the object count.
I have successfully passed simple type (int, string) and also a struct with the following code (using UnmanagedExports 1.2.7):
C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
public struct ItemA
{
// Size: 4
public int Id;
// Size: 100
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string Name;
// Size: 2(4)
public bool IsEnabled;
}
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static public IntPtr GetItemA(int itemId)
{
// Check structure size
Debug.Assert(Marshal.SizeOf(typeof(ItemA)) == 108);
// Get ItemA from a WS
var itemA = client.GetItemsA().First(i => i.Id == itemId);
// Convert ItemA to structure pointer
IntPtr buf = Marshal.AllocHGlobal(Marshal.SizeOf(itemA));
Marshal.StructureToPtr(itemA, buf, false);
return buf;
}
C++
#pragma pack(4)
typedef struct
{
// Size: 4
int Id;
// Size: 100
TCHAR Name[50];
// Size: 2(4)
bool IsEnabled;
} ItemA;
extern "C"
{
ItemA* GetItemA(int itemId);
}
// (...)
void CMyAppDlg::OnBnClickedButtonGetItemA()
{
// Check structure size
static_assert(sizeof ItemA == 108, "Size does not match C# struct size");
ItemA* structItemA = GetItemA(1);
}
I have searched everywhere but I don't find any functionnal response to my specific problem. Can you help to write both C# and C++ codes for GetItemAList which can return an array of struct or another type to C++ application?
Thanks in advance!
Edit1 : I changed my code to solve packing/size struct issue (64 bits compilation).
Edit2 : I replace manual alignment by #pragma pack.
Edit3 : I'm always stuck with GetItemAList. Here is my actual c#/c++ codes with an MemoryException after calling c#. Can you help me with this sh**? Thanks in advance.
C#
[DllExport(CallingConvention = CallingConvention.Cdecl)]
static public void GetItemAList([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 1)] ref ItemA[] myArray, ref int length)
{
// Get ItemAList from a WS
var itemAArray = client.GetItemsAList().ToArray();
// Set myArray values
length = itemAArray.Length;
for (int i = 0; i < length; i++)
{
myArray[i] = itemAArray[i];
}
}
C++
extern "C"
{
void GetItemAList(ItemA*& myArray, int& length);
}
// (...)
void CMyAppDlg::OnBnClickedButtonGetItemA()
{
ItemA* myArray = new ItemA[100];
int length = 100;
GetItemAList(myArray, length);
}
I have not done any checking, but your C++ version is probably compiled with 4 bytes packing (assuming your are compiling in 32 bits).
In that case, you would have on C++ side:
// Field, Field size, Total size
// -------------------------------
// Id, 0, 4
// Name, 50, 54
// IsEnabled, 1, 55
// (padding) 1, 56
On that side, you should add a static_assert validating that you got the expected size.
static_check(sizeof ItemA == 56, "Size does not match C# struct size")
On the hand on C# side, you would have:
// Field, Field size, Total size
// -------------------------------
// Id, 0, 4
// Name, 50, 54
// IsEnabled, 4, 58
// (no padding) 0, 58
And if bool was 1 byte, you would have a size of 55 instead. In both case, it would not match the size on C++ side.
As it is somewhat hard to know all rules and not make any mistake and also to ensure nothing is broken in future maintenance, you should always add validation in your code.
On C# side, typical validation would be something like: Debug.Assert(Marshal.Sizeof(typeof(ItemA)) == 58.
You could also validate the offset or size of some fields but generally if the size you compute by hand do match on both sides, you probably got it right particularily if you use 1 byte packing.
I'm trying to unmarshall an array of variable length of a structure nested inside another structure as in the following code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CardInfoRequest
{
public ulong CardId;
public byte AppListLength;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)]
public CardApp[] AppList;
}
[Serializable()]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CardApp
{
public ulong CardAppId;
public byte SomeInformation;
}
And I am unmarshalling it by doing:
var lDataPointer = Marshal.AllocHGlobal(pSize);
Marshal.Copy(pData, 0, lDataPointer, pSize);
var lResult = Marshal.PtrToStructure(lDataPointer, typeof(CardInfoRequest));
Marshal.FreeHGlobal(lDataPointer);
Where pData is a byte array containing the marshalled structure and pSize is its size in runtime (18 when the array has one item, 27 when the array has two items and so forth...).
However, no matter the size in bytes of the stream, whenever I unmarshall it I am always getting AppList.Length == 1.
Can I unmarshall it correctly? Should I do it byte by byte?
Thanks in advance!
The pinvoke marshaller has no idea what size array it needs to create. A fixed-size buffer cannot work either. You have to do it in two steps, first unmarshal the first 2 members, you now know the array size from AppListLength. Create the array, then unmarshal the array elements one by one in a loop.
So roughly (untested)
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct CardInfoRequestHeader
{
public ulong CardId;
public byte AppListLength;
}
public struct CardInfoRequest
{
public CardInfoRequestHeader Header;
public CardApp[] AppList;
}
...
var req = new CardInfoRequest();
req.Header = (CardInfoRequestHeader)Marshal.PtrToStructure(pData, typeof(CardInfoRequestHeader));
req.AppList = new CardApp(req.Header.AppListLength);
pData += Marshal.SizeOf(CardInfoRequestHeader);
for (int ix = 0; ix < req.AppList.Length; ++ix) {
req.AppList = (CardInfo)Marshal.PtrToStructure(pData, typeof(CardInfo));
pData += Marshal.SizeOf(CardInfo);
}
Beware the ulong and Pack = 1 are Red Flags, unmanaged data rarely looks like that. The code snippet does make the hard assumption that Pack=1 is accurate.
I am having a problem with marshalling a C character array. I have the following C# structure:
[StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi), Serializable]
internal struct Header
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
[FieldOffset(0)]
public string header;
[FieldOffset(4)]
public int version;
[FieldOffset(8)]
public int diroffset;
[FieldOffset(12)]
public int direntries;
}
and the following code to read this structure from a stream:
public static T ReadStruct<T>(this Stream stream) where T : struct
{
var sz = Marshal.SizeOf(typeof(T));
var buffer = new byte[sz];
stream.Read(buffer, 0, sz);
var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var structure = (T) Marshal.PtrToStructure(
pinnedBuffer.AddrOfPinnedObject(), typeof(T));
pinnedBuffer.Free();
return structure;
}
Now my problem is that the header field misses a character after the struct is read. The file where the struct is read from contains the four bytes VPVP but after the struct has been read by ReadStruct the header string only contains VPV. If I take a look at the byte array in the read function in the debugger then that array contains the values 86, 80, 86, 80 which is VPVP. I also tried using LayoutKind.Sequential for the StructLayout but that didn't change anything.
Am I doing something wrong or why is there a character missing in my string?
The problem you're having lies in the struct definition, not in writing the bytes to it.
The problem lies right here:
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
As you've stated, you're writing out the text VPVP, which is 4 characters long, you'd think. This, however, is not the case. In C, you could declare the string as such:
char mystring[] = { 'V', 'P', 'V', 'P', '\0' };
You need that null character (\0) at the end, to mark off the end of the string. You need to take this into account when marshalling, because you need to reserve space for that "null terminator byte", if you do not, the C# string will add it for you in the available memory, so it will eat away your last character. So if you're gonna use a null-terminated string, you will have to make it of length 5.
EDIT: Here is a better solution, where you don't have to worry about null-terminators, you just use a char[] (and you also keep the magic 16 byte size):
[StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi), Serializable]
internal struct Header
{
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]
[FieldOffset(0)]
private char[] headerCharArray;
public string header
{
get { return new string(headerCharArray); }
set
{
if (value.Length == 4)
{
headerCharArray = value.ToArray();
}
else
{
throw new InvalidOperationException("String length was not 4.");
}
}
}
[FieldOffset(4)]
public int version;
[FieldOffset(8)]
public int diroffset;
[FieldOffset(12)]
public int direntries;
}
That way the char[] is stored in memory, and you can access it as a string through the property, which doesn't take in any memory of the struct itself.
his question is about converting between a struct and a byte array. Many solutions are based around GCHandle.Alloc() and Marshal.StructureToPtr(). The problem is these calls generate garbage. For example, under Windows CE 6 R3 about 400 bytes of garbarge is made with a small structure. If the code below could be made to work the solution could be considered cleaner. It appears the sizeof() happens too late in the compile to work.
public struct Data
{
public double a;
public int b;
public double c;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct DataWrapper
{
private static readonly int val = sizeof(Data);
[FieldOffset(0)]
public fixed byte Arr[val]; // "fixed" is to embed array instead of ref
[FieldOffset(0)]
public Data; // based on a C++ union
}
You can in fact use the constant value 1 as the size of the fixed array in DataWrapper. There is no bounds checking when accessing an unsafe fixed array nor is there any mechanism to request its length at runtime. The presence of the Data member at offset 0 is enough to ensure that you can actually read and write sizeof(Data) bytes to/from DataWrapper.Arr.
With that said, a more straight forward solution would be to cast a Data* pointer to byte* as follows:
unsafe {
Data d = new Data();
d.a = 125.5;
d.b = 0x7FEEDDEE;
d.c = 130.0;
byte* bytes = (byte*)&d;
for (int i = 0; i < sizeof(Data); i++) {
Console.WriteLine(i + ": " + bytes[i]);
}
}