F#:使用自定义类型参数调用本机函数
F#: Calling native functions with custom type parameters
我正在从 F# 调用本机 DLL。对于不带参数或仅带基本类型的函数,事实证明它非常简单,但我很难让我的代码正常工作。
C++代码为:
struct CatDef
{
uint8_t cat;
uint8_t arrogance;
CatDef() : cat(0), arrogance(0) {}
};
struct HomeInfo
{
enum { MAX_CATDEF = 5 };
enum {
LONE_CAT = 0x01,
};
CatDef cd[MAX_CATDEF];
uint8_t notoriety;
uint8_t etc;
HomeInfo()
: notoriety(0), etc(0)
{
memset(cd, 0, sizeof(cd));
}
size_t howBig() const
{
size_t hb = 0;
for (; hb < MAX_CATDEF; ++hb)
if (!cd[hb].cat)
break;
return hb;
}
};
struct HomeQuery
{
char addr[ADDR_LENGTH]; // ADDR_LENGTH is 1024
char verifiedAddr[ADDR_LENGTH];
HomeInfo homeData;
unsigned int id;
Status st; // Status is an enum
HomeQuery()
: homeData()
, id(0)
, st(Unprocessed)
{
memset(addr, 0, sizeof(addr));
memset(verifiedAddr, 0, sizeof(verifiedAddr));
}
};
__declspec(dllexport) bool GetHomeInfo(
HomeQuery* queries, size_t qCount, void (*callback)(HomeQuery*, size_t) = 0);
到目前为止,我有 F# 的这个:
[<Struct>]
type CatDef =
[<DefaultValue>]
val mutable cat : uint8
[<DefaultValue>]
val mutable arrogance : uint8
[<Struct>]
type HomeInfo =
[<DefaultValue; MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)>]
val mutable cd : CatDef array
[<DefaultValue>]
val mutable notoriety : uint8
[<DefaultValue>]
val mutable etc : uint8
[<Struct>]
type HomeQuery =
[<DefaultValue; MarshalAs(UnmanagedType.LPStr, SizeConst = 1024)>]
val mutable addr : string
[<DefaultValue; MarshalAs(UnmanagedType.LPStr, SizeConst = 1024)>]
val mutable verifiedAddr : string
[<DefaultValue>]
val mutable homeData : HomeInfo
[<DefaultValue>]
val mutable id : uint32
[<DefaultValue>]
val mutable st : int
type HomeQueryCallback = delegate of (HomeQuery array * uint) -> unit
module private Wrapper =
[<DllImport(
"Homes.dll",
EntryPoint = "?GetHomeInfo@People@@NA_WBYUHomeQuery@1@IP6AX0I@Z@Z",
CallingConvention = CallingConvention.Cdecl)>]
extern bool GetHomeInfo(HomeQuery[] queries, uint qCount, HomeQueryCallback callback)
[<RequireQualifiedAccess>]
module HomeLib =
let getHomeInfo queries = Wrapper.GetHomeInfo (queries, uint queries.Length, null)
关于类型编组的规则,我一直在参考https://docs.microsoft.com/en-us/dotnet/standard/native-interop/type-marshaling#default-rules-for-marshaling-common-types。
当我调用 HomeLib.getHomeInfo
并进入本机库 (Homes.dll) 时,我看到 HomeQuery
数组中第一个(也是唯一一个)项目的字段包含乱码信息。 (qCount
和 callback
未损坏,并包含预期值。)如您所见,我尝试将 MarshalAs
属性添加到几个字段,但这没有任何积极影响。也没有用 [<StructLayout(LayoutKind.Sequential)>]
.
装饰任何类型
没关系,我已经解决了。
对于可能关心的任何人,我的 F# 代码只需进行几处更改即可工作:
- 我把
UnmanagedType.LPStr
改成了UnmanagedType.ByValTStr
- 在
extern
函数声明中,我用[<In; Out>]
修饰了HomeQuery[] queries
(即[<In; Out>]HomeQuery[] queries
)。
就这些了。
我正在从 F# 调用本机 DLL。对于不带参数或仅带基本类型的函数,事实证明它非常简单,但我很难让我的代码正常工作。
C++代码为:
struct CatDef
{
uint8_t cat;
uint8_t arrogance;
CatDef() : cat(0), arrogance(0) {}
};
struct HomeInfo
{
enum { MAX_CATDEF = 5 };
enum {
LONE_CAT = 0x01,
};
CatDef cd[MAX_CATDEF];
uint8_t notoriety;
uint8_t etc;
HomeInfo()
: notoriety(0), etc(0)
{
memset(cd, 0, sizeof(cd));
}
size_t howBig() const
{
size_t hb = 0;
for (; hb < MAX_CATDEF; ++hb)
if (!cd[hb].cat)
break;
return hb;
}
};
struct HomeQuery
{
char addr[ADDR_LENGTH]; // ADDR_LENGTH is 1024
char verifiedAddr[ADDR_LENGTH];
HomeInfo homeData;
unsigned int id;
Status st; // Status is an enum
HomeQuery()
: homeData()
, id(0)
, st(Unprocessed)
{
memset(addr, 0, sizeof(addr));
memset(verifiedAddr, 0, sizeof(verifiedAddr));
}
};
__declspec(dllexport) bool GetHomeInfo(
HomeQuery* queries, size_t qCount, void (*callback)(HomeQuery*, size_t) = 0);
到目前为止,我有 F# 的这个:
[<Struct>]
type CatDef =
[<DefaultValue>]
val mutable cat : uint8
[<DefaultValue>]
val mutable arrogance : uint8
[<Struct>]
type HomeInfo =
[<DefaultValue; MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)>]
val mutable cd : CatDef array
[<DefaultValue>]
val mutable notoriety : uint8
[<DefaultValue>]
val mutable etc : uint8
[<Struct>]
type HomeQuery =
[<DefaultValue; MarshalAs(UnmanagedType.LPStr, SizeConst = 1024)>]
val mutable addr : string
[<DefaultValue; MarshalAs(UnmanagedType.LPStr, SizeConst = 1024)>]
val mutable verifiedAddr : string
[<DefaultValue>]
val mutable homeData : HomeInfo
[<DefaultValue>]
val mutable id : uint32
[<DefaultValue>]
val mutable st : int
type HomeQueryCallback = delegate of (HomeQuery array * uint) -> unit
module private Wrapper =
[<DllImport(
"Homes.dll",
EntryPoint = "?GetHomeInfo@People@@NA_WBYUHomeQuery@1@IP6AX0I@Z@Z",
CallingConvention = CallingConvention.Cdecl)>]
extern bool GetHomeInfo(HomeQuery[] queries, uint qCount, HomeQueryCallback callback)
[<RequireQualifiedAccess>]
module HomeLib =
let getHomeInfo queries = Wrapper.GetHomeInfo (queries, uint queries.Length, null)
关于类型编组的规则,我一直在参考https://docs.microsoft.com/en-us/dotnet/standard/native-interop/type-marshaling#default-rules-for-marshaling-common-types。
当我调用 HomeLib.getHomeInfo
并进入本机库 (Homes.dll) 时,我看到 HomeQuery
数组中第一个(也是唯一一个)项目的字段包含乱码信息。 (qCount
和 callback
未损坏,并包含预期值。)如您所见,我尝试将 MarshalAs
属性添加到几个字段,但这没有任何积极影响。也没有用 [<StructLayout(LayoutKind.Sequential)>]
.
没关系,我已经解决了。
对于可能关心的任何人,我的 F# 代码只需进行几处更改即可工作:
- 我把
UnmanagedType.LPStr
改成了UnmanagedType.ByValTStr
- 在
extern
函数声明中,我用[<In; Out>]
修饰了HomeQuery[] queries
(即[<In; Out>]HomeQuery[] queries
)。
就这些了。