Forum: PC-Programmierung Problem mit C und API (LineTo etc)


von Stefan (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
ich versuche grade mit einem normalen C Compiler Dev-Cpp unter WinXP ein 
Programm zu erstellen, das mir eine grafische Ausgabe ermöglicht.
In dem Programm wird nur ein Kasten gezeichnet. In diesen Kasten soll 
eine Sinusfunktion dargestellt werden. Das funktioniert soweit auch. 
Allerdings werden die Linien nach einer bestimmten Anzahl an Ausgaben 
alle dünn und schwarz, wie in dem Bild im Anhang zu sehen. So wie es bis 
jetzt aussieht kann ich ca 1000 Linien zeichnen bis dieses Problem 
auftritt. Wenn man die TIMESTEP Konstante auf z.B. 0.01 reduziert 
verringern sich natürlich die Anzahl der ausgegebenen Objekte, dann ist 
alles in Ordnung. Ich musste ein Foto machen, da Windows wenn das 
Programm läuft meint es wäre nicht mehr genügend Arbeitsspeicher zum 
Screenshot machen da.

mfg
Stefan

1
#include <windows.h>
2
#include <math.h>
3
4
#define rot RGB (255,0,0)
5
#define gruen RGB (0,255,0)
6
#define blau RGB (0,0,255)
7
#define gelb RGB (255,255,0)
8
#define schwarz RGB (0,0,0)
9
10
#define SCALE 1000       //Pixel / Sekunde
11
#define PI 3.141592654
12
#define ENDTIME 0.9
13
#define TIMESTEP 0.0001
14
#define FREQ 10
15
16
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
17
18
//Linie zeichnen
19
/**************************************************/
20
void linie(HWND hWnd,int x1,int y1,int x2,int y2,int breite,int color)
21
{
22
 HDC   hDC = GetDC(hWnd);
23
 SelectObject( hDC, CreatePen( PS_SOLID, breite, color ) );           
24
 MoveToEx(hDC, x1, y1, NULL);
25
 LineTo(hDC, x2, y2);
26
 ReleaseDC(hWnd, hDC);
27
}
28
29
//Hauptprogramm
30
/***************************************************/
31
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
32
                   PSTR szCmdLine, int iCmdShow)
33
{
34
   HWND         hWnd;
35
   MSG          msg;
36
   WNDCLASS     wc;
37
   
38
   char         szAppName[] = "API Testprogramm";
39
   double time;
40
   int wert,wert1;
41
   
42
   wc.cbClsExtra          = 0;
43
   wc.cbWndExtra          = 0;
44
   wc.hbrBackground       = (HBRUSH)GetStockObject(WHITE_BRUSH);
45
   wc.hCursor             = LoadCursor(NULL, IDC_ARROW);
46
   wc.hIcon               = LoadIcon(NULL, IDI_APPLICATION);
47
   wc.hInstance           = hInstance;
48
   wc.lpfnWndProc         = WndProc;
49
   wc.lpszClassName       = szAppName;
50
   wc.lpszMenuName        = NULL;
51
   wc.style               = CS_HREDRAW | CS_VREDRAW;
52
   
53
   RegisterClass(&wc);
54
   
55
   hWnd = CreateWindow(  szAppName,
56
                         szAppName,
57
                         WS_OVERLAPPEDWINDOW,
58
                         10,
59
                         10,
60
                         1000,  
61
                         700,
62
                         NULL,
63
                         NULL,
64
                         hInstance,
65
                         NULL);
66
                         
67
   ShowWindow(hWnd, iCmdShow);
68
   UpdateWindow(hWnd);
69
70
71
   //Text ausgeben
72
   PAINTSTRUCT ps;
73
   const char  szText[] = "Testprogramm";
74
   HDC   hDC = BeginPaint(hWnd, &ps);
75
   {
76
    TextOut(hDC, 400, 10, szText, sizeof(szText) - 1);
77
   }
78
   EndPaint(hWnd, &ps);
79
80
   //Kasten zeichnen       
81
   linie(hWnd,10,50,10,250,3,rot);
82
   linie(hWnd,10,250,910,250,3,rot);
83
   linie(hWnd,910,250,910,50,3,rot);
84
   linie(hWnd,910,50,10,50,3,rot);
85
86
   //Mittellinie
87
   linie(hWnd,10,150,910,150,2,gruen);
88
89
   wert1=150;   //Vorinitialisieren      
90
91
   //Sinusfunktion zeichnen
92
   for(time=0;time<ENDTIME;time=time+TIMESTEP)
93
   { 
94
    wert= 150 - (int) 100 * sin(2*PI*FREQ*time);
95
    linie(hWnd,10+(int)(SCALE*(time-TIMESTEP)),wert1,10+(int)(SCALE*(time-TIMESTEP)),wert,3,blau);
96
    linie(hWnd,10+(int)(SCALE*(time-TIMESTEP)), wert,10+(int)(SCALE*time)           ,wert,3,blau);
97
    wert1=wert;
98
   }
99
    
100
       
101
   while (GetMessage(&msg, NULL, 0, 0))
102
   {
103
      TranslateMessage(&msg);
104
      DispatchMessage(&msg);
105
   }
106
   
107
   return msg.wParam;
108
}
109
110
//
111
/*************************************************************/
112
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
113
{   
114
   switch (message)
115
   {
116
      case WM_CREATE:
117
      {
118
119
         return 0;
120
      }
121
122
      case WM_DESTROY:
123
      {
124
         PostQuitMessage(0);
125
         return 0;
126
      }
127
128
      case WM_RBUTTONDOWN:
129
      {
130
 
131
         return 0;
132
      }
133
134
       case WM_LBUTTONDOWN:
135
      {
136
 
137
         return 0;
138
      }
139
140
      case WM_LBUTTONUP:
141
      {
142
143
         return 0;
144
      }
145
146
      case WM_PAINT:
147
      {
148
149
          return 0;
150
      }
151
152
   }
153
   
154
   return DefWindowProc(hWnd, message, wParam, lParam);
155
}

von Geniesser (Gast)


Lesenswert?

Vor allem würde ich PAINTSTRUCT erstmal nicht in WinMain deklarieren, 
sondern innerhalb der CALLBACK Funktion und dann BeginPaint() / 
Endpaint() als Ereignis einer WM_PAINT Nachricht verarbeiten.

von Stefan (Gast)


Lesenswert?

Hallo,
ich habe mal probiert alles in den Teil zu kopieren das es bei der 
WM_PAINT Nachricht ausgeführt wird. Das bringt keinen Unterschied, wie 
ich allerdings auch erwartet habe.

mfg
Stefan

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Erzeuge nicht für jeden lineto-Aufruf einen neuen Pen (mit CreatePen), 
sondern mach das genau einmal.
Sofern zwischenzeitlich im DC auch nichts anderes ausgegeben wird, ist 
es auch nicht erforderlich, den Pen wieder und wieder in den DC zu 
selektieren.

von Karl H. (kbuchegg)


Lesenswert?

Aus den Anfangstagen der Windows Programmierung gibt
es 2 Grundregeln

* Objekte die man erzeugt, muessen auch wieder zerstört werden.
  Ich sehe zwar den Aufruf für CreatePen, aber kein dazu
  korrespondierendes DeleteObject

* Objekte die in einen DC selektiert werden, müssen aus dem DC
  wieder deselektiert werden, bevor ein DC zerstört werden kann.
  Dazu merkt man sich den Rückgabewert von SelectObject und setzt
  diesen wieder, bevor der DC zerstört wird.


Insbesondere letztere Regel war in Win 3.1 besonders wichtig, da
Windows nur über eine begrenzte, kleine Anzahl von DCs verfügte.
Hatte man im DC den Urzustand nicht wieder hergestellt, dann wurde
der DC auch nicht zerstört und man rannte sehr schnell 'out of DC'.

von Olaf S. (olaf2001)


Lesenswert?

Diese Begrenzung gibt es auch in WinXP, allerdings fängt dann das 
Programm das herumspacken an ("Kann Fenster nicht öffnen", "Ungültiges 
Window-Handle" und derlei mehr), Fehlermeldungen oder ähnliches vom 
Betriebssystem gibt es nicht.

von Stefan (Gast)


Lesenswert?

Hallo,
danke, so funktioniert es.

Wenn ich weis das ich in meinem gesamten Programm nur 3 Pens verwende, 
könnte ich diese am Programmanfang anlegen und am Ende wieder löschen?

Also im Prinzip so:
1
//Folgende Pens Global
2
HPEN pen_rot,pen_gruen,pen_blau,penalt; 
3
4
5
//Im Hauptprogramm beim Aufruf Pens erstellen
6
hpen_rot = (HPEN)CreatePen(PS_SOLID,2, RGB(255,0,0));   
7
hpen_gruen = (HPEN)CreatePen(PS_SOLID,2, RGB(0,255,0));   
8
hpen_blau = (HPEN)CreatePen(PS_SOLID,2, RGB(0,0,255));   
9
10
11
//Da wo was gezeichnet werden soll den entsprechenden Pen anwählen
12
switch(color)
13
{
14
 case 1:
15
  SelectObject(hDC, pen_rot);
16
 break;
17
 case 2:
18
  SelectObject(hDC, pen_gruen);
19
 break;
20
 case 3:
21
  SelectObject(hDC, pen_blau);
22
 break;
23
}
24
25
26
27
//Beim Fenster schließen alle Pens löschen
28
SelectObject(hDC, penalt); //Sicherstellen das die zu löschenden Pens nicht angewählt sind
29
DeleteObject(hpen_rot);
30
DeleteObject(hpen_gruen);
31
DeleteObject(hpen_blau);

Kann das so in etwa funktionieren?

mfg
Stefan

So ist es jetzt bei mir und läuft:
1
//Linie zeichnen
2
/**************************************************/
3
void linie(HWND hWnd,int x1,int y1,int x2,int y2,int breite,int color)
4
{
5
 HPEN  hpen, hpenOld;                              //Merker für Urzustand
6
 HDC   hDC = GetDC(hWnd);                          //Device Context ermitteln
7
 hpen = (HPEN)CreatePen(PS_SOLID,breite, color);   //eigenen Pen erstellen
8
 hpenOld = (HPEN)SelectObject(hDC, hpen);          //eigenen Pen wählen und den vorherigen merken
9
 MoveToEx(hDC, x1, y1, NULL);                      //Startposition 
10
 LineTo(hDC, x2, y2);                              //Linie zur Endposition
11
 SelectObject(hDC, hpenOld);                       //Vorherigen Pen wieder anwählen
12
 DeleteObject(hpen);                               //und den neuen löschen
13
 ReleaseDC(hWnd, hDC);                             //Device Context wieder freigeben
14
}

von Karl H. (kbuchegg)


Lesenswert?

Stefan wrote:
> Hallo,
> danke, so funktioniert es.
>
> Wenn ich weis das ich in meinem gesamten Programm nur 3 Pens verwende,
> könnte ich diese am Programmanfang anlegen und am Ende wieder löschen?

Sicher kannst du.

>
> Also im Prinzip so:
> [c]
> //Folgende Pens Global
> HPEN pen_rot,pen_gruen,pen_blau,penalt;

> //Da wo was gezeichnet werden soll den entsprechenden Pen anwählen
> switch(color)
> {
>  case 1:
>   SelectObject(hDC, pen_rot);
>  break;
>  case 2:
>   SelectObject(hDC, pen_gruen);
>  break;
>  case 3:
>   SelectObject(hDC, pen_blau);
>  break;
> }

Wenn sicher gestellt ist, dass Color immer einen gültigen
Wert von 1 bis 3 hat, dann kannst du das auch so machen

HPEN pens[3];

  pens[0] = CreatePen( ... );
  pens[1] = ...
  pens[2] = ...

....

  SelectObject(hDC, pens[ color - 1]);

Wie auch immer du das machen willst.

>
>
>
> //Beim Fenster schließen alle Pens löschen
> SelectObject(hDC, penalt); //Sicherstellen das die zu löschenden Pens
> nicht angewählt sind

Du wirst sehen, dass diese ständige 'alte xxx' merken und wieder
restaurieren auf Dauer lästig wird. Denn xxx kann ja alles mögliche
sein: Eine Bitmap, ein Brush, ein Pen, ein Font, ....

Daher möchte ich dir die Funktionen
SaveDC und RestoreDC  ans Herz legen. Die vereinfachen das
Ganze etwas. Am Anfang deiner Funktion rufst du SaveCD auf
und am Ende (vor einem ev. Löschen von Resourcen) kommt
der RestoreDC und du kannst sicher sein, dass du nichts
vergessen hast zu restaurieren.

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.