2

I am currently using PInvoke to call some unmanaged functions from C++ in C#; specifically from PhysX 3.3.3. I am relatively new to PInvoke so I started with some simple methods. I can easily call functions that take no parameters but am having trouble calling any that do.

Starting simple, I used a function that passed in a boolean and discovered that booleans are non-blittable types so they must be marshaled. However, adding the boolean marshaling to the PInvoke signature still did not work.

[DllImport("PhysX3CommonCHECKED_x64.dll", CallingConvention = CallingConvention.Cdecl,
           EntryPoint = "?getReportAllocationNames@Foundation@shdfnd@physx@@UEBA_NXZ")]
public static extern bool GetAllocationNames();

[DllImport("PhysX3CommonCHECKED_x64.dll", CallingConvention = CallingConvention.Cdecl,
           EntryPoint = "?setReportAllocationNames@Foundation@shdfnd@physx@@UEAAX_N@Z")]
public static extern void SetAllocationNames([MarshalAs(UnmanagedType.U1)]bool name);

When I call SetAllocationNames(true), I get an AccessViolationException. I have also tried using other member names for the UnmanagedType enumeration (e.g. U1, Bool) but to no avail.

I am loading the DLLs prior to calling the functions, I am using the correct mangled name as the entry point, and I am calling all parameters associated with this function (just one in this case). Is there something else I'm missing?

Dyn0myte
  • 650
  • 1
  • 5
  • 11
  • Are you sure you have C calling convention and decorated names?! – Adriano Repetti Jun 30 '15 at 17:23
  • @AntoinePelletier Pinvoke is fine with parameters. @Robert However, it looks like `setReportAllocationNames` is a virtual method to be defined by an inheritor class. Have you done this work correctly? – whoisj Jun 30 '15 at 17:26
  • You shouldn't need to use the full name of the entry point. The C++ code should have a label on the method as an entry point. This would put the name of method in the symbol list in side the dll. You will get an access violation if the method isn't public. – jdweng Jun 30 '15 at 17:27
  • @whoisj It is indeed a virtual method but the function should be called by the inheritor class. – Dyn0myte Jun 30 '15 at 17:35
  • 3
    I try to make myself clear: you cannot pinvoke c++ instance methods (unless you schedule much much more effort). Moreover thiscall c.c. isn't supported with pinvoke. Use c++/cli or export c functions. – Adriano Repetti Jun 30 '15 at 17:36
  • @AdrianoRepetti How do you know that it isn't supported by PInvoke? Are not all exposed functions supported? Also, what do you mean by "c.c."? – Dyn0myte Jun 30 '15 at 17:40
  • 1
    @jdweng From my understanding, unless the function uses 'extern "C" ' to indicate C linkage, you will need to use the full mangled name. I don't see any other explicit label in the C++ code. – Dyn0myte Jun 30 '15 at 17:45
  • There are sometimes more than on pseudo-op that can be used. Declaring an Entry Point is equivalent to an extern. But if a method isn't public you have to follow Adriano response. If you compile with the MAP option, it will give all the info you need. – jdweng Jun 30 '15 at 18:39
  • @robert cc is calling convention. Name decoration, who cleans stack, how and where parameters and return value are passed. With pinvoke you can't do thisc – Adriano Repetti Jun 30 '15 at 19:19
  • Can't do thiscall without much more effort (because...this in c# and this in c++ are very different things, just think about virtual and multiple inheritance). – Adriano Repetti Jun 30 '15 at 19:23
  • What I want to say: it's c++ class? Wrap it in a managed class with c++/cli or export c functions. – Adriano Repetti Jun 30 '15 at 19:24
  • @AdrianoRepetti Thanks for the advice but I'm still a little unclear. CallingConvention is supported by PInvoke, according to Microsoft's documentation. Also, from my understanding, mangled names are used because inheritance and overloaded functions exists in C++. I add the name in the Entry Point to clarify which function to use. https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.71).aspx – Dyn0myte Jun 30 '15 at 19:41
  • Absolutely right. Now think what "this" pointer is. Think about its size (mixing one or more base classes and with or without public inheritance). Now think about vtable structure (no common abi even in native world). Now transpose everything to c#! How you control that stuff? "this" in clrcall cc is *completely* different. You have to invoke constructor, pass somehow this pointer (in registry) and then *maybe* it will work! Possible. Not easy. Same thing in stdcall or cdecl or fastcall is pretty straightforward. – Adriano Repetti Jun 30 '15 at 20:18
  • Errata: virtual, not public, inheritance – Adriano Repetti Jun 30 '15 at 20:19
  • @AdrianoRepetti Thanks for the clarification! – Dyn0myte Jun 30 '15 at 22:26
  • It's way more than the this pointer. Virtual methods for a start. And how are you going to invoke a C++ constructor? – David Heffernan Jun 30 '15 at 22:38
  • @DavidHeffernan yes, I mentioned vtable in another comment. Well it's _possible_ to pinvoke constructor (`??0Foundation@...`) after you allocated memory for its vtable (non standard! compiler dependent! brainkiller game for multiple/virtual inheritance!) with `Memory.Alloc` and manually fill it... You then use `ThisCall` for everything and add a parameter to your "vtable" struct to each method. Method invocation in vtable (all but ctor and dtor) via `Marshal.GetFunctionPointerForDelegate`. OK, it's crazy...that's why _"...much more effort..."_ unless you do it just for fun... – Adriano Repetti Jun 30 '15 at 22:57

2 Answers2

0

Okay, I think I understand now. After further research, it does seem that inherited functions are problematic for PInvoke. Many sources recommend flattening the C++ code to a C style interface. I will have to use a C++/CLI project instead. Thanks!

Dyn0myte
  • 650
  • 1
  • 5
  • 11
0

Unfortunately, it is mostly impossible to share C++ libraries with other C++ applications if they were compiled with a different compiler (and even worse, different versions of the same compiler) , yet alone using PInvoke.

Secondly, you're not even close at tackling this problem. C++'s calling convention (in x86) for methods (functions that belong to a class) is thiscall, which means that this (the class the method is called upon) is passed via the ecx or rcx register. cdecl doesn't pass one. Secondly, you didn't even define the appropriate classes...

What you should do is to write a C++ wrapper

Write functions, not methods (i.e - not bound to any class or struct), that do whatever you need to be done, and make sure that you know their calling convention (make it something conventional, like cdecl or fastcall). This way you can write C/C++ code to do the work for you, and write C# with PInvoke.

Mark Segal
  • 5,427
  • 4
  • 31
  • 69