использование вложенных структурных указателей в C / C ++ DLL в C # - PullRequest
0 голосов
/ 22 мая 2018

скажем, у нас есть следующий код в dll, написанный на C-lang, где я пытаюсь отобразить некоторые функции, определенные в dll, как указатели функций, отобразить их фактические функции, я иду по следующей ссылке, чтобы добраться до здесь https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays

Dlltest.h

typedef struct VersionInfo
{
  UINT uMajor;
  UINT uMinor;
  UINT uMRevision;
} STRUCT_VERSION_INFO;

typedef struct DllTestFPStruct
{
  int(*Close) (void);
  int(*Init) (STRUCT_VERSION_INFO *sVersInfo);
  int(*RegisterClient) (int id);
} STRUCT_DLL_TEST_FP_STRUCT;

typedef struct DllTestMgr
{
  STRUCT_DLL_TEST_FP_STRUCT *psItf;
  int iDummy;
} STRUCT_DLL_TEST_MGR;

extern "C"
{ __declspec(dllexport) void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP); }

Dlltest.c

static int Close(void);
static int Init(STRUCT_VERSION_INFO *sVersInfo);
static int RegisterClient(int id);

STRUCT_DLL_TEST_FP_STRUCT sFP =
{
  &Close,
  &Init,
  &RegisterClient,
};

DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{ psFP->psItf = &sFP; }

static int Close(void)
{ printf("Closed called.\n"); }

static int Init(STRUCT_VERSION_INFO *sVersInfo)
{ printf("Init called.\n");}

static int RegisterClient(STRUCT_VERSION_INFO *sVersInfo)
{  printf("RegisterClient called.\n");}

Теперь я хочу написать AC# приложение, которое использует эту DLL, особенно оно должно использовать функцию «GetDllTestFP», которая отображает указатели функций на их актуальную функцию.Прямо сейчас мое C # -приложение выглядит следующим образом:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int FP_DLL_TEST_CLOSE(ref VersionInfo sVersInfo);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int FP_DLL_TEST_INIT(ref VersionInfo sVersInfo);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int FP_DLL_TEST_RC(ref VersionInfo sVersInfo);

    [StructLayout(LayoutKind.Sequential)]
    public struct DllTestFPStruct
    {
        public FP_DLL_TEST_CLOSE Close;   
        public FP_DLL_TEST_INIT Init;
        public FP_DLL_TEST_RC RegisterClient;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct DllTestMgr
    {
        public IntPtr psItf;
        public int iDummy;
    }

    [DllImport("DllTestC.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern void GetDllTestFP(ref DllTestMgr ps);

    static void Main(string[] args)
    {
        VersionInfo vInfo = new VersionInfo();
        DllTestFPStruct dllFPs = new DllTestFPStruct();
        DllTestMgr dllTest = new DllTestMgr();
        IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(dllFPs));
        Marshal.StructureToPtr(dllFPs, buffer, false);
        dllTest.psItf = buffer;
        GetDllTestFP(ref dllTest);  // Funtionpointers are not mapped, still null
        dllFPs.Close(ref vInfo);
  }

Проблема в том, что функции не отображаются на их исполнительные функции в DLL.Любая идея, как я могу достичь своей цели?

Спасибо

1 Ответ

0 голосов
/ 22 мая 2018

Код C #:

DllTestMgr dllTest = new DllTestMgr();
GetDllTestFP(ref dllTest);
DllTestFPStruct dllFPs = (DllTestFPStruct)Marshal.PtrToStructure(dllTest.psItf, typeof(DllTestFPStruct));

VersionInfo vInfo = new VersionInfo();
dllFPs.Close(ref vInfo);

Вам не нужно выделять dllTest.psItf, поскольку в GetDllTestFP вы делаете:

DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{
    psFP->psItf = &sFP;
}

Таким образом, вы копируете адрес из sFP.

Обратите внимание, что в целом это плохая идея, потому что вы предоставляете "клиенту" прямой доступ к вашим данным (структура sFP).Альтернатива состоит в том, что клиент передает память (как вы писали ранее), а затем вы делаете:

(*psFP->psItf) = sFP; 

(но затем не забудьте освободить выделенную память!)

Третья альтернатива,C-сторона выделяет блок памяти через общий распределитель (тот, который может использоваться C #, поэтому здесь нет malloc / new), а затем C # должен освободить его.

неправильное решение -

STRUCT_DLL_TEST_FP_STRUCT sFP2 = sFP;
psFP->psItf = &sFP2;

Время жизни sFP2 заканчивается, когда метод возвращается.psFP->psItf теперь указывает на кусок стека, который больше не существует. не делайте этого!

Ах ... как написано @Hans Passant, в зависимости от того, кто выделяет память, GetDllTestFP может быть ref или out.Если память выделена C #, то она должна быть ref, если она не выделена (как сейчас) или выделена C ++, тогда out в порядке, и вы сэкономите на маршалинге в одном направлении.

...