2011-01-21 190 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。