Παρασκευή, 13 Μαΐου 2016

Πώς δουλεύει ο διερμηνευτής της Μ2000 Μέρος Β.

Ας κάνουμε εδώ ένα πισωγύρισμα. Ας πάμε από την αρχή του κώδικα του διερμηνευτή και το περιβάλλοντος της Μ2000, το m2000.exe

Το πρόγραμμα ξεκινάει στην Sub Main. Δηλαδή ξεκινάει χωρίς να φορτώσει φόρμες. Στο IDE της VB6 το βρίσκουμε άμεσα με Ctrl F γράφουμε SUB MAIN με επιλογή Current Project.
Αν προσέξει κανείς τον κώδικα της Sub Main θα δει ότι έχει αφαιρεθεί με comments το InitCommonControlsEx, το οποίο θα χρειάζονταν αν χρησιμοποιούσαμε τα common controls που δίνει η VB. Ελάχιστα στοιχεία ελέγχου (controls) φορμών χρησιμοποιούμε, και θα τα δούμε ξεχωριστά. Παρατηρούμε επίσης πως φορτώνεται μια φόρμα Form5 και παρακάτω εκτελείται μια μέθοδος στην Form1, η Form1.Something (δεν κάνουμε σχόλια για τα ονόματα...θέλει φαντασία στο να δίνεις το κατάλληλο όνομα).
Η Form5 είναι μια φόρμα που δεν έχει τίποτα πάνω της! Ο λόγος ύπαρξης είναι ότι δίνει ένα σημείο πάνω στο οποίο στέκονται οι άλλες φόρμες, Παίζει το ρόλο "ασπίδας" όταν βάζουμε τη κεντρική φόρμα (Form1) να γίνεται ημιδιαφανείς. Αν δηλαδή ορίσουμε ένα χρώμα στη Form1 να είναι εντελώς διάφανο τότε μπορεί κανείς να κάνει κλικ στο παράθυρο πίσω από την φόρμα, αλλά δεν το αφήνουμε με την Form5. Αν θέλουμε να ανοίξουμε τις ιδιότητες εκτυπωτή δείτε μέρος από τον κώδικα:
VB6 Code
If form5iamloaded Then
Call PrinterProperties(Form5.hWnd, gp)
Else
Call PrinterProperties(Form1.hWnd, gp)
End If

Επειδή ενδέχεται να έχουμε αφαιρέσει το Form5, κοιτάμε μέσω μιας μεταβλητής αν είναι φορτωμένο το form5 και αν ναι δίνουμε το handler του παραθύρου (Window handler = hWnd) που είναι μια ιδιότητα που έχουν όλες οι φόρμες της VB, αλλά και όλα τα παράθυρα στα Windows, δηλαδή είναι ένα νούμερο που έρχεται από το λειτουργικό, και είναι μοναδικό για την τρέχουσα λειτουργία του υπολογιστή. Ενδέχεται να αλλάξει το νούμερο και για το λόγο αυτό τα hWnd δεν τα αποθηκεύουμε, τα ζητάμε αμέσως πριν κάνουμε τη δουλειά μας)
 
Δεν θα μείνουμε στην Form5, απλά θα μείνει στο μυαλό μας (και θα το βλέπουμε στο κώδικα) τη διπλή επιλογή,  του Form1.hWnd ή του Form5.hWnd για να δίνουμε βάση στο άνοιγμα μιας φόρμας. Η βάση δηλαδή πάντα είναι κάτω από το παράθυρο, και αν έχουμε ανοίξει ας πούμε σε μια βάση δυό παράθυρα τότε αν επιλέξουμε τη βάση δεν θα βγει ποτέ μπροστά από τα άλλα δυο, ενώ αν επιλέξουμε ένα από τα δύο, θα αλλάξουν επίπεδο (η z θέση όπως λέγεται) και το επιλεγμένο θα έρθει μπροστά (foreground) και τα άλλα που έχουν κοινή βάση θα πάνε πίσω, αλλά όχι πιο πίσω από την κοινή βάση. (αυτά τα "κόλπα" είναι της Vb και του συστήματος, μια μίξη δηλαδή)

Στις μεταβλητές που ορίζουμε στο module1 (mod_Text.bas) φτιάχνουμε και το βασικό αντικείμενο εκτέλεσης τύπου basetask. Αυτό ρυθμίζεται κατάλληλα και δίνεται μαζί με την εντολή που θέλουμε για να εκτελεστεί ο πρώτος κώδικας σε Μ2000. Κάθε φορά που καλούμε μια συνάρτηση ή ένα τμήμα ένα νέο αντικείμενο δίνεται και στην επιστροφή καταστρέφεται. Όμως το παρακάτω θα καταστραφεί στο τέλος εκτέλεσης του m2000.exe
VB6 code
Public basestack1 As New basetask 

Από την Sub Main καλείται η Form1.Something. Όταν καλεί κανείς μια μέθοδο σε φόρμα (λέγεται μέθοδος η sub σε μια φόρμα, γιατί η φόρμα είναι αντικείμενο στην VB6), τότε αν η φόρμα δεν έχει φορτωθεί..φορτώνεται και εκτελείται το Load γεγονός. Έτσι πριν κοιτάξουμε την Form1.Something θα δούμε την Sub Form_Load() (ενώ καλέσαμε μια μέθοδο, έγινε παραγωγή γεγονότος και κλήθηκε η Load() και μετά συνέχισε η εκτέλεση της Something). Στη Load() ρυθμίζουμε τα στοιχεία ελέγχου της φόρμας, ό,τι δεν γίνεται αυτόματα (δηλαδή ότι δεν γίνεται από το PropertyBag στο Inintialize γεγονός σε κάθε στοιχείο), το κάνουμε εκεί. Αλλά μπαίνουν και άλλα όπως δείτε αυτό:
ThereIsAPrinter = IsPrinter
το οποίο κοιτάει αν υπάρχει εκτυπωτής! (γενικά υπάρχει μια ελευθερία εδώ η οποία ας πούμε ότι γίνεται αποδεκτή, ίσως κάποια πιο αυστηρά άτομα να μην το δεχόντουσαν αυτό).

Στο ίδιο μέρος, υπάρχει και η s$ = CommandW όπου διαβάζουμε την command line σε Unicode. H Command Line περιέχει ότι συνόδευε την κλήση του προγράμματος. Έτσι αν καλέσουμε το m2000.exe με ένα όνομα αρχείου, τότε θα το πάρουμε ακριβώς εδώ και θα τρέξουμε το πρόγραμμα χωρίς να κάνουμε Show (να δείξουμε) την φόρμα (αφήνουμε το πρόγραμμα να δώσει την εντολή Show ή Άναψε, ή θα γίνει αυτόματα αν παίξει μια εντολή για εισαγωγή). Εκτός από εντολή για άνοιγμα αρχείου διαβάζουμε και τους λεγόμενους διακόπτες ή switches. 
Δείτε την εντολή Switches para$, όπου ότι έχει μαζευτεί ως παράμετρος στέλνεται για εκτέλεση στο Switches. Π.χ. μπορούμε να ορίσουμε το πώς θα καταλαβαίνει ο διερμηνευτής την προτεραιότητα στις λογικές εκφράσεις.
Αν όλα πάνε καλά στο Load() θα έχουμε το l_complete = True (αν υπήρξε λάθος πιο πριν δεν θα φτάσει ποτέ σε αυτό το σημείο το Load() αλλά δεν θα σταματήσει το πρόγραμμα. Να γιατί το κοιτάμε στην Sub Main και παίρνουμε βαριά μέτρα, δηλαδή κάνουμε End που διαγράφει όλα τα αντικείμενα, ελευθερώνει δηλαδή τη μνήμη, το οποίο δεν είναι καλό σε άλλες περιπτώσεις επειδή αφήνει handler του χρήστη (π.χ. σε αρχείο) χωρίς να τους κάνει close. Πλην όμως για κάθε ενδεχόμενο στη sub main, πριν το End (που δεν το δίνουμε αν όλα πάνε καλά στο Load), εκτελούμε αυτό:
VB6 code
CloseAllConnections 
CleanupLibHandles
Το πρώτο κλείνει όλες τις συνδέσεις σε βάσεις δεδομένων (όπως και αυτή για το help), και το δεύτερο αποδεσμεύει dll που μπορεί να έχει ανοίξει ο χρήστης. Τυχόν αντικείμενα COM που έχει ανοίξει ο χρήστης αποδεσμεύονται με την End, που θα καθαρίσει όλες τις αναφορές σε αντικείμενα.

Ας παραμείνουμε στην Form1.Something, δηλαδή έχει πετύχει το Load() και συνεχίζουμε παρακάτω, και βλέπουμε αυτό:
VB6 code
Me.WindowState = 0
Αυτή είναι μια έμμεση εντολή Show. Δηλαδή λέει να εμφανιστεί η φόρμα...χωρίς να εμφανιστεί, δηλαδή να εμφανιστεί με ελαχιστοποίηση. Επειδή η Form1 δεν έχει επικεφαλίδα (caption) δεν θα εμφανιστεί στην Task Bar. Παρόλα αυτά βλέπουμε κάτι στην Task Bar αλλά αυτό βγαίνει με άλλη φόρμα! Αν δώσει κανείς caption σε μια φόρμα της VB6 τότε αυτή αυτόματα βάζει decoartion ή ελληνικά βάζει πλαίσιο επικεφαλίδας και περιθώριο, χωρίζοντας τη φόρμα σε ωφέλιμο και διακοσμητικό στοιχείο. Στην Μ2000 θέλουμε η φόρμα να πιάνει όλη την οθόνη άρα να μην έχει "μη ωφέλιμο" χώρο. Έτσι μια άλλη φόρμα, η Form3 παίζει ρόλο στο να εμφανίζει στο Task Bar caption. Και πώς γίνεται να εμφανίζει μια φόρμα στη Task Bar και να μην φαίνεται; Απλά βρίσκεται η φόρμα Form3 εκτός ορατού χώρου στην οθόνη μας! Η φόρμα Form3 φορτώνεται με την απόδοση τίτλου.


Βλεπουμε μια εντολή, στην Form1.Something:
VB6 code
basestack1.Owner.Move 0, 0, ScaleWidth, ScaleHeight

η ιδιότητα Owner του Basestack1 έχει ρυθμιστεί στην αρχή του Something να δείχνει το DIS το οποίο είναι ένα PictureBox και είναι ο χώρος που γράφουμε και διαβάζουμε, η κονσόλα δηλαδή. Σε άλλα αντικείμενα εκτέλεσης αυτή η ιδιότητα μπορεί να δείχνει κάτι άλλο, αλλά πάντα θα δείχνει κάτι στο οποίο γράφουμε-διαβάζουμε (με εξαίρεση στον εκτυπωτή και στις φόρμες χρήστη όπου δεν διαβάζουμε, στις φόρμες χρήστη διαβάζουμε σε στοιχεία ελέγχου). Η μέθοδος Move βάζει το αντικείμενο στη θέση και το μέγεθος που θέλουμε. Εδώ του δίνει το μέγιστο.

VB6 code
NOEXECUTION = False

Όπου στο κώδικα υπάρχει σε μεταβλητή (πάντα σε boolean) το NO μπροστά σημαίνει ότι η καλή κατάσταση είναι το False. Αν κάποια στιγμή αργότερα το NOEXECUTION γίνει True τότε θα έχουμε την κακή κατάσταση, θα έχουμε διακοπή του προγράμματος που εκτελεί ο διερμηνευτής (όχι τη διακοπή του διερμηνευτή).

Αμέσως μετά εκτελείται το παρακάτω:
VB6 code
myBreak basestack1
Δίνουμε το αντικείμενο εκτέλεσης (κρατάει το αντικείμενο εξόδου, εδώ το DIS που είναι ένα Picturebox πάνω στην Form1) στην myBreak, και αυτή κάνει ότι όταν πατάμε το Break κουμπί στο πληκτρολόγιο, και βασική δουλειά είναι να κοιτάει ιδιωτικές καταχωρήσεις στη registry των Windows (μερικά από τα οποία αλλάζουμε με την φόρμα Ρυθμίσεις ή Settings).

Αμέσως μετά έχουμε αυτό εδώ:
VB6 code
If cLine <> "" Then
LASTPROG$ = cLine
'
Original basestack1, " : NEW : CLEAR : " & "TITLE " & """" + ExtractNameOnly(cLine) + """" & ", 0"
Else
Original basestack1, " : NEW : CLEAR :" & "TITLE " & """" & "M2000" & """"
End If

Δείτε τι γίνεται. Κοιτάμε αν έχουμε στο cLine κάτι. Αυτό έχει την command line σε αρχείο όμως (έχει γίνει ένα αρχικό ξεκαθάρισμα του τι έχει η command line). Και η εντολή Original παίζει κώδικα της Μ2000, αφού πρώτα καθαρίσει ότι πρόγραμμα τυχόν υπήρχε με την New, καθαρίσει όλες τις μεταβλητές με την Clear, δώσει τίτλο (άρα θα φορτωθεί το Form3). Δείτε ότι αν έχουμε cLine τότε κρατάμε αντίγραφο και στον τίτλο βάζουμε παράμετρο 0 που σημαίνει να βγει ελαχιστοποιημένη η φόρμα. Είναι χρήσιμο αυτό, ειδικά όταν έχουμε φόρμες να μην ανοίγει η κονσόλα. (ανοίγει αν κάνουμε κλικ πάνω στο εικονίδιο στην Task Bar, αλλά μπορούμε αν θέλουμε να το αφαιρέσουμε)

αμέσως μετά σημαδεύουμε την έναρξη, γράφοντας σε μια boolean global την κατάσταση True.
s_complete = True

Πιο κάτω δυσκολεύει ο κώδικας. Μια σύντομη περιγραφή είναι αυτή: Στο κώδικα της Something υπάρχει το βασικό loop για τον CLI, τον command  line interpereter. Αν έχουμε κάτι στη cLine (ένα όνομα αρχείου δηλαδή) τότε εκτελείται το παρακάτω:
VB6 code
   sHelp "", "", 0, 0
   qq$ = "LOAD" & """" + cLine + """"
   If Len(Left$(cLine, rinstr(cLine, "\"))) > 0 Then
    mcd = Left$(cLine, rinstr(cLine, "\"))
   End If
   cLine = ""

η sHelp καθαρίζει το σύστημα βοήθειας. (το ctrl+F1), και η qq$ παίρνει την εντολή εκτέλεσης, δηλαδή μια Load με το όνομα (και μονοπάτι) του αρχείου που θα καλέσει (επειδή μπορεί να έχει κενά το όνομα, το βάζουμε σε εισαγωγικά). Κάνουμε εξαγωγή του μονοπατιού και το θέτουμε ως τρέχον φάκελο στο mcd (my current directory). Αμέσως μετά καθαρίζουμε τη cLine και έτσι στην συνέχεια του loop θα πάμε στην εισαγωγή εντολής. Όμως ενδέχεται αυτό που φορτώνουμε να έχει εντολές που εκτελούνται άμεσα, και θα εκτελεστούν. Αν δε υπάρχει η END ή ΤΕΛΟΣ τότε θα τερματίσει το πρόγραμμα. Αλλά όλες τις πιθανές εκδοχές θα τις δούμε στην επόμενη ανάρτηση.

Συνεχίζεται...