如何调用C#中带有参数并返回结构体的dll C++函数

本文关键字:返回 参数 结构体 函数 C++ dll 何调用 调用 | 更新日期: 2023-09-27 18:28:50

我有一个定义如下的结构:

typedef struct
{
  int number;
  void *ptr;
}clist_t;

和dll中的两个函数:

DLL_API clist_t GetArgs1(const wchar_t* const name)
{
  static clist_t arg;
  arg.number = 1;
  arg.ptr = sth(name);
  return arg;
}
DLL_API clist_t GetArgs2()
{
  static clist_t arg;
  arg.number = 1;
  arg.ptr = sth2();
  return arg;
}

接下来我有一个C#中的包装器,它用dll名称初始化:

public class DllWrapper
{
  private int m_dllHndl;
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)] // <-- added as suggested in comment
  private delegate ArgsList ArgsDelegate();
  [UnmanagedFunctionPointer(CallingConvention.Cdecl)] // <-- added as suggested in comment
  private delegate ArgsList ArgsStringDelegate([MarshalAs(UnmanagedType.LPWStr)] string name);
  private ArgsStringDelegate GetFunc1 = null;
  private ArgsDelegate GetFunc2 = null;
  public DllWrapper(string dllPathName)
  {
    m_dllHndl = Win32APIWrapper.LoadLibrary(dllPathName);
    if(m_dllHndl == 0)
    {
      throw new Exception("Could not load the library: " + dllPathName);
    }
    GetFunc1 = (ArgsStringDelegate)findFunc("GetArgs1", typeof(ArgsStringDelegate));
    GetFunc2 = (ArgsDelegate)findFunc("GetArgs2", typeof(ArgsDelegate));
  }
  private Delegate findFunc(string name, Type t)
  {
    int func = Win32APIWrapper.GetProcAddress(m_dllHndl, name);
    if (func == 0)
    {
       throw new Exception("Function not found in the library: " + name);
    }
    return Marshal.GetDelegateForFunctionPointer((IntPtr)func, t);
  }
  public ArgsList Get1(string name)
  {
    ArgsList list = GetFunc1(name);
    return list; // <-- here list is corrupted
  }
  public ArgsList Get2()
  {
    ArgsList list = GetFunc2();
    return list; // <-- here list is correct
  }
}

ArgsList的定义如下:

[StructLayout(LayoutKind.Sequential)]
public struct ArgsList
{
    public int number;
    public IntPtr ptr;
};

当我调用Get2()时,结果是正确的,list.number是1,指针可以被解组。但在Get1()之后,返回的结构类似于:list.number=0,list.ptr=48,这显然是错误的。

两种方法的不同之处仅在于缺少Get2的参数。我在调试器中检查了字符串参数是否正确传递到dll。然后结构clist_t被正确地填充在dll中,但在返回时,当控制从dll传递回C#时,返回的结构在某种程度上被损坏了。

你能告诉我出了什么问题吗为什么只有在没有参数的情况下才能正确返回结构

EDIT:我在dll中声明函数时使用extern "C"

如何调用C#中带有参数并返回结构体的dll C++函数

GetDelegateForFunctionPointer()假定__stdcall调用约定,但函数具有__cdecl(当您返回某些内容或传递多个参数时,会出现意外行为)。

将本机API更改为:

DLL_API clist_t __stdcall GetArgs1(const wchar_t* const name);
DLL_API clist_t __stdcall GetArgs2();

OR装饰您的代理以指示框架使用正确的调用约定:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ArgsList ArgsDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ArgsList ArgsStringDelegate(
    [MarshalAs(UnmanagedType.LPWStr)] string name);

如果clist_t.ptr也是一个函数指针,那么也不要忘记修饰该委托。