Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

30.07.2011, 22:43

Listing 10.3 ohne Globale Variablen

Der Autor schreibt ja, man soll dieses Listing so umschreiben zu versuchen, dass keine Globalen Varibalen mehr benötigt werden.
Soweit so gut.

Nun steh ich aber vor dem Problem, dass ich nicht weiß, wie ich der CALLBACK Funktion die Handles übergeben soll.

Die CALLBACK Funktion wird ja der Membervariable windowclass.lpfnWndProc zugewiesen.

Nun wird diese aber im Code selber kein zweites Mal aufgerufen. Wie aber kann ich jetzt die Handles übergeben, um nicht mehr auf Globale Variablen angewiesen zu sein?

Hier nochmal der Code von Listing 10.3:
(PS: Ich hab ein paar Sachen sowie die meisten Kommentar entfernt, um das Ganze nicht zu großwerden zu lassen.
Die entscheidenen Stellen sind mit Kommentaren markiert)

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
include <windows.h>

LRESULT CALLBACK WindowProc (HWND hWnd, UINT message,
                            WPARAM wParam, LPARAM lParam);

HWND ErstelleHauptfenster (HINSTANCE hInst);
void ErstelleSteuerelemente (HWND hWnd, HINSTANCE hInst);

#define ID_STATICTEXT   4000
#define ID_EDITBOX      4001
#define ID_BTN_UEBERNEHMEN 4002
#define ID_BTN_BEENDEN  4003

// Globale Variablen
// Don't do this at home!
//
//Wie kireg ich die hier Weg?
HWND hText;     // Handle für den statischen Text
HWND hEditBox;  // Handle für die Editbox
HWND hUebernehmen; // Handle für Button "Übernehmen"
HWND hBeenden;  // Handle für Button "Beenden"

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
                    LPSTR lpcmdline, int ncmdshow)
{
  HWND hWnd;   // Fenster-Handle
  MSG message; // Nachricht

  hWnd = ErstelleHauptfenster (hInst);


  if (hWnd == NULL)
    return (0);

  ErstelleSteuerelemente (hWnd, hInst);

  while (GetMessage (&message, NULL, 0, 0) )
  {
    TranslateMessage (&message);
    DispatchMessage  (&message);

  }

 
  return (int)(message.wParam);

} 


HWND ErstelleHauptfenster (HINSTANCE hInst)
{
  HWND      hWnd;       // Fenster-Handle
  WNDCLASSEX  windowclass; // Nachricht

  // Der Klassenname des Fensters ist frei wählbar
  const char szClassName[] = "Zweites Fenster";

  windowclass.cbSize = sizeof (WNDCLASSEX);

  windowclass.style = CS_HREDRAW | CS_VREDRAW;

  //Wie übergebe ich hier die Handles?
  //
  //
  windowclass.lpfnWndProc = WindowProc;

  windowclass.cbClsExtra = 0;
  windowclass.cbWndExtra = 0;

  windowclass.hInstance = hInst;

  windowclass.hIcon   = LoadIcon (NULL, IDI_APPLICATION);
  windowclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
  windowclass.hCursor = LoadCursor (NULL, IDC_ARROW);

  windowclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND+1;

  windowclass.lpszMenuName = NULL;

  windowclass.lpszClassName = szClassName;

  if (!RegisterClassEx (&windowclass) )
    return (NULL);

  hWnd = CreateWindowEx (NULL,
                        szClassName,
                        "Eine kleine Anwendung",
                        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                        CW_USEDEFAULT, CW_USEDEFAULT,
                        300, 135,
                        NULL,
                        NULL,
                        hInst,
                        NULL);

  return (hWnd);

} 

LRESULT CALLBACK WindowProc (HWND hWnd, UINT message,
                            WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_DESTROY:
    {
    PostQuitMessage (0);
    return (0);

    }

    case WM_COMMAND:
    {
    switch (wParam)
    {
        case ID_BTN_UEBERNEHMEN:
        {
        char szText[256];

        GetWindowText (hEditBox, szText, 256);//Wie zeige ich dem Programm, was hEditbox ist?
//
//

        SetWindowText (hText, szText);
        SetWindowText (hEditBox, "");

        return (0);

        }

        case ID_BTN_BEENDEN:
        {
        int Resultat; // Rückgabewert der Messagebox

        Resultat = MessageBox (hWnd, "Wirklich beenden?",
                                "Programm beenden",
                                MB_YESNO | MB_ICONQUESTION);

        if (Resultat == IDYES)
        {
            PostQuitMessage (0);
            return (0);

        }
        return (0);

        }
    } break;
    } break;
  }
  return (DefWindowProc (hWnd, message, wParam, lParam) );

} // WindowProc


Ich hoffe, ich habe mich verständlich ausgedrückt :whistling:

2

30.07.2011, 23:46

´ne Klasse schreiben?

MfG
Check

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

3

31.07.2011, 00:04

Erzeug die Steuerelemente in der WM_CREATE Nachricht (wird an das Fenster gesendet in dem Moment da es erzeugt wurde, noch bevor CreateWindowEx() returned) und schau dir mal GetDlgItem() (liefert dir ein HWND zu einer ID) und SetWindowLongPtr() (erlaubt es dir (z.B. über GWLP_USERDATA) selbst definierte Daten (wie z.B. einen Pointer) mit dem Fenster zu verbinden) an ;)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (31.07.2011, 00:09)


4

31.07.2011, 20:53

Ok danke :) .

Werd ich mich mal dranmachen.

5

08.08.2011, 13:41

Ok, ich hab mich jetzt hiermit einige Tage auseinander gesetzt, konnte das Problem allerdings nicht lösen.

Dass mit WM_CREATE hab ich schon einigermaßen verstanden, allerdings bleibt mir eine Frage noch offen:
Die Childwindows rufen ja auch jeweils die Nachricht auf, wie verhindere ich, dass dann neue Fenster erstellt werden, oder es einen Fehler gibt?

Zu GetDlgItem, nun gut. Ich kriege hier also ein Handle zu einer ID und noch etwas anderem ( in meinem Fall dem Parentwindow?).
Dieses Handle könnt ich mir theoretisch doch auch so in der WM_CREATE erzeugen, oder?
Wo ist denn hier der Haken :?:
Wie benutze ich denn dieses Handle weiter?
Ich muss es ja irgendwie sichern.

Zum Schluss noch SetWindowLongPtr:
Ich kann hier also diverse Sachen verändern.
Allerdings bin ich mir nicht sicher was denn jezt genau.
Und vorallem was brauch ich hierfür?

Lange Rede, kurzer Sinn, eigentlich brauch ich nur eine etwas ausführlichere Erklärung :whistling:

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

6

08.08.2011, 15:00

Dass mit WM_CREATE hab ich schon einigermaßen verstanden, allerdings bleibt mir eine Frage noch offen:
Die Childwindows rufen ja auch jeweils die Nachricht auf, wie verhindere ich, dass dann neue Fenster erstellt werden, oder es einen Fehler gibt?

Die Childwindows rufen nicht das WM_CREATE deines Fenster auf, das Problem das du da siehst existiert nicht ;)

Zu GetDlgItem, nun gut. Ich kriege hier also ein Handle zu einer ID und noch etwas anderem ( in meinem Fall dem Parentwindow?).
Dieses Handle könnt ich mir theoretisch doch auch so in der WM_CREATE erzeugen, oder?

GetDlgItem() liefert dir das Fenster das zu einer entsprechenden ID gehört. Anstatt dir die einzelnen Fensterhandles in globalen Variablen zu merken kannst du sie damit einfach abfragen.

Wie benutze ich denn dieses Handle weiter?
Ich muss es ja irgendwie sichern.

Ja nein, eben genau nicht. Anstatt es zu sichern kannst du es damit einfach immer wenn dus brauchst abfragen.

Zum Schluss noch SetWindowLongPtr:
Ich kann hier also diverse Sachen verändern.
Allerdings bin ich mir nicht sicher was denn jezt genau.
Und vorallem was brauch ich hierfür?

SetWindowLongPtr() erlaubt es dir über GWLP_USERDATA einen beliebigen Pointer an das Fenster anzuhängen den du über GetWindowLongPtr() auch wieder abfragen kannst. Damit kannst du beispielsweise ein Fenster durch ein C++ Objekt repräsentieren

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class MyWindow
{
private:
  HWND hwnd;
  
  // Unsere Nachrichtenprozedur ist eine Memberfunktion
  LRESULT WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
  {
    // Hier hast du Zugriff auf sämtliche Member
    switch (msg)
    {
      ...
    }
    ...
  }
  
  // Dummyfunktion die die Methode aufruft
  static LRESULT CALLBACK WndProcThunk(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
    MyWindow* obj = reinterpret_cast<MyWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    return obj->WndProc(msg, wParam, lParam);
  }
  
  // Fenster wird mit dieser WndProc erzeugt
  static LRESULT CALLBACK BootstrapWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
    if (msg == WM_CREATE)
    {
      MyWindow* obj = reinterpret_cast<MyWindow*>(reinterpret_cast<CREATESTRUCT*>(lParam)->lpCreateParams);
      // Den this-Pointer in GLWP_USERDATA stecken
      SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(obj));
      // Neue WndProc setzen die alle Nachrichten ab jetzt an unser Objekt weiterleitet
      SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&WndProcThunk));
      return obj->WndProc(msg, wParam, lParam);
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
  }
  
public:
  MyWindow()
    : hwnd(CreateWindowEx(0, TEXT("MyWindowClass"), TEXT("hello world"), WM_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, 0, 0, hInstance, this))
  {
  }
};

7

08.08.2011, 18:23

Wow, danke erstmal.

Hab mir das jetzt ne Weile angesehen und gleich einige neue Fragen :whistling:

Zitat

Die Childwindows rufen nicht das WM_CREATE deines Fenster auf, das Problem das du da siehst existiert nicht ;)
Nun gut, das wäre dann schon geklärt ^^

Zitat

Ja nein, eben genau nicht. Anstatt es zu sichern kannst du es damit einfach immer wenn dus brauchst abfragen.
Heißt, ich nutze jetzt immer diese Funktion mit der ID als Parameter wenn ich das Handle benötige?

Zitat

SetWindowLongPtr() erlaubt es dir über GWLP_USERDATA einen beliebigen Pointer an das Fenster anzuhängen den du über GetWindowLongPtr() auch wieder abfragen kannst. Damit kannst du beispielsweise ein Fenster durch ein C++ Objekt repräsentieren
Hm, also erstmal brauch ich keine neue Nachrichten Funktion mehr zu erstellen, wenn ich dies in einer Klasse bereits getan habe?
In deinem Beispiel wird also ein Zeiger erstellt, mit dem dem Fenster die ThunkProc Funktion zugewiesen wird.
Glaube ich zumindest :D

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

8

08.08.2011, 20:04

Zitat

Ja nein, eben genau nicht. Anstatt es zu sichern kannst du es damit einfach immer wenn dus brauchst abfragen.
Heißt, ich nutze jetzt immer diese Funktion mit der ID als Parameter wenn ich das Handle benötige?

exakt.

Zitat

SetWindowLongPtr() erlaubt es dir über GWLP_USERDATA einen beliebigen Pointer an das Fenster anzuhängen den du über GetWindowLongPtr() auch wieder abfragen kannst. Damit kannst du beispielsweise ein Fenster durch ein C++ Objekt repräsentieren
Hm, also erstmal brauch ich keine neue Nachrichten Funktion mehr zu erstellen, wenn ich dies in einer Klasse bereits getan habe?
In deinem Beispiel wird also ein Zeiger erstellt, mit dem dem Fenster die ThunkProc Funktion zugewiesen wird.
Glaube ich zumindest :D

Bin mir nicht sicher was genau du da meinst. Aber egal, mein Beispiel da ist schon etwas anspruchsvollerer Code. Ich vermute mal du bist noch nicht ganz so weit fortgeschritten mit C++. Das macht ja nix, du kannst es dir ja für später aufheben, wenn du etwas sattelfester mit Klassen etc. bist ;)

9

08.08.2011, 23:36

Nunja, der Windowskram verwirrt mich doch schon ein gutes Stück ^^

Ich meinte, dass die Callback Funktion, die im Buch ja außerhalb einer Klasse ist (logisch, da gibts ja keine :D )
nun in der Klasse ist, wenn ich das richtig verstanden habe und das im Beispiel über einen Zeiger die Funktion eines Fensters mit geändert wird.

Kannst du vielleicht nochmal den Part, wo mit SetWindowLongPtr(), GetWindowLongPtr() und GWLP_USERDATA gearbeitet wird, genau (also für Dummies :D ) erklären, was da genau passiert?
Ich find zu GWLP_USERDATA auch nichts in der MSDN, was ist das genau?


Also der this-Zeiger (kann der auch ein anderer Zeiger sein? Sorry für die dumme Frage, möchte aber doch auf Nummer sicher gehen) wird als Parameter an SetWindowLongPtr() übergeben, danach bekommt die selbe Funktion eine Adresse an die selbe Stelle, also für den Zeiger. Jetzt zeigt der Zeiger auf WndProcThunk.
Achja, davor wird obj einem lParam zugewiesen, seh ich das richtig?
Warum wird immer WndProc returned? Was passiert da genau?
In der Dummy Funktion bekomme ich einen Zeiger, nur was kann ich mit diesem anstellen?

So massig Fragen, hoffe ich konnte mich verständlih ausdrücken.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

10

08.08.2011, 23:49

Also ich weiß nicht so ganz was ich jetzt noch mehr dazu sagen sollte als ich schon gesagt hab. Lies vielleicht meine vorigen Antworten nochmal genau durch. Die entsprechenden Seiten in der MSDN hab ich ja in meinem ersten Posting schon verlinked. Vielleicht hilft dir das!?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (09.08.2011, 00:05)


Werbeanzeige