17

I have created a C++ Dll project which contains a class "myCppClass" and tried to Dll export it using the following code as described by: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx

class __declspec(dllexport) CExampleExport : //public CObject { ... class definition ... }; 

I have omitted the "public CObject" as that requires afx.h and implies it is an MFC Dll. I am not sure if this is a good thing or not but it differed from the DLL project default settings.

From the above linked documentation I am led to believe that all "public functions and member variables" are available for import. How do I accomplish this in C#? Can simply instantiate the class?

Edit: I just realized that the Title of the post may be misleading. The emphasis should be on DllImport-ing from C# and ensuring that I followed the documentation properly in C++

1
  • 1
    You will have to make the DLL COM visible. I post this as a comment because I don't have time to give a more lengthy explanation. Commented Jan 19, 2011 at 21:50

5 Answers 5

20

C# cannot directly import C++ classes (which are effectively name-mangled C interfaces).

Your options are exposing the class via COM, creating a managed wrapper using C++/CLI or exposing a C-style interface. I would recommend the managed wrapper, since this is easiest and will give the best type safety.

A C-style interface would look something like this (warning: untested code):

extern "C" __declspec(dllexport) void* CExampleExport_New(int param1, double param2) { return new CExampleExport(param1, param2); } extern "C" __declspec(dllexport) int CExampleExport_ReadValue(void* this, int param) { return ((CExampleExport*)this)->ReadValue(param) } 

A C++/CLI-style wrapper would look like this (warning: untested code):

ref class ExampleExport { private: CExampleExport* impl; public: ExampleExport(int param1, double param2) { impl = new CExampleExport(param1, param2); } int ReadValue(int param) { return impl->ReadValue(param); } ~ExampleExport() { delete impl; } }; 
Sign up to request clarification or add additional context in comments.

4 Comments

You can't dynamic_cast from void*. Might as well use the real pointer type, C# will use IntPtr in the p/invoke signatures.
@Ben You're right. I changed it from a (T*) cast when proofreading... changed back now. Could also use the real pointer type, but I'd rather emphasise the lack of safety in this approach.
If you prefer a C++ template-style cast, then static_cast is the one to use.
@Ben Only when it has some useful behaviour. dynamic_cast is the only one I ever use (unless I'm trying to set a good example... doesn't really come up all that often).
14

As far as I know, C# can only interop with COM interfaces. Lucky enough it doesn't need to be a full blown COM object with registry, it can be any plain old C++ class implementing IUnknown.

So do something like this in C++:

#include <Windows.h> // Generate with from VisualStudio Tools/Create Guid menu static const GUID IID_MyInterface = { 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } }; struct MyInterface: public IUnknown { // add your own functions here // they should be virtual and __stdcall STDMETHOD_(double, GetValue)() = 0; STDMETHOD(ThrowError)() = 0; }; class MyClass: public MyInterface { volatile long refcount_; public: MyClass(): refcount_(1) { } STDMETHODIMP QueryInterface(REFIID guid, void **pObj) { if(pObj == NULL) { return E_POINTER; } else if(guid == IID_IUnknown) { *pObj = this; AddRef(); return S_OK; } else if(guid == IID_MyInterface) { *pObj = this; AddRef(); return S_OK; } else { // always set [out] parameter *pObj = NULL; return E_NOINTERFACE; } } STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&refcount_); } STDMETHODIMP_(ULONG) Release() { ULONG result = InterlockedDecrement(&refcount_); if(result == 0) delete this; return result; } STDMETHODIMP_(DOUBLE) GetValue() { return 42.0; } STDMETHODIMP ThrowError() { return E_FAIL; } }; extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance() { return new MyClass(); } 

And on the C# side you do something like this:

[ComImport] [Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface MyInterface { [PreserveSig] double GetValue(); void ThrowError(); } class Program { [DllImport("mylib.dll")] static extern MyInterface CreateInstance(); static void Main(string[] args) { MyInterface iface = CreateInstance(); Console.WriteLine(iface.GetValue()); try { iface.ThrowError(); } catch(Exception ex) { Console.WriteLine(ex); } Console.ReadKey(true); } } 

You can do pretty much anything you want this way, as long as the communication between C++ and C# goes through the virtual interface.

Comments

6

You cannot create a C++ class instance through pinvoke from C#. This is a troublesome implementation detail, only the C++ compiler knows how much memory needs to be allocated and when and how to properly call the constructor and destructor. The object size is by far the hardest nut to crack, there is no way whatsoever to make that reliable.

If you cannot flatten the C++ class out into static methods then you need to write a managed wrapper. That's done with the C++/CLI language, you'd write a "ref class" that has the unmanaged class object stored as a pointer, created in the constructor and deleted in the destructor and finalizer.

Comments

0

Actually, you can refer to the mangled names directly, using the EntryPoint property of the DllImport attribute. See this answer for more details.

Comments

0

C# and C++ are NOT ABI compatible like C++ and Delphi, so you cannot export virtual class members (methods) and declare them purely virtual on the calling side an invoke them, because C# cannot handle vtbl's of C++ objects. I would suggest you to wrap your C++ classes by COM, so you have another positive side effect, that other COM compatible languages can use your classes too.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.