none
Calling C# from unmanaged C++

    Question

  • I've been searching a bit and I'm just getting twisted around about what I need to do here.  Apologies if this has been answered many times, but I haven't been able to find it.

    I have a C# class library containing a form called DisplayWindow.  It is being built succesfully.  Now, I'd like to be able to call my DisplayWindow form to load from some C++ code.  Once loaded, I'll be making additional calls into the DisplayWindow from C++.

    I've seen some examples using COM, but that doesn't look quite like what I want to do.  Also, I've seen some stuff for calling C++ from C#, and that looks much easier...  Can anyone help with this?

    Thanks!
    Tuesday, September 12, 2006 9:21 PM

Answers

  • What you want to do would be to compile only the files where you want to load the C# code /clr and then call away using C++/CLI.  The rest of your app will remain native while those cpp files you compile /clr will be mixed native and CLR.  You should be able to just call into your C# library using C++/CLI syntax.

     

    Once you throw /clr on the cl.exe command line for a file, within that file, you can call any managed code that you want, regardless of whether it was written in VB.net, C# or C++/CLI.  This is by far the easiest way to do what you want to do (call C# code from your native C++ app), although it does have its caveat's and limitations.  By and large though "It Just Works".  Also, it's faster than p/invokes.

    Wednesday, September 13, 2006 1:20 AM
  • Well, FWIW, here's an example I put together:

    // Pull in ATL utilities
    #include <atlbase.h>
    #include <atlcom.h>

    // pick a dll that has a typelib baked in.
    #import "C:\Program Files\Microsoft Office\OFFICE11\mscal.ocx"

    // mscal.tlh tells you the namespace. Scope into it now.
    using namespace MSACAL;

    int main()
    {
       // initialize COM
       CoInitialize( 0 ); 

       // Use ATL's COM smart pointer. You need to tell it an interface, which you get from mscal.tlh.
       CComPtr<ICalendar> myCal;

       // Create the object. You need to tell it the object's CLSID, which you get from mscal.tlh (the coclass)
       // Note that we don't have to retype the CLSID. __declspec(uuid) sets it, and __uuidof gets it back.
       if( SUCCEEDED( myCal.CoCreateInstance(__uuidof(Calendar)))) 
       {
          printf(
    "The day is %d\n",myCal->GetDay()); // Intellisense helps you out after you type myCal->
       }

       // uninitalize COM
       CoUninitialize();

       return 0;
    }

     

    Wednesday, September 13, 2006 9:08 PM

All replies

  • This is a fairly classic case of growing pains.  Who's the boss?  As long as you keep your C++ app the boss, you'll have a lot of misery on your hands.   Displaying Windows Forms forms from a C++ app is, technically, possible but doesn't work well in practice.  ShowDialog() is the only workable option.  Doing it the other way around is much easier.  Make your C# code the 'boss' by letting it be in charge of what windows are going to be displayed.  Calling into unmanaged C++ from C# now gets to be a lot easier, you've got P/Invoke, COM and managed C++ wrappers as options...
    Tuesday, September 12, 2006 9:42 PM
  • Thanks for the info...  Sadly, this is not an option for us.  Our primary application is C++ and we are trying to use C# (rather than MFC) for writing some utilities.
    Tuesday, September 12, 2006 9:50 PM
  • That would be a growing pain for your boss.  Good luck!

    Tuesday, September 12, 2006 9:52 PM
  • As a first step, you could create a new C++/CLI application (use CLR new project option).  From the managed main function, delegate to your unmanaged main function().  The whole dependency tree from that point should be purely unmanaged, while your application as a whole is mixed. 

    There may be issues in compiling under the /clr flag, since there are some C++ constructs that C++/CLI doesn't like.  Cross those bridges as you discover them.  Then run it to verify that the code runs as expected

    At this point, you have the ability to write managed classes and call into .NET or your own class libraries written in C#.  Use the gcroot<> template to encapsulate the handles into these objects.

    As your app evolves, you can decide how much of it to move to C++/CLI or C#.

    Edit: it may not be necessary to rewrite your main().  The gcroot link shows a native main() only.

    Brian

     

     

    Tuesday, September 12, 2006 11:47 PM
  • What you want to do would be to compile only the files where you want to load the C# code /clr and then call away using C++/CLI.  The rest of your app will remain native while those cpp files you compile /clr will be mixed native and CLR.  You should be able to just call into your C# library using C++/CLI syntax.

     

    Once you throw /clr on the cl.exe command line for a file, within that file, you can call any managed code that you want, regardless of whether it was written in VB.net, C# or C++/CLI.  This is by far the easiest way to do what you want to do (call C# code from your native C++ app), although it does have its caveat's and limitations.  By and large though "It Just Works".  Also, it's faster than p/invokes.

    Wednesday, September 13, 2006 1:20 AM
  • Whether its calling the C# code or the C++/CLI managed classes, there is still going to be an unmanaged -> managed boundary, and gcroot<> is needed to cross that.  As for partitioning the application code into cpp files compiled with /clr and those without, I am not sure if it makes much of a difference.  Unmanaged classes (as opposed to managed classes marked with ref class) are still compiled natively even under /clr.
    Wednesday, September 13, 2006 2:23 AM
  • This is great info.  I'm still missing some pieces here, though...

    I have my DisplayWindow class written and working in C#.  Now, to make the calls from C++, I have a class that tries to use gcroot<> to access the DisplayWindow.  Of course, my C++ has no idea of what a DisplayWindow is.  How to I make the C# classes visible to the C++ code?
    Wednesday, September 13, 2006 4:02 PM
  • Okay, made a little more progress with #using my compiled C# DLL...  Getting closer...
    Wednesday, September 13, 2006 4:38 PM
  • Yep, that's the right approach.
    Wednesday, September 13, 2006 4:52 PM
  • Now I'm having a problem loading the C++ DLL using LoadLibrary().

    So, where I've come to is:
    - C++ adapter DLL compiled with /clr and #using the C# resource DLL.
    - An application that attempts to use LoadLibrary() on the C++ DLL.

    The LoadLibrary() call is failing...  Any help here?
    Wednesday, September 13, 2006 5:20 PM
  • Note sure about the caveats of trying to use LoadLibrary on an DLL that is also a .NET assembly.   Taking a step back, you should consider exposing your C++/CLI objects as COM objects.  The C++ client uses COM to talk to the object.   Google on "COM-callable wrapper" and pick your favorite article.

    I suspect that you may be able to find info about LoadLibrary on managed C++ DLLs by googling for it also. 

    Brian

     

    Wednesday, September 13, 2006 5:32 PM
  • Okay, I'm definitly not a COM-programmer.

    I followed the steps at http://tinyurl.com/k53jo and now I am trying to find out how to load my COM object.  Any pointers there?
    Wednesday, September 13, 2006 5:44 PM
  • I had to let this project go...  I was able to get Java calls through JNI up and running in about an hour, so we've decided to go that way.
    Wednesday, September 13, 2006 8:38 PM
  • Well, FWIW, here's an example I put together:

    // Pull in ATL utilities
    #include <atlbase.h>
    #include <atlcom.h>

    // pick a dll that has a typelib baked in.
    #import "C:\Program Files\Microsoft Office\OFFICE11\mscal.ocx"

    // mscal.tlh tells you the namespace. Scope into it now.
    using namespace MSACAL;

    int main()
    {
       // initialize COM
       CoInitialize( 0 ); 

       // Use ATL's COM smart pointer. You need to tell it an interface, which you get from mscal.tlh.
       CComPtr<ICalendar> myCal;

       // Create the object. You need to tell it the object's CLSID, which you get from mscal.tlh (the coclass)
       // Note that we don't have to retype the CLSID. __declspec(uuid) sets it, and __uuidof gets it back.
       if( SUCCEEDED( myCal.CoCreateInstance(__uuidof(Calendar)))) 
       {
          printf(
    "The day is %d\n",myCal->GetDay()); // Intellisense helps you out after you type myCal->
       }

       // uninitalize COM
       CoUninitialize();

       return 0;
    }

     

    Wednesday, September 13, 2006 9:08 PM