I am reading a binary file of a specific format, and am used to being able to cast a block of data to a struct then reading the struct to pull fields out of the binary datafile.
I'm trying to do this but failing in C#, they seem more like classes than structures in the C sense. Is it possible to do what I want? For example..
public struct Datum {
byte type;
ushort payload;
}
public struct DiskPage {
ushort pageNumber;
Datum[] data = Datum[170];
}
I want to be able to read 512 bytes of a file and cast it to a DiskPage, then to be able to read the values from the DiskPage structure. Is this possible in c# - or is there another preferred approach? Should I just leave that code in C and link it in?
Thanks!
Reza
The compiler doesn't respect ordering of your fields by default (also it might move the fields and leave gaps in the memory = Packing). By using StructLayout you can enforce a different behavior (Default, Sequential, or Explicit).
Use MarshalAs for specific options on a field. Be careful with endianess.
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public struct MySystemTime
{
[FieldOffset(0)]public ushort wYear;
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek;
[FieldOffset(6)]public ushort wDay;
[FieldOffset(8)]public ushort wHour;
[FieldOffset(10)]public ushort wMinute;
[FieldOffset(12)]public ushort wSecond;
[FieldOffset(14)]public ushort wMilliseconds;
}
I suggest you read Mastering C# structs that discusses using marshaling (Marshal.Copy in particular) for that purpose.
You can use fixed arrays in C# if you compile with Unsafe option enabled.
You can also use pointers.
internal unsafe struct MyStruct
{
public fixed byte MyFixedArray[128];
public byte* MyPointer;
}
It is preferred to use marshalling, field offset and avoid unsafe code.
Use unsafe code if you really need too in small portion of your code.
Don't expose to external world a fixed array or a pointer (interfaces or public classes\structs) if you can avoid it.
Related
I'm reading all about structure marshalling between C and C# in 64bit environment without success.
What I need to do is to pass the structure
typedef struct DescStructTa
{
char* pszNa;
unsigned long ulTId;
char* pszT;
unsigned short usRId;
unsigned long ulOff;
unsigned long ulSi;
char szAcc[2];
unsigned char bySSize;
} DescStruct;
from a C# application to a C DLL not made by us calling the method
MT_DLL_DECL long GetAll(UINTPTR ulHandler, DescStruct **ppList, unsigned long *pulNumS);
After long search I wrote the following code:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct PlcSymbolDescStruct
{
[MarshalAs(UnmanagedType.LPStr)]
string pszNa;
UInt32 ulTId;
[MarshalAs(UnmanagedType.LPStr)]
string pszT;
[MarshalAs(UnmanagedType.U2)]
ushort usRId;
UInt32 ulOff;
UInt32 ulSi;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
string szAcc;
[MarshalAs(UnmanagedType.U1)]
byte bySSize;
}
[DllImport("DataSource.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.U8)]public static extern long GetAll(ulong ulHandler, out DescStruct [] ppList,
out uint pulNumS);
Unfortunately this is still giving me a heap corruption.
If I try to pack less than 8 it gives (as expected) an access violation, but I expected this to work and I'm not able to figure out what could be wrong.
Any suggestion?
Thanks in advance!
I’m not sure you need Pack = 8. The default packing appears to be more or less compatible between C# and C++.
The szAcc field probably uses Unicode. To switch, specify CharSet=CharSet.Ansi in your [StructLayout]
Finally, if your C API has that double pointer to the array of them, you have to allocate that memory in a special way, either CoTaskMemAlloc or LocalAlloc, forgot which one.
It’s easier, also more efficient, to implement 2 functions instead. One to obtain the required size of the array. Another one to write them into caller-allocated buffer, a single pointer in C++ as opposed to double pointer, and UnmanagedType.LPArray in C#.
Update
allocation should not be necessary since the documentation states that the call returns a pointer to it's internal data structure
The runtime doesn’t know what was written in that documentation, attempts to free anyway, and crashes. You can use out IntPtr in C# API, and marshal manually.
If you have modern .NET, use unsafe, cast the IntPtr to PlcSymbolDescStruct* raw pointer, and construct ReadOnlySpan<PlcSymbolDescStruct> from that raw pointer.
if you’re stuck with old desktop .NET, call Marshal.PtrToStructure<PlcSymbolDescStruct> in a loop, incrementing the pointer by Marshal.SizeOf<PlcSymbolDescStruct>() bytes between loop iterations. No need to use unsafe code in this case.
I'd like to import an extern function of Win32API.
The Code from the API (in C) looks like this:
typedef struct _BLUETOOTH_ADDRESS {
union {
BTH_ADDR ullLong;
BYTE rgBytes[6];
};
} BLUETOOTH_ADDRESS;
My C# implementation looks like this:
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct BLUETOOTH_ADDRESS
{
[FieldOffset(0)]
public ulong ullLong;
[FieldOffset(2)]
public byte[] rgBytes;
};
The problem is: As soon as I create the struct, it throws me a TypeLoadException, error Code:
System.TypeLoadException: "Could not load type 'BLUETOOTH_ADDRESS' from assembly 'BleLab, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 2 that is incorrectly aligned or overlapped by a non-object field."
Do you have any idea to fix this or where the problem is?
Best regards
Edit:
Forgot to provide the way of calling:
var ba = new Win32API.BLUETOOTH_ADDRESS();
ba.rgBytes = new byte[6];
There's really no point in trying to declare this as a union. It's an unsigned 64 bit type. Just use ulong instead of the struct.
If you never need to display the address, then you would need to pick out just the first 6 bytes of that ulong. A Bluetooth address is a 48 bit value, hence 6 bytes.
But for your purposes there's nothing to be gained by trying to express that nuance in the type used for interop. Which is why I would recommend working with ulong for the interop and picking out the meaningful bytes if necessary as a separate action.
To help you understand the error, note the following:
All members of a C union type have overlapping storage that begins at offset 0, so the offset of member rgBytes in your C# struct should use [FieldOffset(0)], not [FieldOffset(2)].
The type of the rgBytes member in the C union is a fixed array of 6 bytes. In your C# struct it is a byte[] array type. A (normal) array type in C# is a "reference type", which can be thought of as being like a C pointer. That is, the object just holds a reference (pointer) to a value on the heap.
You can create a fixed array using the fixed keyword as follows:
fixed byte rgBytes[6];
Fixed arrays are unsafe code, so the above needs to be declared unsafe. It is also declared public in your struct, so the full declaration of the rgBytes member can be as follows:
public unsafe fixed byte rgBytes[6];
Putting it all together gives the following C# declaration for BLUETOOTH_ADDRESS:
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct BLUETOOTH_ADDRESS
{
[FieldOffset(0)]
public ulong ullLong;
[FieldOffset(0)]
public unsafe fixed byte rgBytes[6];
};
You can probably leave out the Size = 8 part.
As per David Heffernan's answer, you are probably better off just using a ulong, especially as that will avoid any "unsafe" code.
I'm converting an old Visual BASIC program to C#. It sends messages to some industrial machinery over ethernet. To do this it assembles a stream of bytes from fixed-size user defined chunks.
Most of these chunks are small and in C# it's easy to create structs of a few bytes or ints and control their size and layout using StructLayout's, for example
[StructLayout(LayoutKind.Sequential, Pack = 1)]
...so when we go into unmanaged space to do a bytewise copy we don't have byte order or padding problems.
But some of the VB6 structures are big arrays, for example,
Private Type SEND_MSG_BUFFER_320_BYTES
bytes(0 To 319) As Byte '320 bytes
End Type
and I'm struggling with how to do this in C#. I can make a fixed size array in a class, e.g.,
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class SOME_BYTES
{
public byte[] b = new byte[320];
}
but to do the byte-wise copy I need to be able to discover the size of this at runtime and System.Runtime.InteropServices.Marshal.SizeOf returns a 4 for this.
Any suggestions for how do do this will be much appreciated.
You can use fixed size buffers if you're okay with unsafe code, and changing your class into a struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct SomeBytes
{
public fixed byte MessageData[320];
}
Personally I would try to avoid all of this if possible. If you're just sending the data over the network, why do you need to "go into unmanaged space"? Can you remove that requirement somehow? (Maybe it's fundamental - but it's not clear from your question.)
You can use a fixed-size array:
unsafe struct SomeBytes {
public fixed byte b[320];
}
I think you want to do something like this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class SOME_BYTES
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=320)]
public byte[] b;
}
You would initialise it as follows:
SOME_BYTES data = new SOME_BYTES {b = new byte[320]};
Then you can populate data.b[] and use marshalling to get the data to send. The MarshalAs attribute tells the marshaller what fixed size buffer to use when marshalling the data.
You don't need to use the unsafe fixed keyword to do this kind of thing, and I strongly recommend that you avoid it.
I am trying to create the following struct in this msdn article. I am trying to learn the whole FieldOffset but have no clue where to start.
I basically did something like this.
[StructLayout(LayoutKind.Explicit, Size=12)]
public struct DHCP_OPTION_DATA_ELEMENT {
[FieldOffset(0)]
public DHCP_OPTION_DATA_TYPE OptionType;
[FieldOffset(4)]
public byte ByteOption;
[FieldOffset(4)]
public uint WordOption;
[FieldOffset(4)]
public UInt32 DWordOption;
[FieldOffset(4)]
public UInt32 DWordDWordOption;
[FieldOffset(4)]
public uint IpAddressOption;
[FieldOffset(4)]
public IntPtr StringDataOption;
[FieldOffset(4)]
public DHCP_BINARY_DATA BinaryDataOption;
[FieldOffset(4)]
public DHCP_BINARY_DATA EncapsulatedDataOption;
[FieldOffset(4)]
public string Ipv6AddressDataOption;
}
However, it barked at me stating the following exception.
it contains an object field at offset 4 that is incorrectly aligned or
overlapped by a non-object field.
Treat it as an IntPtr, instead of a string.
However, when using an IntPtr, be darn sure you take care of cleaning up after yourself, because you will now be working with unmanaged memory and thus the GC won't be helping you out, leading to a nice memory leak each time you pass this struct around.
You're going to want to be using the Marshal.PtrToStringUni function, most likely, as suggested by shf301 in another answer.
The error
it contains an object field at offset 4 that is incorrectly aligned or
overlapped by a non-object field.
Is due to overlapping a non-object (blittable) type (e.g. Uint32) with an object type (non-blittable). The marshaler cannot handle that. The marhshaler doesn't know which field of the union is valid (since it doesn't know how to decode OptionType so it doesn't know if it should marshal a string value or an integer value. Trying to marshal an integer value to a string would lead to a crash (since an integer value won't point to a valid string), so the marshaller throws the exception instead of allowing you to crash.
So you have to marshal the strings manually by defining them as IntPtr's and using Marshal.PtrToStringUni() or Marshal.PtrToStringAnsi().
You may have the same issue with DHCP_BINARY_DATA as well.
You have this code:
[FieldOffset(4)]
public string Ipv6AddressDataOption;
String is reference type (object), and other fields are value type (non-object). So you have to change the Offset for the Ipv6AddressDataOption.
I am trying to use marshalling in C#. In C++ I have a this struct:
struct aiScene
{
unsigned int mFlags;
C_STRUCT aiNode* mRootNode;
unsigned int mNumMeshes;
C_STRUCT aiMesh** mMeshes;
unsigned int mNumMaterials;
C_STRUCT aiMaterial** mMaterials;
unsigned int mNumAnimations;
C_STRUCT aiAnimation** mAnimations;
unsigned int mNumTextures;
C_STRUCT aiTexture** mTextures;
unsigned int mNumLights;
C_STRUCT aiLight** mLights;
unsigned int mNumCameras;
C_STRUCT aiCamera** mCameras;
}
So, C# eqvivalent is:
[StructLayout(LayoutKind.Sequential)]
public struct aiScene
{
public uint mFlags;
public unsafe aiNode* mRootNode;
public uint mNumMeshes;
public unsafe aiMesh** mMeshes;
public uint mNumMaterials;
public unsafe aiMaterial** mMaterials;
public uint mNumAnimations;
public unsafe aiAnimation** mAnimations;
public uint mNumTextures;
public unsafe aiTexture** mTextures;
public uint mNumLights;
public unsafe aiLight** mLights;
public uint mNumCameras;
public unsafe aiCamera** mCameras;
}
But many on this structs are managed ( aiNode, aiMesh, aiLight ) etc. So, I have this error:
Cannot take the address of, get the
size of, or declare a pointer to a
managed type ('Assimp.aiNode')
Any ideas on how to solve this issue?
This could get very complicated depending on what you are trying to do. However, as you are working it could help you do declare each of the pointers in the C# code like this. It uses the very useful IntPtr, which was described on here earlier today. :)
Please note that this will not magically get your code to work. I'd need to see a lot more of what's going on before I could give you input on that.
public struct aiScene
{
public uint Flags;
public IntPtr RootNode;
...
}
The main problem you're having is that you've defined managed objects with the same names as the unmanaged types. The pointer types, like "aiNode", should be defined as structs, not classes. You can define differently named managed wrapper classes which provide managed access to the underlying unsafe struct data. For example:
public struct aiNode {}
public struct aiScene
{
public uint mFlags;
public unsafe aiNode* mRootNode;
// ...
}
At a high level, it looks like you're trying to use AssImp from C#. That can be done today using assimp-net. However, in case someone runs into a marshalling situation like this and wants a generic answer..
I strongly recommend against using IntPtr for anything, because they are basically an untyped void* with no type checking. Unsafe-struct-pointers offer a safer disambiguation of unmanaged pointer types. SafeHandle is another option with better safety and handling of some race-conditions. See my article on the topic.
If you literally want to copy the data from unmanaged to managed land, then for each type (aiNode, aiMesh, etc) you need to define both an unsafe-struct (to match the unmanaged layout), and a managed class. Then, write (or generate) unsafe code to copy the unmanaged tree to managed objects. Be sure to be careful of any unmanaged objects with more than one reference.
Sometimes a better alternative is to write a wrapper which provides access to unmanaged data "in-place". If you are doing it "safely", then the managed wrappers should be thin-objects which manage lifetime for the unmanaged objects and have properties to access their data. Alternatively, you can do it unsafely by merely defining the unsafe structs and using them in an unsafe context.