2015-04-21 33 views
1

我有一個非託管DLL只導出一個返回類的新實例的C樣式工廠方法(這裏簡化看起來很簡單)。當成員函數未被導出時,從C#調用C++ native/unmanaged成員函數

hello.h

#if defined(HWLIBRARY_EXPORT) // inside DLL 
# define HWAPI __declspec(dllexport) 
#else // outside DLL 
# define HWAPI __declspec(dllimport) 
#endif 

struct HelloWorld{ 
    public: 
    virtual void sayHello() = 0; 
    virtual void release() = 0; 
}; 
extern "C" HWAPI HelloWorld* GetHW(); 

HELLO.CPP

#include "hello.h" 

struct HelloWorldImpl : HelloWorld 
{ 
    void sayHello(){ 
    int triv; 
    std::cout<<"Hello World!"; 
    std::cin>>triv; 
}; 
void release(){ 
    this->HelloWorldImpl::~HelloWorldImpl(); 
}; 
HelloWorld* GetHW(){ 
HelloWorld* ptr = new HelloWorldImpl(); 
return ptr; 
}; 

現在,我可以使用dllimport的訪問GetHW(),但有訪問返回的「結構的成員函數的方式'...即,說你好,並釋放?

我也被困在同樣的問題。這個問題曾被問過一陣子。我評論了它的任何更好的解決方案,但沒有得到任何答覆。所以,重新發布它。

當我GOOGLE時,能夠找出兩個解決方案。

解決方案1:爲現有的dll公開C樣式的所有成員函數。我不能這樣做,因爲它是第三方DLL。解決方案2:編寫一個託管的C++ dll,暴露本機C++ dll的功能,以後可以在您的C#dll中使用它。這裏有很多類/功能。所以,創建將需要大部分時間。

我從下面的鏈接中獲得了上述解決方案。 How To Marshall

請讓我知道是否有任何更好的解決方案比以上兩種解決方案?

我有C++解決方案的源代碼。但是,我雖然不是碰C++的DLL。如果有可能在C#中做到這一點,那就太好了。

如果沒有其他選擇,我需要遵循指定的兩個解決方案中的任何一個。

+0

查看[在.NET應用程序中使用非託管C++庫(DLL)](http://www.codeproject.com/Articles/14180/Using-Unmanaged-C-Libraries-DLLs-in-NET-應用程序) – WaeCo

+0

您正在使用此代碼重新創建COM。但它不是COM兼容的,所以不能直接從C#程序中使用。 C++/CLI包裝器需要正確執行。 –

回答

4

C++代碼使用抽象類由Visual C++編譯器實現的方式。 http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx。該內存佈局是「固定的」,因爲它用於實現COM接口。內存中結構的第一個成員將是一個指向包含函數指針的vtable的指針。所以對於一個

struct HelloWorldImpl : public HelloWorld 
{ 
    public: 
    int value1; 
    int value2; 
} 

在內存中的 「真實」 的佈局將是:

其中vtbl是:

struct HelloWorldVtbl 
{ 
    void *sayHello; 
    void *release; 
} 

只是爲了做一個完全反應的緣故,我寫這個簽名的例子:

struct HelloWorld { 
    public: 
    virtual int sayHello(int v1, int v2, int v3) = 0; 
    virtual void release() = 0; 
}; 

C#代碼:

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern IntPtr GetHW(); 

[StructLayout(LayoutKind.Sequential)] 
struct HelloWorldVtbl 
{ 
    public IntPtr sayHello; 
    public IntPtr release; 
} 

你的功能void Func(void)int Func(int, int, int),但實際上他們有一個隱藏的參數,this,所以你可以把它們寫爲:

int sayHello(HelloWorld*, int, int, int); 
void release(HelloWorld*); 

所以在C#中的委託是

[UnmanagedFunctionPointer(CallingConvention.ThisCall)] 
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3); 

[UnmanagedFunctionPointer(CallingConvention.ThisCall)] 
public delegate void VoidMethodVoid(IntPtr ptr); 

然後你可以使用

IntPtr ptr = GetHW(); 
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0); 

HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl)); 

Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32)); 
int res = sayHello(ptr, 1, 2, 3); 
Console.WriteLine(res); 

VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid)); 
release(ptr); 
+0

非常感謝您給出詳細的答案。我會盡力實施。 :) – chandrakanth

+0

@chandrakanth請注意,'this-> HelloWorldImpl ::〜HelloWorldImpl()'會泄漏內存:請參閱http://stackoverflow.com/a/1036031/613130。你應該'刪除這個;' – xanatos