从返回P / Invoke的函数返回时访问冲突

时间:2013-03-04 18:04:04

标签: 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 ++侧标题上有以下内容:

#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#应用程序的一部分。

1 个答案:

答案 0 :(得分:2)

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

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

其他一些观点:

  1. 不要将SetLastError参数设置为true,因为您的函数没有设置Win32最后一个错误。
  2. 请勿在此代码中的任何位置使用unsafe。作为一般规则,您应该避免使用unsafe,而您根本不需要它。