Holy cow, I wrote a book!
If you're looking to get into some p/invoke action, you'd be well-served to check out the pinvoke wiki to see if somebody else has done it too. If what you need isn't there, you may end up forced to write your own, and here are some gotchas I've seen people run into:
bool
BOOLEAN
System.Boolean
BOOL
VARIANT_BOOL
char
System.Char
long
System.Int64
CoTaskMemAlloc
CoTaskMemFree
That last one is particularly gnarly on 64-bit systems, where alignment requirements are less forgiving than on x86. The structure declarations on pinvoke.net tend to ignore 64-bit issues. For example, the declaration of the INPUT structure (as of this writing—it's a wiki so it's probably changed by the time you read this) reads as follows:
INPUT
[StructLayout(LayoutKind.Explicit)]struct INPUT { [FieldOffset(0)] int type; [FieldOffset(4)] MOUSEINPUT mi; [FieldOffset(4)] KEYBDINPUT ki; [FieldOffset(4)] HARDWAREINPUT hi; }
This structure layout is correct for 32-bit Windows, but it's incorrect for 64-bit Windows.
Let's take a look at that MOUSEINPUT structure, for starters.
MOUSEINPUT
typedef struct tagMOUSEINPUT { LONG dx; LONG dy; DWORD mouseData; DWORD dwFlags; DWORD time; ULONG_PTR dwExtraInfo; } MOUSEINPUT, *PMOUSEINPUT, FAR* LPMOUSEINPUT;
In 64-bit Windows, the LONG and DWORD members are four bytes, but the dwExtraInfo is a ULONG_PTR, which is eight bytes on a 64-bit machine. Since Windows assumes /Zp8 packing, the dwExtraInfo must be aligned on an 8-byte boundary, which forces four bytes of padding to be inserted after the time to get the dwExtraInfo to align properly. And in order for all this to work, the MOUSEINPUT structure itself must be 8-byte aligned.
LONG
DWORD
dwExtraInfo
ULONG_PTR
time
Now let's look at that INPUT structure again. Since the MOUSEINPUT comes after the type, there also needs to be padding between the type and the MOUSEINPUT to get the MOUSEINPUT back to an 8-byte boundary. In other words, the offset of mi in the INPUT structure is 8 on 64-bit Windows, not 4.
type
mi
Here's how I would've written it:
// This generates the anonymous union [StructLayout(LayoutKind.Explicit)] struct INPUT_UNION { [FieldOffset(0)] MOUSEINPUT mi; [FieldOffset(0)] KEYBDINPUT ki; [FieldOffset(0)] HARDWAREINPUT hi; }; [StructLayout(LayoutKind.Sequential)] struct INPUT { int type; INPUT_UNION u; }
I introduce a helper structure to represent the anonymous union that is the second half of the Win32 INPUT structure. By doing it this way, I let somebody else worry about the alignment, and it'll be correct for both 32-bit and 64-bit Windows.
static public void Main() { Console.WriteLine(Marshal.OffsetOf(typeof(INPUT), "u")); }
On a 32-bit system, this prints 4, and on a 64-bit system, it prints 8. The downside is that you have to type an extra u. when you access the mi, ki or hi members.
u.
ki
hi
input i; i.u.mi.dx = 0;
(I haven't checked what the PInvoke Interop Assistant comes up with for the INPUT structure.)