Im Thread Paint ausführen



  • danke erstmal

    ich habe jetzt...

    VOID Thread(PVOID pvoid)
        {
    
          HWND hwnd;
          HDC hdc;
    
             while(true)
             {           
              bx=bx+bdx;
              by=by+bdy; 
    
              if(by>=585 || by<=10)
              {
                bdy=bdy*-1;        
              }         
    
              if(bx>=685 || bx<=0)
              {
                bdx=bdx*-1;        
              }         
    
              InvalidateRect(hwnd, NULL, TRUE);
              UpdateWindow(hwnd);
    
              Sleep(10);
             }  
         }
    

    aber er schickt immer noch keine Nachricht zum neuzeichnen



  • Übergib mal InvalidateRect () und UpdateWindow () das Handle des Fensters in dem gezeichnet werden soll.



  • Einen eigenen Thread für den Ball zu verwenden ist IMHO ein ganz schlechter Ansatz.



  • ich habe doch das handle meines fenstern in dem gezeichnet wird übergeben.

    UpdateWindow(hwnd);

    zum anderen, wieso ist das ein schlechter Ansatz, begründe das mal.

    Für mich schien das ideal, da ich auch den Thread stoppen (Pause) kann und das so mit der Bedienung des Spiels nebenherläuft.

    weiß noch jemand was zum eigentlichen Problem?



  • helpme schrieb:

    ich habe doch das handle meines fenstern in dem gezeichnet wird übergeben.

    Aber nicht in dem Quellcode den du hier geposted hast 😉



  • zum anderen, wieso ist das ein schlechter Ansatz, begründe das mal.

    Weil es nicht notwändig ist, und du dir (unnötigerweise) die Komplexität einhandelst die eben mit mehreren Threads daherkommt.

    Dein Sleep(10) garantiert dir auch nicht dass du auf jedem Rechner bzw. nichtmal zu jeder Zeit auf einem einzigen Rechner gleich viele Durchläufe pro Sekunde bekommst. Wenn du eine gleichmässige Geschwindigkeit ("N Pixel pro Sekunde") haben willst musst du also sowieso dein Programm so abändern dass es mit unterschiedlichen Zeitspannen zwischen 2 Updates klarkommt, und dann kannst du statt der while Schleife im Thread z.B. gleich WM_TIMER ohne Thread verwenden.

    So wie du auf 'bdx' und 'bdy' zugreifst ist das schonmal nicht sicher, zumindest nicht wenn du aus dem "Haupt Thread" auch darauf zugreifst.

    Und: das UpdateWindow wird wohl nicht funktionieren, da die Message in dem Thread verarbeitet werden muss der das Fenster angelegt hat. Da UpdateWindow allerdings die Nachricht _sendet_ wird sie im "Ball Thread" verarbeitet, bzw. eben gerade nicht verarbeitet, weil falscher Thread.

    Anstelle von UpdateWindow kannst du "PostMessage(hwnd, WM_PAINT, 0, 0)" versuchen, aber das ist immer noch ein Hack.
    Die "saubere" Version wäre eine eigene Message zu verwenden mit der du dem "Fenster Thread" mitteilst dass es sich gefälligst neu zu zeichnen hat. In dem Handler für diese eigene Message kannst du dann InvalidateRect() und UpdateWindow() verwenden genau so wie du es aus dem "falschen" Thread heraus versuchst.

    Und genau weil das alles so ein riesen Quargel und viel zu kompliziert ist: verwende dafür keinen eigenen Thread sondern mach brav allen im Hauptthread.

    Für mich schien das ideal, da ich auch den Thread stoppen (Pause) kann und das so mit der Bedienung des Spiels nebenherläuft.

    Ach, funktioniert es denn???
    BTW: du glaubst nur dass es ideal ist weil du noch nicht genug Erfahrung mit sowas hast, bzw. dich mit Threads bzw. Threads unter Windows nicht wirklich auskennst.


  • Mod

    Anmerkung: Die beiden Aufrufe InvalidateRect und UpdateWindow lassen sich auch durch RedrawWindow einfach zusammenfassen.



  • so danke erstmal...

    leider bin ich noch nicht viel weiter gekommen, weil das PostMessage() und das RedrawWindow() irgendwie nicht funktioniert (wird wahrscheinlich mein Fehler sein).

    daher würde ich mich freuen, wenn mir jemand hilft, da mein ball immernochnicht akutalisiert wird.

    ach ja wenn ich die deklaration von HWND hwnd ganz oben mache, dann wird mein ball schön aktualisiert, aber leider mit ihm auch der ganze desktop und das nervt ein bisschen.

    also es wäre schön wenn mir noch jemand helfen könnte, mein fenster aus dem Thread heraus neu zu zeichnen (wenn es mit thread nicht geht, würde eine Lösung ohne auch gehen, hätte aber lieber die mit Thread)

    so hier folgt der komplette code, ach ja den code für die bilder habe ich ein bisschen zusammenkopiert, weil der erst nicht richtig wollte und die rc datei brauch ich ja nicht extra posten, steht ja nicht viel drin.

    danke

    #include <windows.h>
    #include <process.h>
    //#include "PongPing0.7b.rc"
    
    /*  Declare Windows procedure  */
    LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
    
    /*  Make the class name into a global variable  */
    char szClassName[ ] = "Pong Ping V0.7";
    static int                   bx=100,by=100,bdx=3, bdy=5;
    //HWND   hwnd;   
    
    int WINAPI WinMain (HINSTANCE hThisInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR lpszArgument,
                        int nFunsterStil)
    
    {
        HWND hwnd;               /* This is the handle for our window */
        MSG messages;            /* Here messages to the application are saved */
        WNDCLASSEX wincl;        /* Data structure for the windowclass */
    
        /* The Window structure */
        wincl.hInstance = hThisInstance;
        wincl.lpszClassName = szClassName;
        wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
        wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
        wincl.cbSize = sizeof (WNDCLASSEX);
    
        /* Use default icon and mouse-pointer */
        wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
        wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
        wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
        wincl.lpszMenuName = NULL;                 /* No menu */
        wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
        wincl.cbWndExtra = 0;                      /* structure or the window instance */
        /* Use Windows's default color as the background of the window */
        wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
    
        /* Register the window class, and if it fails quit the program */
        if (!RegisterClassEx (&wincl))
            return 0;
    
        /* The class is registered, let's create the program*/
        hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               "Pong Ping V0.7",       /* Title Text */
               WS_OVERLAPPEDWINDOW, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               700,                 /* The programs width */
               600,                 /* and height in pixels */
               HWND_DESKTOP,        /* The window is a child-window to desktop */
               NULL,                /* No menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
               );
    
        /* Make the window visible on the screen */
        ShowWindow (hwnd, nFunsterStil);
    
        /* Run the message loop. It will run until GetMessage() returns 0 */
        while (GetMessage (&messages, NULL, 0, 0))
        {
            /* Translate virtual-key messages into character messages */
            TranslateMessage(&messages);
            /* Send message to WindowProcedure */
            DispatchMessage(&messages);
        }
    
        /* The program return-value is 0 - The value that PostQuitMessage() gave */
        return messages.wParam;
    }
    
        VOID Thread(PVOID pvoid)
        {
    
          //HWND hwnd;
          HDC hdc;
    
             while(true)
             {           
              bx=bx+bdx;
              by=by+bdy; 
    
              if(by>=585 || by<=10)
              {
                bdy=bdy*-1;        
              }         
    
              if(bx>=685 || bx<=0)
              {
                bdx=bdx*-1;        
              }         
    
              //InvalidateRect(hwnd, NULL, TRUE);
              //UpdateWindow(hwnd);
              //PostMessage(hwnd, WM_PAINT, 0, 0);
              //RedrawWindow(hwnd,NULL,NULL,NULL);
    
              Sleep(10);
             }  
         }
    
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        static HBITMAP               hBitmapKicker, hBitmapBall;
               BITMAP                bitmapkicker,bitmapball;
               static int            cxClient, cyClient,cxkickerSource,cykickerSource,cxballSource,cyballSource;
               HDC                   hdc, hdcKicker, hdcBall, hdcWindow;
               HINSTANCE             hInstance;
        static int                   kx=250,ky=500;
               PAINTSTRUCT            ps;    
    
        switch (message)                  /* handle the messages */
        {
            case WM_CREATE:
              hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
    
              hBitmapKicker = LoadBitmap (hInstance, TEXT ("KickerNormal")) ;
              hBitmapBall = LoadBitmap (hInstance, TEXT ("Ballgelb")) ;
    
              GetObject (hBitmapKicker, sizeof (BITMAP), &bitmapkicker) ;
              GetObject (hBitmapBall, sizeof (BITMAP), &bitmapball) ;
    
              cxkickerSource = bitmapkicker.bmWidth ;
              cykickerSource = bitmapkicker.bmHeight ;
    
              cxballSource = bitmapball.bmWidth ;
              cyballSource = bitmapball.bmHeight ;
    
              _beginthread(Thread,0,NULL);
    
              return 0 ;
    
            case WM_SIZE:
              cxClient = LOWORD (lParam) ;
              cyClient = HIWORD (lParam) ;
              return 0 ;
    
            case WM_PAINT:
              hdc = BeginPaint (hwnd, &ps) ;
              hdcKicker = CreateCompatibleDC (hdc) ;
              hdcBall = CreateCompatibleDC (hdc) ;
    
              SelectObject (hdcKicker, hBitmapKicker) ; //kicker =150x29
              BitBlt (hdc, kx,ky,cxkickerSource, cykickerSource, hdcKicker, 0, 0, SRCCOPY) ;
    
              SelectObject (hdcBall, hBitmapBall) ;      //ball =13x13        
              BitBlt (hdc, bx,by,cxballSource, cyballSource, hdcBall, 0, 0, SRCCOPY) ;
    
              DeleteDC (hdcKicker) ;
              DeleteDC (hdcBall) ;
              EndPaint (hwnd, &ps) ;
              return 0 ;
    
            case WM_KEYDOWN:
            {
                 switch (wParam)
                 {
                 case VK_LEFT:
                      if(kx>0)
                      {
                      kx=kx-12;
                      }
                      break;
    
                case VK_RIGHT:
                     if(kx<540)
                      {
                      kx=kx+12;
                      }
                     break;
    
             default:
                return 0;
             }
             InvalidateRgn(hwnd, NULL, TRUE);
             return 0;
          }
    
            case WM_DESTROY:
              DeleteObject (hBitmapKicker) ;
              DeleteObject (hBitmapBall) ;
              PostQuitMessage (0) ;
              return 0 ;
    
            default:                      /* for messages that we don't deal with */
                return DefWindowProc (hwnd, message, wParam, lParam);
        }
    
        return 0;
    }
    

    danke



  • vergiss bitte einfach dass es sowas wie threads gibt. du machst so grundlegende dinge falsch, beschäftige dich erstmal damit ordentlich programmieren zu lernen. DANN sieh dir threads an wenns denn unbedingt sein muss.



  • 1. du hast recht das ich mich mit threads noch nicht beschäftigt habe (ausser ein bisschen in Java)

    Aber so wie ich das sehe ist mein problem auch nicht unbedingt der thread, ich habe vorhin auch schon ein bisschen anders rumprobiert (ohne Thread) und ich habe einfach ein problem, weil ich nicht weiß, wie ich zB aus WM_TIMER WM_PAINT aufrufe
    und soweit ich weiß brauche ich ja PostMessage() oder SendMessage() und die Funktion will wieder den handle meines Fensters, da würde ich sagen das es hwnd ist aber das funktioniert wieder nicht...

    Genauso weiß ich auch nicht wie ich den handle ausserhalb der WndProc Funktion und der Winapi Funktion bekomme, bei HWND hwnd; bekomme ich den handle des gesamten desktops, aber ich will ja nur den meines programms

    also ich habe quasi ein Problem und suche eine Lösung bzw. Hilfe, ich habe auch schon 2 Bücher gewälzt und mir die finger warm gegoogelt und das ohne ERFOLG

    aber sry wenn ich versucht habe in einem forum hilfe zu finden, ich habe einfach gedacht, dasd ich einen kleinen fehler habe, ein hilfbereiter kommt, eben eine funktion einsetzt die ich übersehen hatte und dann alles läuft...



  • Du kannst z.B. bei _beginthread() das gültige hWnd mit übergeben und bei deiner Thread-Funktion aus pvoid wieder rausfischen.

    Bisher deklarierst du da zwar ein hwnd, aber initialisiert es nirgends 😉



  • klar ich kann es ja übergeben, gar nicht dran gedacht

    tausend dank, es läuft jetzt genau so wie ich es wollte...

    aber echt genau so einen denkanstoss habe ich gesucht 👍

    danke nochmal geeky...

    @hustbaer so einen denkanstoss hättest du mir doch auch geben können, dann wäre es viel einfacher gewesen

    danke auch an die anderen 👍



  • Ehrlich gesagt hab' ich den Window Handle Patzer übersehen, und bin dann auf dumme Ideen gekommen.
    Es geht natürlich in einem anderen Thread Funktionen wie InvalidateRect etc. aufzurufen, es ist nur trotzdem meistens keine gute Idee.

    Dein Programm hat z.B. einen "Schönheitsfehler" (milde gesagt), nämlich beendest du den "Ball Thread" niemals. Da du aus dem "Ball Thread" aber auf dein Fenster zugreifst ... solltest du ihn beenden bevor du das Fenster schliesst. Würdest du diesen "Schönheitsfehler" beheben wollen sähe die "naive" Variante wahrsheinlich etwa so aus:

    HANDLE g_endBallThreadEvent = 0;
    HANDLE g_ballThread = 0;
    
    //...
    
    int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)
    {
        // ...
        g_endBallThreadEvent = CreateEvent(0, true, 0, 0);
    
        // ...
        // message loop hier
        // ...
    
        // cleanup:
        CloseHandle(g_endBallThreadEvent);
        CloseHandle(g_ballThread);
    
        // ...
    }
    
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        // ...
              //_beginthread(Thread,0,NULL);
              g_ballThread = (HANDLE) _beginthreadex(...);
        // ...
    
            case WM_DESTROY:
              // zuerst thread beenden, dann erst fenster schliessen:
              SetEvent(g_endBallThreadEvent);
              WaitForSingleObject(g_ballThread, INFINITE);
              // ...
    }
    
    unsigned Thread(PVOID pvoid)
    {
        // ...
        while (WaitForSingleObject(g_endBallThreadEvent, 0) != WAIT_OBJECT_0)
        {           
            // ...
            InvalidateRect(hwnd, ...);
        }
    }
    

    Sieht noch halbwegs einfach aus. Was man aber auf den ersten Blick nicht unbedingt sieht: wenn der "Ball Thread" gerade dabei ist InvalidateRect aufzurufen, während der Hauptthread in "WaitForSingleObject(g_ballThread, INFINITE);" wartet ... wird das Progamm "feststecken", und du hast einen Deadlock gebaut.

    Warum? InvalidateRect "sendet" die Message an das Fenster. Da das Fenster zu diesem Zeitpunkt noch existiert versucht das Betriebssystem eben die Message zu "senden", und "senden" bedeutet dass darauf gewartet wird dass die Message vom Hauptthread (dem das Fenster gehört) verarbeitet wurde. Der Hauptthread wartet allerdings gerade in "WaitForSingleObject", und in "WaitForSingleObject" verarbeitet er eben keine Messages, auch nicht welche die "gesendet" werden. Ergo wartet der "Ball Thread" darauf dass der Hauptthread seine Message verarbeitet, und der Hauptthread darauf dass sich der "Ball Thread" beendet. Der "Hauptthread" verarbeitet allerdings keine Messages mehr bevor der "Ball Thread" sich nicht beendet hat, und der "Ball Thread" beendet sich nicht bevor der "Hauptthread" nicht seine Message verarbeitet hat. Ein klassischer Deadlock.

    Natürlich gibt es Möglichkeiten das zu fixen, aber dadurch wird alles bloss noch viel komplizierter.

    Glaubst du mir jetzt dass Threads die Hölle sind (zumindest wenn man korrekte Programme schreiben will, die u.a. auch immer so funktionieren wie sie sollten)?


  • Mod

    @hustbaer:
    InvalidateRect versendet keine Nachricht und kann auch von eionem fremden Thread ohne Gefahr aufgerufen werden! Anders UpdateWindow/RedrawWindow!



  • Ok, in dem Fall würde InvalidateRect noch gehen aber der Aufruf von UpdateWindow (der im Source Code des OP ja auch enthalten ist) würde den Deadlock komplett machen.

    Worauf ich eigentlich hinaus wollte: mehrere Threads + Fenster = schwierig. Gewisse Dinge wie PostMessage kann man eigentlich immer gefahrlos machen, bei anderen Funktionen wird's schnell kompliziert.

    p.S.: sorry für den faktischen Fehler 😞



  • ok, kein problem

    danke für den code ich werd ihn mir nachher mal in ruhe anschauen... 👍

    PS: ich bin momentan dabei sound und punkte einzubauen und die Optik des Spiels zu verbessern, wahrscheinlich treffe ich bald wieder auf probleme, dann meld ich mich nochmal... 😉


  • Mod

    @hustbaer: Leider geht das aus den API Funktionen oft nicht hervor, dass Windows Nachrichten durch sie erzeugt werden. Dadurch kann man sich hier schon leicht vertun.



  • Martin Richter schrieb:

    @hustbaer: Leider geht das aus den API Funktionen oft nicht hervor, dass Windows Nachrichten durch sie erzeugt werden. Dadurch kann man sich hier schon leicht vertun.

    Genau deswegen hab' ich ja vermutet dass InvalidateRect eine Message senden würde - weil so viele andere Fenster-Manipulations-Funktionen es machen ohne dass es explizit irgendwo stehen würde.


Anmelden zum Antworten