Frage zu Handle-Überprüfung

Gast #2660146
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
#2660200
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.
Persönliche Seite #2660696
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.
Gast #2660957
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
#2661008
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.
#2661181
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.
Gast #2661185
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
Gast #2661196
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
Gast #2661201
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
#2661207
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.
Gast #2661225
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
#2661270
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.
Gast #2661313
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren