从C ++调用C ++ DLL工作,但不是从C#

从C ++调用C ++ DLL工作,但不是从C#

问题描述:

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

I have a DLL called tccdvc.dll which is part of an SDK available here:

http://www.commell.com .tw /下载/驱动程序/工业%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文件。所有导出的函数都被声明为externC(所以没有C ++名称)和APIENTRY(so __stdcall)。

The DLL was written in C++ and examining the DLL shows it was linked with linker version 6.0, so I assume it was written with VC++ 6.0. The DLL does not come with source code, only a .lib file and a .h file. All exported functions are declared as extern "C" (so no C++ name mangling) and with APIENTRY (so __stdcall).

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

I have written a C++ (not .NET) program in Visual Studio 2010 on Windows XP SP3 (32-bit) to access this tccdvc.dll. This works fine, both when using the provided .lib file and when using LoadLibrary/GetProcAddress. I have also written a C++ DLL (let's call it mywrapper.dll) that uses tccdvc.dll and, again, in two versions, one using the .lib file, the other using LoadLibrary/GetProcAddress. Again, this works fine. This mywrapper.dll uses the __cdecl calling convention. It contains a function called InitMyWrapperDLL() which loads the tccdvc.dll. The version of mywrapper.dll that uses LoadLibrary/GetProcAddress has code like this:

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#中,我使用:

Again, using a C++ front-end, this works fine. However, when calling it from C# (on the same machine), the LoadLibrary("tccdvc.dll") call returns NULL. In C#, I am using:

[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加载。

When compiling mywrapper.dll using the provided tccdvc.lib file instead, it fails as well, with error code 0x8007045a (also known as 1114), meaning the DLL initialisation failed, and it gives mywrapper.dll as the name of the DLL. It turns out that the failure is because of tccdvc.dll, which gets loaded through mywrapper.dll.

在C#中使用以下命令也失败:

Using the following in C# fails as well:

[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()。

I have also used "unsafe" in the declaration, but that did not make any difference. Predictable, because LoadLibrary() fails, so it does not even get to TCC_DVCOpen().

要确定问题,我使用了LoadLibrary / GetProcAddress版本的mywrapper.dll再次将以下代码放在我的C#程序中:

To pinpoint the problem, I used the LoadLibrary/GetProcAddress version of mywrapper.dll again and put the following code in my C# program:

[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)。

After this, hdll1 has a valid value, but hdll2 is 0. When using the .NET 3.5 Framework, GetLastError() returns 0x8007045a again, but when using .NET 4.0, GetLastError() returns 0 (ERROR_SUCCESS).

我使用Sysinternals的Process Monitor获取更多信息,我可以看到tccdvc.dll正在被成功读取和映射。没有任何进程监视器显示给我任何提示,为什么使用C#时失败,但不是使用C ++时。

I used Process Monitor by Sysinternals to get more information and I can see that tccdvc.dll is being read and mapped successfully. Nothing that Process Monitor displays gives me any hint as to why it fails when using C#, but not when using C++.

任何想法?
谢谢!

Any ideas? Thanks!

我有几个建议:


  • 您可以创建一个C ++ / CLI类libary项目,然后在C#项目中引用它。

  • 在我的情况下,我发现UnmanagedFunctionPointerAttribute是对于通话来说至关重要,

  • 有一种情况,无论我做什么,对.DLL的调用从来没有从C#工作,只有.LIB为我工作(这意味着实现我的第一个建议)。故障排除导致我的DLL空间不适合该特定库。

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

(Regarding last sentence : in no way I am a C++ expert, actually that's the only project I did so far. This certainly deserves more details but I never knew nor had the knowledge to find the source of the problem, but it got fixed because I simply needed it. Thanks for pointing any mistake / better explanations.)

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

Here is some of the solution that applied to my problem :

如何使用C#中的C回调
有一个链接到.DLL,如果你想看到我的所有代码只是问。

How to use a C callback from C#? There is the link to the .DLL, if you want to see all my code just ask.

另外,一些工具,对我有帮助此网域:

Also, a few tools that have been helpful to me in this domain :

  • PE Explorer for viewing DLL exports
  • PInvoke Interop Assistant helps in creating PInvoke declarations

可以与你在一起: - )

May the force be with you :-)