5
    [<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.

turkinator
  • 905
  • 9
  • 25
  • 3
    Why not use IntPtr? – Foole Sep 30 '16 at 03:59
  • 3
    `someAllowIntPtr` is a `System.IntPtr`, but its the result of `Marshal.AllocHGlobal`, which is technically a managed pointer to an unmanaged pointer, which is what `IntPtr.ToPointer()` should resolve, but passing the managed IntPtr of nonmanaged memory throws an error at runtime unless its wrapped in a `SecureHandle()` subclass, which seems like it could present reliability issues with interop\externs affording to the ref-ref\out-out rule on MSDN: https://msdn.microsoft.com/en-us/library/ms182131.aspx – turkinator Sep 30 '16 at 04:23
  • 4
    void* is not a valid managed type. The F# compiler wisely wrote IntPtr to the assembly metadata so you are expected to use IntPtr in your C# program as the error message tells you. You already have one, someAllocIntPtr. Do keep in mind that CloseHandle() only accepts a *handle*. Which is represented as IntPtr or HandleRef or one of the SafeHandle derived class objects in managed code. "someAlloc" does not exactly sound like a handle. The odds that you should *ever* use CloseHandle() yourself are minimal, all system objects are wrapped by .NET wrapper classes. – Hans Passant Sep 30 '16 at 11:51
  • @HansPassant Thanks! VERY informative! My motivation for manually closing handles is to force clean up handle leaks which are known to occur in `OpenProcessToken()` with `TOKEN_DUPLICATE` flag (on UAC-enabled machines [with splitTokens]) – turkinator Sep 30 '16 at 17:43

1 Answers1

1

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#.

  • Solid example and working solution to my problem. Extra points for `CreateToolhelp32Snapshot` and brevity, but I'm not sure it solves the question of whether the type `System.Void*` can be used in F# for other people who might be looking for that answer. – turkinator Oct 05 '16 at 02:15
  • 1
    @epicTurkey: Please see my addition to the answer (About void*) –  Oct 05 '16 at 05:49
  • For anyone that missed it (like I did briefly), @HansPassant 's comment on my OP points out that void* is not a valid managed type, and the f# compiler [wisely] replaces it with IntPtr. – turkinator Oct 14 '16 at 03:49