Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a struct as argument to C function called from F#

Tags:

c#

.net

f#

In C# C struct: s can be passed as arguments to C functions called with DllImport using the StructLayout attribute

http://msdn.microsoft.com/en-us/library/aa984739%28VS.71%29.aspx

How does this work in F#? Apparently it is possible somehow, but I didn't find any example code by Google and I don't know the language very well yet

like image 996
Atte Saarela Avatar asked Jan 05 '10 23:01

Atte Saarela


People also ask

How do you pass a struct as a function argument?

How to pass structure as an argument to the functions? Passing of structure to the function can be done in two ways: By passing all the elements to the function individually. By passing the entire structure to the function.

Can you pass a struct into a function in C?

To pass a structure variable to a function all we have to do is write the name of the variable and it will pass a copy of the structure variable.

Are structs passed by reference C?

Passing struct by referenceYou can also pass structs by reference (in a similar way like you pass variables of built-in type by reference). We suggest you to read pass by reference tutorial before you proceed. During pass by reference, the memory addresses of struct variables are passed to the function.

How is a struct passed in C?

A structure can be passed to any function from main function or from any sub function. Structure definition will be available within the function only. It won't be available to other functions unless it is passed to those functions by value or by address(reference).


1 Answers

I recently spent a lot of time figuring this out. Here's a brain dump... Hopefully it'll get you started.

First, realize that for the most part, the rules you have to follow are dictated by .Net, not by C#. So all of the MSDN reference material on marshalling structures with P/Invoke apply. Personally, I found it very helpful to read the rules regarding the usage of InAttribute and OutAttribute, and the default marshaling behavior for the primitive .Net data types.

Here's 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 set up for things like DWORD64 to make the code more similar to .h files that it is based on.)

(I prefer the verbose syntax for structs. You could use the light syntax too.)

The mutable keyword is optional. It is needed if you'll modify individual fields after construction.

In-line arrays are done like this:

[<MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)>] val Reserved : DWORD64[]

You may have to set the array subtype if the default marshaling doesn't suit you.

In-line character arrays (aka strings) are done like this:

[<DefaultValue>] [<MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)>] val ModuleName : string

In which case, the structure's CharSet field matters:

[<StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)>]

You must have a default constructor that sets all fields to "0". But you can have other constructors that initialize fields differently. If you do this, you must mark the fields that are not initialized by the constructors with the DefaultValueAttribute:

[<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 structs contain unions, you unfortunately have to use explicit layout and set multiple fields to have 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 get your structures defined, it's time to define the external functions that use them with 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
)

Notice the syntax for structures passed by reference: In and Out, and the & for byref. In this case, to call the function, you must declare the structure mutable:

let mutable stackframe' = ... let mutable mcontext = ... let result = DbgHelp.StackWalk64( DbgHelp.IMAGE_FILE_MACHINE_TYPE.I386, ... &stackframe', &mcontext, ...

For functions returning strings and their length, you may find StringBuilder useful. This is the same as for C#.

If you're using HANDLES, check out Microsoft.Win32.SafeHandles. The marshaller will send these as HANDLES to the native side, but it can be set up to call CloseHandle on them for you when they're collected.

You can oftentimes get by without applying any MarshalAsAttributes, if you understand the marshaller's default behavior. I found this desireable because in some cases performance fell through the floor if things were marshalled that didn't really need to be.

like image 110
Jason Avatar answered Sep 25 '22 02:09

Jason