F #: вызов собственных функций с параметрами настраиваемого типа - PullRequest
2 голосов
/ 06 августа 2020

Я вызываю родную DLL из F #. Это оказалось довольно просто для функций, которые не принимают аргументов или используют только базовые c типы, но у меня много проблем с тем, чтобы заставить мой код работать в противном случае.

Код 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)>].

1 Ответ

2 голосов
/ 06 августа 2020

Неважно, у меня это сработало.

Для всех, кому может быть интересно, мой код F # работал всего с парой изменений:

  1. Я изменил UnmanagedType.LPStr на UnmanagedType.ByValTStr
  2. В объявлении функции extern я украсил HomeQuery[] queries [<In; Out>] (т.е. [<In; Out>]HomeQuery[] queries).

Вот и все.

...