2015-03-30 75 views
4

我一直在尝试过去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包装函数也被调用,并且函数指针确实被返回。但是,任何尝试调用该函数指针都会导致访问冲突。这意味着指针的类型是错误的或者我目前丢失的其他东西。我已经尝试了无数的.net导出函数的变体void *, StdCall等。所有的结果都是相同的问题。

是否有任何其他方式来返回一个C++类的函数指针?或者,我是否做了完全错误的事情?

在此先感谢!

+0

这是一个完整的动物园,你把老虎放在猴屋里。 – 2015-03-30 23:18:19

+0

@HansPassant它是这样,我不能更改API。 – FailedDev 2015-03-31 05:47:12

回答

1

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

第一步是用正确的调用约定导出功能:

static RTextLexerCliWrapper _lexerWrapper = new RTextLexerCliWrapper(); 

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

在这种情况下,公约必须是STDCALL。否则堆栈指针将失效,因此例外。

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

我静态存储CLI包装类的一个实例,以便它不会得到GCed。 (_lexerWrapper)。

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

的CLI包装类看起来是这样的:

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++对象。