mikrocontroller.net

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


Autor: Stefan (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

#include <windows.h>
#include <math.h>

#define rot RGB (255,0,0)
#define gruen RGB (0,255,0)
#define blau RGB (0,0,255)
#define gelb RGB (255,255,0)
#define schwarz RGB (0,0,0)

#define SCALE 1000       //Pixel / Sekunde
#define PI 3.141592654
#define ENDTIME 0.9
#define TIMESTEP 0.0001
#define FREQ 10

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//Linie zeichnen
/**************************************************/
void linie(HWND hWnd,int x1,int y1,int x2,int y2,int breite,int color)
{
 HDC   hDC = GetDC(hWnd);
 SelectObject( hDC, CreatePen( PS_SOLID, breite, color ) );           
 MoveToEx(hDC, x1, y1, NULL);
 LineTo(hDC, x2, y2);
 ReleaseDC(hWnd, hDC);
}

//Hauptprogramm
/***************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   HWND         hWnd;
   MSG          msg;
   WNDCLASS     wc;
   
   char         szAppName[] = "API Testprogramm";
   double time;
   int wert,wert1;
   
   wc.cbClsExtra          = 0;
   wc.cbWndExtra          = 0;
   wc.hbrBackground       = (HBRUSH)GetStockObject(WHITE_BRUSH);
   wc.hCursor             = LoadCursor(NULL, IDC_ARROW);
   wc.hIcon               = LoadIcon(NULL, IDI_APPLICATION);
   wc.hInstance           = hInstance;
   wc.lpfnWndProc         = WndProc;
   wc.lpszClassName       = szAppName;
   wc.lpszMenuName        = NULL;
   wc.style               = CS_HREDRAW | CS_VREDRAW;
   
   RegisterClass(&wc);
   
   hWnd = CreateWindow(  szAppName,
                         szAppName,
                         WS_OVERLAPPEDWINDOW,
                         10,
                         10,
                         1000,  
                         700,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);
                         
   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);


   //Text ausgeben
   PAINTSTRUCT ps;
   const char  szText[] = "Testprogramm";
   HDC   hDC = BeginPaint(hWnd, &ps);
   {
    TextOut(hDC, 400, 10, szText, sizeof(szText) - 1);
   }
   EndPaint(hWnd, &ps);

   //Kasten zeichnen       
   linie(hWnd,10,50,10,250,3,rot);
   linie(hWnd,10,250,910,250,3,rot);
   linie(hWnd,910,250,910,50,3,rot);
   linie(hWnd,910,50,10,50,3,rot);

   //Mittellinie
   linie(hWnd,10,150,910,150,2,gruen);

   wert1=150;   //Vorinitialisieren      

   //Sinusfunktion zeichnen
   for(time=0;time<ENDTIME;time=time+TIMESTEP)
   { 
    wert= 150 - (int) 100 * sin(2*PI*FREQ*time);
    linie(hWnd,10+(int)(SCALE*(time-TIMESTEP)),wert1,10+(int)(SCALE*(time-TIMESTEP)),wert,3,blau);
    linie(hWnd,10+(int)(SCALE*(time-TIMESTEP)), wert,10+(int)(SCALE*time)           ,wert,3,blau);
    wert1=wert;
   }
    
       
   while (GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
   
   return msg.wParam;
}

//
/*************************************************************/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{   
   switch (message)
   {
      case WM_CREATE:
      {

         return 0;
      }

      case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }

      case WM_RBUTTONDOWN:
      {
 
         return 0;
      }

       case WM_LBUTTONDOWN:
      {
 
         return 0;
      }

      case WM_LBUTTONUP:
      {

         return 0;
      }

      case WM_PAINT:
      {

          return 0;
      }

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

Autor: Geniesser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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'.

Autor: Olaf Stieleke (olaf2001)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
//Folgende Pens Global
HPEN pen_rot,pen_gruen,pen_blau,penalt; 


//Im Hauptprogramm beim Aufruf Pens erstellen
hpen_rot = (HPEN)CreatePen(PS_SOLID,2, RGB(255,0,0));   
hpen_gruen = (HPEN)CreatePen(PS_SOLID,2, RGB(0,255,0));   
hpen_blau = (HPEN)CreatePen(PS_SOLID,2, RGB(0,0,255));   


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



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

Kann das so in etwa funktionieren?

mfg
Stefan

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.