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

时间:2012-07-05 13:48:49

标签: c# c++ dll loadlibrary

我有一个名为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时都可以正常工作。我还编写了一个C ++ DLL(让我们称之为mywrapper.dll),它使用tccdvc.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。事实证明,失败的原因是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 ++时。

有什么想法吗? 谢谢!

2 个答案:

答案 0 :(得分:1)

我有几点建议:

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

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

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

How to use a C callback from C#? 如果你想查看我的所有代码,只有.DLL链接。

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

愿力量与你同在: - )

答案 1 :(得分:0)

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

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