c# - 导出非托管函数指针时发生访问冲突

标签 c# .net pointers c++-cli dllexport

过去 4 个小时我一直在尝试解决一个非常神秘的问题。

我正在为 Notepad++ 编写一些插件。要实现语法高亮,必须导出这样一个函数:

//this function is exported via exports.def file
LexerFactoryFunction SCI_METHOD GetLexerFactory(unsigned int index)
{
    return (index == 0) ? RTextLexer::LexerFactory : nullptr;
}

在哪里,

LexerFactoryFunction is typedef ILexer *(*LexerFactoryFunction)();
#define SCI_METHOD __stdcall

我已经设法让这个东西与 C++ 完美地工作,但是插件的另一部分是用 C# 编写的,所以我尝试使用 Fody Costura NuGet 包合并两者(以便 CLI .dll 嵌入到主.dll ),但是没有成功。

我尝试过的:

public ref class RTextLexerCliWrapper
{
public:
    delegate ILexer * GetLexerFactoryDelegate();

IntPtr GetLexerFactory()
{
    return System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_lexerFactoryPtr);
}

RTextLexerCliWrapper();
private:
    GetLexerFactoryDelegate ^ _lexerFactoryPtr;
    GCHandle gch;

    ~RTextLexerCliWrapper();
};

RTextLexerCliWrapper::RTextLexerCliWrapper()
{
    _lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
    gch = GCHandle::Alloc(_lexerFactoryPtr);
}


RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
    gch.Free();
}

此 CLI 包装器在我的主 .dll 中引用如下:

static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();

[DllExport(CallingConvention = CallingConvention.Cdecl)]
static IntPtr GetLexerFactory(uint index)
{            
     return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}

那么发生的事情是,我的 .net 函数确实被调用,cli 包装器函数也被调用,并且确实返回了一个函数指针。但是,任何调用该函数指针的尝试都会导致访问冲突。这意味着指针的类型错误或我当前缺少的其他内容。我用 void *、StdCall 等尝试了 .net 导出函数的无数变体。所有这些都会导致相同的问题。

有没有其他方法可以返回 C++ 类的函数指针?还是我做错了什么?

提前致谢!

最佳答案

所以我终于找到了解决问题的方法。

第一步是使用正确的调用约定导出函数:

    static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();

    [DllExport(CallingConvention = CallingConvention.StdCall)]
    static IntPtr GetLexerFactory(uint index)
    {            
        return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
    }

本例中的惯例必须是 StdCall。否则堆栈指针无效,因此出现异常。

现在为了返回 C++ 实例的函数指针,事情有点棘手。

我正在静态存储 CLI 包装器类的一个实例,这样它就不会被 GC 处理。 (_lexerWrapper)。

此实例有一个名为 GetLexerFactory 的函数,它返回 C++ 实例的函数指针(然后其他一些 .dll 使用它来获取某个对象的实际实例)。

CLI Wrapper 类如下所示:

    public ref class RTextLexerCliWrapper
    {
    public:

        delegate ILexer * GetLexerFactoryDelegate();

        IntPtr GetLexerFactory()
        {
            return System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(_lexerFactoryPtr);
        }

        RTextLexerCliWrapper();

    private:
        GetLexerFactoryDelegate ^ _lexerFactoryPtr;
        GCHandle gch;

        ~RTextLexerCliWrapper();
    };

其中 ILexer * 是我们稍后要返回的对象的类型。

    RTextLexerCliWrapper::RTextLexerCliWrapper()
    {
        _lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
        gch = GCHandle::Alloc(_lexerFactoryPtr);
    }


    RTextLexerCliWrapper::~RTextLexerCliWrapper()
    {
        gch.Free();
    }

因此,我们在这里管理的是,通过 .NET 导出一个能够返回纯 C++ 对象的函数指针。

关于c# - 导出非托管函数指针时发生访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29356316/

相关文章:

c# - 为什么这个对 C# 方法的 jquery ajax GET 调用不起作用?

c# - 处理实时数据

c# - 通过表达式树编译动态实例方法,具有私有(private)和 protected 访问权限?

c 赋值中的类型不兼容,指针有问题?

c# - 这很有用,但我不确定为什么会起作用

c# - 将元素插入到MongoDB中的嵌套数组中

c# - 检查集合中的重复项

c# - 使用 XSD 正确验证 XML 文档

c - 分配从函数返回的指针

通过将指针传递给 c 中的函数来创建二维数组