导出非托管函数指针时发生访问冲突
Access Violation while exporting an unmanaged function pointer
过去 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 包装函数也被调用了,函数指针确实被 returned。但是,任何调用该函数指针的尝试都会导致访问冲突。这意味着指针的类型错误或我当前缺少的其他内容。我用 void *, StdCall
等尝试了 .net 导出函数的无数变体。所有这些都会导致同样的问题。
有没有其他方法可以return C++ class 的函数指针?还是我做错了什么?
提前致谢!
所以我终于找到了解决问题的方法。
第一步是使用正确的调用约定导出函数:
static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();
[DllExport(CallingConvention = CallingConvention.StdCall)]
static IntPtr GetLexerFactory(uint index)
{
return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}
本例中的约定必须是 StdCall。否则堆栈指针无效,因此出现异常。
现在为了 return C++ 实例的函数指针,事情有点棘手。
我正在静态存储 CLI 包装器 class 的一个实例,这样它就不会被 GC 处理。 ( _lexerWrapper
).
此实例有一个名为 GetLexerFactory 的函数,它 return 是 C++ 实例的函数指针(然后其他一些 .dll 使用它来获取某个对象的实际实例)。
CLI 包装器 class 如下所示:
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 * 是我们稍后将 returning 的对象类型。
RTextLexerCliWrapper::RTextLexerCliWrapper()
{
_lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
gch = GCHandle::Alloc(_lexerFactoryPtr);
}
RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
gch.Free();
}
所以,我们在这里管理的是,通过 .NET 导出一个函数指针,它能够 return 一个纯 C++ 对象。
过去 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 包装函数也被调用了,函数指针确实被 returned。但是,任何调用该函数指针的尝试都会导致访问冲突。这意味着指针的类型错误或我当前缺少的其他内容。我用 void *, StdCall
等尝试了 .net 导出函数的无数变体。所有这些都会导致同样的问题。
有没有其他方法可以return C++ class 的函数指针?还是我做错了什么?
提前致谢!
所以我终于找到了解决问题的方法。
第一步是使用正确的调用约定导出函数:
static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper();
[DllExport(CallingConvention = CallingConvention.StdCall)]
static IntPtr GetLexerFactory(uint index)
{
return (index == 0) ? _lexerWrapper.GetLexerFactory() : IntPtr.Zero;
}
本例中的约定必须是 StdCall。否则堆栈指针无效,因此出现异常。
现在为了 return C++ 实例的函数指针,事情有点棘手。
我正在静态存储 CLI 包装器 class 的一个实例,这样它就不会被 GC 处理。 ( _lexerWrapper
).
此实例有一个名为 GetLexerFactory 的函数,它 return 是 C++ 实例的函数指针(然后其他一些 .dll 使用它来获取某个对象的实际实例)。
CLI 包装器 class 如下所示:
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 * 是我们稍后将 returning 的对象类型。
RTextLexerCliWrapper::RTextLexerCliWrapper()
{
_lexerFactoryPtr = gcnew GetLexerFactoryDelegate(&RTextLexer::LexerFactory);
gch = GCHandle::Alloc(_lexerFactoryPtr);
}
RTextLexerCliWrapper::~RTextLexerCliWrapper()
{
gch.Free();
}
所以,我们在这里管理的是,通过 .NET 导出一个函数指针,它能够 return 一个纯 C++ 对象。