【问题标题】:access violation upon return from function which does reverse P/Invoke从执行反向 P/Invoke 的函数返回时访问冲突
【发布时间】:2013-03-04 18:04:04
【问题描述】:

我有一个导入非托管 c++ dll 的 c# 托管项目。我想要的是使用我在 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# 应用程序的一部分。

【问题讨论】:

  • 在上面,init 被声明为 void,但你返回的是 true。
  • 当你调用logger("WOOO");时,它真的成功调用了C#代码吗?另外,我假设你上面有 loggerPtr 你真的是指 loggerDelegate??
  • 抱歉,放在这里出错了。我想我现在修好了。
  • 我回滚了你的问题。您的编辑只是将我的代码移动到问题中,使我的答案看起来很愚蠢。

标签: c# c++ logging dll pinvoke


【解决方案1】:

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

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

其他几点:

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

【讨论】:

  • 感谢您的帮助。不幸的是,我仍然遇到问题。我认为这与 LPCTSTR 类型或其他编组问题有关。
  • 尝试最新更新。我在调用约定中发现了另一个不匹配的地方。
  • 我刚刚测试了我的代码。有用。我猜你的接受意味着你发现了同样的事情。
  • 在阅读了this question 之后,我明白为什么现在会出现问题。 “在 CDECL 中,参数以相反的顺序推入堆栈,调用者清除堆栈并通过处理器注册表返回结果(稍后我将其称为“寄存器 A”)。在 STDCALL 中有一个区别,调用者不清除堆栈,被调用者做。”
  • 是的,堆栈清理是问题所在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-25
  • 2020-12-13
  • 1970-01-01
相关资源
最近更新 更多