views:

353

answers:

4

From what I have learned, to use P/Invoke in F#, the function signature has to be declared first using DllImport like this:

[<DllImport("kernel32.dll", EntryPoint="CopyFile")>]
extern bool copyfile(char[] lpExistingFile, char[] lpNewFile, bool bFailIfExists);

That's all good when the DLL name is known at compile time. How do I interface with an unmanaged C/C++ DLL if I can only discover the name at runtime?

+1  A: 

This problem is not specific to F#. You would need to build a proxy DLL in C++/CLI which will be responsible for loading necessary dll using LoadLibrary function from win API.

Vitaliy Liptchinsky
+4  A: 

The native APIs for this are LoadLibrary() and GetProcAddress(). Not sure if there are managed versions of that, but a Google search found something interesting:

Type-safe Managed wrappers for kernel32!GetProcAddress.

Opening line of that is:

Pinvoke is cool in managed code, but sometimes you need to get straight at kernel32!GetProcAddress. For example, maybe you need dynamic control over which unmanaged dll you want to load.

Sounds like what you want to do, no? Rest of the article here.

asveikau
+5  A: 

Alternatively, you can come up with a solution that generates PInvoke methods dynamically.

open System
open System.Reflection
open System.Reflection.Emit
open System.Runtime.InteropServices

let assembly = AppDomain.CurrentDomain.DefineDynamicAssembly (new AssemblyName ("PInvokeLibrary"), AssemblyBuilderAccess.Run)
let module_builder = assembly.DefineDynamicModule ("PInvokeLibrary")

let define_dynamic_pinvoke<'d when 'd :> Delegate> (name, library) =
  let invoke = typeof<'d>.GetMethod ("Invoke") (* signature of delegate 'd *)
  let parameters =
    invoke.GetParameters ()
    |> Array.map (fun p -> p.ParameterType)
  let type_builder = module_builder.DefineType (name, TypeAttributes.Public)
  let method_builder =
    type_builder.DefinePInvokeMethod (
      name,
      library,
      MethodAttributes.Public ||| MethodAttributes.Static ||| MethodAttributes.PinvokeImpl,
      CallingConventions.Standard,
      invoke.ReturnType,
      parameters,
      CallingConvention.Winapi,
      CharSet.Ansi)
  method_builder.SetImplementationFlags (method_builder.GetMethodImplementationFlags () ||| MethodImplAttributes.PreserveSig)
  let result_type = type_builder.CreateType ()
  let pinvoke = result_type.GetMethod (name)
  Delegate.CreateDelegate (typeof<'d>, pinvoke) :?> 'd

let beep = define_dynamic_pinvoke<Func<int, int, bool>> ("Beep", "kernel32.dll")

beep.Invoke (800, 100)
Jb Evain
I guess it would be better if you could pass the signature of a native F# function to define_dynamic_pinvoke, like define_dynamic_pinvoke<(int * int -> bool)> ("Beep", "kernel32.dll"), but I admit I don't know how to achieve that.
Jb Evain
A: 

I guess it would be better if you could pass the signature of a native F# function to define_dynamic_pinvoke, like define_dynamic_pinvoke<(int * int -> bool)> ("Beep", "kernel32.dll"), but I admit I don't know how to achieve that.

You can rewrite the sample as:

let beep = define_dynamic_pinvoke<delegate of (int * int) -> bool> ("Beep", "kernel32.dll")

js mouret
Nope, doesn't compile.
Jb Evain