简体   繁体   中英

How to call dll C++ func which takes parameter and returns struct in C#?

I have a struct defined like this:

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

and a two functions in 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;
}

Next I have a wrapper in C# which is initialized with dll name:

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 is defined as follows:

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

When I call Get2() then the result is correct, list.number is 1 and pointer can be unmarshaled. But after Get1() the returned struct is sth like: list.number = 0, list.ptr = 48, which is obviously wrong.

Both methods are different only in lack of argument for Get2. I checked in debugger that string parameter is passed correctly to dll. Then struct clist_t is correctly filled in dll, but upon return, when control is passed back from dll to C#, the returned struct is somehow corrupted.

Could you give me some hint what is going wrong? Why struct is returned correctly only if there are no parameters?

EDIT: I'm using extern "C" when declaring functions in dll.

GetDelegateForFunctionPointer() assumes __stdcall calling convention but your functions have __cdecl (and you'll have unexpected behaviors when you return something or you pass more than one parameter).

Change native API to:

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

OR decorate your delegate to instruct framework to use right calling convention:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ArgsList ArgsDelegate();

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

If also clist_t.ptr is a function pointer then don't forget to decorate that delegate too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM