Calling C++ DLL from C++ works, but not from C#

2019-06-16 10:18发布

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

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

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).

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");
  ...
}

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();

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.

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();

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().

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();

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).

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!

2条回答
Lonely孤独者°
2楼-- · 2019-06-16 10:48

I have a few suggestions for you :

  • You could create a C++/CLI class libary project, then reference it in your C# project.
  • In my case I found out that UnmanagedFunctionPointerAttribute was essential to the call,
  • There was a case that whatever I would do, the call to the .DLL never worked from C#, only the .LIB worked for me (which means implementing my first suggestion). Troubleshooting led me that the 'DLL space' was not appropriate for that particular library.

(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 :

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 :

May the force be with you :-)

查看更多
孤傲高冷的网名
3楼-- · 2019-06-16 10:55

So, your C code works when called from a C++ application, but the identical code fails when called from a .NET binary, where it fails in LoadLibrary...

This is just a hunch so I'm not sure if describes your circumstances, but LoadLibrary has some complicated criteria for resolving the paths of relative DLL names, which may vary according to certain parameters. I don't know all of them off the top of my head. It could be that something in the EXE manifest of the .NET binary is preventing it from finding the DLL according to those rules. You could try adjusting the PATH environment variable or loading the DLL with an absolute path. (You can use GetModuleFileName etc. to find the absolute path of your own code...)

查看更多
登录 后发表回答