最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to call std::function within an unmanaged C++ DLL from C#? - Stack Overflow

programmeradmin1浏览0评论

Looking for some pointers (no pun intended...) as to the best approach for this scenario.

So, I have a DLL (Provided Externally) that has the following exported function:

typedef std::function<void (
                    unsigned int msgType,
                    unsigned int size,
                    const char* const value
                    ) > SCallback;

__declspec(dllexport) int StartS( SCallback fnS );

I'm attempting to call this from a C# Console Application, and this is what I am doing:

[DllImport("SeqLib.dll", EntryPoint = "?StartS@SApi@@YAKV?$function@$$A6AXIIPBD@Z@std@@@Z", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int StartS(DCallback cb);

public delegate void DCallback(int msgType, int size, IntPtr value);

public static void MessageReceived(int msgType, int size, IntPtr value)
{
    return 0;
}

DCallback callback = new DCallback(MessageReceived);

StartS(callback);

Running this gives:

 System.AccessViolationException: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Exception aside, is there something wrong with the basic approach above? Can anyone advise on the correct approach to calling an exported function that has a std::function parameter as above?

Thanks in advance.

Looking for some pointers (no pun intended...) as to the best approach for this scenario.

So, I have a DLL (Provided Externally) that has the following exported function:

typedef std::function<void (
                    unsigned int msgType,
                    unsigned int size,
                    const char* const value
                    ) > SCallback;

__declspec(dllexport) int StartS( SCallback fnS );

I'm attempting to call this from a C# Console Application, and this is what I am doing:

[DllImport("SeqLib.dll", EntryPoint = "?StartS@SApi@@YAKV?$function@$$A6AXIIPBD@Z@std@@@Z", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int StartS(DCallback cb);

public delegate void DCallback(int msgType, int size, IntPtr value);

public static void MessageReceived(int msgType, int size, IntPtr value)
{
    return 0;
}

DCallback callback = new DCallback(MessageReceived);

StartS(callback);

Running this gives:

 System.AccessViolationException: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Exception aside, is there something wrong with the basic approach above? Can anyone advise on the correct approach to calling an exported function that has a std::function parameter as above?

Thanks in advance.

Share Improve this question asked Jan 31 at 10:29 MikeMike 5654 silver badges12 bronze badges 5
  • 3 Yes your basic approach is too naive (basically .Net/dllimport can handle "C" API's but not "C++" ones). You need to create an interop layer by hand using managed C++/CLR. The good thing about writing your own interop layer is that this will give you a lot better control over lifetime of objects etc. (which are handled completely different in C++ as compared to .Net). – Pepijn Kramer Commented Jan 31 at 10:32
  • "(basically .Net/dllimport can handle "C" API's but not "C++" ones)" You learn something new everyday. Thankyou very much. – Mike Commented Jan 31 at 10:47
  • 1 You're welcome, maybe this will help you Quick C++/CLI - Learn C++/CLI in less than 10 minutes – Pepijn Kramer Commented Jan 31 at 11:05
  • You need extern "C" to prevent name mangling. @PepijnKramer That is really not correct, it's perfectly possible to use C++ functions, you just need to prevent mangling and not use C++ objects (as C++ can't interact or destroy them) – Charlieface Commented Jan 31 at 12:06
  • @Charlieface extern "C" is still equivalent to writing a wrapper function, but this time in "C" instead of managed C++ (and you have to change from an OO style to a functional style interface where needed) – Pepijn Kramer Commented Jan 31 at 15:53
Add a comment  | 

1 Answer 1

Reset to default -2

You need to use extern "C" to prevent name mangling. And you need to specify the calling convention on the delegate.

You should also use string on the value parameter and marshal it properly

extern "C" {
    __declspec(dllexport) int StartS( SCallback fnS );
}

And in C#

[DllImport("SeqLib.dll", EntryPoint = "StartS", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int StartS(DCallback cb);

[UnamangedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public delegate void DCallback(int msgType, int size, string value);

If the C++ side holds on to the function pointer in global state past the end of the function then you need to keep the delegate alive. For example, put it into a field.

private DCallback _callback = MessageReceived;

private void SetCallback()
{
    _ = StartS(callback);
}

Note that C# cannot handle C++ objects such as smart pointers and classes. You must only use basic types such as numbers, string pointers and array pointers. C# also can't usually free the pointers correctly, you'd need to free them on the C++ side.

发布评论

评论列表(0)

  1. 暂无评论