关于dllimport:F#将Null传递给非托管导入的DLL

F# Passing Nulls to Unmanaged Imported DLL

在F#中,我正在使用外部DLL(在本例中为SDL图形库),正在按如下方式导入所需的方法...

1
2
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, int& access, int& w, int& h)

这工作正常,我可以使用以下方法成功调用该方法...

1
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, &access, &w, &h)

问题在于,本机SDL方法为许多指针参数接受空值。 在某些情况下(功能类似于重载方法),这是必需的。 我找不到从传递空值的F#调用这些方法的任何方法。

例如,这失败并显示为"没有null作为适当值"

1
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, null, &w, &h)

我了解了[AllowNullLiteral]属性,但似乎只能将其应用于定义的类型,而不能应用于导入的DLL中使用的预定义类型。

有什么办法可以做到吗?


如果要指定空值,则需要使用"原始指针",它们由类型nativeintnativeptr< T >表示。

1
2
3
4
5
6
7
8
9
10
11
12
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, nativeint access, int& w, int& h)

// Call without null
let access = 42
let pAccess = NativePtr.stackalloc<int> 1
NativePtr.write pAccess access
SQL_QueryTexture( textTexture, &format, NativePtr.toNativeInt pAccess, &w, &h )
let returnedAccess = NativePtr.read pAccess

// Call with null
SQL_QueryTexture( textTexture, &format, null, &w, &h )

注意:请注意stackalloc。 在堆栈上分配内存非常方便,因为您无需显式释放它,但是一旦退出当前函数,指向它的指针将变为无效。 因此,只有在确定该函数不会存储该指针并稍后尝试使用时,才能将此类指针传递给外部函数。

如果您需要将指针传递到不会在任何地方使用的实际堆内存,则需要Marshal.AllocHGlobal。 但不要忘记释放! (要不然 :-)

1
2
3
4
5
let access = 42
let pAccess = Marshal.AllocHGlobal( sizeof<int> )
NativePtr.write (NativePtr.ofNativeInt pAccess) access
SQL_QueryTexture( textTexture, &format, pAccess, &w, &h )
Marshal.FreeHGlobal( pAccess )