1

I created a DLL file which includes two empty functions below.

extern "C" __declspec(dllexport) void __stdcall myFunc1() {
    // just empty function
}

extern "C" __declspec(dllexport) void __cdecl myFunc2() {
    // just empty function
}

In C#, I could call the functions using DLLImport attribute like below.

[DllImport("myDLL", CallingConvention=CallingConvention.StdCall)]
private extern static void myFunc1();

[DllImport("myDLL", CallingConvention=CallingConvention.Cdecl)]
private extern static void myFunc2();

So I tried again directly with LoadLibrary() of kernel32.dll instead of DllImport attribute.

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void MyFunc1();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void MyFunc2();

However a runtime error occurs when I call MyFunc1() where MyFunc2() works.

So I replaced __stdcall with __cdecl in C++, recompiled the DLL and then called MyFunc1() again in C#.

And.. It worked.

Why on earth doesn't __stdcall calling convention work with pinvoke in C#?

Jenix
  • 2,996
  • 2
  • 29
  • 58
  • 2
    `DllImport` *is* P/Invoke. What on Earth are you trying to do? :D `UnmanagedFunctionPointer` handles marshalling a managed delegate as an unmanaged function pointer (as the name suggests) - it has nothing to do with calling an unmanaged function in a DLL. Okay, not "nothing" - obviously, you have a delegate you can pass or receive from the unmanaged side, but that's not what you're trying to do here. – Luaan Sep 06 '15 at 19:35
  • 1
    Also, as per documentation for `UnmanagedFunctionPointer`, `if you do not specify a field name, UnmanagedFunctionPointerAttribute is ignored.` - I don't see you specifying any name. – Luaan Sep 06 '15 at 19:39
  • Yes, you're right. I'm using LoadLibrary(), FreeLibrary() and GetProcAddress() of "kernel32.dll" this time. I didn't post the rest of the code because I thought it was just a basic thing to expert guys here. – Jenix Sep 06 '15 at 19:41
  • Sorry to ask such a basic question, but how do I specify the name of the function in UnmanagedFunctionPointer?? I'm just getting the pointer using Marshal.GetDelegateForFunctionPointer, and I specified the name there. – Jenix Sep 06 '15 at 19:49
  • `GetDelegateForFunctionPointer` expects a type for the second parameter. Pass it `typeof(MyFunc1)` ;-) – Lucas Trzesniewski Sep 06 '15 at 20:08
  • Thanks but I said I'm doing it.. – Jenix Sep 06 '15 at 20:09

1 Answers1

6

What's happening here is that when you switch from __cdecl to __stdcall in the C++ code, the compiler decorates the name by which the function is exported. Instead of myFunc1 it is exported as myFunc1@0 or perhaps _myFunc1@0. All the same, the name is decorated. You can check that this is so with dumpbin or Dependency Viewer.

When you call GetProcAddress, it cannot find a function named myFunc1 and so returns NULL. You don't check for return values, and so carry on regardless. When you try to call the function, a run time error is thrown.

I've had to guess most of this because you did not show complete code. The other big lesson is to check for errors when calling Win32 functions.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks, I'll check it as you said. If the reason is the name decoration, what can I do? I thought the keyword extern "C" would prevent problems related to naming. – Jenix Sep 06 '15 at 22:21
  • 3
    Need to use a def file to export – David Heffernan Sep 06 '15 at 22:22
  • Checked with dumpbin and it was exactly as you said. I thought I was checking null but there was a stupid mistake as well. "if ((dgMyFunc1 = (MyFunc)Marshal.GetDelegateForFunctionPointer(GetProcAddress(myDLL, "myFunc1"), typeof(MyFunc))) == null)".. – Jenix Sep 06 '15 at 23:30
  • Thanks again, btw Dependency Viewer is better than dumpbin? – Jenix Sep 06 '15 at 23:30
  • `IntPtr fnptr = GetProcAddress(myDLL, "myFunc1"); if (fnptr == IntPtr.Zero) throw new Win32Exception();` – David Heffernan Sep 07 '15 at 07:00
  • 1
    Dependency Viewer is different. It can do more. It's visual. dumpbin lists exports perfectly well though. – David Heffernan Sep 07 '15 at 07:01
  • Just curious but how can I get a function pointer with ordinal number in C# using GetProcAddress? Like "GetProcAddress(myDLL, (LPCSTR)5);" in C++. I know [DllImport] can deal with ordinal values, just curious.. – Jenix Sep 08 '15 at 19:46
  • 2
    Declare an overload that accepts `IntPtr` as the second parameter and pass the ordinal value to that parameter. – David Heffernan Sep 08 '15 at 19:48
  • Works great! Thanks. Really sorry but last question, is there any function like 'GetProcAddress()' for a global variable? I use '__declspec(dllimport)' for it in C++ but I can't in C#.. – Jenix Sep 08 '15 at 19:55
  • 2
    If the variable is exported then you import it with `GetProcAddress` – David Heffernan Sep 08 '15 at 20:16
  • I thought GetProcAddress() only works with functions.. I'll try it! Thanks – Jenix Sep 08 '15 at 21:39
  • 2
    It's documented: *If the function succeeds, the return value is the address of the exported function or variable.* – David Heffernan Sep 08 '15 at 21:41