Datum:
Hallo Leute, ich versuche gerade das Beispiel "listing_10.3" aus dem Buch "C++ für Spieleprogrammierer" dahingehend zu ändern, dass für die Funktion "ErstelleSteuerelemente" ein Rückgabewert definiert wird. Prosa hierzu aus dem Buch: "Im Anschluss daran wird die Funktion *ErstelleSteuerelemente" aufgerufen. Wenn man es genau nimmt, dann müsste auch diese Funktion einen Wert zurückliefern, anhand dessen man prüfen kann, ob alles richtig funktioniert hat. Dazu müsste man dann die einzelnen Handles überprüfen, die man in der Funktion ErstelleSteuerelemente zurückgeliefert bekommt." Hier ist der gezeigte Code aus dem Buch
#include <windows.h> // Prototypen LRESULT CALLBACK WindowProc (HWND bWnd, UINT message, WPARAM wParam, LPARAM lParam); HWND ErstelleHauptfenster (HINSTANCE hInst); void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst); // IDs der Child-Fenster #define ID_STATICTEXT 4000 #define ID_EDITBOX 4001 #define ID_BTN_UEBERNEHMEN 4002 #define ID_BTN_BEENDEN 4003 // Globale Variablen HWND hText; // Handle für den eigentlichen Text HWND hEditBox; // Handle für die Editbox HWND hUebernehmen; // Handle für Button "Übernehmen" HWND hBeenden; // Handle für Button "Beenden" // Hauptprogramm int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpcmdline, int ncmdshow) { HWND hWnd; // Fenster-Handle MSG message; // Nachricht // Hauptfenster erstellen hWnd = ErstelleHauptfenster (hInst); // Prüfen, ob das Erstellen des Hauptfensters geklappt hat if (hWnd == NULL) return (0); // Alle Steuerelemente erstellen ErstelleSteuerelemente (hWnd, hInst); // Nachrichten abholen, übersetzen und weiterleiten while (GetMessage (&message, NULL, 0, 0) ) { TranslateMessage (&message); DispatchMessage (&message); } // Programm beenden return (int)(message.wParam); } HWND ErstelleHauptfenster (HINSTANCE hInst) { HWND hWnd; // Fenster-Handle WNDCLASSEX windowsclass; // Struktur für Fenstereigenschaften const char szClassName[] = "Zweites Fenster"; // Klassenname des Fensters frei wählbar windowsclass.cbSize = sizeof (WNDCLASSEX); // Größe der Struktur zwischenspeichern windowsclass.style = CS_HREDRAW | CS_VREDRAW; // Fenster beim Verschieben neu zeichnen windowsclass.lpfnWndProc = WindowProc; // Zeiger auf Callback-Funktion windowsclass.cbClsExtra = 0; // Kein erweiterten Einstellungen windowsclass.cbWndExtra = 0; windowsclass.hInstance = hInst; // Instanz speichern windowsclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); // Icons und Cursor festlegen windowsclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION); windowsclass.hCursor = LoadCursor (NULL, IDC_ARROW); windowsclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND+1; // Hintergrundfarbe windowsclass.lpszMenuName = NULL; // Kein Menü windowsclass.lpszClassName = szClassName; // Klassenname angeben if (!RegisterClassEx (&windowsclass)) return (NULL); // Fensterklasse registrieren hWnd = CreateWindowEx ( NULL, // Fenster erzeugen szClassName, "Eine kleine Anwendung", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, // Windows setzt Fensterposition automatisch 300, 135, // Breite und Höhe NULL, NULL, hInst, NULL); return (hWnd); } // ErstelleHauptfenster // Alle Steuerelemente erstellen void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst) { // Statischen Text als Child-Fenster erstellen hText = CreateWindow ( "STATIC", "Eingegebener Text", WS_VISIBLE | WS_CHILD | ES_CENTER, 0, 0, 300, 20, hWnd, (HMENU)ID_STATICTEXT, hInst, NULL); // Editbox als Child-Fenster erstellen hEditBox = CreateWindow ("EDIT", "Bitte Text eingeben", WS_VISIBLE | WS_CHILD | WS_BORDER, 0, 20, 300, 20, hWnd, (HMENU) ID_EDITBOX, hInst, NULL); // Übernehmen-Button als Child-Fenster erstellen hUebernehmen = CreateWindow ("BUTTON", "Übernehmen", BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, 20, 50, 95, 40, hWnd, (HMENU) ID_BTN_UEBERNEHMEN, hInst, NULL); // Beenden-Button als Child-Fenster erstellen hBeenden = CreateWindow ("BUTTON", "Beenden", BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD, 175, 50, 95, 40, hWnd, (HMENU) ID_BTN_BEENDEN, hInst, NULL); } // ErstelleSteuerelemente // Callback-Funktion zur Nachrichtenverarbeitung LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { // Messages auswerten switch(message) { case WM_DESTROY: // Fensters schließen (auch Alt-F4) { PostQuitMessage (0); // Nachricht zum Beenden schicken return (0); } // Ab hier Nachrichten unserer Child-Fenster bearbeiten case WM_COMMAND: { switch (wParam) { case ID_BTN_UEBERNEHMEN: { char szText[256]; // Text aus der Editbox holen GetWindowText (hEditBox, szText, 256); // Diesen Text in das Label schreiben und den Text der Editbox löschen SetWindowText (hText, szText); SetWindowText (hEditBox, ""); return (0); } case ID_BTN_BEENDEN: { int Resultat; // Rückgabewert der Messagebox // Messagebox für Sicherheitabfrage Resultat = MessageBox (hWnd, "Wirklich beenden?", "Programm beenden", MB_YESNO | MB_ICONQUESTION); if (Resultat == IDYES) { PostQuitMessage (0); // Nachricht zum Beenden schicken return (0); } // Nein, also ganz normal weiter return (0); } } break; } break; } // Die Nachricht wurde nicht von uns bearbeitet, also Windows überlassen return (DefWindowProc (hWnd, message, wParam, lParam) ); } // WindowProc |
Der Autor spricht von mehreren Handles, die man zurückgeliefert bekommt. Ich frage mich nur, wie man die Funktion deklarieren muss, um mehrere Handles zu erhalten? Statt
void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst);
|
so
HWND ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst); |
Kann mir jemand erklären, wie man richtig angeht? VG, Joachim
Datum:
Joachim schrieb: > Der Autor spricht von mehreren Handles, die man zurückgeliefert bekommt. > Ich frage mich nur, wie man die Funktion deklarieren muss, um mehrere > Handles zu erhalten? Nö. Davon spricht er nicht. Er spricht davon, dass in der Funktion ErstelleSteuerelemente eine ganze Latte von CreateWindow Aufrufen gemacht wird, von denen kein einziger überprüft wird, ob sie auch gut gegangen sind. Sprich: Ob die Handles, die sie geliefert haben auch gültig sind. Und er spricht davon, dass die Funktion ErstelleSteuerelemente ihrem Aufrufer mitteilen sollte: Hör mal, ich hab da versucht ein paar Fenster mittels CreateWindow anzulegen, aber da ist was schief gelaufen. Er spricht von 'einem Wert, den man zurückliefern müsste'. Aber an keiner Stelle sagt er, dass dieser Wert der/die Handles selber sein müssen. Nur weil sich das in ErstelleHauptfenster so angeboten hat, heißt das ja noch lange nicht, dass das hier auch so sein muss. Ein BOOL beispielsweise wäre absolut fein.
Datum:
Joachim schrieb: > Kann mir jemand erklären, wie man richtig angeht? Das Buch ist wohl schon etwas älter. Heutzutage macht man das alles mit DirectX.
Datum:
>Ein BOOL beispielsweise wäre absolut fein. OK, dann kann man diese Überprüfungen innerhalb der Funktion vornehmen. Sobald eine Überprüfung fehlschlägt, setze ich den Rückgabewert auf FALSE. Vielen Dank für die Klarstellung. >Das Buch ist wohl schon etwas älter. Es liest sich jedenfalls sehr gut. VG, Joachim
Datum:
CreateWindow liefert NULL, wenns Fenster erstellen schief ging. Wenn man "void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst)" z.b. zu "int ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst)" ändern würde,bei jedem CreateWindow auf NULL prüft und im true-fall ein "return NULL" ausführt und am Ende nach den CreateWindow-aufrufen ein "return 1" einfügen würde, könnte man in der Main mit if prüfen ob alles gut gelaufen ist und mit GetLastError und Formatmessage sogar noch den Anwender anschnauzen und das Programm beenden. Es empfiehlt sich vielleicht das du dir mal die Win32-API besorgst, z.b. die Hilfedatei Win32API-Big. Des Englischen mächtig sein ist aber pflicht. Und Mister DirectX ... Viel Spaß bei Dialogen und DirectX.
Datum:
Hallo, habe jetzt eine Bool-Variable in der Funktion "ErstelleSteuerelemte" eingeführt, die die zurückgelieferten Handles überprüft. Funktioniert, soweit sogut. Nun habe ich im Zusammenhang dieses Beispiels noch eine Frage bezüglich der verwendeten Handles. Im gezeigten Beispiel werden die Handles "hText", "hEditBox", "hUebernehmen", "hBeenden" global deklariert. Möchte man die globalen Deklarationen vermeiden, so müsste man die Handles übergeben. Ich würde also hergehen und die Handles in "WinMain" deklarieren und der Funktion "ErstelleSteuerelemente" übergeben, also so:
//Prototyp bool ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst, HWND hText, HWND hEditBox, HWND hUebernehmen, HWND hBeenden); |
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpcmdline, int ncmdshow) { bool handles_ok; handles_ok = ErstelleSteuerelemente (hWnd, hInst, hText, hEditBox, hUebernehmen, hBeenden); |
Nun frage ich mich, wie die Callback-Funktion auf diese Handles zugreifen kannn, wenn sie nicht global deklariert sind? Danke und VG, Joachim
Datum:
Joachim schrieb: > Nun frage ich mich, wie die Callback-Funktion auf diese Handles > zugreifen kannn, wenn sie nicht global deklariert sind? Und spätestens an dieser Stelle sind wir wieder an dem Punkt angelangt, an dem man ohne ein dezidiertes C-Buch und das damit verbundene Basiswissen nicht mehr weiterkommt. Nichts gegen dein Buch. Nichts gegen Spieleprogrammierung. Aber erst mal kommt: Beherrsche dein Handwerk Im konkreten: Unterschied zwischen 'Call by Value' und 'Call by Reference' und wie sich dieser Unterschied in C manifestiert.
void foo( int i ) { printf( "%d\n", i ); } int main() { foo( 5 ); } |
void foo( int * i ) { *i = 8; } int main() { int j; foo( &j ); printf( "%d\n", j ); } |
Dein Spiele-Buch setzzt voraus, dass du mit C an sich nicht mehr auf Kriegsfuss stehst. Es könnte für dich daher noch ein wenig über deinem allgemeinen C-Niveau sein, was du in Kürze alles sehen wirst.
Datum:
Hallo Karl Heinz, dein Beispiel bezüglich "Call-By-Value" und "Call-By-Referenz" ist mir geläufig. Hierzu gibt es in besagtem Buch auch ein Kapitel, das ich durchgearbeitet habe. Dennoch bin ich mir nicht im Klaren darüber, wie ich dies auf die Handles übertragen kann? VG, Joachim
Datum:
Im Übrigen: Auch wenn das Buch den Titel "C++ für Spieleprogrammierer" trägt, so kann man es als "normales" Buch für C++ sehen,
Datum:
LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); |
Die Funktion "WindowProc" kann doch bezüglich ihrer Parameter nicht erweitert werden? Also wie kann ich hier die Handles als Paramter (sei es als "Call-By-Value" oder als "Call-By-Reference") übergeben? VG, Joachim
Datum:
Joachim schrieb: >
> LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, > LPARAM lParam); > |
> > > Die Funktion "WindowProc" kann doch bezüglich ihrer Parameter nicht > erweitert werden? Was willst du mit der WindowProc? Du hast eine Funktion ErstelleSteuerelemente und die soll jetzt Werte (nämlich die Handle Werte) nach aussen liefern. In völliger Analogie zu meiner 2.ten Funktion foo, die eben einen int nach aussen liefert. Die Funktion WindowProc interessiert hier doch keinen.
Datum:
>> // Globale Variablen >> HWND hText; // Handle für den eigentlichen Text >> HWND hEditBox; // Handle für die Editbox >> HWND hUebernehmen; // Handle für Button "Übernehmen" >> HWND hBeenden; // Handle für Button "Beenden" >> Nun frage ich mich, wie die Callback-Funktion auf diese Handles >> zugreifen kannn, wenn sie nicht global deklariert sind? hab ich was übersehen ? Stefan
Datum:
@Stefan Im Original-Code sind die Handle-Deklarationen global, dies möchte ich nun vermeiden. VG, Joachim
Datum:
Autsch. Mein Fehler. In der Zwischenzeit hat sich das Topic verändert. Ging es ursprünglich im die Init-Sequenz, in der die Fenster erzeugt werden, hast du den Focus ja jetzt auf die Callback Funktion verschoben. Das hab ich nicht mitbekommen. Sorry, für die Konfusion. Die Antowort: keine Chance. Die müssen global sein.
Datum:
>Die Funktion WindowProc interessiert hier doch keinen.
Die Funktion WindowProc verwendet doch bspw. "hEditBox"
...
GetWindowText (hEditBox, szText, 256);
|
Im Falle global deklarierter Handles kennt die Funktion WindowProc den Handle "hEditBox". Aber woher nimmt sie diese Information, wenn die Handles nicht global deklariert sind? Sorry, aber ich stehe auf dem Schlauch. VG, Joachim
Datum:
Aus dem Buch: "Wenn Du in den vorherigen Kapiteln aufgepasst hast, dann weißt Du, dass man globale Variablen meiden sollte, wo es nur geht (und es geht fast immer). Dass diese Regel hier gebrochen wurde ... dass der Quelltext einfacher gehalten und leichter zu lesen sein sollte. Da es in diesem Kapitel keine Aufgabe mehr geben wird, wäre es nicht verkehrt, wenn Du im Anschluss versuchen würdest, das Programm so umzuschreiben, dass die globalen Variablen vermieden werden können." VG, Joachim
Datum:
Joachim schrieb: > Aus dem Buch: > > "Wenn Du in den vorherigen Kapiteln aufgepasst hast, dann weißt Du, dass > man globale Variablen meiden sollte, wo es nur geht (und es geht fast > immer). Dass diese Regel hier gebrochen wurde ... dass der Quelltext > einfacher gehalten und leichter zu lesen sein sollte. Da es in diesem > Kapitel keine Aufgabe mehr geben wird, wäre es nicht verkehrt, wenn Du > im Anschluss versuchen würdest, das Programm so umzuschreiben, dass die > globalen Variablen vermieden werden können." Du kannst natürlich die globalen Variablen in Funktionen verstecken. Das ist aber nur syntaktischer Zucker. Das Grundproblem wird dadurch nicht gelöst. Irgendetwas muss global verfügbar sein. Allenfalls könnte man noch eine Datenstruktur an den Window Data Member des Hauptfensters klemmen und dort die Werte unterbringen. Denn den hWnd des Hauptfensters kriegst du in der WindowProc frei Haus geliefert. Wobei ich mir jetzt aber ehrlich gesagt nicht sicher bin, ob die WindowProc immer nur für das Hauptfenster aufgerufen wird. Ist schon zu lange her, dass ich die Windows-API auf dieser Ebene programmiert habe.
Datum:
http://msdn.microsoft.com/en-us/library/aa930761.aspx WM_COMMAND wNotifyCode = HIWORD(wParam); wID = LOWORD(wParam); hwndCtl = (HWND) lParam; wNotifyCode Value of the high-order word of wParam. Specifies the notification code if the message is from a control. If the message is from an accelerator, this parameter is 1. If the message is from a menu, this parameter is 0. wID Value of the low-order word of wParam. Specifies the identifier of the menu item, control, or accelerator. hwndCtl Handle to the control sending the message if the message is from a control. Otherwise, this parameter is NULL. Stefan
Datum:
also "irgendwas" muss immer global sein und wenn es eine liste ist die die Window-handle den einzelnen Objekten zuordnet (nicht sicher ob das geht, ...) oder pro object-instance "eigene" callback Funktionen hat (delphi macht das so ähnlich, einfach mal nach MakeObjectInstance(.. in Forms.pas suchen)
Datum:
>> also "irgendwas" muss immer global sein
ja die Klimaerwärmung. Hier geht's um C (ohne plus plus)!
Datum:
aha >Buch "C++ für >Spieleprogrammierer" hier wird/wurde das wohl diskutiert vielleicht hilfts http://www.c-plusplus.de/forum/39356-full
Datum:
Das vom TO gezeigte Programm lässt sich rein C kompilieren. Es is kein Borland nötig! Keines der Handles muss global sein da WM_COMMAND das Handle bei Bedarf liefert. Stefan
Datum:
Stefan schrieb: > Das vom TO gezeigte Programm lässt sich rein C kompilieren. > Es is kein Borland nötig! Keines der Handles muss global sein da > WM_COMMAND das Handle bei Bedarf liefert. > Stefan Stefan Sieh dir den Code an. Wenn du bei Betätigung eines Buttons aus dem Edit Control dir den Text abholen willst, musst du den Handle zum Edit-Control irgendwo herkriegen. WindowProc liefert dir den nicht! Der Teil
case ID_BTN_UEBERNEHMEN:
{
char szText[256];
// Text aus der Editbox holen
GetWindowText (hEditBox, szText, 256);
// Diesen Text in das Label schreiben und den Text der Editbox löschen
SetWindowText (hText, szText);
SetWindowText (hEditBox, "");
return (0);
}
|
ist ohne irgendetwas Globales nicht machbar (mal vom Data Member abgesehen. Aber so toll ist das dann auch wieder nicht) Allenfalls könnte man noch mittels GetChildWnd (oder wie das heißt) ausgehend vom Hauptfenster und der Resource-Id den Handle bestimmen. Dann fungiert eben die Resource Id als das 'globale Element'. Irgendeinen Tod muss man immer sterben, wenn Callbacks im Spiel sind und es keine Möglichkeit gibt, wie die Applikation einen beliebigen Paramter durch das Callback Interface durchkriegt.
Datum:
>> Allenfalls könnte man noch mittels GetChildWnd (oder wie das heißt) >> ausgehend vom Hauptfenster und der Resource-Id den Handle bestimmen. http://msdn.microsoft.com/en-us/library/windows/de... HWND WINAPI GetDlgItem( __in_opt HWND hDlg, __in int nIDDlgItem ); >> Dann fungiert eben die Resource Id als das 'globale Element'. Na ja die ID's während der Erstellung per rand() zu generieren und dann gezielt ein Fenster zu manipulieren wird dann echt schwer. >> Irgendeinen Tod muss man immer sterben, wenn Callbacks im Spiel sind und >> es keine Möglichkeit gibt, wie die Applikation einen beliebigen Paramter >> durch das Callback Interface durchkriegt http://msdn.microsoft.com/en-us/library/windows/de... LONG WINAPI SetWindowLong( __in HWND hWnd, __in int nIndex, __in LONG dwNewLong ); Das wird dann aber echt abgefahren ;) Edit erstellen Label erstellen Button erstellen , SetWindowLong MAKELONG(LabelID,EditID) oder Edit erstellen Label erstellen SetWindowLong EditHandle Button erstellen , SetWindowLong LabelHandle Stefan