|
JLI Spieleprogrammierung
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
mrxcptn Mini JLI'ler
Alter: 49 Anmeldedatum: 02.06.2008 Beiträge: 12
Medaillen: Keine
|
Verfasst am: 05.06.2008, 15:57 Titel: GDI-Fenster stürzt ab |
|
|
Folgendes Problem:
Habe ein GDI-Fenster geschrieben, dass bei einem Linksklick Text ausgibt, der Maus folgt solange die linke Maustaste geklickt ist und beim loslassen der Taste wieder Text ausgibt.
Es funktioniert auch soweit einwandfrei, mit einer Einschränkung:
Wenn man zu schnell hintereinander auf den selben Punkt klickt, hängt es und zeichnet weder Linien noch gibt es Text aus.
Danach kommt diese Windows-Fehlermeldung "Programm hat ein Problem festgestellt und muss beendet werden".
Außerdem macht das Fenster unter Windows Vista scheinbar Probleme, genauere Angaben hab ich nicht bekommen von meiner Testerin.
Hier mal der Code:
CPP: | /*
Written by mrxcptn. 3.6.08, 23:12 GMT+2
*/
#include<windows.h> //Windows.h einbinden um auf die Funktionen Zugriff zu haben
#include<stdio.h>
//Prototyp von Fenstererzeugung
HWND CreateMainWindow(HINSTANCE hInstanceCreatemw);
//Prototyp von Callback-Funktion
LRESULT CALLBACK MessageHandler(HWND hHandleCallback, UINT MsgNachricht,
WPARAM Parameter1, LPARAM Mauskoordinaten);
//Globale Variable zum Speichern des Fensterhandles
HWND hHandle = 0;
//Deklarieren und initialisieren von zwei Integerpointern, für die Verwendung mit den Heap-Adressen in der Callbackfunktion
int* x_Heap=0;
int* y_Heap=0;
//Main-Funktion unter Windows
int WINAPI WinMain(HINSTANCE hInstance, /*Handle mit dem man auf das Fenster(Die Instanz) des Programms zugreifen kann */
HINSTANCE hPrevInstance, /*Handle, mit dem man feststellen kann ob das Programm noch laeuft */
LPSTR lpArgumente, /*Zeiger auf eine Zeichenkette, in diesem Fall auf Argumente, z.B. aus der Cmdline*/
int nStartstatus) /*Ganzzahl, die Infos darueber enthaelt wie das Programm gestartet werden soll, z.B. maximiert oder minimiert*/
{
/*Init der Handle-Variable mit dem zuraeckgegebenen Handle von CreateMainWindow
da dieser haeufig gebraucht wird. Uebergabe des Programms, das das Fenster verwendet als hInstance*/
hHandle = CreateMainWindow(hInstance);
/*Erstellen einer Fehlermeldung, wenn der Handle leer ist = beim Fenstererstellen ein Fehler
aufgetreten ist.*/
if(0 == hHandle)
{
/*Ausgeben der Fehlermeldung*/
MessageBox(0,/*HWND, Fensterhandle oder 0*/
"Fehler beim Erzeugen des Fensters",/*LPCTSTR, Fenstertext*/
"Programmfehler",/*LPCTSTR, Fenstertitel*/
MB_OK/*UINT, Schaltflaechen der Box, hier nur ein OK-Button*/
);
/*Abbrechen des Programms*/
return 0;
}
else
{
/*Ausgeben einer Erfolgsmeldung mit Uebergabe eines Fensterhandles an MessageBox*/
MessageBox(hHandle, "Fenstererzeugung erfolgreich",
"Fenster",MB_OK);
}
//Struktur mit Informationen zur gesendeten Aktion(Nachricht) Deklarieren, scheinbar Standardstruktur in windows.h
MSG MsgNachricht;
/*Schleife, die so lange laeuft bis das Programm beendet werden soll(Wenn die uebergebene Nachricht WM_QUIT ist)*/
while(GetMessage(&MsgNachricht, NULL, 0,0)) //Solange der Aufruf von GetMessage nicht False zurueckgibt laufen, False kommt bei WM_QUIT
{
//Callback-Funktion benachrichtigen, Uebergabe der Nachricht um passende Reaktion zu bekommen
DispatchMessage(&MsgNachricht);
}
return 0; //Rueckgabewert an Windows, dass das Programm erfolgreich war
}
HWND CreateMainWindow(HINSTANCE hInstanceCreatemw) //Deklaration von CreateMainWindow
{
LOGBRUSH lbPinsel = { BS_SOLID, RGB(255,255,255), 0}; /*Deklarieren von lbPinsel Struktur:
"typedef struct tagLOGBRUSH { UINT lbStyle; COLORREF lbColor; LONG lbHatch;}"*/
/*CBrush cbPinsel; /*Erstellen eines uninitialisierten CBRUSH-Objekts. Struktur:
"CBrush( int nIndex//Hatchstyle, COLORREF crColor//RGBFarbe )";*/
/*cbPinsel.CreateBrushIndirect(&lbPinsel); /*Initialisieren des CBRUSH-Objekts mit den Werten des LOGBRUSH-Objekts*/
WNDCLASSEX FensterKlasseGrundstruktur =
{
sizeof(WNDCLASSEX), /*UINT, Groesse angeben*/
CS_DBLCLKS | CS_HREDRAW | /*UINT,Fenstereigenschaften festlegen, CS_DBLCLKS=Fenster erhaelt Nachricht ueber Doppelclick*/
CS_VREDRAW | CS_OWNDC, /*CS_V(vertikal)/H(horizontal)REDRAW=Zeichnen Fenster neu, wenn sich Hoehe/Breite aendern
CS_OWNDC=Fenster bekommt eigenen Device Context*/
MessageHandler, /*WNDPROC,Zeiger auf die CallBackfunktion, wird erreicht durch Namen der Funktion*/
0, 0, /*int, Koennen genutzt werden um Speicher zu reservieren(z.b. weitere Klasseninfos)*/
hInstanceCreatemw, /*HINSTANCE, Instanz angeben die auf Fenster zugreift*/
LoadIcon(NULL, IDI_WINLOGO), /*HICON, StandardwindowsIcon Laden*/
LoadCursor(NULL, IDC_ARROW), /*HCURSOR, Standardcursor Laden*/
(HBRUSH)CreateBrushIndirect(&lbPinsel), /*HBRUSH, HBRUSH bereitstellen*/
NULL, /*LPCTSTR, Wenn Menue, dann hier, ansonsten NULL*/
"FensterKlasse", /*LPCTSTR, Name der Klasse*/
LoadIcon(NULL, IDI_WINLOGO) /*HICON, Kleines Logo, links neben Fenstertitel*/
};
RegisterClassEx(&FensterKlasseGrundstruktur); /*FensterKlasse registrieren, sodass sie verwendet werden kann, z.B. in CreateWindowEx()*/
return CreateWindowEx(
NULL, /*DWORD, fuer erweiterte Stile, hier keine solche*/
"FensterKlasse", /*LPCTSTR, Namen der verwendeten WNDCLASSEX angeben, definiert in WNDCLASSEX, muss mit RegisterClassEx registriert sein*/
"Einfaches Fenster", /*LPCTSTR, Titel des Fensters*/
WS_OVERLAPPEDWINDOW | /*DWORD, Fenstereigenschaften, WS_OVERLAPPEDWINDOW=Definiert Fenster mit Max/Minbox, Schliesstaste & Systemmenue*/
WS_VISIBLE, /*WS_VISIBLE=Fenster ist von Anfang an sichtbar*/
100, 100, /*int, Anfangsposition, x, y, Ausgehend von links oben*/
1000, 750, /*int, Groesse des Fensters in Pixel, Breite, Hoehe*/
NULL, /*HWND, wenn Fenster Unterfenster eines bereits vorhandenen ist, hier Handler zum Mutterfenster angeben, sonst NULL*/
NULL, /*HWND, Handle des Menues*/
hInstanceCreatemw, /*HINSTANCE, Instanz die das Fenster verwendet angeben*/
NULL /*LPVOID, fuer multi document interface*/
);
}
LRESULT CALLBACK MessageHandler(HWND hHandleCallback, /*Deklarieren der CallbackFunktion MessageHandler HWND, Handle*/
UINT MsgNachricht, /*UINT, Empfangene Nachricht*/
WPARAM Parameter1, /*WPARAM, Gedrueckte Tasten*/
LPARAM Mauskoordinaten) /*LPARAM, Mausposition*/
{
int x,y; //Deklarieren von Variablen für die Mauskoordinaten
x = LOWORD(Mauskoordinaten); //Belegen der x-Mauskoordinate
y = HIWORD(Mauskoordinaten); //Belegen der y-Mauskoordinate
HDC hDeviceContext; //Deklarieren von Variable für Device Context
char szHiergeklickt[]="Hier linke Maustaste geklickt!"; //Strings..
char string[]="Maus bewegt mit gedrueckter linker Maustaste";
char szHierlosgelassen[]="Hier linke Maustaste losgelassen!";
HPEN penBlack = CreatePen(PS_SOLID, 5, RGB(0,0,0)); //Initialisieren eines Stifts, der die Linien zeichnen soll
switch(MsgNachricht) /*switch-Kontrollstruktur mit der Empfangenen Nachricht*/
{
case WM_DESTROY: /*Wenn WM_DESTROY(=Fenster entfernen) als Nachricht empfangen*/
PostQuitMessage(0); /*WM_QUIT(=Programm benden) versenden*/
return 0;
break;
case WM_LBUTTONDOWN: /*Wenn die Linke Maustaste gedrückt wird*/
x_Heap=new int; //Erstellen von 2 Heapobjekten
y_Heap=new int;
*x_Heap=x;//Speichern der Koordinaten auf dem Heap, damit sie nicht verschwinden
*y_Heap=y;
hDeviceContext=GetDC(hHandleCallback); //Device Context abfragen
TextOut(hDeviceContext,x,y,szHiergeklickt,strlen(szHiergeklickt)); //Text ausgeben
return 0;
break;
case WM_LBUTTONUP: /*Wenn die Linke Maustaste losgelassen wird*/
delete x_Heap;//Loeschen der 2 Heapobjekte
delete y_Heap;
hDeviceContext=GetDC(hHandleCallback);
TextOut(hDeviceContext,x,y,szHierlosgelassen,strlen(szHierlosgelassen));
return 0;
break;
case WM_MOUSEMOVE:
if(Parameter1 == MK_LBUTTON)
{
//PAINTSTRUCT psPaintStructure;
hDeviceContext=GetDC(hHandleCallback);
//BeginPaint(hHandleCallback, &psPaintStructure);
//SetPixel(hDeviceContext,x,y,RGB(0,0,0));
TextOut(hDeviceContext,0,0,string,strlen(string));//Text links oben ausgeben
SelectObject(hDeviceContext,penBlack);//Stift auswaehlen
MoveToEx(hDeviceContext,*x_Heap,*y_Heap,NULL);//Zum Ort des Linksklicks bewegen
LineTo(hDeviceContext,x,y);//Linie zu aktuellen Koordinaten ziehen
MoveToEx(hDeviceContext,0,0,NULL);//Wieder zurück nach 0,0 bewegen
*x_Heap=x; *y_Heap=y; //Speichern der letzten Koordinaten in den Heapobjekten, um von dort weiter zu zeichnen
DeleteObject(penBlack);//Stift freigeben
//EndPaint(hHandleCallback, &psPaintStructure);
return 0;
break;
}
}
return DefWindowProc(hHandleCallback, MsgNachricht, Parameter1, Mauskoordinaten); /*Defaultanweisung, Standardfunktion, falls switch noch nichts returned hat*/
} |
Liegt das an meinem Code, wenn ja wo? Bin ich vielleicht falsch mit dem Heap umgegangen, oder kommt das Programm einfach nicht schnell genug nach?
Ich verwende Code::Blocks als IDE, mit dem Visual C++ 2003 Toolkit als Compiler.
Lösung wäre schön, hat aber geringe Priorität. |
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 05.06.2008, 16:46 Titel: |
|
|
Hi,
ich denke das liegt an der Multithreadness der WinProc. Das ist so, dass die WinProc von Windows aufgerufen wird sobald eine Nachricht vorliegt. Da Windows aber ein Multitaskingsystem ist, kann es vorkommen, dass die WinProc von zwei Nachrichten gleichzeitig aufgerufen wird (daher das "zu schnell"). Heißt im Klartext: es kann vorkommen dass erst LBUTTONDOWN gesendet und neuer Speicher zugewiesen wird, dann zur gleichen Zeit schon wieder LBUTTONUP und der Speicher wird freigegeben. Dann wird in LBUTTONDOWN aber auf den wieder freigegebenen Speicher zugegriffen Absturz. Lösung wären z.B. Mutexe benutzen oder eine Prüfung auf x_Heap und y_Heap != 0 in einer der beiden Routinen. Wenn das zu oberflächlich ist sag ruhig, dann zeig ich dir ein beispiel.
Gruß DXer |
|
Nach oben |
|
|
mrxcptn Mini JLI'ler
Alter: 49 Anmeldedatum: 02.06.2008 Beiträge: 12
Medaillen: Keine
|
Verfasst am: 05.06.2008, 18:08 Titel: |
|
|
Danke für die Hilfe.
Also war meine Vermutung schon fast richtig.
Wäre das hier besser?
CPP: | case WM_LBUTTONDOWN:
if(x_Heap==0 &&y_Heap==0)
{
x_Heap=new int;
y_Heap=new int;
*x_Heap=x;
*y_Heap=y;
}
hDeviceContext=GetDC(hHandleCallback);
TextOut(hDeviceContext,x,y,szHiergeklickt,strlen(szHiergeklickt));
return 0;
break;
case WM_LBUTTONUP:
if(x_Heap!=0&&y_Heap!=0)
{ delete x_Heap;
delete y_Heap;
}
hDeviceContext=GetDC(hHandleCallback);
TextOut(hDeviceContext,x,y,szHierlosgelassen,strlen(szHierlosgelassen));
return 0;
break; |
So wie ich das verstanden habe, entsteht dann auch ein Memory Leak, da bereits erstellter Heapspeicher überschrieben wird, oder?
Und wie funktioniert Mutexe? |
|
Nach oben |
|
|
Deviloper Junior JLI'ler
Anmeldedatum: 31.05.2006 Beiträge: 77
Medaillen: Keine
|
Verfasst am: 05.06.2008, 18:59 Titel: |
|
|
Fehlt oben kein TranslateMessage?
Denke aber nicht das es an zu schnell ankommenden Nachrichten liegt. Würde es dann einfach mal mit static machen und Heap komplett rauslassen. Dann siehste ob es daran gelegen hat. |
|
Nach oben |
|
|
DirectXer Dark JLI'ler
Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 05.06.2008, 19:38 Titel: |
|
|
du musst auf jeden fall nach dem delete die beiden zeiger noch auf 0 setzen, da das nicht von alleine geschieht. Bereits erstellter Speicher wird nur überschrieben wenn du zweimal new hintereinander machst (memory leak da der erste nicht freigegeben wurde), aber dabei kommt es noch nicht zum absturz. erst wenn du einen zeiger deletest und ihn dann derefenzierst kommt der absturz. Mutexe sind Objekte, die bei Multitasking andere Variablen davor schützen, dass sie von 2 seiten aus gleichzeitig manipuliert werden. Findest bestimmt hier am Board oder bei Google was interessantes, ist aber in diesem fall eher unwichtig da das hier auch anders geht.
@Deviloper: TranslateMessage ist hier unnötig da nur es acceleration keys übersetzt, die benutzt er aber nicht. Trotzdem kann man es sicherheitshalber dazuschreiben (ist in fast allen winapi-tuts bei). Zu schnelle Nachrichten ist durchausdenkbar, da gerade Methoden wie TextOut etc. etwas länger brauchen und die Routine in windows auch nicht die schnellste ist (hängt mit Prioritäten zusammen). Hinzu kommt auch noch die MOUSEMOVE nachricht zur gleichen zeit (da solltest du übr auch auf != 0 prüfen da du auch hier x_Heap und y_Heap benutzt)
Gruß DXer |
|
Nach oben |
|
|
mrxcptn Mini JLI'ler
Alter: 49 Anmeldedatum: 02.06.2008 Beiträge: 12
Medaillen: Keine
|
Verfasst am: 05.06.2008, 21:34 Titel: |
|
|
Okay, danke für die Tipps. Ich werds morgen ausprobieren. Mutexe werd ich mir auch mal genauer ansehen, ist sicher oft nützlich. |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
Powered by phpBB © 2001, 2005 phpBB Group Deutsche Übersetzung von phpBB.de
|