Convert C++ union of structs to C# - c#

I am trying to use C# to use a C++ library that includes a struct that is documented as being laid out like this:
struct {
long outervar;
long othervar;
union {
struct {
long a;
} firststruct;
struct {
long p;
long q;
long r;
long s;
} secondstruct;
struct {
long x;
long y;
long z;
} thirdstruct;
} myunion;
} outerstruct;
Ideally I would like to implement something like this:
struct outerstruct
{
UInt32 outervar;
UInt32 othervar;
[FieldOffset(0)]
struct firststruct
{
UInt32 a;
}
[FieldOffset(0)]
struct secondstruct
{
UInt32 p;
UInt32 q;
UInt32 r;
UInt32 s;
}
[FieldOffset(0)]
struct thirdstruct
{
UInt32 x;
UInt32 y;
UInt32 z;
}
}
Of course I can't use FieldOffset or MarshalAs on structs, but I am at a loss as to how to implement this. If anybody has some ideas how to implement this in C# I would appreciate it.
Thanks!

[FieldOffset] is fine on struct fields. But your declaration just has nested types (note that in the C++ code, the structs are actually anonymous, but used to declare a new field...your C# code just declares a named struct, but no actual field). Note that you will also need to include [StructLayout(LayoutKind.Explicit)], as well as provide offsets for all fields in a struct.
Try something like this:
struct firststruct
{
UInt32 a;
}
struct secondstruct
{
UInt32 p;
UInt32 q;
UInt32 r;
UInt32 s;
}
struct thirdstruct
{
UInt32 x;
UInt32 y;
UInt32 z;
}
[StructLayout(LayoutKind.Explicit)]
struct union
{
[FieldOffset(0)]
firststruct firststruct;
[FieldOffset(0)]
secondstruct secondstruct;
[FieldOffset(0)]
thirdstruct thirdstruct;
}
struct outerstruct
{
UInt32 outervar;
UInt32 othervar;
union union;
}

You certainly can use FieldOffset and that is the answer. But you neeed to define the union as a distinct type.
Define each struct in the usual way. I'm going to assume you know how to do this.
Then define the union:
[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
[FieldOffset(0)]
public FirstStruct firststruct;
[FieldOffset(0)]
public SecondStruct secondstruct;
[FieldOffset(0)]
public ThirdStruct thirdstruct;
}
Finally put it all together:
[StructLayout(LayoutKind.Sequential)]
public struct OuterStruct
{
public int outervar;
public int othervar;
public MyUnion myunion;
}
The key is always to make the union a distinct type and use a field offset of zero for every member.
FWIW, C++ long on Windows is a signed 32 bit integer. That's int in C#.

Related

Marshaling complex C structs with c unions for C#

i am desperate to get a complex c datatype correctly marshaled for C#. I already read all the other posts regarding that topic and i am running out of ideas although it seems to me to be quite close to the solution.
The main issue is that the c-struct is having a union of two different struct types. On with only basic types and one including arrays, which causes trouble.
I have created an example to showcase the situation. The struct worring me is called dataStreamConfiguration. The c code looks like this, the struct in question is at the bottom of the example c-code:
#include "stdint.h"
#include "stddef.h"
typedef enum viewCapEnum {
X = 0,
}viewCapEnum;
typedef struct fraction{
uint8_t nominator;
uint8_t denominator;
}fraction;
typedef struct comSize{
fraction A;
fraction B;
}comSize;
typedef enum someEnum{
A = 0,
B,
C,
D
}someEnum;
typedef struct someSize{
fraction X;
fraction Y;
}someSize;
typedef struct featTemplateCap{
someEnum A;
someSize Size;
}featTemplateCap;
typedef struct featTypeCap{
someEnum AB;
someSize CD;
}featTypeCap;
typedef struct viewCap{
uint8_t A;
uint8_t B;
size_t BCount;
viewCapEnum ViewCapEnum[50];
comSize MinComSize;
size_t CapaCount;
featTemplateCap TemplCap[14];
size_t TypeCapaCount;
featTypeCap FeatTypeCapa[14];
uint8_t GCount;
}viewCap;
typedef struct featX{
uint16_t A;
uint16_t B;
int16_t C;
int16_t D;
}featX;
typedef struct pathCap{
uint8_t Count;
uint8_t Size;
featX Feat;
}pathCap;
typedef struct dataStreamConfiguration{
size_t FeatureSelector;
union {
viewCap AsViewCap;
pathCap AsPathCap;
}dataStream;
}dataStreamConfiguration;
The marshalling of datatypes between C and the C# world is working for almoust all but this dataStreamConfiguration struct. So I got the following code, where instead of mapping (somehow) a union to c# both datatypes have been put one after another. So clearly this was not working correctly. It looked like that:
public unsafe struct UInt32Struct {
public UInt32 value;
}
public unsafe struct fraction{
public Byte nominator;
public Byte denominator;
}
public unsafe struct comSize{
public fraction A;
public fraction B;
}
public unsafe struct someSize{
public fraction X;
public fraction Y;
}
public unsafe struct featTemplateCap{
public UInt32 A;
public someSize Size;
}
public unsafe struct featTypeCap{
public UInt32 AB;
public someSize CD;
}
public unsafe struct viewCap{
public Byte A;
public Byte B;
public UInt16 BCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50]
public UInt32Struct[] ViewCapEnum;
public comSize MinComSize;
public UInt16 CapaCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 14]
public featTemplateCap[] TemplCap;
public UInt16 TypeCapaCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 14]
public featTypeCap FeatTypeCapa[14];
public Byte GCount;
}
public unsafe struct featX{
public UInt16 A;
public UInt16 B;
public Int16 C;
public Int16 D;
}
public unsafe struct pathCap{
public Byte Count;
public Byte Size;
public featX Feat;
}
public unsafe struct dataStreamConfiguration{
public UInt16 FeatureSelector;
public viewCap AsViewCap;
public pathCap AsPathCap;
}
So to get the union to c# I came across the LayoutKind.Explicit and did following:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct dataStreamConfiguration{
[FieldOffset(0)]
public UInt16 FeatureSelector;
[FieldOffset(2)]
public viewCap AsViewCap;
[FieldOffset(2)]
public pathCap AsPathCap;
}
This was not working due to the alignment of the object types, which are incorrectly aligned or overlapped by non-object fields.. I googled a lot. Adjusted the alignment to 4 by [StructLayout(LayoutKind.Explicit, Pack=4)]. However, 4,8,16,32, whatever alignment i have choosen, I got the same error during runtime - incorrectly aligned or overlapped issue.
Next thing I did - I felt quite lucky about - was to unroll all the arrays in C# datatype for all the arrays in viewCap struct. As I have read that this might cause alignment issues. Well, It didn't work. And I found that the memory has been modified, so I could not find the values I have seen in C appearing now in C#. Most of the values in C# are 0. Ok.
To get rid of this memory modification stuff I put in C# to all other structs [StructLayout(LayoutKind.Sequential)] to keep the order of elements as they are in C. Sadly it didn't help much, I could not find the values of the c-struct in c# either. However, it was finally working, when I got rid of the union and deleted either AsViewCap or AsPathCap (my weak moment of blind rage). Ok, but that was not the solution.
Last help was having a try with IntPtr, so i have created a new struct called dataStreamConfigurationPtr:
public unsafe struct dataStreamConfigurationPtr{
public UInt16 FeatureSelector;
public void* Ptr;
}
[StructLayout(LayoutKind.Sequential)]
public unsafe struct dataStreamConfiguration{
public UInt16 FeatureSelector;
public viewCap AsViewCap;
public pathCap AsPathCap;
}
Instead of having an overlapping memory with StructLayout.Explicit I used an void* to point to the unmanaged memory location. For this I used the old struct definition to get the memory and instead having a union I took the first version where both types are laid out one over another. The idea was to use it like that:
MyFunction(dataStreamConfigurationPtr X, int Status){
//Create obj and appropraite space for data
dataStreamConfiguration DataStream = new dataStreamConfiguration();
DataStream.FeatureSelector = X.FeatureSelector;
unsafe{
IntPtr Ptr = new IntPtr(&X.Ptr);
DataStream.AsViewCap = Marshal.PtrToStructure<viewCap>(Ptr);
DataSteram.AsPathCap = Marshal.PtrToStructure<pathCap>(Ptr);
}
WCFCallback(DataStream, Status);
}
Now the IntPtr is pointing to the right memory, however, this works only for the first item of the structs. So for viewCap the first item A has its correct data whereas item B, BCount,.. all the other item seem to have at least misalligned values or accidental values.. I am quite desperate what to do now, i feel i am so close to a solution but have no idea how to get the other data of the struct from c to c#.
Any suggestions and comments are highly welcome!
Best regards,
Tobias
I would assume you have two use cases and want to interprete the union part based FeatureSelector either as AsViewCap or as AsPathCap on C# side.
That means I assume that you don't intend to do type punning.
One could create two structs then on the managed C# side:
public struct dataStreamConfigurationAsViewCap
{
public UInt64 FeatureSelector;
public viewCap AsViewCap;
}
public struct dataStreamConfigurationAsPathCap
{
public UInt64 FeatureSelector;
public pathCap AsPathCap;
}
You could then only inspect FeatureSelector first and based on the result either interprete it dataStreamConfigurationAsViewCap or as dataStreamConfigurationAsPathCap.
Size
You have several variables with size_t on C side (FeatureSelector, BCount, CapaCount, TypeCapaCount) which you map all to UInt16, which is wrong. UInt16 is the minimum size in the C standard, but the usual implementations especially on platforms running .NET are larger, see also this nice answer. For example on my macOS machine it is 8 bytes.
Maybe it would be a good idea to start with a smaller test case and expand it step by step so that you can identify such problems. And when you encounter a problem, you can more easily create a minimal, complete and testable example.
One approach in this direction could be the following:
Small Test Case
some.h
#ifndef some_h
#define some_h
#include <stdio.h>
typedef struct viewCap {
uint8_t A;
uint8_t B;
} viewCap;
typedef struct pathCap {
uint16_t X;
uint16_t Y;
size_t Num;
} pathCap;
typedef struct dataStreamConfiguration {
size_t FeatureSelector;
union {
viewCap AsViewCap;
pathCap AsPathCap;
} dataStream;
} dataStreamConfiguration;
dataStreamConfiguration *dscViewCap(void);
dataStreamConfiguration *dscPathCap(void);
extern void free_struct(dataStreamConfiguration *ptr);
#endif /* some_h */
some.c
#include "some.h"
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
dataStreamConfiguration *dscViewCap(void) {
dataStreamConfiguration *dsc = calloc(1, sizeof(dataStreamConfiguration));
dsc->FeatureSelector = 0;
dsc->dataStream.AsViewCap.A = 42;
dsc->dataStream.AsViewCap.B = 84;
return dsc;
}
dataStreamConfiguration *dscPathCap(void) {
dataStreamConfiguration *dsc = calloc(1, sizeof(dataStreamConfiguration));
dsc->FeatureSelector = 1;
dsc->dataStream.AsPathCap.X = 0xAAAA;
dsc->dataStream.AsPathCap.Y = 0x5555;
dsc->dataStream.AsPathCap.Num = 0x3333333333333333;
return dsc;
}
void free_dsc(dataStreamConfiguration *ptr) {
free(ptr);
}
int main(void) {
return 0;
}
UnionFromC.cs
using System.Runtime.InteropServices;
namespace UnionFromC
{
public static class Program
{
public struct viewCap
{
public Byte A;
public Byte B;
}
public struct pathCap
{
public UInt16 X;
public UInt16 Y;
public UInt64 Num;
}
public struct dataStreamConfigurationAsViewCap
{
public UInt64 FeatureSelector;
public viewCap AsViewCap;
}
public struct dataStreamConfigurationAsPathCap
{
public UInt64 FeatureSelector;
public pathCap AsPathCap;
}
[DllImport("StructLib", EntryPoint = "dscViewCap")]
private static extern IntPtr NativeDSCViewCap();
[DllImport("StructLib", EntryPoint = "dscPathCap")]
private static extern IntPtr NativeDSCPathCap();
[DllImport("StructLib", EntryPoint = "free_dsc")]
private static extern void NativeFreeDSC(IntPtr ptr);
static void Main()
{
IntPtr _intPtrViewCap = NativeDSCViewCap();
var viewDSC = Marshal.PtrToStructure<dataStreamConfigurationAsViewCap>(_intPtrViewCap);
Console.WriteLine("\nInterpretation as view cap:");
Console.WriteLine($" FeatureSelector: {viewDSC.FeatureSelector}");
Console.WriteLine($" A: {viewDSC.AsViewCap.A}");
Console.WriteLine($" B: {viewDSC.AsViewCap.B}");
NativeFreeDSC(_intPtrViewCap);
IntPtr _intPtrPathCap = NativeDSCPathCap();
var pathDSC = Marshal.PtrToStructure<dataStreamConfigurationAsPathCap>(_intPtrPathCap);
Console.WriteLine("\nInterpretation as path cap:");
Console.WriteLine($" FeatureSelector: {pathDSC.FeatureSelector}");
Console.WriteLine($" A: {pathDSC.AsPathCap.X:X4}");
Console.WriteLine($" B: {pathDSC.AsPathCap.Y:X4}");
Console.WriteLine($" Num: {pathDSC.AsPathCap.Num:X8}");
NativeFreeDSC(_intPtrPathCap);
}
}
}
Output of the Test
Interpretation as view cap:
FeatureSelector: 0
A: 42
B: 84
Interpretation as path cap:
FeatureSelector: 1
A: AAAA
B: 5555
Num: 3333333333333333
This is one solution how to get data from C/C++ to C#. Here I will describe what I did wrong and what one has to take care of.
To recall in our minds my requirement has been (still is) that any data represented as union in C/C++, needs to be represented as such in C#. This means for following structure:
typedef struct dataStreamConfiguration{
size_t FeatureSelector;
union {
viewCap AsViewCap;
pathCap AsPathCap;
}dataStream;
}dataStreamConfiguration;
Any data inside AsViewCap must have its kind of representation in AsPathCap, because its simple the same memory. If one of these two are modified, the other is also. To handle C/C++ unions in C# you need to provide a memory layout. As Stephan Schlecht already meantioned it is vital to know the alignment! My project is compiled for 32bit the alignment is at the 4 Byte boarder. Thus my initial layout in my question was simply wrong. You need to check the layout in your C/C++ project and adjust it in the C# struct definion properly: Here is my corrected code, both union members start at 4th Byte:
[StructLayout(LayoutKind.Explicit)]
public unsafe struct dataStreamConfiguration{
[FieldOffset(0)]
public UInt16 FeatureSelector;
[FieldOffset(4)]
public viewCap AsViewCap;
[FieldOffset(4)]
public pathCap AsPathCap;
}
Doing this you are half way there! Yes! But there is one more thing. With this change the Code will compile but, you will get an exception at runtime. Yes, at runtime. Quite quick but it is so. The error message something like: "object-field at offset 4 is incorrectly aligned or overlapped by a non-object field"
C# is bugging is because in C# there are basic types like Integer etc. and reference types.
These reference types can give us errors if we do not handel them correctly. C# has a very nice working marshalling, but in case of unions, its up to you to make it as nice as it can get.
Explenation:
What went wrong in my code is that the struct viewCap is having arrays, marshalled by the C# marshaller. The marshaller is doing his duty and creating an array. However, an array is a reference type and created on the heap. What you will get on the stack (data transfer C++ <-> C#) is the reference address to the array on the heap. Hmpf! Thus the second structure in the union with its basic types would overlapping the address and thus invalidating the reference. Gladly the runtime environment stops us from doing that :-) Moreover, C# is defragmenting the memory. If the struct layout is not efficient regarding memory usage C# will reoder the content. You can avoid this by annotating the struct which the layout kind: Sequential.
Remember:
If you got an array inside a type which is part of a union (C/C++), you cannot use the C# marshaller! Use Layoutkind Explicit for unions and Sequential for structs!
Solution:
There might be several other solutions I am not aware of, but what is 100% working is to unroll the arrays. Yes, it's a lot of work. But it works!
So the final struct looks like this:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct viewCap{
public Byte A;
public Byte B;
public UInt16 BCount;
public UInt32Struct ViewCapEnum_0;
public UInt32Struct ViewCapEnum_1;
public UInt32Struct ViewCapEnum_2;
public UInt32Struct ViewCapEnum_3;
[...]
public comSize MinComSize;
public UInt16 CapaCount;
public featTemplateCap TemplCap_0;
public featTemplateCap TemplCap_1;
public featTemplateCap TemplCap_2;
public featTemplateCap TemplCap_3;
[...]
public UInt16 TypeCapaCount;
public featTypeCap FeatTypeCapa_0;
public featTypeCap FeatTypeCapa_1;
public featTypeCap FeatTypeCapa_2;
public featTypeCap FeatTypeCapa_3;
[...]
public Byte GCount;
}
Happy runtime environment, happy life!
Yes, for this specific solution you would need to adjust the code which is using the arrays. However, its bulletproof and understandable how this works under the hood which makes it easy for maintenance. As my code is generated, an unrolled array is no big deal.

How to marshal a structure containing a union from C to C#

Could anyone please help me with the following?
I have a dll written in C and want to call a certain function in it from C#.
The function returns a pointer to a structure. Here is the structure:
typedef struct
{
char crv_name[40];
char crv_name2[12];
char units[40];
char creator[24];
char index_units[8];
double first_dep_tim;
double last_dep_tim;
double level_spacing;
EmptyValU empty_val;
long num_ele;
}
Now, I know (from calling this from a C client) that the last member (num_ele) will be set to 1.
Now, that penultimate member is of type EmptyValU which is defined as:
typedef union
{
double d;
float f;
long l;
ulong ul;
short s;
ushort us;
char c;
}EmptyValU;
Now, I can call this ok fom C# and read everything up until empty_val. My value for num_ele is nonsense as I am clearly misaligned in memory after empty_val.
Here is my C# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CurveInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string crv_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
public string crv_name2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string units;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
public string creator;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string index_units;
public double first_dep_tim;
public double last_dep_tim;
public double level_spacing;
[MarshalAs(UnmanagedType.Struct, SizeConst = 8)]
public EmptyValU empty_val;
public long num_ele;
}
and I have defined EmptyValU as:
[StructLayout(LayoutKind.Explicit, Size = 8)]
public struct EmptyValU
{
[FieldOffset(0)]
public double d;
[FieldOffset(0)]
public float f;
[FieldOffset(0)]
long l;
[FieldOffset(0)]
ulong ul;
[FieldOffset(0)]
short s;
[FieldOffset(0)]
ushort us;
[FieldOffset(0)]
char c;
}
Like I say, when I call the function which returns a pointer to such a structure all the values are populated correctly up until the EmptyValU member, which, along with num_ele is not correctly populated. Something about the way I have to define the union in C# is what I am missing in order to keep the alignment correct.
Thanks for any help,
Mitch.
I've solved this.
I C, a long (and ulong) is 4 bytes wide, but in C# they are 8 bytes wide.
So instead of:
[FieldOffset(0)]
long l;
[FieldOffset(0)]
ulong ul;
I should have had:
[FieldOffset(0)]
int l;
[FieldOffset(0)]
uint ul;
becasue in C# int and uint are 4 bytes wide.
For the same reason, on the C# side instead of:
public long num_ele;
I need:
public int num_ele;
Now it all works.

how to convert c struct with void* to c#

I'm write a plugin with c# . the program with c# will use a dll which writes by c ,so I have to call c function in my c# program ,but unfortunately the c function's parameter is a struct and it is so complex that I never find any
help information about how to convert it to c# parameter.
the struct has an embedded function and void* parameter ,I didn't find anyway to convert them to c#.
the struct is mostly like this
struct first_struct{
char* parameter1;
int parameter2;
unsigned long parameter3;
unsigned short parameter4;
void* parameter5;
int (*parameter6)(int,void *);
second_struct parameter7;
};
struct second_struct{
char parameter8[64] ;
char parameter9[256];
};
I want change this c struct to c# struct but I have no idea how to do it
thanks #Biesi Grr and #Ian Abbott ‘s help it sames that I can change the c struct to c# like that.
public delegate int parameter6(int volcnt, System.IntPtr vod);
[StructLayout(LayoutKind.Sequential)]
public struct csharp_firstSturct
{
[MarshalAs(UnmanagedType.LPStr)]
public string parameter1;
public int parameter2;
public ulong parameter3;
public ushort parameter4;
public System.IntPtr parameter5;
public parameter6 m_parameter6;
}
but I still has no idea about how to convert parameter7 from C to C#,what should I do to convert parameter7 form c to c#.
Assuming your char arrays are null terminated strings you can do it the following way
public delegate int parameter6(int volcnt, System.IntPtr vod);
[StructLayout(LayoutKind.Sequential)]
public struct csharp_firstSturct
{
[MarshalAs(UnmanagedType.LPStr)]
public string parameter1;
public int parameter2;
public ulong parameter3;
public ushort parameter4;
public System.IntPtr parameter5;
public parameter6 m_parameter6;
[MarshalAs(UnmanagedType.Struct)]
public second_struct parameter7;
}
[StructLayout(LayoutKind.Sequential)]
public struct second_struct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string parameter8;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string parameter9;
}

Cannot declare pointer to non-unmanaged type

I'm trying to learn how to write a wrapper around a DLL and I've hit a bit of a road-block. I've got a struct declared as such:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_Surface
{
public readonly UInt32 flags;
public readonly SDL_PixelFormat* format;
public readonly int w, h;
public readonly int pitch;
public void* pixels;
/// <summary>Application data associated with the surface</summary>
public void* userdata;
/// <summary>information needed for surfaces requiring locks</summary>
public readonly int locked;
public readonly void* lock_data;
/// <summary>clipping information</summary>
public readonly SDL_Rect clip_rect;
/// <summary>info for fast blit mapping to other surfaces</summary>
private SDL_BlitMap *map; // <--- Cannot take the address of, get the size of, or declare a pointer to a managed type
/// <summary>Reference count -- used when freeing surface</summary>
public int refcount;
}
When I try to compile my project, it gives the above error.
But you will notice above it, I do have a pointer to another struct. I'm trying to figure out what the difference between these two structs is that makes one work but the other doesn't, but I'm not sure; they're both unsafe structs. They are as follows:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_PixelFormat
{
UInt32 format;
SDL_Palette *palette;
byte BitsPerPixel;
byte BytesPerPixel;
fixed byte padding [2];
UInt32 Rmask;
UInt32 Gmask;
UInt32 Bmask;
UInt32 Amask;
byte Rloss;
byte Gloss;
byte Bloss;
byte Aloss;
byte Rshift;
byte Gshift;
byte Bshift;
byte Ashift;
int refcount;
SDL_PixelFormat *next;
}
unsafe internal delegate int SDL_blit(SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect);
[StructLayout(LayoutKind.Sequential)]
unsafe struct SDL_BlitMap
{
SDL_Surface* dst;
int identity;
SDL_blit blit;
void* data;
SDL_BlitInfo info;
/* the version count matches the destination; mismatch indicates
an invalid mapping */
UInt32 dst_palette_version;
UInt32 src_palette_version;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_Rect
{
int x, y;
int w, h;
}
So what do I have to change to make this compile?
I believe it's the reference to SDL_blit in SDL_BlitMap that's causing the problem. I've declared it as a delegate; is there something else I should be declaring it as? It's defined as this, in C:
typedef int (*SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect,
struct SDL_Surface * dst, SDL_Rect * dstrect);
Any struct that contains a managed type cannot have its address taken.
Delegates are a reference type, therefore they are also a managed type.
This means that an SDL_Blitmap is a managed type because it contains a managed reference to an SDL_blit, thus you cannot get a pointer to it without fixing it first.
If the function you are trying to invoke is already available in the dll, I suggest you have a look at the DllImportAttribute.(http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute(v=vs.100).aspx)
By combining a public static extern declaration with a DllImportAttribute, you can invoke any global function declared in the dll you are interop-ing.
Alternatively you'll need to create a C/C++ side function that takes a function pointer and invokes it, which could get messy.
What if you don't rely on unsafe code? It may affect affect performance though if your code is performance/speed critical. Something along the lines of this:
[StructLayout(LayoutKind.Sequential)]
struct SDL_PixelFormat
{
UInt32 format;
IntPtr palettePtr;
byte BitsPerPixel;
byte BytesPerPixel;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
byte padding[];
UInt32 Rmask;
UInt32 Gmask;
UInt32 Bmask;
UInt32 Amask;
byte Rloss;
byte Gloss;
byte Bloss;
byte Aloss;
byte Rshift;
byte Gshift;
byte Bshift;
byte Ashift;
int refcount;
IntPtr nextPtr;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_Surface
{
public readonly UInt32 flags;
public readonly IntPtr format;
public readonly int w, h;
public readonly int pitch;
public IntPtr pixels;
/// <summary>Application data associated with the surface</summary>
public IntPtr userdata;
/// <summary>information needed for surfaces requiring locks</summary>
public readonly int locked;
public readonly IntPtr lock_data;
/// <summary>clipping information</summary>
public readonly SDL_Rect clip_rect;
/// <summary>info for fast blit mapping to other surfaces</summary>
private IntPtr mapPtr;
/// <summary>Reference count -- used when freeing surface</summary>
public int refcount;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_BlitMap
{
IntPtr dstPtr;
int identity;
SDL_blit blit;
IntPtr data;
SDL_BlitInfo info;
/* the version count matches the destination; mismatch indicates
an invalid mapping */
UInt32 dst_palette_version;
UInt32 src_palette_version;
}
[StructLayout(LayoutKind.Sequential)]
struct SDL_Rect
{
int x, y;
int w, h;
}
[UnmanagedFunctionPointer(CallingConvention.ToBeAdded)]
internal delegate int SDL_blit(ref SDL_Surface src, ref SDL_Rect srcrect, ref SDL_Surface dst, ref SDL_Rect dstrect);
When you need to read any structure pointers:
var palette = (SDL_Palette) Marshal.PtrToStructure(surface.palettePtr, typeof (SDL_Pallete));
Otherwise, if you wish to stick to your current code, try declaring the function pointer as an IntPtr instead of a delegate and create the delegate during runtime using Marshal.GetDelegateForFunctionPointer.

Can I set the size attribute of StructLayout at runtime?

Im trying to use SendInput to simulate keyboard presses in my app and want to support both 32-bit and 64-bit.
I've determined that for this to work, I need to have 2 different INPUT structs as such
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk; // Virtual Key Code
public ushort wScan; // Scan Code
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit, Size = 28)]
public struct INPUT32
{
[FieldOffset(0)]
public uint type; // eg. INPUT_KEYBOARD
[FieldOffset(4)]
public KEYBDINPUT ki;
}
[StructLayout(LayoutKind.Explicit, Size = 40)]
public struct INPUT64
{
[FieldOffset(0)]
public uint type; // eg. INPUT_KEYBOARD
[FieldOffset(8)]
public KEYBDINPUT ki;
}
I wanted to know if there was a way to set the StructLayout size and FieldOffsets at runtime so I could use just one INPUT struct and determine the size and fieldoffset depending on the machine.
I have tried the code below but I would like to know if the same is possible at runtime instead of compile time.
#if _M_IX86
[StructLayout(LayoutKind.Explicit, Size = 28)]
#else
[StructLayout(LayoutKind.Explicit, Size = 40)]
#endif
public struct INPUT
{
[FieldOffset(0)]
public uint type; // eg. INPUT_KEYBOARD
#if _M_IX86
[FieldOffset(4)]
#else
[FieldOffset(8)]
#endif
public KEYBDINPUT ki;
}
Unfortunately, no.
Attributes are "fused" to the type at compile-time, which is why all values passed to an attribute constructor must be constants.
And at runtime you can't modify the attributes attached to the type. You can grab a copy and modify its values, but the actual attribute attached to the type will remain unchanged, so you can't "trick" mscorlib code into seeing your changes instead of the original, either.
You could always have 2 structs and determine which one to use at runtime.
With a proper design you could limit code duplication to a few lines. (Plus having the structures twice.)

Categories

Resources