Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 470
zarulad Avatar asked Jul 05 '12 13:07

zarulad


1 Answers

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 :

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

May the force be with you :-)

like image 173
aybe Avatar answered Nov 05 '22 02:11

aybe