2011-01-21 192 views
3

我在非託管的Visual C++中編寫了一個DLL,並且在使用C#和C++應用程序時遇到了一些問題。下面是原型在C++ DLL的樣子:從託管代碼調用非託管DLL函數時出錯

extern "C" __declspec(dllexport) int WINAPI ZBNConnect(UCHAR dev, LPARAM hWnd, ZBCallbackFn rfn, ZBCallbackFn nfn, int DevType, byte * DevAddr, ZBCallbackFn dfn); 

我的C#應用​​程序可以鏈接到的功能,沒有問題,但是,當它試圖調用一個拋出異常的函數:

catch (Exception e) { /* ... */ } 

e.Message =「未將對象引用設置爲對象的實例。」

奇怪的是,如果我將WINAPI帶出DLL中的原型,並重新編譯,C#應用程序將調用該函數而不會有任何問題。不幸的是,WINAPI必須保留,因爲這是如何在C++應用程序中定義函數。

public delegate int ZBNConnectDelegate(uint dev, IntPtr hWnd, USBCallbackDelegate rfn, NotifyCallbackDelegate nfn, uint DevType, byte[] DevAddr, ZBdebugCallbackDelegate dfn); 
public ZBNConnectDelegate ZBNConnect; 

procName = "ZBNConnect"; 
fUintPtr = Kernel32.GetProcAddress(dllHandle, procName); 

if (fUintPtr == UIntPtr.Zero) 
{ 
    throw new ArgumentException(procName); 
} 

fIntPtr = unchecked((IntPtr)(long)(ulong)fUintPtr); 
ZBNConnect = (ZBNConnectDelegate)Marshal.GetDelegateForFunctionPointer(fIntPtr, typeof(ZBNConnectDelegate)); 

如何修改C#應用程序得到這個工作:

功能在C#應用程序一樣,這是目前的原型?謝謝。

編輯:附加信息

靜態鏈接([DllImport...])不是因爲這取決於哪個硬件附接至系統,支持連接的硬件是在運行時加載的不同DLL的選項。這兩個DLL都具有相同的API調用。

+1

這不是DLL地獄,是完全不同的 – 2011-01-21 23:07:20

+0

你試圖把外部原型? – bratao 2011-01-21 23:17:32

+1

那麼,你如何初始化`ZBNConnect`並使其指向非託管函數呢?正如所寫,它將是`null`。爲什麼不使用P/Invoke(`[DllImport] static extern`)? – 2011-01-22 00:00:39

回答

0

事實證明,將WINAPI添加到函數聲明和定義導致DLL中的函數名稱被損壞。不幸的是,需要WINAPI才能保持與已部署應用程序的兼容性。修復是增加一個額外的出口到鏈接器:

#pragma comment(linker, "/EXPORT:[email protected]") 
3

東西基本上是錯的。你聲明瞭一個委託,好像該函數是一個回調。根本看起來不像回調,它看起來像你應該用[DllImport]聲明的東西。爲了使它像你一樣工作,你必須鎖定LoadLibrary和GetProcAddress()。 [DllImport]在底層做了什麼。我沒有看到你使用它。

0

WINAPI宏改變了調用約定。

嘗試

[UnmanagedFunctionPointer(CallingConvention = CallingConvention.StdCall] 
public delegate Int32 ZBNConnectDelegate(Byte dev, IntPtr hWnd, USBCallbackDelegate rfn, NotifyCallbackDelegate nfn, Int32 DevType, Byte[] DevAddr, ZBdebugCallbackDelegate dfn); 
0

extern "C" __declspec(dllexport) int WINAPI ZBNConnect(UCHAR dev, LPARAM hWnd, ZBCallbackFn rfn, ZBCallbackFn nfn, int DevType, byte * DevAddr, ZBCallbackFn dfn);

public delegate int ZBNConnectDelegate(uint dev, IntPtr hWnd, USBCallbackDelegate rfn, NotifyCallbackDelegate nfn, uint DevType, byte[] DevAddr, ZBdebugCallbackDelegate dfn);

C++ UCHARs(DEV)爲1字節的值和C#UINT的是4,你搗毀堆棧時,你的本地通話,出於某種原因,WINAPI正在讓你擺脫困境。此外,像這樣使用委託而不是執行普通的P/Invoke DllImport很可能會導致你的問題,因爲你無法自定義如何編組。

0

如果你能檢測到從C#端運行時正確的DLL,我會使用兩個的DllImport declaractions:

[DllImport("Dll1.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "ZBNConnect")] 
extern static int ZBNConnect1(...) 
[DllImport("Dll2.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "ZBNConnect")] 
extern static int ZBNConnect2(...) 

public static int ZBNConnect(...) 
{ 
    if (UseDll1) 
     return ZBNConnect1(...); 
    return ZBNConnect2(...); 
} 

注意

的P/Invoke是通過動態加載流傳。如果你永遠不會調用ZBNConnect1,Dll1.dll永遠不會被加載,反之亦然。

更新

新增的EntryPoint =到的DllImport。