从C ++调用C ++ DLL可以正常工作,但不能从C#调用

我有一个名为tccdvc.dll的DLL,它是SDK的一部分:

http://www.commell.com.tw/Download/Driver/Industrial%20Peripheral/Driver/MPX-885/MPX-885%20SDK%20(1.2)/SetupCOMMELL%20MPX-885_20100627.rar

DLL是用C ++编写的,检查DLL显示它与链接器版本6.0链接,因此我假设它是用VC ++ 6.0编写的。 DLL没有源代码,只有.lib文件和.h文件。 所有导出的函数都声明为extern“C”(因此没有C ++名称修改)和APIENTRY(所以__stdcall)。

我在Windows XP SP3(32位)上的Visual Studio 2010中编写了一个C ++(非.NET)程序来访问此tccdvc.dll。 这在使用提供的.lib文件和使用LoadLibrary / GetProcAddress时都可以正常工作。 我还编写了一个使用tccdvc.dll的C ++ DLL(我们称之为mywrapper.dll),并且在两个版本中,一个使用.lib文件,另一个使用LoadLibrary / GetProcAddress。 再次,这很好。 这个mywrapper.dll使用__cdecl调用约定。 它包含一个名为InitMyWrapperDLL()的函数,它加载tccdvc.dll。 使用LoadLibrary / GetProcAddress的mywrapper.dll版本的代码如下:

typedef int (APIENTRY *TCCPROCTYPE01)(); HMODULE TCCmodule; TCCPROCTYPE01 Proc_TCC_DVCOpen; extern "C" __declspec(dllexport) void InitMyWrapperDLL () { TCCmodule = LoadLibrary("tccdvc.dll"); Proc_TCC_DVCOpen = (TCCPROCTYPE01)GetProcAddress(TCCmodule, "TCC_DVCOpen"); ... } 

再次,使用C ++前端,这很好。 但是,当从C#(在同一台机器上)调用它时,LoadLibrary(“tccdvc.dll”)调用返回NULL。 在C#中,我正在使用:

 [DllImport("mywrapper.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="InitMyWrapperDLL")] private static extern void InitMyWrapperDLL (); ... InitMyWrapperDLL(); 

当使用提供的tccdvc.lib文件编译mywrapper.dll时,它也会失败,错误代码为0x8007045a(也称为1114),这意味着DLL初始化失败,并且它将mywrapper.dll作为DLL的名称。 事实certificate,失败是因为tccdvc.dll,它通过mywrapper.dll加载。

在C#中使用以下内容也会失败:

 [DllImport("tcc.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="TCC_DVCOpen")] private static extern Int32 TCC_DVCOpen (); ... TCC_DVCOpen(); 

我在声明中也使用了“不安全”,但这没有任何区别。 可预测,因为LoadLibrary()失败,所以它甚至没有到达TCC_DVCOpen()。

为了查明问题,我再次使用mywrapper.dll的LoadLibrary / GetProcAddress版本并将以下代码放入我的C#程序中:

 [DllImport("kernel32.dll")] private static extern IntPtr LoadLibrary (string lpLibFileName); [DllImport("kernel32.dll")] private static extern Int32 GetLastError (); ... IntPtr hdll1 = LoadLibrary("mywrapper.dll"); IntPtr hdll2 = LoadLibrary("tccdvc.dll"); Int32 errcode = GetLastError(); 

在此之后,hdll1具有有效值,但hdll2为0.使用.NET 3.5 Framework时,GetLastError()再次返回0x8007045a,但使用.NET 4.0时,GetLastError()返回0(ERROR_SUCCESS)。

我使用Sysinternals的Process Monitor来获取更多信息,我可以看到tccdvc.dll正在被成功读取和映射。 Process Monitor显示的内容没有任何提示,说明在使用C#时它失败的原因,但在使用C ++时却没有。

有任何想法吗? 谢谢!

我有几点建议:

  • 您可以创建一个C ++ / CLI类库项目,然后在C#项目中引用它。
  • 在我的例子中,我发现UnmanagedFunctionPointerAttribute对于调用至关重要,
  • 有一种情况说,无论我做什么,对.DLL的调用从来没有用过C#,只有.LIB对我有效(这意味着实现了我的第一个建议)。 故障排除告诉我’DLL空间’不适合该特定库。

(关于最后一句话:我绝不是C ++专家,实际上这是我迄今为止唯一的项目。这当然值得更多细节,但我从来不知道也没有知识找到问题的根源,但它得到了修复,因为我只需要它 。感谢您指出任何错误/更好的解释。)

以下是适用于我的问题的一些解决方案:

如何使用C#的C回调? 如果你想查看我的所有代码,只需要提供指向.DLL的链接。

此外,一些在这个领域对我有帮助的工具:

  • 用于查看DLL导出的PE Explorer
  • PInvoke Interop Assistant有助于创建PInvoke声明

愿原力与你同在 :-)

因此,当从C ++应用程序调用时,您的C代码可以正常工作,但是当从.NET二进制文件调用时,相同的代码会失败,在LoadLibrary中它会失败…

这只是一种预感,所以我不确定是否描述了您的情况,但LoadLibrary有一些复杂的标准来解析相对DLL名称的路径,这可能因某些参数而异。 我不知道他们都是我的头脑。 可能是.NET二进制文件的EXE清单中的某些内容阻止它根据这些规则查找DLL。 您可以尝试调整PATH环境变量或使用绝对路径加载DLL。 (您可以使用GetModuleFileName等来查找自己代码的绝对路径…)