Win32 tutorial

από τον Nuclear / theLab

Πρόλογος

Αυτό το tutorial απευθύνετε σε όσους ξέρουν προγραμματισμό σε C/C++ και θέλουν να κάνουν το πέρασμα στα windows, σε αυτό το tutorial θα δούμε τα βασικά στοιχεία του Win32 API πώς δομείτε ένα Win32 application κλπ.

Επίσης να τονίσω πριν αρχίσουμε ότι καλό είναι να υπάρχει καλή γνώση της γλώσσας C ή C++ πριν αρχίσει κάποιος να ασχολείται με windows programming.

Αλλά ας αρχίσουμε !

Τα βασικά, θεωρία

Στα windows όλη η δουλειά γίνετε με τα Windows Messages, όταν συμβαίνει κάποιο event τα windows βάζουν κάποιο message στο message queue του προγράμματος μας, εμείς ορίζουμε μια Callback συνάρτηση (μια δικιά μας συνάρτηση που καλούν τα windows κάθε φορά που έρχεται ένα message) με την οποία ανταποκρινόμαστε σε όποια messages θέλουμε, ενώ τα υπόλοιπα τα αφήνουμε να τα αναλάβουν τα windows...

Ελπίζω να μη σας μπέρδεψα με όλα αυτά, τα πράματα είναι πολύ πιο απλά απο ότι φαίνοντε. Ας δούμε όμως τα πράματα από την αρχή και όλα θα ξεκαθαρίσουν.

Σε ένα κανονικό C/C++ πρόγραμμα το πρόγραμμα ξεκινάει από την main() στα windows το πρόγραμμα ξεκινάει από την WinMain() η οποία δηλώνετε ως εξής:

int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrev, LPSTR CmdLn, int ShowCmd )

το WINAPI στην αρχή είναι το calling convention το WINAPI είναι ουσιαστικά το _pascal calling convention. οι παράμετροι έχουν ως εξής:

Μια άλλη συνάρτηση που πρέπει να έχουμε είναι η WinProc(), αυτό είναι το callback που καλούν τα windows όταν ένα message υπάρχει στο message queue και μέσα από αυτή τη συνάρτηση ενεργούμε ανάλογα για το κάθε message. Ας δούμε πώς δηλώνουμε αυτή τη συνάρτηση. (σημ. Το όνομα της συνάρτησης αυτής μπορεί να είναι όποιο θέλουμε, αλλά συνηθίζετε να την ονομάζουμε WinProc ή WindowProc ή κάτι παρεμφερές)

LRESULT CALLBACK WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )

Το CALLBACK calling convention είναι και πάλι το _pascal convention.

Οι παράμετροι:

Το Πρώτο μας παράθυρο

Στην WinMain() το πρώτο πράγμα που έχουμε να κάνουμε είναι να δημιουργήσουμε ένα παράθυρο ... για να το κάνουμε αυτό πρέπει πρώτα να κάνουμε ένα Window Class, αυτό το κάνουμε συμπληρώνοντας ένα structure με διάφορες πληροφορίες, και μετά καλούμε τη συνάρτηση RegisterClass() Το structure αυτό είναι το WNDCLASS και περιέχει τα παρακάτω πεδία:

Μόλις δημιουργήσουμε και κάνουμε register την κλάση μας μπορούμε να καλέσουμε την συνάρτηση CreateWindow() με την οποία θα δημιουργήσουμε το πρώτο μας παράθυρο. Η οποία είναι ως εξής:

HWND CreateWindow(LPSTR lpszClassName, LPSTR lpszWindowName, DWORD dwStyle, int x, int y,
        int nWidth, int nHeight, HWND hwndParent, HMENU hmenu, HANDLE hinst, LPVOID lpvParam)

Η συνάρτηση αυτή μας επιστρέφει ένα handle για το παράθυρο που δημιουργούμε, από εδώ και στο εξής θα αναφερόμαστε στο παράθυρο αυτό, μέσα στο πρόγραμμα από το handle του αυτό, ας δούμε όμως τις παραμέτρους μία-μία.

Αφού καλέσουμε αυτή τη συνάρτηση έχει δημιουργηθεί το παράθυρο μας, μας μένει να το εμφανίσουμε με τη συνάρτηση ShowWindow().

Αλλά ας δούμε τώρα ένα παράδειγμα του κώδικα μέχρι στιγμής.

code part1

το Message Loop

Αφού δημιουργήσαμε το παράθυρο της εφαρμογής τώρα πρέπει να την κάνουμε να αντιδρά στα windows messages, Ένα κλασικό message loop για μια win32 εφαρμογή είναι κάπως έτσι:

message loop

H συνάρτηση GetMessage() παίρνει το πρώτο message που υπάρχει στο message queue και το βάζει στην διεύθυνση που της λέμε στην πρώτη παράμετρο, το msg είναι ένα MSG structure που έχουμε δηλώσει προηγουμένως. Η GetMessage() επιστρέφει TRUE εκτός αν το message είναι WM_QUIT οπότε επιστρέφει FALSE βγαίνουμε από το loop, και κλείνουμε το πρόγραμμα.

Μέσα στο loop καλούμε πρώτα την συνάρτηση TranslateMessage() και μετά την DispatchMessage() η οποία καλεί την Callback συνάρτηση που ορίσαμε για να αναλάβει τις απαραίτητες ενέργειες ανάλογα με το message, την οποία θα δούμε σε λίγο.

Message Handler

Ας δούμε την callback συνάρτηση που αναλαμβάνει να αντιδράσει στο κάθε message:

message handler

Σε αυτή τη συνάρτηση έχουμε ένα μεγάλο switch με όσα messages θέλουμε να αναλάβουμε, τα υπόλοιπα απλά τα αφήνουμε στην DefWindowProc() στην οποία περνάμε ότι παραμέτρους πήραμε και αναλαμβάνει τα υπόλοιπα messages που δεν θέλουμε να χειριστούμε εμείς.

Εδώ στο παράδειγμα αναλαμβάνουμε το WM_KEYDOWN message και κοιτάμε αν είναι το πλήκτρο esc VK_ESCAPE, αν ναι, βάζουμε ένα WM_CLOSE message στο queue αλλιώς βγαίνουμε από το switch και αφήνουμε τα windows να αναλάβουν.

Χειριζόμαστε το WM_CLOSE βγάζοντας ένα message box στην οθόνη και καταστρέφοντας το παράθυρο με την DestroyWindow().

Στο WM_DESTROY καλούμε την PostQuitMessage() η οποία βάζει ένα WM_QUIT message στο queue.

Τέλος αναλαμβάνουμε και τα left mouse clicks βγάζοντας ένα μήνυμα ότι πατήθηκε το κουμπί, προφανώς υπό κανονικές συνθήκες θα κάναμε κάτι πιο χρήσιμο, αλλά αυτό είναι ένα sample του τι μπορούμε να κάνουμε με τα windows messages.

Αλλά ας δούμε τελικά πως διαμορφώνεται ο κώδικας του προγράμματος μας μετά από όλα αυτά.

code part1 code part2 message handler

Επίλογος

Εδώ κάπου κλείνει αυτό το tutorial, μάθαμε πώς να γράφουμε ένα απλό Win32 πρόγραμμα, δημιουργία παραθύρων, message handling κλπ, για περισσότερες πληροφορίες για το Win32 API κοιτάξτε το documentation που υπάρχει μαζί με κάθε compiler ικανό να κάνει win32 προγράμματα.

Ο κώδικας σε αυτό το tutorial είναι καθαρά win32 και πρέπει να γίνετε compile σε όλους τους compilers χωρίς πρόβλημα, πάντως προτείνω την MS Visual C++.

Α και ένα τελευταίο σχόλιο, σε αυτό το tutorial χρησιμοποιήσαμε την GetMessage() στο message loop, αυτή η συνάρτηση περιμένει να έρθει κάποιο message στο queue, αυτό είναι ακριβός ότι χρειάζεται σε ένα κλασικό event driven windows application αλλά δεν κάνει για real-time προγράμματα (π.χ. Demos) σε αυτή την περίπτωση πρέπει να χρησιμοποιήσουμε την PeekMessage(). Την οποία θα καλύψω στο επόμενο tutorial που θα είναι μια εισαγωγή στο DirectDraw.

Για οποιαδήποτε διευκρίνιση πάνω στο tutorial η και για διορθώσεις, η οτιδήποτε άλλο μην διστάσετε να επικοινωνήσετε μαζί μου στο email: nuclear@siggraph.org

Nuclear / the Lab