DirectX Bereich in windows forms application: wie?



  • Hallo allerseits!

    Ich würde gerne eine windows form application mit einem Bereich für eine DirectX Ausgabe programmieren.

    Dazu habe ich mit dem aktuellen DirectX SDK auf der Basis eines Tutorials zunächst eine DirectX Anwendung erstellt, die im wesentlichen meinen Anforderungen genügt.
    **
    Jetzt würde ich diese DirectX Anwendung gerne in eine windows forms application (WFA) einbauen und auch von dort aus kontrollieren.**

    Allerdings waren meine bisherigen Versuche, DirectX in eine WFA einzubinden erfolglos.

    Probiert habe ich bisher folgendes:

    1. Versuch:
      Erstellung eines neuen standard WFA Projectes, in das ich den Quelltext der DirectX Anwendung (ist nur ein .cpp File, ich neenne es hier mal directx.cpp) als neue Source einband. Die winmain Funktion der directx.cpp kommentierte ich dabei sicherheitshalber erstmal aus. Der Versuch, das zu kompilieren, war allerdings erfoglos, es ratterten nur so die Fehlermeldungen (die ich bei Bedarf nachreiche...)
      Man kann das leicht nachvollziehen, in dem man in ein neues WFA Projekt die *.cpp eines einfachen DirectX C++ Tutorials aus dem SDK einbaut.
    2. Versuch:
      Hinzufügen einer GUI zu einem bestehenden funktionierenden DirectX Projektes:
      ZU meinem bestehenden DirectX C++ Projekt versuchte ich eine GUI hunzuzufügen:
      Visual Studio Solution Explorer > Rechtsklick auf Projektnamen > Add > New Item > UI > Windows Form.
      Der Versuch, sowas zu kompilieren, führt allerdings zu folgendem Error:
    cl : Command line error D8016 : '/MT' and '/clr' command-line options are incompatible
    

    Damit kann ich leider nichts anfangen, ich habe in den Projekt Optionen gesucht, aber nichts gefunden, um diese Optionen ein- oder auszuschalten...

    1. Versuch:
      Verwendung von managed code zur Erstellung von DirectX statt C++.
      Beim Aufruf eines Managed Code Tutorials aus dem DirectX SDK wird in Visual Studio ein Fenster angezeigt, in das man auch wie normal Buttons und ähnliches aus der Toolbox hinzufügen kann. Allerdings wird diese Gestaltung des Fensters beim Kompilieren überhaupt nicht berücksichtigt!
      Es wird einfach nur das DirectX Fenster erstellt, vom selbst zusammengestellten Fenster sieht man nichts mehr!

    Also:
    Wie kann ich doch noch einen DirectX Bereich in ein normales WFA Projekt integrieren?
    Welcher der drei oberen Ansätze ist wohl am geeignetsten und was mache ich bisher falsch?
    Kennt jemand Beispiele / Tutorials zum Thema DirectX und WFA? (Ich habe noch keine gefunden....)

    Danke und Gruß,
    dong



  • 1. Tip: lass die finger vom managed directx. Jeder der nativ dx kennt ist auf anhib davon begeistert, da man in 30s ne lauffähige app hat, die begeisterung beginnt sich allerdings ab der 31s (spätestens wenn das teil auf ne anderen als auf deinem pc laufen soll) in furstation umzuwandeln.
    2. '/MT' and '/clr' command-line options are incompatible
    MT bedeutet, dass du statisch gegen die c-runtime linkst, was sich mir /clr (manage code erlaubt) nicht vertägt. Du kannst das in Properties->C++->Code Generation->Runtime library auf Multi-threaded DLL umstellen (/MD)
    Als nächsten schritt besog dir ein DirectX tutorial, das zeigt wie mal ein IDirect3DDevice9 erstellt. In D3DPRESENT_PARAMETERS kannst du dann angeben in welches fenster er rendern soll



  • Vielen Dank für Deinen Tipp, werde ich gleich ausprobieren. 🙂

    Was ich bisher für DirectX geschrieben hatte ist zum Glück sowieso native C++ und nicht managed, da brauche ich dann wenigstens nichts umzuschreiben.

    Als Tutorial habe ich das da gefunden, werde mich mal daran versuchen und bei weiteren Fragen hier berichten

    Gruß,
    Dong



  • So, tatsächlich läßt sich das ganze jetzt nach Methode 2 kompilieren, das ist ja schon was, danke 🙂

    Allerdings wird erwartungsgemäß trotzdem nur das D3D Window, nicht aber das UI Window angezeigt.

    Mittlerweile ist mir ja klar, wo ich die Anzeige auf das andere Fenster umleiten könnte, ich weiß aber leider noch nicht, wie:

    Der Einfachheit halber beziehe ich mich im folgenden auf das erste und einfachste Tutorial des aktuellen DirectX SDK, "Tutorial 1: Creating a Device" (hab ich online leider nicht gefunden, außer eben im SDK... ..wenn ich recht verstanden habe, wird auch dort ein IDirect3DDevice9 erstellt [siehe unten]) den recht kurzen Quelltext werde ich reproduktionshalber unten komplett angeben.

    D3DPRESENT_PARAMETERS wird dort folgendermaßen gesetzt:

    D3DPRESENT_PARAMETERS d3dpp; 
        ZeroMemory( &d3dpp, sizeof(d3dpp) );
        d3dpp.Windowed = TRUE;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    

    Insbesondere hDeviceWindow wird also noch nicht gesetzt, wovon ich annehme, das man dort das Ausgabefenster wählen kann. In der Dokumentation steht dazu:

    hDeviceWindow
    The device window determines the location and size of the back buffer on screen. This is used by Direct3D when the back buffer contents are copied to the front buffer during Present.
    For a full-screen application, this is a handle to the top window (which is the focus window).

    For applications that use multiple full-screen devices (such as a multimonitor system), exactly one device can use the focus window as the device window. All other devices must have unique device windows.

    For a windowed-mode application, this handle will be the default target window for Present. If this handle is NULL, the focus window will be taken.
    Note that no attempt is made by the runtime to reflect user changes in window size. The back buffer is not implicitly reset when this window is reset. However, the Present method does automatically track window position changes.

    Die Probleme dabei sind:

    1. Ich weiß nicht, wie das andere UI Fenster heißt, ich weiß also nicht, auf welchen Wert ich in diesem Fall d3pp.hDeviceWindow setzen soll.
    2. Und noch grundsätzlicher: Das UI Fenster wird ja gar nicht angezeigt! Dazu sollte ich vielleicht erwähnen, wie ich es erzeuge:
    • Solution Explorer > Rechtsklick auf Projektname > Add > New Item > Visual C++ > UI > Windows Form > Add (ich nannte es: test)
    • Daraufhin kommt eine Warnmeldung: "You are adding a CLR component to a native project. Your Project will be converted to have Common Language Runtime Support."
    • Um das Projekt danach noch kompilieren zu können, muss wie oben von CMatt beschrieben auf /MD umgestellt werden.
    • Dem Projekt wurden somit zwei Dateien zugefügt:
      test.cpp mit Inhalt:
    #include "test.h"
    

    und test.h mit dem üblichen Standardinhalt, den ich der Vollständigkeit unten angebe.

    **Dementsprechend lauten meine Fragen:

    1. Was muss ich tun, damit das neue UI Fenster überhaupt angezeigt wird?
    2. Auf welchen Wert muss ich d3pp.hDeviceWindow [sofern ich recht verstehe und das der richtige Parameter dafür ist....] dann setzten, damit die DirectX Anzeige in das neue UI Fenster umgeleitet wird?**

    Danke und Gruß,
    Dong

    Anhang (nur sicherheitshalber, lesen wohl nicht unbedingt notwendig...):
    createDevice.cpp:

    //-----------------------------------------------------------------------------
    // File: CreateDevice.cpp
    //
    // Desc: This is the first tutorial for using Direct3D. In this tutorial, all
    //       we are doing is creating a Direct3D device and using it to clear the
    //       window.
    //
    // Copyright (c) Microsoft Corporation. All rights reserved.
    //-----------------------------------------------------------------------------
    #include <d3d9.h>
    #pragma warning( disable : 4996 ) // disable deprecated warning 
    #include <strsafe.h>
    #pragma warning( default : 4996 ) 
    
    //-----------------------------------------------------------------------------
    // Global variables
    //-----------------------------------------------------------------------------
    LPDIRECT3D9             g_pD3D       = NULL; // Used to create the D3DDevice
    LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; // Our rendering device
    
    //-----------------------------------------------------------------------------
    // Name: InitD3D()
    // Desc: Initializes Direct3D
    //-----------------------------------------------------------------------------
    HRESULT InitD3D( HWND hWnd )
    {
        // Create the D3D object, which is needed to create the D3DDevice.
        if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
            return E_FAIL;
    
        // Set up the structure used to create the D3DDevice. Most parameters are
        // zeroed out. We set Windowed to TRUE, since we want to do D3D in a
        // window, and then set the SwapEffect to "discard", which is the most
        // efficient method of presenting the back buffer to the display.  And 
        // we request a back buffer format that matches the current desktop display 
        // format.
        D3DPRESENT_PARAMETERS d3dpp; 
        ZeroMemory( &d3dpp, sizeof(d3dpp) );
        d3dpp.Windowed = TRUE;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
        d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
    
        // Create the Direct3D device. Here we are using the default adapter (most
        // systems only have one, unless they have multiple graphics hardware cards
        // installed) and requesting the HAL (which is saying we want the hardware
        // device rather than a software one). Software vertex processing is 
        // specified since we know it will work on all cards. On cards that support 
        // hardware vertex processing, though, we would see a big performance gain 
        // by specifying hardware vertex processing.
        if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                          D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                          &d3dpp, &g_pd3dDevice ) ) )
        {
            return E_FAIL;
        }
    
        // Device state would normally be set here
    
        return S_OK;
    }
    
    //-----------------------------------------------------------------------------
    // Name: Cleanup()
    // Desc: Releases all previously initialized objects
    //-----------------------------------------------------------------------------
    VOID Cleanup()
    {
        if( g_pd3dDevice != NULL) 
            g_pd3dDevice->Release();
    
        if( g_pD3D != NULL)
            g_pD3D->Release();
    }
    
    //-----------------------------------------------------------------------------
    // Name: Render()
    // Desc: Draws the scene
    //-----------------------------------------------------------------------------
    VOID Render()
    {
        if( NULL == g_pd3dDevice )
            return;
    
        // Clear the backbuffer to a blue color
        g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
    
        // Begin the scene
        if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
        {
            // Rendering of scene objects can happen here
    
            // End the scene
            g_pd3dDevice->EndScene();
        }
    
        // Present the backbuffer contents to the display
        g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
    }
    
    //-----------------------------------------------------------------------------
    // Name: MsgProc()
    // Desc: The window's message handler
    //-----------------------------------------------------------------------------
    LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
    {
        switch( msg )
        {
            case WM_DESTROY:
                Cleanup();
                PostQuitMessage( 0 );
                return 0;
    
            case WM_PAINT:
                Render();
                ValidateRect( hWnd, NULL );
                return 0;
        }
    
        return DefWindowProc( hWnd, msg, wParam, lParam );
    }
    
    //-----------------------------------------------------------------------------
    // Name: WinMain()
    // Desc: The application's entry point
    //-----------------------------------------------------------------------------
    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
    {
        // Register the window class
        WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
                          GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                          "D3D Tutorial", NULL };
        RegisterClassEx( &wc );
    
        // Create the application's window
        HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01: CreateDevice", 
                                  WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                                  NULL, NULL, wc.hInstance, NULL );
    
        // Initialize Direct3D
        if( SUCCEEDED( InitD3D( hWnd ) ) )
        { 
            // Show the window
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );
    
            // Enter the message loop
            MSG msg; 
            while( GetMessage( &msg, NULL, 0, 0 ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
        }
    
        UnregisterClass( "D3D Tutorial", wc.hInstance );
        return 0;
    }
    

    test.h:

    #pragma once
    
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    
    namespace CreateDevice {
    
    	/// <summary>
    	/// Summary for test
    	///
    	/// WARNING: If you change the name of this class, you will need to change the
    	///          'Resource File Name' property for the managed resource compiler tool
    	///          associated with all .resx files this class depends on.  Otherwise,
    	///          the designers will not be able to interact properly with localized
    	///          resources associated with this form.
    	/// </summary>
    	public ref class test : public System::Windows::Forms::Form
    	{
    	public:
    		test(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: Add the constructor code here
    			//
    		}
    
    	protected:
    		/// <summary>
    		/// Clean up any resources being used.
    		/// </summary>
    		~test()
    		{
    			if (components)
    			{
    				delete components;
    			}
    		}
    
    	private:
    		/// <summary>
    		/// Required designer variable.
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// Required method for Designer support - do not modify
    		/// the contents of this method with the code editor.
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			this->components = gcnew System::ComponentModel::Container();
    			this->Size = System::Drawing::Size(300,300);
    			this->Text = L"test";
    			this->Padding = System::Windows::Forms::Padding(0);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            }
    #pragma endregion
    	};
    }
    


  • habe deinen quelltext nicht ganz durchgelesen, aber das problem liegt glaube ich and diesem HWND. bei windows forms kommst du via property Handle dran. allerdings brauchst du keine MsgProc oder WinMain. eher etwa so:

    int main()
    {
        test form;
        InitD3D(static_cast<HWND>(form.Handle.ToPointer()));
        // 
        return 0;
    }
    


  • Mensch schrieb:

    habe deinen quelltext nicht ganz durchgelesen, aber das problem liegt glaube ich and diesem HWND. bei windows forms kommst du via property Handle dran. allerdings brauchst du keine MsgProc oder WinMain. eher etwa so:

    int main()
    {
        test form;
        InitD3D(static_cast<HWND>(form.Handle.ToPointer()));
        // 
        return 0;
    }
    

    Das kann gut sein. Allerdings weiß ich nicht, was ich in diesem komkreten Fall für "form.Handle.ToPointer" einsetzen muss, oder geht das einfach so?

    Allerdings hast Du schon echt.
    Die Herstellung des Fensters unterscheidet sich hier doch recht stark von der Herstellung eines WFA Fensters:

    Hier im DirectX Tutorial:

    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01: CreateDevice", 
                                  WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
                                  NULL, NULL, wc.hInstance, NULL );
    

    im Standard WFA Fall:

    // Enabling Windows XP visual effects before any controls are created
    	Application::EnableVisualStyles();
    	Application::SetCompatibleTextRenderingDefault(false); 
    
    	// Create the main window and run it
    	Application::Run(gcnew Form1());
    

    würde der oben genannte Tipp diese beiden Fälle unter einen Hut bringen?

    Ich bin da kritisch, weil in menschs Codeschnipsel sehe ich gar keine Stelle, wo überhaupt ein Fenster erzeugt wird....

    --------------

    Mittlerweile habe ich noch einen ganz anderen blöden Fehler entdeckt, der mir allerdings einige Kompiler Error ersparte:

    Wenn das Fenster "test" angezeigt werde soll, muss wohl im Hauptfile erstaml test.h included werden.

    Also ergänzte ich createDevice.cpp um die Zeile

    #include "test.h"
    

    (Inhalt von test.h: siehe oben) mit dem Ergebnis, das jede Menge neue Error beim Builden auftauchten:
    Z.B.:

    test.h(3) : error C2871: 'System' : a namespace with this name does not exist

    Was sehr seltsam ist, in einer normalen WFA macht dieser Aufruf ja kein Problem.... 😕

    Woran könnte das liegen?

    Danke und Gruß,
    Dong



  • Mh, das scheint ja ziemlich schwierig zu sein....

    ...eigentlich würde es mir auch reichen, wenn durch einen Button ein Fenster, in dem der DirectX Kram laufen könnte, erzwugt würde.

    Ich habe das auch probiert, hat aber leider nicht funktioniert.
    Da das aber ein anderes Thema ist, habe ich dazu einen neuen Thread erstellt.

    Gruß,
    Dong


Anmelden zum Antworten