unmanaged c++ Projekt mit managed GUI



  • Ich habe eine unmanaged directx app. Für die erste Gui wurde die Winapi direkt verwendet. Später habe ich die Winapi gegen QT ausgetauscht. Aufgrund von QT bugs bin ich mit der QT nicht mehr wirklich glücklich. Nun um auch mal was neues auszuprobieren, habe ich mir überlegt .net einzubeziehen. Bisher habe ich damit keine Erfahrungen. Ich würde den Kern der Applikation aus Geschwindigkeitsgründen als unmanaged c++ dll erstellen und die Gui mit C# + WPF. Nun habe ich im Vorfeld eine Frage zur Kommunikation. Im unmanaged Bereich soll dvideo, dsound und dinput gehandelt werden. Dafür brauche ich jedoch ein Windows handle. Kriege ich dies vom managed Bereich in den unmanaged Bereich? Eigentlich soll in den managed Bereich die Gui + Präsentationslogik und natürlich die Nachrichtenschleife / Eventsystem oder wie auch immer das in .Net jetzt funktioniert.


  • Administrator

    Ich habe ein wenig Mühe, deine eigentliche Frage aus deinem Text herauszulesen. Fragst du einfach, ob man ein HANDLE aus der WinAPI in C# reinkriegt? Dann, ja das geht. HANDLE ist nichts anderes als ein typedef auf void* . Daher kann man in C# das Gegenstück zu void* verwenden und dies wäre IntPtr .
    Aber mit dieser Information wirst du wohl nicht all zu viel anfangen können, wenn du dich nicht in .Net, C# oder C++/CLI auskennst. Wenn du genaueres wissen willst, muss du auch genauer fragen.

    Grüssli



  • Ich meinte es eher umgekehrt. Bekomme ich ein managed GUI handle in den unmanaged Bereich um dort die directx Komponenten mit der Gui im managed Bereich zu verbinden? Bevor ich den Umbau angehe, muss ich mir überlegen wo dvideo, dsound und dinput statt findet.


  • Administrator

    In WinForms ist dies kein Problem, da WinForms nur ein Wrapper um die WinAPI ist. Die Basisklasse Control bietet dazu ein Property Handle an.

    In WPF ist das ein wenig schwieriger. WPF setzt selber auf DirectX, um die Controls zu zeichnen, und ist daher etwas unabhängiger von der WinAPI-GUI. Allerdings repräsentieren alle Window Objekte grundsätzlich ein HWND Fenster aus der WinAPI-GUI. Um an das Handle vom Window Objekt zu kommen, benötigst du die WindowInteropHelper Klasse. Ich kenne mich in WPF und Interop aber zu wenig aus. Vor allem auch wie das aussieht, wenn man ein Control als Ausgabe oder dergleichen benutzen möchte. Allenfalls gäbe es noch HwndHost , bzw. WindowsFormsHost .

    Grüssli



  • Ich kenne mich mit WPF leider auch nicht wirklich aus, aber...

    Du musst ja im Prinzip nur zwei Dinge hinbekommen

    1. An das Window Handle kommen mit dem das D3D Device von WPF initialisiert wurde (damit du das verwenden kannst um die DSound/DShow/DInput etc. Devices zu initialisieren) und
    2. das Video irgendwie anzeigen.

    Zu (1) kann ich dir keinen Tip geben, aber (2) sollte gehen wenn du das Video in eine 3D Surface rendern lässt, und dann ein D3DImage WPF Control verwendest um die Surface anzuzeigen.

    Vielleicht geht es aber auch viel einfacher, wie gesagt, ich kenne mich mit WPF nicht wirklich aus 🙂



  • Danke für die Infos. Eigentlich spricht ja nichts dagegen Win Forms zu nehmen. In Vorbereitung auf den .net Umbau laß ich nur, dass die WPF die WinForms mittelfristig ersetzen soll.


  • Administrator

    PiCiJi schrieb:

    In Vorbereitung auf den .net Umbau laß ich nur, dass die WPF die WinForms mittelfristig ersetzen soll.

    Ersetzen glaube ich weniger. WinForms werden nicht entfernt werden. Aber die Zukunft ist ganz klar bei WPF zu finden. Es ist inzwischen sogar fraglich, ob die WinForms überhaupt noch weiterentwickelt werden. Also bei einer neuen Anwendung, würde ich mir schon starke Gedanken machen, ob es nicht klüger wäre, WPF einzusetzen.

    Grüssli



  • so jetzt ist ne Menge Zeit vergangen und ich habe den Umbau soweit fertig. Natürlich tauchen jetzt neue Probleme auf. Vielleicht hat ja jemand nen Tip?

    direct Video (unmanaged) funktioniert mit dem handle eines WPF Windows. Problem ist, dass das main menu durch directX überschrieben wird. Leider gibt es in WPF nur ein handle für das gesamte Fenster und nicht für einen UI-Block wie einem Grid. Wie zum Geier kann ich nun direct video mitteilen, das seine Zeichenfläche unter dem main menu beginnen soll?



  • ich habe es mittels HwndHost gelöst bekommen. Wer das gleiche Problem hat, hier
    eine Beispiel Klasse. Eine Instanz dieser Klasse muss dann einem wpf UI Element zugewiesen werden. z.b. ControlHostElement ist eine Border in meinem XAML code.

    Am Ende bekommt man über den getter in der Klasse das hwnd des WinApi Fensters, welches sich in dem entsprechenden WPF Element befindet. Dieses wiederum kann man frei positionieren.

    ControlHost chost = new ControlHost(height, width);
    mainwindow.ControlHostElement.Child = chost;
    hwndHandle = chost.hwndHostBox;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Windows.Interop;
    
    namespace project {
        class ControlHost : HwndHost {
    
            [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
            internal static extern IntPtr CreateWindowEx(int dwExStyle,
                                                          string lpszClassName,
                                                          string lpszWindowName,
                                                          int style,
                                                          int x, int y,
                                                          int width, int height,
                                                          IntPtr hwndParent,
                                                          IntPtr hMenu,
                                                          IntPtr hInst,
                                                          [MarshalAs(UnmanagedType.AsAny)] object pvParam);
    
            [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
            internal static extern bool DestroyWindow(IntPtr hwnd);
    
            internal const int
                WS_CHILD = 0x40000000,
                WS_VISIBLE = 0x10000000,
                LBS_NOTIFY = 0x00000001,
                HOST_ID = 0x00000002,
                LISTBOX_ID = 0x00000001,
                WS_VSCROLL = 0x00200000,
                WS_BORDER = 0x00800000;
    
            IntPtr hwndControl;
            IntPtr hwndHost;
            int hostHeight, hostWidth;
    
            public ControlHost(double height, double width) {
                hostHeight = (int)height;
                hostWidth = (int)width;
            }
    
            public IntPtr hwndHostBox {
                get { return hwndHost; }
            }
    
            protected override HandleRef BuildWindowCore(HandleRef hwndParent) {
                hwndHost = IntPtr.Zero;
    
                hwndHost = CreateWindowEx(0, "static", "",
                                          WS_CHILD | WS_VISIBLE,
                                          0, 0,
                                          hostWidth, hostHeight,
                                          hwndParent.Handle,
                                          (IntPtr)HOST_ID,
                                          IntPtr.Zero,
                                          0);
    
                return new HandleRef(this, hwndHost);
            }
    
            protected override void DestroyWindowCore(HandleRef hwnd) {
                DestroyWindow(hwnd.Handle);
            }
        }
    }
    


  • und das nächste Problem: fatalexecutionengineerror

    die app ruft 60 mal pro sekunde die unmanaged dll auf. Sporadisch kommt es vor, dass der managed heap vorn Baum geht. Das lustige ist, ich kann es allein durch die Mausbewegung beeinflussen. Bewege ich die Maus nicht oder um das Fenster der Anwendung gibt es kein Problem. Bewege ich die Maus länger über das Fenster der Anwendung wird der heap nach ein paar Sekunden korrupiert, aber auch nur wenn die app die unmanaged dll pollt.

    Die unmanaged dll verursacht mit einem unmanaged Test Programm keine Probleme. Dieses Testprogramm ist eine stark abgespeckte Test Gui, erstellt mit der Winapi.

    edit: habe gerade herausgefunden, dass wenn ich nach ca. 5-10 Sekunden erst anfange die Maus über den Bildschirm zu bewegen, kann ich den heap nicht mehr korrupieren. Das gilt auch, wenn ich die unmanaged dll nach obige Zeit in action versetze. Was ist da nur los, brauch .net ein paar Sekunden um dann sauber zu laufen?



  • Wie bindest Du die unmanaged Aufrufe denn ein? Has Du einen C++/CLI Wrapper oder nutzt Du PInvoke?



  • [DllImport(@"Core.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "setHwnd")]
            static extern void _SetHwnd(IntPtr wih);
    

    hmm, wenn ich in der unmanaged dll sofort nach dem Einsprung ein return setze, gibt es keine Probleme. Also scheint schon irgendwas in der dll das Problem zu verursachen.



  • Schau Dir mal das Thema Pinning genauer an:

    http://msdn.microsoft.com/en-us/23acw07k.aspx

    Pinning temporarily locks the data in its current memory location, thus keeping it from being relocated by the common language runtime's garbage collector. The marshaler pins data to reduce the overhead of copying and enhance performance. The type of the data determines whether it is copied or pinned during the marshaling process. Pinning is automatically performed during marshaling for objects such as String, however you can also manually pin memory using the GCHandle class.



  • hmm bin mir nicht sicher ob das was mit pinning zu tun hat, aber ich denke ich hab das Problem gefunden, jedoch noch nicht gelöst.

    ich reservire Speicher ( byte[] ) im managed Bereich. An den unmanaged Bereich übergebe ich darauf einen pointer ( char* ).

    Der unmanaged Bereich schreibt und liest ständig in diesem Speicher. Kommentiere ich das aus, gibt es keinen Absturz mehr. Ich habe mehrfach überprüft ob ich außerhalb des reservierten Bereichs schreibe. Dem ist nicht so.

    ok lege ich den Speicher direkt im unamanged Bereich an, funzt es. Der Grund warum ich es nicht gleich dort angelegt habe, ist das ich so schnell die Änderungen im managed Bereich speichern kann.



  • PiCiJi schrieb:

    Der unmanaged Bereich schreibt und liest ständig in diesem Speicher.

    Und war dieser managed Speicher auch über die ganze Zeit, wo jemand die Finger drin hatte, gepinned?



  • Und war dieser managed Speicher auch über die ganze Zeit, wo jemand die Finger drin hatte, gepinned?

    Nein. Ich werde das mal nachrüsten. Wenn ich das richtig verstehe, besteht bei nicht pinning die Gefahr, dass der garbage collector den Speicherbereich umschiebt und mein unwissender pointer im unmanaged Bereich davon nix mitkriegt. Das erklärts natürlich. Danke für die info.



  • Exakt. Wenns nicht gepinned ist, dann muss das früher oder später eigentlich crashen 😉


Anmelden zum Antworten