How to pass C++ Struct To C# DLL?

2019-08-13 11:55发布

问题:

I've a C# DLL below like. I want to use this C# DLL in C++ Builder.

But I don't know C# Struct and C++ Struct marshalling:

using RGiesecke.DllExport;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace TestLibrary
{

    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct MyStruct
    {
        public int X;
        public int Y;
    }


    public class Class1
    {
        [DllExport("DoSomething", CallingConvention = CallingConvention.StdCall)]
        public static int DoSomething(int x, int y, ref MyStruct myStruct)
        {
            myStruct.X = 50;
            myStruct.Y = 75;
            return x + y;
        }

    }
}

I want to pass "myStruct" parameter from C++ Builder below like.

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  struct MyStruct
  {
    int X;
    int Y;
   };

  int (__stdcall *DoSomething)(int,int,MyStruct);


  HINSTANCE dllHandle = NULL;
  dllHandle = LoadLibrary( edtdllPath->Text.c_str());
  if(dllHandle == NULL) return;
  int status = -1;

  try
  {
    DoSomething =(int (__stdcall *)(int,int,MyStruct)) GetProcAddress(dllHandle, "DoSomething");
  }
  catch(Exception &Err)
  {
    ShowMessage(Err.Message);
  }

  if(DoSomething != NULL)
  {
    try
    {
      MyStruct myStruct;
      status = DoSomething(5,5,myStruct);
      String strStatus = status;

      ShowMessage(strStatus);
      ShowMessage(myStruct.X);
      ShowMessage(myStruct.Y);
    }
    catch(EAccessViolation &err)
    {
     ShowMessage(err.Message);
    }
  }

}

When I debug code,myStruct.X and myStruct.Y value is wrong.

Where is my wrong ?

回答1:

The C# project declares the struct parameter like this:

ref MyStruct myStruct

That marshals as a pointer to the struct. In C++ that means

MyStruct*

So change the declaration of the function pointer variable to be like so:

int (__stdcall *DoSomething)(int,int,MyStruct*);

And use the same type when you cast:

DoSomething =(int (__stdcall *)(int,int,MyStruct*)) GetProcAddress(dllHandle, "DoSomething");

Note that a typedef would serve you better here to avoid repeating yourself. And note also that GetProcAddress does not raise exceptions. It signals errors by returning NULL. You don't check for errors properly.

When you call the function pass the address of the struct:

status = DoSomething(5,5,&myStruct);

It's also a little pointless to declare status and initialise it to -1, but then overwrite that value later. It would be more idiomatic to declare and initialise it like this:

int status = DoSomething(5,5,&myStruct);


回答2:

You are not passing your struct as pointer to c#, yet in c# you told that it would be a pointer (ref).