Ergebnis 1 bis 8 von 8

Thema: Problem mit seltsamen Fehlern in GUI-Anwendung [c++ | WinAPI]

  1. #1

    Problem mit seltsamen Fehlern in GUI-Anwendung [c++ | WinAPI]

    Hallöchen,

    bei einigen meiner Programme stelle ich in letzter Zeit seltsames Verhalten fest. Dies tritt immer auf wenn man zu viele Bitmaps auf ein Fenster zeichnet. Ich hab zuerst vermutet, dass es daran liegen könnte, dass ich die DeviceContexts nicht freigegeben hatte nach jeder Zeichenoperation, aber der Fehler tritt immer noch auf trotz (afaik) korrekt freigegebener DCs.

    Was genau ist denn der Fehler, werdet ihr euch fragen. Das kann ich auch nicht so recht beantworten. Nach einiger Zeit wird plötzlich das ganze Bild weiß und alle GUIs der laufenden Programme sind für kurze Zeit zerschossen ("invalid" und müssen neugezeichnet werden, etwa durch minimieren und erneut wiederherstellen) (Also keine schlimmen Fehler die Schäden hinterlassen könnten, nur eben seltsam und keinesfalls akzeptabel.)
    Probiert es bitte mal aus, ob es bei euch genauso aussieht und vielleicht findet ja jemand hier den Fehler. Um diesen Fehler hervorzurufen müsst ihr im Programm wie in einem Zeichenprogramm mit der Maus in dem grauen Kästchen "malen". Also linke Maustaste gedrückt halten und innerhalb des Kästchens rumfahren. Dann zeichnen sich dort Bitmaps ab. Wenn ihr das lange genug macht tritt plötzlich dieser Fehler auf.
    (EDIT: Es reicht sogar aus wenn man nur das Fenster eine Zeit lang hin und her verschiebt. Irgendwann flimmert die GUI und das Fenster wird seltsam zerstückelt).

    Wie im Titel geschrieben verwende ich C++ mit der WinAPI.

    Das Programm zum Download: http://derfeind2k.de/daten/project1.exe

    Dies ist der Programmcode:
    Code:
    /*  Programm: First Window
        Author:   Stefan Hof
        Date:     5. January 2004
    */
    
    #include <windows.h>
    #include <iostream.h>
    #include <fstream.h>
    #include <string.h>
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    HINSTANCE h_areainstance;
    HWND hwnd, hwnd_maparea;
    
    char cords[20][20];
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int showpref)
    {
        static char AppName[] = "WinTextedit 1.0";
        MSG msg;
        WNDCLASS wndclass;
    
        wndclass.style = NULL;
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = hInstance;
        wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH) COLOR_MENU + 3;
        wndclass.lpszMenuName = NULL;
        wndclass.lpszClassName = AppName;
        
        if (RegisterClass(&wndclass) == false)
        {
            MessageBox(NULL, "Es ist ein Fehler aufgetreten. ", "Error! ", MB_ICONERROR);
            return 0;
        }
        
        hwnd = CreateWindow (AppName,
                             TEXT("WinProgramm 1.0"),
                             WS_OVERLAPPEDWINDOW,
                             100,
                             100,
                             500,
                             500,
                             NULL,
                             NULL,
                             hInstance,
                             NULL);
    
        ShowWindow(hwnd, SW_SHOWNORMAL);
        UpdateWindow(hwnd);
        
        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    
        HDC hdc_winclient, hdc_bmp_mem;
        HBITMAP hbitmap;
        hbitmap = LoadBitmap(NULL, (char*)OBM_CHECK);
        int mouse_x, mouse_y;
        PAINTSTRUCT ps;
    
        switch(message)
        {
       
        case WM_CREATE:
    
            hwnd_maparea = CreateWindow("STATIC",
                                        "maparea",
                                        WS_CHILD | WS_VISIBLE | SS_GRAYFRAME,
                                        100, 30, 200, 200,
                                        hwnd,
                                        HMENU(0),
                                        h_areainstance,
                                        NULL);
    
            return 0;
    
    
        case WM_COMMAND:
            return 0;
            
        
        case WM_PAINT:
            hdc_winclient = BeginPaint(hwnd, &ps);
            hdc_bmp_mem = CreateCompatibleDC(hdc_winclient);
            SelectObject(hdc_bmp_mem, hbitmap);
    
            for(int x = 0; x<20; x++)
            {
                for(int y = 0; y<20; y++)
                {
                    if (cords[x][y] == 1)
                    {
                        BitBlt(hdc_winclient, (x * 10) +100, (y * 10)+30, 40, 40, hdc_bmp_mem, 0, 0, SRCCOPY);                    
                    }
                }
            }
        
            EndPaint(hwnd, &ps);
            DeleteDC(hdc_bmp_mem);
            return 0;
    
    
        case WM_LBUTTONDOWN:
            mouse_x = LOWORD(lParam);
            mouse_y = HIWORD(lParam);
    
            if ((mouse_x >= 100) and (mouse_x <= 290) and (mouse_y >= 30) and (mouse_y <= 220))
            {
            
                cords[(mouse_x - 100) / 10][(mouse_y - 30) / 10] = 1;
                
                hdc_winclient = GetDC(hwnd);
                hdc_bmp_mem = CreateCompatibleDC(hdc_winclient);
                SelectObject(hdc_bmp_mem, hbitmap);
                BitBlt(hdc_winclient, (mouse_x / 10)*10, (mouse_y / 10) *10, 40, 40, hdc_bmp_mem, 0, 0, SRCCOPY);
                ReleaseDC(hwnd, hdc_winclient);
                DeleteDC(hdc_bmp_mem);
            }
            return 0;
            
            
        case WM_MOUSEMOVE:
            if (wParam == MK_LBUTTON)
            {
                mouse_x = LOWORD(lParam);
                mouse_y = HIWORD(lParam);
            
                if ((mouse_x >= 100) and (mouse_x <= 290) and (mouse_y >= 30) and (mouse_y <= 220))
                {
            
                    cords[(mouse_x - 100) / 10][(mouse_y - 30) / 10] = 1;
                    
                    hdc_winclient = GetDC(hwnd);
                    hdc_bmp_mem = CreateCompatibleDC(hdc_winclient);
                    SelectObject(hdc_bmp_mem, hbitmap);
                    BitBlt(hdc_winclient, (mouse_x / 10)*10, (mouse_y / 10) *10, 40, 40, hdc_bmp_mem, 0, 0, SRCCOPY);
                    ReleaseDC(hwnd, hdc_winclient);
                    DeleteDC(hdc_bmp_mem);
    
                }   
            }
            return 0;     
    
        case WM_DESTROY:
            DeleteObject(hbitmap);
            PostQuitMessage(0);    
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    Bitte überprüft das mal, ob der Fehler nur bei mir auftritt oder woran es liegen könnte.

    mfg.

    Sunny

    Geändert von Ynnus (06.01.2005 um 02:45 Uhr)

  2. #2
    Ich kenn mich bei Winapi zwar nicht so gut aus aber ersetze mal:
    Code:
        HBITMAP hbitmap;
        hbitmap = LoadBitmap(NULL, (char*)OBM_CHECK);
    durch:
    Code:
        static HBITMAP hbitmap = LoadBitmap(NULL,(char*)OBM_CHECK);
    btw.: Ich finde es eleganter wenn du für das Child Window eine eigene Callback Funktion deklarierst, das erspart dir dann die Abfragen der Mauskoordinaten.

    btw².: Du solltest bei deinen Header Dateien imho darauf achten immer zb.: #include<iostream>, #include <fstream> und dann using namespace std; zu machen, anstatt #include<iostream.h>..., da das neuer Standard ist.

  3. #3
    Jo, irrer fehler der einem den Angstschweis auf die Stirn schiessen lässt O.ô

    Ich bin in C noch nicht so durch und glaube auch nicht das es was hilft, aber versuch doch mal deinen DC aus dem PAINT event raus zu nehmen. Sprich 'CreateCompartibleDC, SelectObject und DeleteDC' global zu machen wie dein hBitmap Object.

  4. #4
    Zitat Zitat
    btw.: Ich finde es eleganter wenn du für das Child Window eine eigene Callback Funktion deklarierst, das erspart dir dann die Abfragen der Mauskoordinaten.
    Ich bin in der WinAPI leider noch nicht so erfahren und hab bisher nur simple Fenster aufgebaut. Wie würde denn ein programm mit 2 Callbacks aussehen? Bisher werden alle Messages aller Fenster im gleichen Callback aufgefangen und verarbeitet. Wenn ich eine weitere WndProc erstellen will für das Childwindow, müsste ich dann für das Childwindow eine neue Fensterklasse registieren mit diesem Zeug hier wieder: ?
    Code:
        MSG msg;
        WNDCLASS wndclass;
    
        wndclass.style = NULL;
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = hInstance;
        wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH) COLOR_MENU + 3;
        wndclass.lpszMenuName = NULL;
        wndclass.lpszClassName = AppName;
        
        if (RegisterClass(&wndclass) == false)
        {
            MessageBox(NULL, "Es ist ein Fehler aufgetreten. ", "Error! ", MB_ICONERROR);
            return 0;
        }
    So hab ich ja die WndProc-Funktion dieser Fensterklasse zugewiesen. (Wenn ich das alles so recht verstanden habe. )

    Eigentlich soll die Malen-operation ja auch nur auf dem Childwindow geschehen, sodass nur Mausklicks auf dem Childwindow registriert werden, bisher empfange ich allerdings alle Mausklicks und prüfe auf die Koordinaten und lasse dann gegebenenfalls das Bitmap zeichnen.

    Noch eine kleine andere Frage - wie kann man innerhalb der WndProc erfahren welches Fenster (in meinem Falle Childwindow (hwnd_maparea) oder Parentwindow (hwnd)) diese aktuelle Message gesendet hat?


    Nochmal betreffend dem alten Problem, es war tatsächlich so dass ich bei jeder Message das Bitmap neu eingeladen habe und dies bei einer Menge von ein paar hundert oder tausend Messages irgendwann diesen Fehler hervorgerufen hatte. Danke für diese Erkenntniss, ich hab die Einladefunktion jetzt in die WM_CREATE Message gepackt sodass es in jedem Fall nur einmalig eingeladen wird.

  5. #5
    aff ich hasse Winapi ;_;

    Code:
    /*  Programm: First Window
        Author:   Stefan Hof
        Date:     5. January 2004
    */
    
    #include <windows.h>
    #include <iostream>
    #include <fstream>
    #include <string.h>
    
    using namespace std;
    
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    LRESULT CALLBACK ChildProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam);
    HINSTANCE h_areainstance;
    HWND hwnd, hwnd_maparea;
    
    const char szChildName[]="STATIC";
    
    char cords[20][20];
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int showpref)
    {
        static char AppName[] = "WinTextedit 1.0";
        MSG msg;
        WNDCLASS wndclass;
    
        wndclass.style = 0;
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = hInstance;
        wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH) COLOR_MENU + 3;
        wndclass.lpszMenuName = NULL;
        wndclass.lpszClassName = AppName;
        
        if (RegisterClass(&wndclass) == false)
        {
            MessageBox(NULL, "Es ist ein Fehler aufgetreten. ", "Error! ", MB_ICONERROR);
            return 0;
        }
        wndclass.hbrBackground       = (HBRUSH) COLOR_MENU + 3;
    	wndclass.hIcon               = NULL;
    	wndclass.lpfnWndProc         = ChildProc;
    	wndclass.lpszClassName       = szChildName;
        
        
        if (RegisterClass(&wndclass) == false)
        {
            MessageBox(NULL, "Es ist ein Fehler aufgetreten. ", "Error! ", MB_ICONERROR);
            return 0;
        }
        
    	hwnd = CreateWindow (AppName,
                             TEXT("WinProgramm 1.0"),
                             WS_OVERLAPPEDWINDOW,
                             100,
                             100,
                             500,
                             500,
                             NULL,
                             NULL,
                             hInstance,
                             NULL);
    
        ShowWindow(hwnd, SW_SHOWNORMAL);
        UpdateWindow(hwnd);
        
        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    
        switch(message)
        {
       
        case WM_CREATE:
    
            hwnd_maparea = CreateWindow(szChildName,
                                        "maparea",
                                        WS_CHILD | WS_VISIBLE | SS_GRAYFRAME,
                                        100, 30, 200, 200,
                                        hwnd,
                                        NULL,
                                        ((LPCREATESTRUCT)lParam)->hInstance,
                                        NULL);
    
            return 0;
    
    
            
         case WM_DESTROY:
            PostQuitMessage(0);    
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    
    LRESULT CALLBACK ChildProc(HWND hWnd,UINT message, WPARAM wParam,LPARAM lParam){
        static int xOffset=100,yOffset=30;
    	HDC hdc_winclient, hdc_bmp_mem;
        static HBITMAP hbitmap = LoadBitmap(NULL,(char*)OBM_CHECK);
        int mouse_x, mouse_y;
        PAINTSTRUCT ps;
        
        switch(message){
    
        case WM_PAINT:
            hdc_winclient = BeginPaint(hwnd, &ps);
            hdc_bmp_mem = CreateCompatibleDC(hdc_winclient);
            SelectObject(hdc_bmp_mem, hbitmap);
            for(int x = 0; x<20; x++)
            {
                for(int y = 0; y<20; y++)
                {
                    if (cords[x][y] == 1)
                    {
                        BitBlt(hdc_winclient, (x*10)*10+xOffset, (y*10)+yOffset, 40, 40, hdc_bmp_mem, 0, 0, SRCCOPY);                    
                    }
                }
            }
        
            EndPaint(hwnd, &ps);
            DeleteDC(hdc_bmp_mem);
            return 0;
    
    
        case WM_LBUTTONDOWN:
            mouse_x = LOWORD(lParam);
            mouse_y = HIWORD(lParam);      
            cords[mouse_x/10][mouse_y/10] = 1;
            hdc_winclient = GetDC(hwnd);
            hdc_bmp_mem = CreateCompatibleDC(hdc_winclient);
            SelectObject(hdc_bmp_mem, hbitmap);
            BitBlt(hdc_winclient, (mouse_x / 10)*10+xOffset, (mouse_y / 10) *10+yOffset, 40, 40, hdc_bmp_mem, 0, 0, SRCCOPY);
            ReleaseDC(hwnd, hdc_winclient);
            DeleteDC(hdc_bmp_mem);
            return 0;
            
            
        case WM_MOUSEMOVE:
            if (wParam == MK_LBUTTON)
            {
                mouse_x = LOWORD(lParam);
                mouse_y = HIWORD(lParam);     
           
                cords[mouse_x/10][mouse_y/10] = 1;
                 
                hdc_winclient = GetDC(hwnd);
                hdc_bmp_mem = CreateCompatibleDC(hdc_winclient);
                SelectObject(hdc_bmp_mem, hbitmap);
                BitBlt(hdc_winclient, (mouse_x / 10)*10+xOffset, (mouse_y / 10) *10+yOffset, 40, 40, hdc_bmp_mem, 0, 0, SRCCOPY);
                ReleaseDC(hwnd, hdc_winclient);
                DeleteDC(hdc_bmp_mem);
            }
            return 0;     
    
        case WM_DESTROY:
            DeleteObject(hbitmap);
            PostQuitMessage(0);    
            return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    Also das wäre der Code für das Child Window in einer eigenen Callbackfunktion. Irgendwie ist dabei der Rahmen verschwunden, ich hab leider keine Ahnung warum o_O'.
    Du musst wie du gesagt hast wieder ne neue Fensterklasse registrieren, dann wie gehabt im WM_CREATE aufrufen.

  6. #6
    Sehr schön, es funktioniert soweit.

    Jetzt hab ich aber noch eine Frage bezüglich der Instanzen:
    Code:
            hwnd_maparea = CreateWindow(szChildName,
                                        "maparea",
                                        WS_CHILD | WS_VISIBLE | SS_GRAYFRAME,
                                        100, 30, 200, 200,
                                        hwnd,
                                        NULL,
                                        ((LPCREATESTRUCT)lParam)->hInstance,
                                        NULL);
    Mich interessiert dabei die zweite Zeile von unten. Was genau bedeutet denn diese Zeile mit dem Pfeil? (->) ?
    Wenn man da eine neue Instanz angibt funktioniert das Programm auch (scheinbar?) einwandfrei. Was bewirkt denn die Zeile?

    Und, wie verarbeitet das Programm jetzt die Messages zu den unterschiedlichen WinProcs?
    In dieser Zeile...:
    Code:
        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    ...wird ja die Message an die jeweilige WinProc weitergeleitet. Entscheidet das Programm hier selbstständig welche WndProc die richtige für die Nachricht ist? Denn durch den 2ten Parameter "NULL" nimmt er ja alle Nachrichten jedes Fensters an. Wenn man da das Handle von "hwnd" einträgt dann müsste er ja normalerweise nur noch dessen Messages abarbeiten. Das Zeichnen, welches in der ChildProc abgearbeitet wird, funktioniert aber dennoch noch immer. Liegt das daran dass einfach das Parentwindow die Nachrichten auch an die Childwindows weiterreicht oder womit ist das verknüpft, dass weiterhin zusätzlich auch die Nachrichten des Childwindows abgearbeitet werden? (trotz dem "hwnd" anstelle von "NULL" im GetMessage() Befehl).
    Dort das Handle des Childs (hwnd_maparea) bewirkt dann, dass man nur noch zeichnen kann, alle anderen Fensteraktionen betreffend das Hauptfenster werden ignoriert (Schließen, Minimieren, Verschieben, usw.) Da funktioniert die Trennung also korrekt ohne dass die Nachrichten eines anderen Fensters abgearbeitet werden.

  7. #7
    Also im WM_CREATE ist lParam ein Pointer auf eine LPCREATESTRUCT Struktur. Diese Struktur hat einen HINSTANCE auf dein Programm gespeichert, und mit (LPCREATESTRUCT)lParam machst du eben eine Typumwandlung auf diese Struktur.
    Da das ganze aber nur ein Pointer auf die Struktur ist, musst du auf die Inhalte mit '->' zugreifen.
    Alternativ kann man das aber auch so wie du machen: eine globale HINSTANCE Variable, die du dann immer bei CreateWindow übergibst.

    Zur Verarbeitung:
    Also afaik müsste ja DispatchMessage entscheiden, zu welchen Fenster die Nachrichten gehören, und die entsprechende Callback Funktion aufrufen.
    Ich hab aber auch keine Ahnung, warum das Child aufgerufen wird, trotzdem du hwnd bei GetMessage einträgst =/.

  8. #8

    Da ist der Fehler, ganz offensichtlich

    CHallo.

    Am Anfang deiner WndProc wird immer, warum auch immer, das gemacht:

    hbitmap = LoadBitmap(NULL, (char*)OBM_CHECK);

    Dieses Object/Handle wird nie in deinem Programm zerstört, wodurch dein Programm schon nach kürzester Zeit tausende von GDI-Objecten verwalten muss (TaskManager->Spalten auswählen->GDI-Objecte, da siehst es).

    Das eine DeleteObject(hbitmap) bei WM_DESTROY wird nur aufgerufen, wenn das Fenster zerstört wird, aber bei jeder Nachricht wird eins erzeugt, all right?!

    MfG, obe

    EDIT: ah, sollte wohl nächstes mal alles lesen, bevor ich lospresche.

    PS: Der TaskManager hilft beim Fehlerfinden.

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •