F# 中是否有 C# & VB 兼容的 System.Void*? (关闭指向非托管 Alloc 的指针?)

Is there a C# & VB compatible System.Void* in F#? (To close a pointer to nonmanaged Alloc?)

    [<DllImport("kernel32")>]
    extern bool CloseHandle(System.Void* handle);
    //System.Void also throws same error
    //extern bool CloseHandle(System.Void handle); 

给出错误:

'System.Void' can only be used as 'typeof' in F#

但是

    extern bool CloseHandle(typeof<System.Void> handle);

不编译。同样的错误,

"System.Void can only be used as typeof..."

F# void* 编译

    extern bool CloseHandle(void* handle);

但在 C# 中使用它会引发设计时转换错误

public void CloseBeforeGarbageCollection(IntPtr someAllocIntPtr)
{
    //test unmanaged block
    var result = CloseHandle(someAllocIntPtr.ToPointer());
    return result;
}

'cannot convert from 'void*' to 'System.IntPtr'

虽然传递托管 IntPtr 将编译

//test managed IntPtr
var result = CloseHandle(someAllocIntPtr); //throws error at runtime

但是当 someAllocIntPtrMarshal.AllocHGlobal 的结果时,它会抛出运行时异常 External component has thrown an exception.。 据我了解,发生这种情况是因为 someAllocIntPtr(作为 Marshal.AllocHGlobal 的结果)在技术上是指向非托管指针的托管指针,与普通 IntPtr 不同。 Peter Ritchie 在对他的回答的回复中指出了这一点:System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception

避免此运行时异常的唯一方法是将句柄包装在 SecureHandle() 子类中,但我认为这违反了 MSDN 上的 ref-ref\out-out 规则:CA1021: Avoid out parameters。 IE,System.Void* realPointer = someAllocIntPtr.ToPointer() 是 ACTUAL 指针(对非托管指针的引用),或者换句话说,SecureHandle safeHandle = new SecureHandle(someAllocIntPtr) 实际上是“ 的引用 - 的另一个引用 - 实际指针",根据 MSDN 文章,不应使用 outref 关键字传递。

我通过以下方式做了一点测试:

在 f# 程序集(dll 库)中,我有以下模块:

module MyWin32
open System
open System.Runtime.InteropServices

[<DllImport("kernel32")>]
extern bool CloseHandle(IntPtr handle);

[<DllImport("kernel32")>]
extern IntPtr CreateToolhelp32Snapshot(IntPtr flag, IntPtr procId);

在引用上述库的 F# 控制台程序中,我有:

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)

在引用上述库的 C# 控制台程序中,我有:

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();
    }
  }
}

F# 和 C# 测试均按预期编译和运行。希望对您有所帮助。

关于void*:

将上面显示的 f# 程序集 MyWin32 更改为以下内容并将 IntPtr 替换为 void* 仍然适用于 F# 和 C# 客户端,无需任何其他修改(MyWin32 的 C# 元数据代码将 void* 替换为 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);

所以以上小测试的结论是可以在F#中使用void*作为IntPtr的有效替代

我认为应该只在 C# 的不安全 {} 部分使用 IntPtr.ToPointer(),因为指针只在 C# 的不安全模式下才有意义。