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
但是当 someAllocIntPtr
是 Marshal.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 文章,不应使用 out
或 ref
关键字传递。
我通过以下方式做了一点测试:
在 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# 的不安全模式下才有意义。
[<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
但是当 someAllocIntPtr
是 Marshal.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 文章,不应使用 out
或 ref
关键字传递。
我通过以下方式做了一点测试:
在 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# 的不安全模式下才有意义。