I recently spent a lot of time on this. This is a brain dump ... I hope this helps you.
First, understand that for the most part, the rules that you must follow are dictated by .Net, not C #. Thus, all MSDN reference material on marshalling structures using P / Invoke is applied. Personally, it was very useful for me to read the rules for using InAttribute and OutAttribute and the default marshaling behavior for primitive .Net data types.
Here is an example structure from DbgHelp:
[<StructLayout(LayoutKind.Sequential)>] type ADDRESS64 = struct val mutable Offset : DWORD64 val mutable Segment : WORD val mutable Mode : ADDRESS_MODE end
(I have type aliases created for things like DWORD64 to make the code more like the .h files it is based on.)
(I prefer detailed syntax for structs. You can also use light syntax.)
The mutable keyword is optional. This is necessary if you change individual fields after construction.
Built-in arrays are performed as follows:
[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)>] val Reserved : DWORD64[]
You may need to set an array subtype if, by default, marshaling does not suit you.
Inline arrays of characters (aka strings) are executed as follows:
[<DefaultValue>] [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)>] val ModuleName : string
In this case, the CharSet structure field has a value:
[<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>]
You should have a default constructor that sets all fields to "0". But you can have other constructors that initialize the fields in different ways. If you do this, you must mark fields that are not initialized by the constructors with the DefaultValueAttribute attribute:
[<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>] type IMAGEHLP_MODULE64 = struct val SizeOfStruct : DWORD [<DefaultValue>] val BaseOfImage : DWORD64 [<DefaultValue>] val ImageSize : DWORD ... new (_ : bool) = { SizeOfStruct = uint32 (Marshal.SizeOf(typeof<IMAGEHLP_MODULE64>)) } end
You can define enumeration types for flags:
[<Flags>] type SymOptions = | ALLOW_ABSOLUTE_SYMBOLS = 0x00000800u | ALLOW_ZERO_ADDRESS = 0x01000000u ...
If your structures contain unions, you, unfortunately, must use an explicit layout and set several fields for the same position.
[<StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)>] type DEBUG_EVENT = struct [<FieldOffset(0)>] val dwDebugEventCode : DebugEventKind [<FieldOffset(4)>] val dwProcessId : DWORD [<FieldOffset(8)>] val dwThreadId : DWORD [<FieldOffset(12)>] val Exception : EXCEPTION_DEBUG_INFO [<FieldOffset(12)>] val CreateThread : CREATE_THREAD_DEBUG_INFO [<FieldOffset(12)>] val CreateProcessInfo : CREATE_PROCESS_DEBUG_INFO ...
Once you have defined your structures, it's time to define the external functions that use them with the DllImportAttribute:
[<DllImport("Dbghelp.dll")>] extern bool StackWalk64( [<In>] IMAGE_FILE_MACHINE_TYPE MachineType, [<In>] SafeFileHandle hProcess, [<In>] HANDLE hThread, [<In>][<Out>] STACKFRAME64& StackFrame, [<In>][<Out>] Win32.CONTEXT& ContextRecord, [<In>] nativeint ReadMemoryRoutine, [<In>] nativeint FunctionTableAccessRoutine, [<In>] nativeint GetModuleBaseRoutine, [<In>] nativeint TranslateAddress )
Pay attention to the syntax of the structures passed by reference: In and Out, as well as for byref. In this case, to call the function, you must declare the structure as mutable:
let mutable stackframe '= ... let mutable mcontext = ... let result = DbgHelp.StackWalk64 (DbgHelp.IMAGE_FILE_MACHINE_TYPE.I386, ... & amp; StackFrame ", & amp; mcontext, ...
For functions that return strings and their length, you may find StringBuilder useful. This is the same as for C #.
If you use HANDLES, go to Microsoft.Win32.SafeHandles. The Marshaller will send them as HANDLES to their home side, but it can be configured to call CloseHandle on them for you when they are assembled.
You can often do without using any marshal attributes if you understand the default behavior of the marshaller. I found this desirable, because in some cases the performance fell to the floor, if everything was connected with what was not really there.