I'm attempting to P/Invoke a C library from F#, and have encountered a peculiar issue. I have a module containing all my extern functions. The underlying C library has two functions with the same name, but different arguments. This, of course, is not allowed in an F# module.
module C =
open System.Runtime.InteropServices
[<DllImport("libc", CallingConvention = CallingConvention.Cdecl)>]
extern int setValue(nativeint source, int value)
[<DllImport("libc", CallingConvention = CallingConvention.Cdecl)>]
extern int setValue(nativeint source, string value)
// the previous function declaration cause the following compile-time error:
// Duplicate definition of value 'setValue'
Is there some special way to work around this? I can not alter the C library.
The EntryPoint attribute should work (e.g. with an ordinal), if MSDN can be trusted (haven't tested in F#). Name your imported functions e.g. setValueInt() and setValueString().
Related
Here is code in log.h file :
struct SUCC_CODE{
static const int RECOGNIZER_OK = 0;
};
The above piece of code in log.h file throwing compiler error:
Type name does not allow storage class to be specified
Struct members may not be static. Remove that specifier, and the compiler should stop complaining. This question explains that it is a valid specifier in C++.
C doesn’t allow you to use static within a struct. It’s not even clear what that would mean in a C struct.
I'm working with some F# code that uses platform invoke. One of the APIs I'm using returns a handle. Instead of using a nativeint, I've implemented my own SafeHandle (specifically SafeHandleMinusOneIsInvalid.) This makes working with the module containing the pinvoke signature a little clunky. Here is an example:
type MySafeHandle() =
inherit SafeHandleZeroOrMinusOneIsInvalid(true)
override this.ReleaseHandle() =
NativeMethods.FreeHandle(base.handle)
true
module NativeMethods =
[<DllImport("mylibrary.dll")>]
extern void GetHandle([<Out>]MySafeHandle& handle)
[<DllImport("mylibrary.dll")>]
extern void FreeHandle(nativeint handle)
This won't compile because the module and the class recursively reference each other, which doesn't work. If I move the module above MySafeHandle, then GetHandle won't see the SafeHandle.
I can't move the platform invoke methods inside of MySafeHandle since it appears that extern methods in F# must be in modules (even though the compiler won't stop you from trying to put them in a class).
It also appears that F#'s recursive types don't work between a module and a class, just classes.
Is there a solution to this problem that does not require declaring two different modules? Ideally I'd like to keep all of my platform invoke code organized into one module.
Well I know of one, because I had the same problem myself.
The thing is, it's kinda ugly I guess:
It involves a static reference that you set to the imported function later in the module.
type MySafeHandle() =
inherit SafeHandleZeroOrMinusOneIsInvalid(true)
static let freeHandle = ref Unchecked.defaultof<_>
static member internal SetFreeHandleRef value = freeHandle := value
override this.ReleaseHandle() =
!freeHandle base.handle
true
module NativeMethods =
[<DllImport("mylibrary.dll")>]
extern void GetHandle([<Out>]MySafeHandle& handle)
[<DllImport("mylibrary.dll")>]
extern void FreeHandle(nativeint handle)
MySafeHandle.SetFreeHandleRef FreeHandle
I'm working on a platform invoke call from F#, and I am getting a compiler error I really can't make that much sense out of. First, let me show the C signature of what I am doing:
int Foo(
ULONG_PTR *phHandle,
DWORD flags
);
In F#, I think the correct way to invoke this natively is as so:
[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
(
[<Out>]nativeint& phHandle,
uint32 flags
)
If I try to call this in a class, I get a compilation error when calling it like so:
type Class1() =
[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
(
nativeint& phHandle,
uint32 flags
)
member this.Foo() =
let mutable thing = nativeint 0
APlatformInvokeCall(&thing, 0u) |> ignore
thing
The error is:
A type instantiation involves a byref type. This is not permitted by the rules of Common IL.
Weirdly, when I do this all in a module, the compilation errors go away:
module Module1 =
[<DllImport("somedll.dll")>]
extern int APlatformInvokeCall
(
nativeint& phHandle,
uint32 flags
)
let Foo() =
let mutable thing = nativeint 0
APlatformInvokeCall(&thing, 0u) |> ignore
thing
Why does this compile as a module, but not as a class?
I don't think it's valid to define an extern method within a class in F#.
If you pull up the F# 3.0 language specification and search for DllImport, near the bottom is a table listing some special attributes and how they can be used. The text for [<DllImport>] says:
When applied to a function definition in a module, causes the F# compiler to ignore the implementation of the definition, and instead compile it as a CLI P/Invoke stub declaration.
That seems to indicate that it's only valid to declare extern methods (that use [<DllImport>]) on functions defined in a module; it doesn't say anything about class members though.
I think you're running into a compiler bug. Please submit this code to fsbugs#microsoft.com so they can fix the error message emitted by the compiler -- it should really be giving you an error about defining an extern method in a class since that's not allowed by the language spec.
Whether this is a bug not withstanding, maybe this is what's going on: If APlatformInvokeCall were considered a static member function, that member have a single argument of tuple type. Tuples are compiled into objects of generic type (see here, at the bottom, or 5.1.3 in the spec). In this case that tuple is
System.Tuple<nativeint&, uint32>
But ECMA 335 II.9.4 says you can't instantiate generic types at byref types. This explains the error reported.
This explanation fits the fact mentioned above that Class1 works (well, compiles) if you modify the extern declaration and call to take instead a single argument. It also fits the fact that the module version works, since in that version there is no considering APlatFormInvokeCall a member function.
The simple solution is to check the spec, here is the class definition grammar:
type type-name pat_opt as-defn)opt =
class
class-inherits-decl_opt
class-function-or-value-defns_opt
type-defn-elements
end
then we have
class-function-or-value-defn :
attributes_opt staticopt let rec_opt function-or-value-defns
attributes_opt staticopt do expr
which doesn't allow extern.
and
type-defn-element :
member-defn
interface-impl
interface-signature
which isn't what you want either.
As a result, we can see that using extern as you are trying to use it can't be done inside a class.
I've got a C# extern declaration that goes like this:
[DllImport("something.dll")]
public static extern ReturnCode GetParent(IntPtr inRef, out IntPtr outParentRef);
How to translate that to F#?
You can try something like the code below. I don't know what ReturnCode is, so the code below expects it is an integer. For any more complex type, you'll need to use [<Struct>] attribute as in the answer referenced by A-Dubb.
type ReturnCode = int
[<System.Runtime.InteropServices.DllImport("something.dll")>]
extern ReturnCode GetParent(System.IntPtr inRef, System.IntPtr& outParentRef);
To call the function, you'd write something like this:
let mutable v = nativeint 10
let n = GetParent(nativeint 0, &v)
BTW: Could you also post a sample C code that implements the function in something.dll? If yes, we could try running the solution before sending an answer...
Maybe this similar question will point you in the right direction. Looks like he used attributes at the parameter level for "in" and "out" F# syntax for P/Invoke signature using MarshalAs
For anyone else trying to use F# with EnvDte via PInvoke this may help:
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern unit CreateBindCtx(System.IntPtr inRef, IBindCtx& outParentRef);
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern unit GetRunningObjectTable(System.IntPtr inRef, IRunningObjectTable& outParentRef);
which apparently is slightly incorrect, but appears to work. the definition should be:
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern int CreateBindCtx(System.IntPtr inRef, IBindCtx& outParentRef);
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern int GetRunningObjectTable(System.IntPtr inRef, IRunningObjectTable& outParentRef);
I have a module which contains an interface to a native DLL; it looks like this:
// nvtt.dll binding module
module private NvTextureTools =
type NvttInputOptions = IntPtr
[<DllImport("nvtt", CallingConvention = CallingConvention.Cdecl)>]
extern NvttInputOptions nvttCreateInputOptions()
[<DllImport("nvtt", CallingConvention = CallingConvention.Cdecl)>]
extern void nvttDestroyInputOptions(NvttInputOptions)
[<DllImport("nvtt", CallingConvention = CallingConvention.Cdecl)>]
extern void nvttSetInputOptionsAlphaMode(NvttInputOptions, AlphaMode alphaMode)
[<DllImport("nvtt", CallingConvention = CallingConvention.Cdecl)>]
extern void nvttSetInputOptionsGamma(NvttInputOptions, float inputGamma, float outputGamma)
[<DllImport("nvtt", CallingConvention = CallingConvention.Cdecl)>]
extern void nvttSetInputOptionsWrapMode(NvttInputOptions, WrapMode mode)
(there are 5x more functions, but this should give the general idea).
Is there any way to specify the DllImport parameters just once? As far as I understand, I can't inherit from DllImport (it's sealed, and anyway I don't think it would work if it was not), and I can't use reflection to add the necessary attributes because I need them at compilation time.
I could make a brand new class with P/Invoke methods using reflection, but this will make calling them cumbersome.
Any thoughts?
I don't know about F#, but in C# you can do something like:
static const string DllName = "nvtt";
[DllImport(DllName, other params...)]
some function signature
[DllImport(DllName, other params...)=
some function signature
So that way the actual string is only declared once - the DllImport attributes themselves all still look a lot alike, but it makes changing things easier. I think you could do the same with CallingConvention, but I've never tried it with an enum.
Just in case you are using Visual Studio - it is possible to create a T4 template and generate all those nasty attributes. This is not an F# or VS specific solution however, any code generating tool would work.