Forum: PC-Programmierung Frage zu Handle-Überprüfung


von Joachim (Gast)


Lesenswert?

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
1
#include <windows.h>
2
3
// Prototypen
4
LRESULT CALLBACK WindowProc (HWND bWnd, UINT message, WPARAM wParam, LPARAM lParam);
5
6
HWND ErstelleHauptfenster (HINSTANCE hInst);
7
void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst);
8
9
// IDs der Child-Fenster
10
#define ID_STATICTEXT    4000
11
#define ID_EDITBOX      4001
12
#define ID_BTN_UEBERNEHMEN  4002
13
#define ID_BTN_BEENDEN    4003
14
15
// Globale Variablen
16
HWND hText;      // Handle für den eigentlichen Text
17
HWND hEditBox;    // Handle für die Editbox
18
HWND hUebernehmen;  // Handle für Button "Übernehmen"
19
HWND hBeenden;    // Handle für Button "Beenden"
20
21
// Hauptprogramm
22
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpcmdline, int ncmdshow)
23
{
24
  HWND hWnd;    // Fenster-Handle
25
  MSG message;  // Nachricht
26
27
  // Hauptfenster erstellen
28
  hWnd = ErstelleHauptfenster (hInst);
29
30
  // Prüfen, ob das Erstellen des Hauptfensters geklappt hat
31
  if (hWnd == NULL) return (0);
32
33
  // Alle Steuerelemente erstellen
34
  ErstelleSteuerelemente (hWnd, hInst);
35
36
  // Nachrichten abholen, übersetzen und weiterleiten
37
  while (GetMessage (&message, NULL, 0, 0) )
38
  {
39
    TranslateMessage (&message);
40
    DispatchMessage  (&message);
41
  }
42
43
  //  Programm beenden
44
  return (int)(message.wParam);
45
}
46
47
48
49
HWND ErstelleHauptfenster (HINSTANCE hInst)
50
{
51
  HWND     hWnd;    // Fenster-Handle  
52
  WNDCLASSEX windowsclass;  // Struktur für Fenstereigenschaften
53
  
54
55
  const char szClassName[] = "Zweites Fenster";    // Klassenname des Fensters frei wählbar
56
57
  windowsclass.cbSize = sizeof (WNDCLASSEX);        // Größe der Struktur zwischenspeichern
58
  windowsclass.style = CS_HREDRAW | CS_VREDRAW;      // Fenster beim Verschieben neu zeichnen
59
  windowsclass.lpfnWndProc = WindowProc;          // Zeiger auf Callback-Funktion
60
  windowsclass.cbClsExtra = 0;                // Kein erweiterten Einstellungen
61
  windowsclass.cbWndExtra = 0;
62
  windowsclass.hInstance = hInst;              // Instanz speichern
63
  windowsclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);  // Icons und Cursor festlegen
64
  windowsclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
65
  windowsclass.hCursor = LoadCursor (NULL, IDC_ARROW);
66
  windowsclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND+1;  // Hintergrundfarbe
67
  windowsclass.lpszMenuName = NULL;            // Kein Menü
68
  windowsclass.lpszClassName = szClassName;        // Klassenname angeben
69
70
  if (!RegisterClassEx (&windowsclass)) return (NULL);    // Fensterklasse registrieren
71
72
  hWnd = CreateWindowEx ( NULL,              // Fenster erzeugen
73
              szClassName,
74
              "Eine kleine Anwendung",
75
              WS_OVERLAPPEDWINDOW | WS_VISIBLE,
76
              CW_USEDEFAULT, CW_USEDEFAULT,    // Windows setzt Fensterposition automatisch
77
              300, 135,              // Breite und Höhe
78
              NULL,
79
              NULL,
80
              hInst,
81
              NULL);
82
  return (hWnd);    
83
}  // ErstelleHauptfenster
84
85
86
87
88
// Alle Steuerelemente erstellen
89
void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst)
90
{
91
  // Statischen Text als Child-Fenster erstellen
92
  
93
  hText = CreateWindow (
94
              "STATIC",
95
              "Eingegebener Text",
96
              WS_VISIBLE | WS_CHILD | ES_CENTER,
97
              0, 0,
98
              300, 20,
99
              hWnd,
100
              (HMENU)ID_STATICTEXT,
101
              hInst,
102
              NULL);
103
  
104
105
  // Editbox als Child-Fenster erstellen
106
  hEditBox = CreateWindow ("EDIT",
107
               "Bitte Text eingeben",
108
               WS_VISIBLE | WS_CHILD | WS_BORDER,
109
               0, 20,
110
               300, 20,
111
               hWnd,
112
               (HMENU) ID_EDITBOX,
113
               hInst,
114
               NULL);
115
116
  // Übernehmen-Button als Child-Fenster erstellen
117
  hUebernehmen = CreateWindow ("BUTTON",
118
              "Übernehmen",
119
              BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD,
120
              20, 50,
121
              95, 40,
122
              hWnd,
123
              (HMENU) ID_BTN_UEBERNEHMEN,
124
              hInst,
125
              NULL);
126
127
  // Beenden-Button als Child-Fenster erstellen
128
  hBeenden = CreateWindow ("BUTTON",
129
              "Beenden",
130
              BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD,
131
              175, 50,
132
              95, 40,
133
              hWnd,
134
              (HMENU) ID_BTN_BEENDEN,
135
              hInst,
136
              NULL);
137
  
138
139
}  // ErstelleSteuerelemente
140
141
142
143
144
145
// Callback-Funktion zur Nachrichtenverarbeitung
146
LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
147
{
148
  // Messages auswerten
149
  switch(message)
150
  {
151
    case WM_DESTROY:        // Fensters schließen (auch Alt-F4)
152
    {
153
      PostQuitMessage (0);      // Nachricht zum Beenden schicken
154
      return (0);
155
    }
156
157
    // Ab hier Nachrichten unserer Child-Fenster bearbeiten
158
    case WM_COMMAND:
159
    {
160
      switch (wParam)
161
      {
162
        case ID_BTN_UEBERNEHMEN:
163
        {
164
          char szText[256];
165
166
          // Text aus der Editbox holen
167
          GetWindowText (hEditBox, szText, 256);
168
169
          // Diesen Text in das Label schreiben und den Text der Editbox löschen
170
          SetWindowText (hText, szText);
171
          SetWindowText (hEditBox, "");
172
          return (0);
173
        }
174
175
        case ID_BTN_BEENDEN:
176
        {
177
          int Resultat;  // Rückgabewert der Messagebox
178
179
          // Messagebox für Sicherheitabfrage
180
          Resultat = MessageBox (hWnd, "Wirklich beenden?", 
181
                      "Programm beenden", 
182
                      MB_YESNO | MB_ICONQUESTION);
183
184
          if (Resultat == IDYES)
185
          {
186
            PostQuitMessage (0);      // Nachricht zum Beenden schicken
187
            return (0);
188
          }
189
190
          // Nein, also ganz normal weiter
191
          return (0);
192
        }
193
      } break;
194
    } break;
195
  }
196
197
  // Die Nachricht wurde nicht von uns bearbeitet, also Windows überlassen
198
  return (DefWindowProc (hWnd, message, wParam, lParam) );
199
}  // 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
1
void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst);

so
1
HWND ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst);

Kann mir jemand erklären, wie man richtig angeht?

VG, Joachim

von Karl H. (kbuchegg)


Lesenswert?

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.

von Harald (Gast)


Lesenswert?

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.

von Joachim (Gast)


Lesenswert?

>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

von Dennis H. (c-logic) Benutzerseite


Lesenswert?

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.

von Joachim (Gast)


Lesenswert?

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:
1
//Prototyp
2
bool ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst, HWND hText, HWND hEditBox, HWND hUebernehmen, HWND hBeenden);
1
int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpcmdline, int ncmdshow)
2
{
3
bool handles_ok;
4
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

von Karl H. (kbuchegg)


Lesenswert?

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.

1
void foo( int i )
2
{
3
  printf( "%d\n", i );
4
}
5
6
int main()
7
{
8
  foo( 5 );
9
}

1
void foo( int * i )
2
{
3
  *i = 8;
4
}
5
6
int main()
7
{
8
  int j;
9
10
  foo( &j );
11
  printf( "%d\n", j );
12
}


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.

von Joachim (Gast)


Lesenswert?

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

von Joachim (Gast)


Lesenswert?

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,

von Joachim (Gast)


Lesenswert?

1
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

von Karl H. (kbuchegg)


Lesenswert?

Joachim schrieb:
>
1
> LRESULT CALLBACK WindowProc (HWND hWnd, UINT message, WPARAM wParam,
2
> LPARAM lParam);
3
>
>
>
> 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.

von Stefan (Gast)


Lesenswert?

>> // 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

von Joachim (Gast)


Lesenswert?

@Stefan

Im Original-Code sind die Handle-Deklarationen global, dies möchte ich 
nun vermeiden.


VG, Joachim

von Karl H. (kbuchegg)


Lesenswert?

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.

von Joachim (Gast)


Lesenswert?

>Die Funktion WindowProc interessiert hier doch keinen.


Die Funktion WindowProc verwendet doch bspw. "hEditBox"
1
...
2
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

von Joachim (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

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

von Robert L. (lrlr)


Lesenswert?

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)

von Stefan (Gast)


Lesenswert?

>> also "irgendwas"  muss immer global sein
ja die Klimaerwärmung. Hier geht's um C (ohne plus plus)!

von Robert L. (lrlr)


Lesenswert?

aha

>Buch "C++ für
>Spieleprogrammierer"

hier wird/wurde das wohl diskutiert
vielleicht hilfts

http://www.c-plusplus.de/forum/39356-full

von Stefan (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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
1
        case ID_BTN_UEBERNEHMEN:
2
        {
3
          char szText[256];
4
5
          // Text aus der Editbox holen
6
          GetWindowText (hEditBox, szText, 256);
7
8
          // Diesen Text in das Label schreiben und den Text der Editbox löschen
9
          SetWindowText (hText, szText);
10
          SetWindowText (hEditBox, "");
11
          return (0);
12
        }
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.

von Stefan (Gast)


Lesenswert?

>> 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/desktop/ms645481(v=vs.85).aspx

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/desktop/ms633591(v=vs.85).aspx

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.