有没有c# &VB兼容系统.f#中的Void* ?(关闭指向非托管Alloc的指针?)

本文关键字:指针 Alloc VB 系统 有没有 Void 中的 | 更新日期: 2023-09-27 18:09:40

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

给出错误:

"系统。Void'只能在f#

中用作'typeof'

,

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

不能编译。同样的错误,

"系统。Void只能用作typeof…"

f# void* does compile

    extern bool CloseHandle(void* handle);

但是在c#中使用它会抛出设计时转换错误

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

'无法从'void*'转换为'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:避免输出参数。IE, System.Void* realPointer = someAllocIntPtr.ToPointer()是实际指针(对非托管指针的引用),或者换句话说,SecureHandle safeHandle = new SecureHandle(someAllocIntPtr)实际上是"的另一个引用 -一个实际指针",根据MSDN文章,不应该与outref关键字一起传递。

有没有c# &VB兼容系统.f#中的Void* ?(关闭指向非托管Alloc的指针?)

我用下面的方法做了一个小测试:

在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#元数据代码用IntPtr替换void*):

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#中只有在不安全的模式下才有意义。