2015-03-30 63 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++對象。