Managed GUI aus C# Library benutzen in C++ ?



  • Hallo,

    mit COM kann man ja eine GUI einer C# Library in C++ benutzen.

    So wie in diesem Qt Bsp hier:

    wmp = new QAxWidget(this);
    
        // CLSID sManagedDLL.myControl
        wmp->setControl("{2919A319-C665-4D9C-9A96-36209807CD4C}");
        wmp->setProperty("ShowControls", false);
        wmp->resize(480,240);
    
        connect(wmp, SIGNAL(onButtonClicked(QString)), this, SLOT(aButtonWasClicked(QString)));
    

    C# Library:

    [Guid("2919A319-C665-4D9C-9A96-36209807CD4C")]
        [ClassInterface(ClassInterfaceType.None)]
        [ComSourceInterfaces(typeof(IButtonEvents))]
        [ComVisible(true)]
        public partial class myControl : UserControl, ICalculator
        {
            public event ButtonClickedDelegate onButtonClicked;
    
    ...
    

    Geht so etwas mit C++/CLI eigentlich auch?
    Oder kann man da durch Hilfe eines Wrappers nur einzelne Methoden exportieren?

    Was sind Vorteile von C++/CLI gegenüber COM?

    Danke!



  • Ja, geht mit C++/CLI auch.
    Keine Ahnung, ob das Vorteile hat... Das ist eigentlich für die andere Richtung interessanter, wenn du eine native Bibliothek mit einer etwas komplexeren Schnittstelle hast und die in C# benutzen willst.



  • Vergiss C++/CLI. C++/CLI ist für GUI Programmierung nicht konzipiert und völlig ungeeignet; Qt über COM aus C++/CLI zu benutzen ist vermutlich das krankteste, was ich je gehört hab. Tu dir selbst einen Gefallen und lass die Finger von C++/CLI. C++/CLI ist nicht C++. Wenn du Qt willst, dann verwend einfach richtiges C++ und Qt. Wenn du .NET willst, dann nimm eine geeignete Sprache wie z.B. C#. Auf keinen Fall solltest du C++/CLI für irgendetwas anderes als das Schreiben von managed/native Interop Layern verwenden. Das ist der eine Anwendungsfall, für den C++/CLI gedacht ist und so ziemlich auch das einzige, wofür es brauchbar ist...



  • Ich glaube, du hast die Frage nicht ganz verstanden, bzw. was der TE machen will ist natürlich nicht so ganz klar. Ich interpretiere die Frage zumindest so, dass es um Wrapper/Bridge Technologien ging, COM vs C++/CLI (und genau dafür ist es auch geeignet), nicht darum, die GUI mit C++/CLI zu schreiben.



  • Ich habe eine existierende GUI, erstellt in C#, die ich als dll habe.
    Ich weiss, dass ich die GUI über COM von meiner C++ Seite (Qt) benutzen kann. Ob dasselbe auch mit C++/CLI geht anstelle von COM weiss ich nicht. Das war meine Frage.
    Wie man einfache Funktionen/Methoden einer C# lib nach C++ mit Hilfe von C++/CLI exportieren kann (Wrapper) habe ich ein Beispiel: http://code.msdn.microsoft.com/windowsdesktop/Consuming-C-Library-in-937458e5/sourcecode?fileId=45720&pathId=252081203

    Ich habe nur überhaupt nichts gefunden mit einer GUI

    @Mechanics: Weiss du einen Link oder Bsp über Export eine GUI mit C++/CLI von C#?



  • verliernix schrieb:

    Ich habe nur überhaupt nichts gefunden mit einer GUI

    Mit einer GUI hat es auch nichts zu tun. Eine C# DLL exportiert Klassen und in C++ verwendet man sie.

    Einfache Beispiele (ohne GUI) z.B. hier unter gcroot bzw. auto_gcroot
    http://www.codeproject.com/Articles/14520/C-CLI-Library-classes-for-interop-scenarios



  • verliernix schrieb:

    @Mechanics: Weiss du einen Link oder Bsp über Export eine GUI mit C++/CLI von C#?

    Anscheinend ist bei der COM Variante hier schon einiges an Magic dabei, weil du das UserControl direkt in ein QaxWidget packen kannst. Ich weiß jetzt nicht im Detail, wie das funktioniert, ich bin grad sogar etwas überrascht, dass es so einfach geht.
    Ich hab mich mit GUI Interop in der Form noch nicht so wirklich beschäftigt. Mir würde jetzt spontan keine sauberere Methode einfallen, als über Windows Handles zu gehen. In C++/CLI erstellst du eine Wrapper Klasse, die intern (.NET seitig) nur eine Instanz von dem Control hält. Dann kannst du entweder ein Handle von dem Parent über den Wrapper an das Control übergeben und dann .NET-seitig den Parent setzen, oder umgekehrt das Handle von dem .NET Control rausholen und als Child deinem Qt Widget hinzufügen. Wenn du letztes machst, gibts noch so lustige Spielereien wie das QWinWidget aus QtSolutions (was wohl nicht weiterentwickelt wird). Wir benutzen das z.B. in der Arbeit und wir brauchen das auch. Bei uns gehts aber in die andere Richtung, wir hosten Qt in einem "nativen" Control, was aber evtl. sogar wieder ein .NET Control ist. Also, ganz so einfach ist das alles nicht. Wenn die COM Variante bei dir einfach funktioniert und das ganze nicht so wahnsinnig wichtig ist, lass es einfach so. Sonst musst du wahrscheinlich etwas rumspielen. Vielleicht würds auf Anhieb gehen, vielleicht würdest du Probleme bekommen (wir haben viele Probleme, aber wir haben auch sehr komplexe Konstellationen).



  • Mechanics schrieb:

    Anscheinend ist bei der COM Variante hier schon einiges an Magic dabei, weil du das UserControl direkt in ein QaxWidget packen kannst. Ich weiß jetzt nicht im Detail, wie das funktioniert, ich bin grad sogar etwas überrascht, dass es so einfach geht.

    In C# kann man ein Windows Forms Control als ActiveX-Control exportieren.(Das machen die Attribute da oben im C# Quelltext.) Ein solches kann Qt dann wieder laden.

    Ähnliche Magic gibt es in MFC und C++/CLI mit der Klasse CWinFormsControl. Auch als MFC-View kann man WinForms hosten. Mit WPF läufte es wohl auf die Verwendung von HwndSource hinaus. Ob es ähnliches für Qt auch fertig gibt, weiß ich nicht.

    Handarbeit halte ich auch für zu aufwendig. Würde aber letztlich auch nur auf exportierte Klassen und Marshalling mit GetDelegateForFunctionPointer usw. hinauslaufen.



  • nn schrieb:

    In C# kann man ein Windows Forms Control als ActiveX-Control exportieren.(Das machen die Attribute da oben im C# Quelltext.)

    Das Attribut im Quellcode ist "nur" ein ComVisible. Das kennt man natürlich. Ich hab jetzt aber nicht gewusst, dass man UserControls so einfach als ActiveX exportieren kann, das ist noch um einiges mehr Aufwand, als nur COM, wenn man das von Hand machen wollen würde.



  • Appropos Aufwand, der Qt-Aufruf

    wmp->setProperty("ShowControls", false);
    

    sieht stark danach aus, dass da IDispatch verwendet wird. Besonders effizient scheint diese Technik daher nicht zu sein.



  • Hab jetzt nur mal ein C++ Beispiel zum laufen gekriegt, aber immerhin ja geht's jetzt:

    C# dll GUI:

    using System;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class FormTM : Form
        {
            public FormTM()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                this.textBox1.Text = "Servus C++/CLI";
            }
    
            private void textBox1_TextChanged(object sender, EventArgs e) { }
            private void checkBox1_CheckedChanged(object sender, EventArgs e) { }
        }
    }
    

    C++/CLI Wrapper:

    Header:

    __declspec(dllexport) public void myFormStart();
    

    und

    using namespace WindowsFormsApplication1;
    
    namespace Wrapper
    {
    	public ref class SampleForm 
    	{
    		SampleForm(void)
    		{
    			formObj = gcnew FormTM();
    		}
    
    	public:
    		FormTM ^ formObj;
    		static SampleForm ^ Instance = gcnew SampleForm();
    	};
    

    Source:

    __declspec(dllexport) public void myFormStart()
        {
    		FormTM ^myForm = SampleForm::Instance->formObj;
    		myForm->ShowDialog();
        }
    

    Von C++

    #include "NativeInterface.h"
    int _tmain(int argc, _TCHAR* argv[])
    {
    	myFormStart();
    
    	return 0;
    }
    

    Eigentlich der Aufwand auch nicht viel größer als mit COM



  • Das ist jetzt aber was anderes als in deinem ersten Post. Dort wird ein ganzes Usercontrol mit seinen Ereignissen und Eigenschaften gewrappt. Da passiert viel mehr im Hintergrund, wie Mechanics richtig bemerkt.

    Was du da jetzt gemacht hast, ist eigentlich kein echter C++/CLI Wrapper, sondern ein C++/CLI Wrapper, der eine C-Funktion exportiert. Es ist aber der Sinn von C++/CLI, dass man ganze Objekte durchreichen kann.

    In etwa so in C++/CLI

    // Datei Wrapper.h
    
    class Wrapper  // kein ref class, das hier ist eine native Klasse mit managed Member
    {
      msclr::auto_gcroot<CSharpKlasse^> obj;
    
      // ...
    };
    

    und dann noch eine zweite Klasse (siehe Pimpl-Idiom)

    // Datei CppStub.h
    
    class Wrapper;  // Vorwärtdeklaration, damit das echte C++ keinen managed Code sieht
    
    class CppStub
    {
      std::unique_ptr<Wrapper> wrappedObj;
      // ...
    };
    

    mit einer gemeisamen Implementierung

    // Datei Wrapper.cpp mit /clr übersetzen
    
    #include <msclr\auto_gcroot.h>
    
    #include "Wrapper.h"
    #include "CppStub.h"
    
    CppStub::CppStub()
    {
      wrappedObj = std::unique_ptr<Wrapper>(new Wrapper);
    }
    
    Wrapper::Wrapper()
    {
      obj = gcnew CSharpKlasse;
    }
    

    im unmanaged C++ verwendet man jetzt nur CppStub-Objekte, dort sieht man gar nicht, dass managed Code dahinter steckt.



  • So geht's auch:

    Wrapper Header:

    class __declspec(dllexport) SampleFormWrapper
    	{
    		public:
    			SampleFormWrapper(void);
    			virtual ~SampleFormWrapper(void);
    			int ShowDialog();
    		private:
    			void *m_impl;
    	};
    

    Wrapper C++/CLI:

    SampleFormWrapper::SampleFormWrapper(void)
    {
    	FormTM ^obj = gcnew FormTM();
    	m_impl = GCHandle::ToIntPtr(GCHandle::Alloc(obj)).ToPointer(); 
    }
    
    int SampleFormWrapper::ShowDialog()
    {
        GCHandle h = GCHandle::FromIntPtr(IntPtr(m_impl));
        FormTM ^ obj = safe_cast<FormTM^>(h.Target);
    
    	obj->ShowDialog();
    	return 0;
    }
    

    C++

    SampleFormWrapper obj;
    
    	obj.ShowDialog();
    

Log in to reply