Benutzer:FbNutzer/FreeBasicThemen

Programme für FreeBasic und QuickBasic

Schließen des gfx-Grafikfensters mit dem X-Button Bearbeiten

Beim FreeBasic-Grafikmodus, der QuickBasic ähnlich sein soll (gfx-Grafikfenster) besteht das Problem, dass der X-Button (Schließbutton-> WM_CLOSE-Ereignis) des Grafikfensters nicht automatisch funktioniert, sondern der Programmierer sich um die Überwachung kümmern muss. Dazu gibt es die einfache Möglichkeit über den Inkey-Befehl, der nicht nur Tastaturbuchstaben, sondern auch das WM_CLOSE-Ereignis empfängt. Das Problem ist aber, dass Inkey den Tastaturpuffer leert und deshalb inkompatibel mit dem praktischen Input-Befehl ist (MultiKey leert nicht den Puffer, empfängt dabei aber nicht das WM_CLOSE-Ereignis. Will man Input nutzen, führt also kein Weg daran vorbei, sich in die Windows-Ereignisbehandlungsprozedur einzuhängen, wie bei einer normalen WIN-API Programmierung auch. Dies macht die Windowprozedur endbeiXbn.gfxWndProc. Wenn ein WM_CLOSE Ereignis ankommt, dann beendet diese das Programm möglichst harmonisch: Sie ruft den Thread endbeiXbn.ProgramCloseThread auf, der ExitProcess(0) aufruft. Dieser Code ist i.w. gleich mit dem Ausgangscode (bei diesem holt sich ScreenControl einmal das Fensterhandle und hängt dann die gfxWndProc-Prozedur ein) eines erfahrenen Nutzers vom FreeBasic Portal (www.freebasic-portal.de).

Es besteht aber noch das Problem, dass beim -evtl. auch nur versuchten- Wechsel vom Fenstermodus in den Vollbildmodus (z.B. via Maximize-Button) sich das Handle des Fensters ändert, das Fenster also jetzt eigentlich ein anderes ist (auch wenn der Inhalt ins neue Fenster hinüberkopiert wurde). Die gfxWndProc-Prozedur ist nach dieser Änderung natürlich dann abgehängt und es muss wieder das Handle geholt werden und die Prozedur wieder eingehängt werden. Es ist also eine stetige Kontrolle des Fensterhandles notwendig und dies macht der Thread endbeiXbn.control. Diese Erweiterung von mir hat also einen evtl. unbegrenzt laufenden Thread und damit eine mögliche Absturzquelle. Der Thread aber pausiert durch sleep 200,1 jeweils um ca. 200 msec und braucht deswegen kaum Ressourcen und es gibt auch praktisch keine Zugriffskonfikte (die Kommunikation mit dem Hauptprogramm erfolgt über zwei Integer-Variablen - jeweils nur in eine Richtung): Durch die globale Variable endbeiXbn.controlRequQuit kann man die Loop Schleife des Threads beenden, dies braucht aber etwas Zeit, ca. 250 msec ,da die Sleep Verzögerung im Thread alleine schon 200 msec dauert. Mit der globalen Variable endbeiXbn.controlIstaktiv kann man überprüfen, ob der Thread beendet ist. Probleme gibt es, wenn man das Auslaufenlassen der Threads unterlässt und einen neuen Screen oder Screenres-Befehl abgibt, dann kann das Programm abstürzen. Bricht der Nutzer aber das Programm mit dem Taskmanager (unvollständig?) ab, dann kann es bei erneutem Aufrufen in bestimmten Konstellationen zu einem Abbruch kommen: beim Anfang des Programms oder erst beim Wechsel in den Vollbildmodus.

Include-Datei endbeiXbn.bas Bearbeiten

(Der Thread endbeiXbn.control ist wichtig, weil bei Maximierung des Grafikfensters sich das Fensterhandle ändern kann und dann endbeiXbn.gfxWndProc nicht mehr aufgerufen werden würde.)

'Davor muss "windows.bi" und "fbgfx.bi" eingebunden sein
'mit Freebasic 0.24.0 übersetzen
'Wenn confirmStr<>"" ist, dann wird vor einem Abbruch erst gefragt
  
Namespace endbeiXbn

dim shared as zstring*255 confirmStr="" 
dim shared as wndproc OldWndProc 
dim shared as integer controlRequQuit=true
dim shared as integer controlIstaktiv=false
 
Sub ProgramCloseThread(hwnd1 as hwnd) 
  if confirmStr="" orelse _ 
  MessageBox (hwnd1,confirmStr, "", mb_yesnocancel)=idyes Then
     ExitProcess(0)
  end if
End Sub
 
Function gfxWndProc(hwnd1 As hwnd, uMsg As uInt, wParam1 As wParam,_
                    lParam1 As lParam) As lResult
    If uMsg = wm_close Then
        ThreadCall ProgramCloseThread(hwnd1)
        Return 0
    End If
    Return OldWndProc(hwnd1, uMsg, wParam1, lParam1)
End Function
 
Sub control
    Static as hwnd hwnd2=null
    Dim as hwnd hWnd1
    Dim Param1 as Integer    
    controlIstaktiv=true    
    Do 
       Sleep 200,1 
       ScreenControl fb.get_window_handle, Param1 
       hWnd1=Cast(hwnd,Param1)
       If hWnd1=null Then
           hWnd2=null  '->bei null das untere nicht ausführen
       End If
       If hwnd1<>hwnd2 Then
          OldWndProc = _
                Cast(wndproc,SetWindowLongPtr(hwnd1, gwlp_wndproc,_
                     Cast(long_ptr, ProcPtr(gfxWndProc))))
          hwnd2=hwnd1
       End If
    Loop until controlRequQuit 
    controlIstaktiv=false
End Sub    
 
Function StartControlThread as Handle
    controlRequQuit=false
    return ThreadCall control
End Function  

Sub controlInitQuit: controlRequQuit=true : End Sub
  
End Namespace

Beispielprogramm, das endbeiXbn.bas nutzt Bearbeiten

' mit Freebasic 0.24.0 übersetzen
#Include "windows.bi"
#Include "fbgfx.bi"
#Include "endbeiXbn.bas"

dim r as double

screen 12
endbeiXbn.StartControlThread

print "Grafik-Modus 12"
print  
input "r=";r  
circle(320,240),r

endbeiXbn.controlInitQuit   
while endbeiXbn.controlIstaktiv 
  print "*";
  sleep 20,1
wend    

input "<Enter druecken>",r 'hier nur Dummy

screenres 700,550,32
endbeiXbn.StartControlThread

print "Grafik-Modus 700x500x32"
print
Do
    input "r= ";r
    if r=-100 then : endbeiXbn.controlInitQuit : end if   'zum Testen
    circle(350,250),r 
Loop
endbeiXbn.controlInitQuit
sleep 250,1
'nur Auslaufenlassen des Programms; schnelles Programmende mit "end"

Anwendungsprogramm, das endbeiXbn.bas verwendet Bearbeiten

' Programm "mult1x1.bas" zur 1x1 Multiplikation, by FbNutzer
' mit Freebasic 0.24.0 übersetzen

#include once "windows.bi"
#include "fbgfx.bi"
#include "endbeiXbn.bas"

'Definition der Variablen und Konstanten
dim as double zeit_min,zeitvorgabe,zeit,zeit1,zeit2 
dim as integer ch,a,b,a1,b1,c,richtige,falsche,punkte
dim as integer zeitsek,anz,ppm,maxppm,typ,r
dim as string frage,taste

const as string uekl = chr(129), aekl = chr(132),oekl = chr(148) 'ü,ä,ö
const as string szett = chr(225), mal="x"
const as integer kurzzeit = 100        '100 Millisekunden

'
function tastedruecken as string  
  print
  print "   [Taste dr";uekl;"cken] ";
  tastedruecken=chr(getkey)
  sleep kurzzeit,1       'kurz verzögern
end function


'Beginn des Hauptprogramms
anz=0
maxppm=0

WindowTitle "1x1 Multiplikation"
screen 12

endbeiXbn.StartControlThread   
'optional, zum Schließen des gfxGrafikfensters mit dem X-Button;
'mögl. Absturzquelle bei Programmbeginn bzw. Wechsel in Vollbildmod. 
                              
color 15,1    '<- weiße Schrift auf blauem Hintergrund

cls          
print
print "                  Multiplikation: Das kleine und gro";_
         szett;"e Einmaleins"
print
print
print "            (Wechsel in den Vollbildmodus und zur";uekl;"ck"; 
print " mit [Alt][Enter])"
print 
print
print "  Punkte = richtige Ergebnisse - falsche Ergebnisse ";mal;" 2"
print
print "  (13 richtige und 5 falsche Ergebnisse -> 3 Punkte)"
print
print "  Kleines 1x1 (k) [Standard] oder gro";szett;"es 1x1 (g) w";_
         aekl;"hlen: ";
input frage
if (frage="g") or (frage="G") then
    typ=1
else
    typ=0
end if
print
if typ=1 then
    print "  Gro";szett;"es 1x1 gew";aekl;"hlt.";
    print " Der 1.Faktor geht bis 20."
    r=19
else
    print "  Kleines 1x1 gew";aekl;"hlt.";
    print " 1.Faktor geht nur bis 10."
    r=9
end if

do
    richtige=0
    falsche=0
    zeit=0
    anz+=1   'zählt wie viele Durchgänge es gibt'
    print:print
    input "  Zeitdauer (1-5 Minuten): ",zeit_min
    if zeit_min<1 then   'übertriebene Werte korrigieren 
        zeit_min=1  
    end if
    if zeit_min>5 then
        zeit_min=5
    end if    
    zeitvorgabe=zeit_min*60          'zeitvorgabe in Sekunden
    print "  Zeitdauer auf ";zeit_min;" Min. =";zeitvorgabe;_
      " Sekunden gesetzt."
    taste=tastedruecken
    a1=2
    b1=2
    randomize
    do 
        do
           a=int(rnd*r)+2
           b=int(rnd*8)+2
        loop while (a=a1) and (b=b1)  'ggf. neue Zufallszahlen   
        a1=a
        b1=b
        do
           cls
           print:print:print:print
           print "           ";a;" ";mal;b;" = ";
           zeit1=timer
           input c
           zeit2=timer
        loop while c=0    'c=0 entsteht wenn nur Return gedrückt wurde 
        zeit=zeit+(zeit2-zeit1)
        print
        sleep kurzzeit,1  'kurz verzögern:  Computer soll auch "denken"
        if a*b=c then
            richtige+=1
            print "            Richtig!"
        else
            sleep kurzzeit,1  'wenn falsch,dann "denkt" der C. noch 
            falsche+=1
            print "        Nein, ";a;" ";mal;b;" = ";a*b
        end if    
        sleep kurzzeit,1
        if zeit<zeitvorgabe then
          print
          print
          zeitsek=int(zeitvorgabe-zeit)
          if zeitsek<=1 then
              print "   Eine Aufgabe geht noch!"
          else    
              print "   Zeit: noch gut ";zeitsek;" Sekunden"
          end if    
          print
          print "   (Abbruch mit ESC-Taste,";
          print " weiter mit beliebiger anderer Taste.)"
        end if      
        taste=tastedruecken
    loop while (zeit<zeitvorgabe) and (taste<>chr(27))  
    cls
    print
    print "   Es gab ";
    if richtige=1 then
        print "1 richtiges Ergebnis und ";
    else
        print richtige;" richtige Ergebnisse und ";
    end if
    if falsche=1 then
        print "1 falsches Ergebnis."
    else
        print falsche;" falsche Ergbnisse."
    end if    
    taste=tastedruecken
    print:print
    print "   richtige E. - falsche E. ";mal;" 2 ->";
    punkte=richtige-falsche*2
    if punkte<=0 then
        print " Leider keine Punkte."
    else 
      if punkte=1 then
         print " 1 Punkt."; 
      else     
        print punkte;" Punkte.";
      end if
      print " Zeit:";int(zeit+0.5);" Sekunden."
      if zeit>45 then      'Score nicht bei zu kurzen Zeiten 
         ppm=int(punkte*60/zeit+0.5)
         print
         print "                                ->";ppm;" Punkte/Minute"
         if ppm>maxppm then
             maxppm=ppm
         end if    
      end if
    end if 
    print
    if anz>=5 then
       print "  Bereits 5 Druchg";aekl;"nge. Jetzt erst mal eine Pause!"
       taste=tastedruecken
       frage="N"
    else
       while inkey<>""          'Tastaturpuffer leeren
       wend    
       print "   Weiter (j/n) ? ";
       ch=GetKey
       if (ch=106) or (ch=74) then
          frage="j"
       else
          frage="n" 
          cls
       end if  
    end if   
loop while frage="j"

print 
if (maxppm>0) and (anz>1) then
  print "  ";anz;" Durchg";aekl;"nge.  H";_
        oekl;"chster Wert: ";maxppm;" Punkte/Minute"
  taste=tastedruecken
end if  
endbeiXbn.controlInitQuit   'setzt nur eine Variable
sleep 250,1
'nur Auslaufenlassen des Programms; schnelles Programmende mit "end"