|
|
| [c#]need some help with p/invoke and unions |
|
Neil Walker
Member #210
April 2000
|
Hello, "Could not load type ...because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field" I've read elsewhere it's to do with value/reference types but I'm not really sure how to fix it. I verified what I was doing using the P/Invoke interop Assistant and it was quite happy. Basically, what is throwing me is it seems to be giving me the error based on the size of the struct items and not object/value type mixing as I've read elsewhere: I've cut things down hugely to show the error: [StructLayout(LayoutKind.Explicit)] public struct GAME_EVENT { [FieldOffsetAttribute(0)] public int type; [FieldOffsetAttribute(0)] public ANY_EVENT display; [FieldOffsetAttribute(0)] public JOYSTICK_EVENT joystick; }; If I remove JOYSTICK_EVENT then the code works. JOYSTICK_EVENT has more data values than ANY_EVENT and if I remove, say, an int from JOYSTICK_EVENT it also works. ANY_EVENT is described as: [StructLayout(LayoutKind.Sequential)] public struct ANY_EVENT { public type; public EVENT_SOURCE source; public double timestamp; }; and JOYSTICK_EVENT is described exactly the same but has an extra int (in reality it isn't but I've managed to get it to fail like this): [StructLayout(LayoutKind.Sequential)] public struct ANY_EVENT { public type; public EVENT_SOURCE source; public double timestamp; int value; // the new value }; When I run my code I get the error as detailed. If I remove 'int value' it works fine. If I remove EVENT_SOURCE then it seems to work. So I'm a little confused as to exactly what the fault is! EVENT_SOURCE is: [StructLayout(LayoutKind.Sequential)] public struct EVENT_SOURCE { /// int[32] [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=32, ArraySubType=UnmanagedType.I4)] public int[] @__pad; }; Any help would be much appreciated. Thanks. Neil. wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie |
|
Erin Maus
Member #7,537
July 2006
|
The second field should be an IntPtr, not an actual structure, like so: 1 [StructLayout(LayoutKind.Sequential, Pack = 0)]
2 public struct AllegroInternalEventHeader
3 {
4 public uint type;
5 public IntPtr source;
6 public double timestamp;
7 }
8
9 [StructLayout(LayoutKind.Sequential, Pack = 0)]
10 public struct AllegroInternalDisplayEvent
11 {
12 public AllegroInternalEventHeader header;
13 public int x, y;
14 public int width, height;
15 }
16
17 [StructLayout(LayoutKind.Sequential, Pack = 0)]
18 public struct AllegroInternalJoystickEvent
19 {
20 public AllegroInternalEventHeader header;
21 public int stick;
22 public int axis;
23 public float pos;
24 public int button;
25 }
26
27 [StructLayout(LayoutKind.Sequential, Pack = 0)]
28 public struct AllegroInternalKeyboardEvent
29 {
30 public AllegroInternalEventHeader header;
31 public IntPtr display;
32 public int keycode;
33 public uint unichar;
34 public uint modifiers;
35 }
36
37 [StructLayout(LayoutKind.Sequential, Pack = 0)]
38 public struct AllegroInternalMouseEvent
39 {
40 public AllegroInternalEventHeader header;
41 public IntPtr display;
42 public int x, y, z, w;
43 public int dx, dy, dz, dw;
44 public uint button;
45 }
46
47 [StructLayout(LayoutKind.Sequential, Pack = 0)]
48 public struct AllegroInternalTimerEvent
49 {
50 public AllegroInternalEventHeader header;
51 public long count;
52 public double error;
53 }
54
55 [StructLayout(LayoutKind.Explicit, Pack = 0)]
56 public struct AllegroInternalEvent
57 {
58 [FieldOffset(0)]
59 public uint type;
60
61 [FieldOffset(0)]
62 public AllegroInternalEventHeader any;
63
64 [FieldOffset(0)]
65 public AllegroInternalDisplayEvent display;
66
67 [FieldOffset(0)]
68 public AllegroInternalJoystickEvent joystick;
69
70 [FieldOffset(0)]
71 public AllegroInternalKeyboardEvent keyboard;
72
73 [FieldOffset(0)]
74 public AllegroInternalMouseEvent mouse;
75
76 [FieldOffset(0)]
77 public AllegroInternalTimerEvent timer;
78 }
That's how I wrap Allegro 5 WIP events in my game. --- |
|
Neil Walker
Member #210
April 2000
|
I'll try that, thanks. I use IntPtr in most places but I wanted to recreate the structs wherever it was possible. Regarding this error, what's the reason behind not being allowed to do this, and is there no way round it? Also, without EVENT_SOURCE how did you do stuff like: Did you just use IntPtr as well? I was just trying to add some readability to the code where possible. I was even tempted to edit the allegro code and make the union just one big struct Neil. wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie |
|
Kibiz0r
Member #6,203
September 2005
|
Neil, a little bit of critique:
--- |
|
Neil Walker
Member #210
April 2000
|
Thanks, there's so much to learn with p/invoke Kibiz0r said: Read about [In] and [Out] Do have any good sources, this is something I've seen a fair bit in code but have been unable to find any reference on the internet whatsoever to explain it as I find it impossible to construct a search to give me anything useful. Quote: Try to name your stuff by .NET standards I appreciate what you're saying and I do follow standards, but my goal in this instance was to make a direct equivalent where with minimal changes code could easily be written and ported. Otherwise you'd end up with effectively a new library. What I plan on doing (if all works and the speed is sufficient in the bindings) add a proper class library around it. The only real concession I've made to this api equivalence is where functions return pointer data (e.g. bitmap creation, etc) I use exceptions and I have some static constructors to automatically run the install methods for those requiring them (e.g. the add-ons, keyboard, etc) Quote: Do [MarshalAs], not [MarshalAsAttribute] This is now redundant as I'm using IntPtr. However, I got MarshalAsAttribute directly from MSDN pinvoke tool, so I assumed Microsoft knew what they were talking about in this case. Why do you say this? Quote: You don't have to specify [StructLayout(LayoutKind.Sequential)] for structs I prefer to explicitly state this for my readibility Quote: Avoid specifying field offsets You need field offsets for unions, that's how create them (all with offset of 0) Quote: they should implement IDisposable That adds overhead, as I mentioned I wanted this thin wrapper layer and the posh stuff comes in a proper managed framework Quote: You need the [UnmanagedFunctionPointer] Ok, again I followed what pinvoke at MSDN gave me for function pointers and assumed they knew what they were doing, as this is my first p/invoke exploration Quote: immutable structs Rightly or wrongly, I did this as it's a low level library and didn't want the overhead of getter/setters. Quote: You can specify [StructLayout] for classes, and when marshalled, classes are passed as if they were by-ref structs Cheers. I guess this is one way of handling the struct pointer functions without using IntPtr or having the issues with the object/value runtime error. Neil. wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie |
|
Kibiz0r
Member #6,203
September 2005
|
Neil Walker said: Do have any good sources, this is something I've seen a fair bit in code but have been unable to find any reference on the internet whatsoever to explain it as I find it impossible to construct a search to give me anything useful. There's a really good article on MSDN or maybe some MS-affiliate that I found a while back. I took a quick peek and didn't find it; if I do, I'll let you know. Suffice it to say that there are behavioral and performance considerations to bear in mind when deciding how to decorate your parameters for P/Invoke methods. It especially comes into play with arrays, where you don't want to waste time copying 1,000 values back and forth between the boundaries. The attributes themselves do exactly what you expect -- [In] copies data into the method (presumably managed to unmanaged, but I think it switches with callbacks) and [Out] copies it back out of the method. (Mimicking passing by reference when language constructs preclude you from actually passing by reference, or for use with arrays.) Quote: I appreciate what you're saying and I do follow standards, but my goal in this instance was to make a direct equivalent where with minimal changes code could easily be written and ported. If it's just for your use, I'll leave you to it. But if you intend for other people to use this, my gut feeling is that this is an auxiliary use case at best. Allegro is just one piece of a large puzzle that would have to be reassembled in C#, for little to no benefit. I'm a big advocate of making your code feel like the language that it's written in. The place I'm writing for right now has one standard that they use for all languages, and it makes the code I'm writing look completely out of place, and in some cases ruins the API. Quote: This is now redundant as I'm using IntPtr. However, I got MarshalAsAttribute directly from MSDN pinvoke tool, so I assumed Microsoft knew what they were talking about in this case. Why do you say this? It's more concise and expressive, it's what shows up in IntelliSense, and [MarshalAsAttribute] can match either class MarshalAsAttribute or class MarshalAsAttributeAttribute. (and yes, I have seen attribute that look like that.) Quote: You need field offsets for unions, that's how create them (all with offset of 0) Ah, I didn't know that. I never tried to make a direct mirror of a union on my A5 binding; instead I inspected the type of the event when I pulled it out and instantiated the correct layout right off the bat. Quote: That adds overhead, as I mentioned I wanted this thin wrapper layer and the posh stuff comes in a proper managed framework
Then your proper managed framework is going to have overhead because you didn't cut right to the heart of the issue. Quote: Ok, again I followed what pinvoke at MSDN gave me for function pointers and assumed they knew what they were doing, as this is my first p/invoke exploration I didn't see any callback code in what you posted, I'm just telling you for when you do need to interact with some. Quote: Rightly or wrongly, I did this as it's a low level library and didn't want the overhead of getter/setters. I'm not talking about getters and setters, I'm talking about complete immutability. You can think of the structs as sort of messages that you send back and forth between C and C#. --- |
|
Tobias Dammers
Member #2,604
August 2002
|
Quote: Read about [In] and [Out]
I'm pretty sure google would turn up something useful for "InAttribute msdn" or similar. --- |
|
|