Windows API Bug??
-
Hallo, ich bins mal wieder.
Hab hier einen Quellcode, den ihr mal bitte in Visual C++ 6.0 einfügen (Win32 Anwendung) und ausführen könntet.
Wenn ihr das nicht wollt, dann schaut euch mal bitte den folgenden Code an, und sagt mir (bitte!!!) was der Fehler in diesem Code ist, denn der Button wird nicht versteckt, wenn man auf ihn klickt, obwohl er das eigentlich sollte (hoffe ich habe keinen Fehler beim Copy und Paste reingemacht
):
#include <stdio.h> #include <windows.h> bool bEndApp = false; FILE *Protokoll; HWND hMainHwnd, hGroup, hButton; WNDPROC pFOldGR; const int nButtonID = 99; // == Fensterprozedur ==================== LRESULT CALLBACK WindowProc(HWND hHwnd, UINT Message, WPARAM WParam, LPARAM LParam) { switch(Message) { case WM_COMMAND: { return 0; } break; case WM_CLOSE: { DestroyWindow(hMainHwnd); return 0; } break; case WM_DESTROY: { bEndApp = true; PostQuitMessage(0); return 0; } break; default: break; } return DefWindowProc(hHwnd, Message, WParam, LParam); } // == Groupbox Subclass proc ==================== LRESULT SubProcGR(HWND Hwnd, UINT Message, WPARAM WParam, LPARAM LParam) { switch (Message) { case WM_COMMAND: { if (LOWORD(WParam) == nButtonID) { fprintf(Protokoll, "Button geklickt!\n"); ShowWindow(hButton, SW_HIDE); // Warum wird er nicht versteckt ??? /*MoveWindow(hButton, 80, 80, 40, 20, true);*/ } return 0; } break; default: break; } return CallWindowProc(pFOldGR, Hwnd, Message, WParam, LParam); } // == WinMain-Funktion ==================================== int WINAPI WinMain(HINSTANCE hHinst, HINSTANCE PrevHinst, LPSTR lpcmdline, int ncmdshow) { Protokoll = fopen("Protokoll.txt", "w"); // Protokolldatei kreieren und öffnen MSG Message; WNDCLASSEX WndClass; fprintf(Protokoll, "Start ....\n\n"); WndClass.cbSize = sizeof(WNDCLASSEX); WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hIcon = NULL; WndClass.hIconSm = NULL; WndClass.hInstance = hHinst; WndClass.lpfnWndProc = WindowProc; WndClass.lpszClassName = "Klasse"; WndClass.lpszMenuName = NULL; WndClass.style = CS_HREDRAW | CS_VREDRAW; if (RegisterClassEx(&WndClass) == 0) { fprintf(Protokoll, " Kritischer Fehler: Aufruf von RegisterClass() Hauptfenster fehlgeschlagen.\n"); return 0; } hMainHwnd = CreateWindowEx(0, "Klasse", "Tesssssssst", WS_MAXIMIZE | WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hHinst, NULL); if (hMainHwnd == NULL) { fprintf(Protokoll, " Kritischer Fehler: Aufruf von CreateWindowEx() Hauptfenster fehlgeschlagen.\n"); return 0; } hGroup = CreateWindowEx(0, "BUTTON", " Hallo ", WS_CHILD | BS_GROUPBOX, 60, 60, 300, 300, hMainHwnd, NULL, hHinst, NULL); ShowWindow(hGroup, SW_SHOW); hButton = CreateWindow("BUTTON", " Halllo ", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, 40, 40, 80, 15, hGroup, (HMENU) nButtonID, hHinst, NULL); pFOldGR = (WNDPROC) SetWindowLong(hGroup, GWL_WNDPROC, int (SubProcGR)); while (!bEndApp) { while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&Message); DispatchMessage(&Message); } }; SetWindowLong(hGroup, GWL_WNDPROC, (DWORD) pFOldGR); fclose(Protokoll); return Message.wParam; }
Auch wenn's peinlich für mich wird: Was ist falsch? Wird der Button bei euch auch nicht versteckt, ist dennoch nicht mehr klickbar? Oder kann das an meinem Windows ME liegen?
Übrigens passiert das gleiche, wenn man ihn verschiebt, er ist an der alten Position noch sichtbar.Danke im Voraus,
Jens
-
Der Quelltext ist ja total chaotisch programmiert/formatiert.
-
Der Button wird irgendwie nur nicht aktualisiert. Wenn man mit einem Fenster drüberfährt, ist der Button dannach nicht mehr sichtbar. Ein InvalidateRect nützt aber auch nichts.
Wenn man statt der Groupbox z.B. testweise eine Listbox nimmt, dann verschwindet der Button ordnungsgemäß. Muss wohl an einer bestimmten Eigenschaft der Groupbox liegen.
-
Group boxes don't erase their own backgrounds, in fact, they are
essentially transparent. They rely on the parent window's WM_ERASEBKGND
and WM_PAINT processing to fill in the background.Bau nach dem ShowWindow mal
InvalidateRect(GetParent(Hwnd), NULL, TRUE);
ein
-
@bitte ändern: Sorry! 1. Ich hab nun mal einen komischen, aber immerhin einheitlichen Stil; 2. Die Tabs und Leerzeichen sind etwas falsch umgesetzt worden.
@F4
Ok, das mit dem InvalidateRect() funktioniert in dem Beispiel einwandfrei.
Nur in einem etwas komplexeren Programm, in dem meinetwegen der Button ein Child eines statischen Fensters ist, welches ein child der groupbox und (genau wie diese auch) subclassiert ist, geht das erstmal auch nicht.
Da dacht ich mir, ich probier mal InvalidateRect(GetParent(GetParent(Hwnd)), NULL, true) aus, was auch funktioniert, nur dass eben dann das gesamte Hauptfenster neugezeichnet wird, was man deutlich sehen kann, das Flimmern sieht sicht wirklich gut aus und ist mir auch aus anderen Programmen nicht bekannt.
Warum geht es nicht, wenn man nur die Groupbox mit InvalidateRect() aufruft?
Und was könnte man noch tun?
-
Dann darfst du bei InvalidateRect eben nicht NULL einsetzen, sondern musst dir mit GetWindowRect erst das RECT ermitteln, das neugezeichnet werden muss (du musst diese aber noch in Clientkoordinaten umrechnen
)
-
Oder WM_ERASEBKGND selbst behandeln.
-
Ich hab die Lösung dann doch gefunden!
Danke für die Hinweise! Ich denke, es handelt sich wirklich um einen Windows Bug, deshalb kann ich ja hier mal Posten, was man genau machen muss:
@flenders: Das InvalidateRect() mit Client Rect der Groupbox ist nicht die ganze Wahrheit, man muss noch etwas mehr machen.
@CU: Man muss auf jeden Fall in der subclass proc der Groupbox WM_ERASEBKGND selbst behandeln, und zwar so, wie die MSDN beschreibt (verdammt schwer zu finden!):
Sie sagt nämlich, dass es ein Problem mit Groupboxes gibt, deren Parent Window die Stile WS_CLIPCHILDREN oder WS_CLIPSIBLINGS hat. Komischerweise hat kein einziges Fenster meiner Anwendung diese Stile. Die Lösung, die die MSDN hat, funzt dennoch auch für mein Prog:
case WM_ERASEBKGND: { HBRUSH hBrush, hOldBrush; HPEN hPen, hOldPen; RECT rect; HDC hDC; hDC = GetDC(hWnd); // Obtain a handle to the parent window's background brush. hBrush = GetClassWord(ghWnd, GCW_HBRBACKGROUND); hOldBrush = SelectObject(hDC, hBrush); // Create a background-colored pen to draw the rectangle // borders, where gWindowColor is some globally defined // COLORREF variable used to paint the window's background hPen = CreatePen(PS_SOLID, 1, gWindowColor); hOldPen = SelectObject(hDC, hPen); // Erase the group box's background. GetClientRect(hWnd, &rect); Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom); // Restore the original objects before releasing the DC. SelectObject(hDC, hOldPen); SelectObject(hDC, hOldBrush); // Delete the created object. DeleteObject(hPen); ReleaseDC(hWnd, hDC); // Instruct Windows to paint the group box text and frame. InvalidateRect(hWnd, NULL, FALSE); // Insert code here to instruct the contents of the group box // to repaint as well. return TRUE; // Background has been erased. }
Es ist ein Artikel in der Knowledge Base Artikel ID Q79982.
Man muss statt GetWindowWord() aber GetWindowLong() verwenden und sollte die Brush lieber außerhalb erzeugen, wobei man vorher die Button face color abfrägt und eben eine solche Brush erzeugt.
Na dann guten Abend.
-
Ich denke, es handelt sich wirklich um einen Windows Bug
Nein.
-
This behavior is by design.
-
This behavior is by design.
Stimmt eigentlich, man kann den Windows Machern ja nicht immer nur Fehler unterstellen.