c# - 从执行反向 P/Invoke 的函数返回时发生访问冲突

标签 c# c++ logging dll pinvoke

我有一个 C# 托管项目,它导入一个非托管 C++ DLL。我想要的是使用我在 C# 代码中编写的日志记录功能来进行日志记录。所以我在 C# 端添加了以下内容:

public struct API 
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void FunctionPointer(string msg);

    [DllImport("mydll.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    unsafe public static extern void setLoggerPointer(IntPtr fctPointer);

    [DLLImport("mydll.dll", SetLastError = true)]
    [return: MarshalAsAttribute(UnmanagedType.I1)] unsafe public static extern bool init();
}

public unsafe class MyInterface
{
    public MyInterface()
    {
        API.FunctionPointer loggerDelegate;
        loggerDelegate = new API.FunctionPointer(Logger.LogMessage);
        IntPtr loggerPtr = Marshal.GetFunctionPointerForDelegate(loggerDelegate);
        API.setLoggerPointer(loggerPtr);

        if (API.init()) 
        {
           //do stuff
        }
    }
}

这是我的 Logger 类定义:

public static class Logger
{
    public static void LogMessage(string msg)
    {
        Console.WriteLine(msg);
        fileStream.Write(msg);
    }
}

我在 C++ 端 header 上有以下内容:

#define MY_C_API extern "C" __declspec(dllexport);

MY_C_API __declspec(dllexport) void __stdcall setLoggerPointer( void *fctPointer(LPCTSTR msg) );
MY_C_API __declspec(dllexport) bool __stdcall init();

在 C++ 源代码中:

//global variable
void *(*logger)(LPCTSTR msg);

void __stdcall setLoggerPointer( void *fctPointer(LPCTSTR msg) )
{
    logger = fctPointer;
}

bool __stdcall init()
{
    logger("WOOO");

    return true;  //I'm getting the AccessViolation right here
}

我在从 mfc100.dll 中的 atlsimpstr.h Release() 函数中的 init() 函数返回时收到 System.AccessViolationException

有人知道我做错了什么吗?我看到的关于这类事情的所有问题都是如何在没有访问冲突的情况下执行反向 P/Invoke 但它对我来说工作正常,只是从其他调用返回时它被搞砸了,好像那部分内存现在被视为 C# 应用程序的一部分。

最佳答案

问题是回调的调用约定不匹配。您的 native 代码期望回调为 cdecl,但托管代码将其声明为 stdcall。您可以在托管代码或 native 代码中修复此问题。为简单起见,我将展示如何在托管代码中修复它。

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FunctionPointer(string msg);

其他几点:

  1. 不要将 SetLastError 参数设置为 true,因为您的函数不会设置 Win32 最后一个错误。
  2. 不要在此代码中的任何地方使用unsafe。作为一般规则,您应该避免 unsafe 并且您只是不需要它。

关于c# - 从执行反向 P/Invoke 的函数返回时发生访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15208003/

相关文章:

c# - 我应该将 ILogger、ILogger<T>、ILoggerFactory 还是 ILoggerProvider 作为库使用?

c# - 执行引擎异常 : Attempting to JIT compile method 'System.Collections.Generic.Dictionary'

c# - C# 中这两个变量赋值有什么区别?

c++ - 将自由函数绑定(bind)为类成员函数

ruby-on-rails - 有谁知道 Ruby On Rails 的任何跨平台 GUI 日志查看器?

python - 复制的 ZODB 数据库上出现错误消息 "no handlers could be found for logger ZODB.FileStorage"

c# - 在 Asp .net 核心 Web Api 中接受 x-www-form-urlencoded

c# - 查找数组中的最短元素

c++ - 原生C++项目中,如何统计每个类的实例数和高峰时间消耗的内存

c++ - 如何正确捆绑 libstdc++.so.*?