Messagebox innerhalb BeginPaint und Endpaint erzeugt neuen WM_PAINT

Klaus1311

Grünschnabel
Hallo,

in meinem Programm werden in der WM_PAINT Bearbeitung über Gdiplus Bitmaps aus Dateien geladen und angezeigt. Im Fehlerfall wird eine Meldung mit einer MessageBox gemeldet. Sobald die MessageBox ein Bitmap überdeckt, wird eine neue WM_PAINT Message generiert und nach dem Quittieren der MessageBox wird eine neue MessageBox ausgegeben und das Ganze beginnt wieder von vorne. Den deadlock habe ich gelöst indem ich ich mir die Fehlermeldung als ausgegeben markiere. Aber 2 MessageBoxen für einen Fehler habe ich trotzdem. Das komische dabei ist, bei graphischen Elementen wie Rectangle kann ich dieses Verhalten nicht feststellen. Weiß jemand Rat? Das Programm wurde mit Visual Studio 2008 geschrieben, alles Einstellungen sind Standard mit Ausnahme von Multibyte Zeichensatz.

Gruß
Klaus



Hier ist ein kleines Programm zum Ausprobieren.:

#include "stdafx.h"
#include "messagebox.h"
#include "commctrl.h"
#include <atlimage.h>

#define MAX_LOADSTRING 100

#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "comctl32.lib")

using namespace Gdiplus;


// Globale Variablen:
HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
ULONG_PTR gdiplusToken;
BOOL Do;
int iCnt;

// Vorwärtsdeklarationen der in diesem Codemodul enthaltenen Funktionen:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int paint(HDC hdc);
int get_image_data(HDC hdc,WCHAR *fname);
HANDLE hImage;
HWND hWndMain;





int APIENTRY _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
MSG msg;
HACCEL hAccelTable;

// Globale Zeichenfolgen initialisieren
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_MESSAGEBOX, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

// Anwendungsinitialisierung ausführen:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MESSAGEBOX));

// Hauptnachrichtenschleife:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Gdiplus::GdiplusShutdown(gdiplusToken);
return (int) msg.wParam;
}



//
// FUNKTION: MyRegisterClass()
//
// ZWECK: Registriert die Fensterklasse.
//
// KOMMENTARE:
//
// Sie müssen die Funktion verwenden, wenn Sie möchten, dass der Code
// mit Win32-Systemen kompatibel ist, bevor die RegisterClassEx-Funktion
// zu Windows 95 hinzugefügt wurde. Der Aufruf der Funktion ist wichtig,
// damit die kleinen Symbole, die mit der Anwendung verknüpft sind,
// richtig formatiert werden.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MESSAGEBOX));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MESSAGEBOX);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassEx(&wcex);
}

//
// FUNKTION: InitInstance(HINSTANCE, int)
//
// ZWECK: Speichert das Instanzenhandle und erstellt das Hauptfenster.
//
// KOMMENTARE:
//
// In dieser Funktion wird das Instanzenhandle in einer globalen Variablen gespeichert, und das
// Hauptprogrammfenster wird erstellt und angezeigt.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HDC hdc;

hInst = hInstance; // Instanzenhandle in der globalen Variablen speichern

hWndMain = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,0,CW_USEDEFAULT,0,NULL, NULL,hInstance,NULL);

if (!hWndMain)
{
return FALSE;
}

ShowWindow(hWndMain, nCmdShow);
UpdateWindow(hWndMain);
INITCOMMONCONTROLSEX InitCtrlEx;

InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCtrlEx.dwICC = ICC_BAR_CLASSES;
InitCommonControlsEx(&InitCtrlEx);

hdc = GetDC(hWndMain);
get_image_data(hdc,L"C:\\Test\\Bitmap.jpg");
ReleaseDC(hWndMain,hdc);

return TRUE;
}

//
// FUNKTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// ZWECK: Verarbeitet Meldungen vom Hauptfenster.
//
// WM_COMMAND - Verarbeiten des Anwendungsmenüs
// WM_PAINT - Zeichnen des Hauptfensters
// WM_DESTROY - Beenden-Meldung anzeigen und zurückgeben
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;

switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Menüauswahl bearbeiten:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
iCnt++;
get_image_data(hdc,L"C:\\Test\\Bitmap.jpg"); // diese Datei existiert
get_image_data(hdc,L"C:\\Test\\Bitmap.111"); // diese Datei existiert nicht
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// Meldungshandler für Infofeld.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);

switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}








int get_image_data(HDC hdc,WCHAR *fname)
{
TCHAR string[MAX_LOADSTRING+1];
int flag;
Bitmap *pBitmap = Bitmap::FromFile(fname);

flag = pBitmap->GetLastStatus();
if (flag)
{
sprintf_s(string,MAX_LOADSTRING,_T("Aufgerufen %d mal"),iCnt);
MessageBox(NULL,string,_T("Anzahl der durchlaufenen WM_PAINT"),MB_ICONHAND | MB_TASKMODAL);
}
Rect rect(1,1,650,600);
Graphics g(hdc);
g.DrawImage(pBitmap,rect);
delete pBitmap;

return TRUE;
}
 

Endurion

Erfahrenes Mitglied
Eine MessageBox hat innerhalb eines BeginPaint/EndPaint-Blocks eigentlich auch nichts zu suchen.

Du solltest das Laden der Grafiken in WM_CREATE bzw. WM_INITDIALOG (bei einem Dialog) machen und dir dein Bitmap-Handle aufbewahren (und dann in WM_DESTROY freigeben). Sonst erfolgt bei jedem Neuzeichnen deines Fensters ein Dateizugriff. Das bremst mächtig aus.
 

Klaus1311

Grünschnabel
Hallo Endurion,

vielen Dank für Deine Antwort. Ich weiß dass bei diesem Vorgehen die Datei immer wieder neu geladen wird, aber aus Ablaufgründen geht es nicht anders(es können mehrere Hundert Bitmaps vorliegen und abhängig von bestimmten Anwender Aktionen werden nur einige angezeigt), außerdem können die Bitmap Inhalte durch andere Programme verändert worden sein. Ich möchte dem Anwender aber evtl. Fehler mittteilen. Die Frage ist doch, warum wird bei graphischen Elementen anders reagiert, als bei Bitmaps,

Gruß
Klaus
 

Endurion

Erfahrenes Mitglied
Zeig mal den Code für das Rectangle.

Bei der MessageBox vermute ich ganz stark, dass zusätzlich ein WM_ERASEBKGND ausgelöst wird; das ist beim Rectangle dann eher nicht der Fall (kommt auch drauf an wo und wie du das darstellst).

Tip (unabhängig vom Problem):
Setze das return DefWindowProc bitte nach dem Switch ein, nicht in den default-case. Es gibt Windows-Nachrichten, bei denen return 0 nicht richtig ist.
 

Klaus1311

Grünschnabel
Hallo Endurion,

entschuldige, dass ich erst heute antworte, aber ich war 14 Tage im Urlaub. Hier ist der Code für das Rechteck. (Wenn ich das Rechteck über GDIplus zeichne, werden auch mehrere WM_PAINT aufgerufen).


int get_image_data(HDC hdc,WCHAR *fname)
{
TCHAR string[MAX_LOADSTRING+1];
int flag;
int old_rop;
HGDIOBJ old_brush;
Bitmap *pBitmap = Bitmap::FromFile(fname);

flag = pBitmap->GetLastStatus();
if (flag)
{
sprintf_s(string,MAX_LOADSTRING,_T("Aufgerufen %d mal"),iCnt);
MessageBox(NULL,string,_T("Anzahl der durchlaufenen WM_PAINT"),MB_ICONHAND | MB_TASKMODAL);
}
//Rect rect(1,1,650,600);
//Graphics g(hdc);
old_rop = SetROP2(hdc,R2_BLACK);
old_brush = SelectObject(hdc,hbBlack);
Rectangle(hdc,1,1,650,600);
SelectObject(hdc,old_brush);
SetROP2(hdc,old_rop);
//g.DrawImage(pBitmap,rect);
delete pBitmap;

return TRUE;
}



vielen Dank für Deine Bemühungen und viele Grüße
Klaus