[<DllImport("kernel32")>]
extern bool CloseHandle(System.Void* handle);
//System.Void also throws same error
//extern bool CloseHandle(System.Void handle);
gives the error:
'System.Void' can only be used as 'typeof' in F#
but
extern bool CloseHandle(typeof<System.Void> handle);
does not compile. Same error,
"System.Void can only be used as typeof..."
F# void*
does compile
extern bool CloseHandle(void* handle);
but using it in C# throws a design-time convert error
public void CloseBeforeGarbageCollection(IntPtr someAllocIntPtr)
{
//test unmanaged block
var result = CloseHandle(someAllocIntPtr.ToPointer());
return result;
}
'cannot convert from 'void*' to 'System.IntPtr'
though passing the managed IntPtr will compile
//test managed IntPtr
var result = CloseHandle(someAllocIntPtr); //throws error at runtime
but when someAllocIntPtr
is the result of Marshal.AllocHGlobal
, it throws a runtime exception External component has thrown an exception.
.
As I understand it, this happens because someAllocIntPtr
(as the result of Marshal.AllocHGlobal) is technically a managed pointer TO an unmanaged pointer, not the same as a normal IntPtr. This is noted by Peter Ritchie in a reply to his answer here: System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception
The only way to avoid this runtime Exception is to wrap the handle in a SecureHandle()
subclass, but I think that's against the ref-ref\out-out
rule on MSDN: CA1021: Avoid out parameters.
IE, System.Void* realPointer = someAllocIntPtr.ToPointer()
is the ACTUAL pointer (a reference to an unmanaged pointer), or in other words, SecureHandle safeHandle = new SecureHandle(someAllocIntPtr)
is actually "a reference of - another reference of - an actual pointer", and should not be passed with out
or ref
keywords, according to the MSDN article.
I have done a little test in the following way:
In an f# assembly (dll library) I have the following module:
module MyWin32
open System
open System.Runtime.InteropServices
[<DllImport("kernel32")>]
extern bool CloseHandle(IntPtr handle);
[<DllImport("kernel32")>]
extern IntPtr CreateToolhelp32Snapshot(IntPtr flag, IntPtr procId);
In an F# Console Program that has a reference to the above lib I have:
open System
open System.Runtime.InteropServices
open MyWin32
[<EntryPoint>]
let main argv =
let handle = CreateToolhelp32Snapshot(IntPtr(4), IntPtr(System.Diagnostics.Process.GetCurrentProcess().Id))
printfn "%A" handle
printfn "%b" (CloseHandle handle)
// A HGlobal should always be released by FreeHGlobal
let intPtr = Marshal.AllocHGlobal(1024)
Marshal.FreeHGlobal(intPtr)
And in a C# Console program that references the above lib I have:
using System;
namespace CSTest
{
class Program
{
static void Main(string[] args)
{
var handle = MyWin32.CreateToolhelp32Snapshot(new IntPtr(4), new IntPtr(System.Diagnostics.Process.GetCurrentProcess().Id));
Console.WriteLine(handle);
Console.WriteLine(MyWin32.CloseHandle(handle));
Console.ReadLine();
}
}
}
Both the F# and C# test compiles and runs as expeceted. I hope this will help you.
About void*:
Changing f# assembly MyWin32 shown above to the following with substution of IntPtr to void* still works for the F# and C# client without any other modfications (The C# metadata code of MyWin32 substitutes void* with IntPtr):
module MyWin32
open System
open System.Runtime.InteropServices
[<DllImport("kernel32")>]
extern bool CloseHandle(void* handle);
[<DllImport("kernel32")>]
extern void* CreateToolhelp32Snapshot(IntPtr flag, IntPtr procId);
So the conclusion of the above small tests is that you can use void* in F# as a valid substitution for IntPtr.
I think one should only use IntPtr.ToPointer() in an unsafe {} section in C#, as pointers only make sense in unsafe mode in C#.