18
Tags:
c++
windows
win32
api
gui
Skrevet af
Bruger #8985
@ 13.09.2011
Forord
Mange aspirerende programmører får sig et chok, når de ser mængden af kode, der skal til for at oprette et tomt vindue alene. En vis procentdel af denne gruppe tyr til andre midler såsom .NET, andre holder sig til de noget banale konsolvinduer og nogle vælger helt at opgive ævred. Det forstår sig alt sammen, eftersom det kan virke som noget at et skridt at tage, når ens 'comfort zone' involverer at lade brugeren indtaste navn og alder og derefter fortælle vedkommende ting, han eller hun egentlig godt vidste i forvejen.
Derfor vil jeg med denne artikel forsøge at formidle Windows-API'et på en så logisk og letforståelig måde som muligt. Selvom det faktisk er alt andet end svært at bruge API'et, når først man har opbygget en grundlæggende forståelse for det, forventer jeg alligevel du har et godt kendskab til C/C++. Der vil være mange ting i artiklen, som ikke direkte har noget med API'et at gøre men snarere selve C++, og derfor vil jeg som oftest tage det for givet, at du kender til disse ting.
Valg af IDE
Det er sådan set ligegyldigt, hvilken IDE du beslutter dig for at skrive dine programmer i, men jeg kan varmt anbefale Visual C++. Microsoft udgav for nyligt 2010-versionen af Visual C++ Express Edition, og den er faktisk slet ikke tosset. Dens 'intellisense' er næsten på højde med Visual C#. Du kan hente den her:
http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-expressEr du ikke til Microsofts produkter, er Dev-C++ også et godt alternativ. Den er gratis og kan hentes fra
http://www.bloodshed.net. Selv bruger jeg Visual C++ 2010, og jeg vil i artiklen gå ud fra, du også bruger den. Tidligere versioner af Visual C++ vil også virke.
MSDN
Micro
Soft
Developer
Network er en uundværlig kilde af information om blandt andet Windows-API'et. Væn dig til at bruge det, for du kommer til at lære at bruge API'et langt hurtigere, hvis du fra et tidligt tidspunkt sætter dig ind i, hvordan de forskellige funktioner fungerer og anvendes. Det kan virke besværligt, men du bør slå enhver ny funktion, du støder på i denne artikel, op i MSDN. Jeg bruger den tit, hvis jeg eksempelvis har glemt, hvilke parametre en funktion har. Du finder MSDN her:
http://msdn.microsoft.comPrøv eventuelt at slå en funktion op ved at indtaste navnet på den i søgemaskinen. For eksempel 'CreateWindow'.
Konsollen
Jeg foreslår, vi lægger ud med at bruge konsollen kort, da der er visse ting du får brug for at vide for at kunne oprette vinduer. Bare rolig, langt størstedelen af artiklen fokuserer på at skabe vinduer. En ting du skal vide, er at de indbyggede typer som int og char sjældent forekommer i programmer, der anvender API'et. Nogle af dem har alternative navne; eksempelvis vil man typisk skrive 'UINT' fremfor 'unsigned int'. Andre, som TCHAR, kan både være char og wchar_t, afhængig af om konstanten 'UNICODE' er defineret eller ej. For at man ikke selv skal sidde og bøvle med dette, er det praktisk bare at bruge de navne, API'et har tildelt typerne, og det vil vi også gøre i denne artikel.
Nå, lad os springe ud i det med et simpelt program. Programmet skal bede brugeren indtaste en sætning, og denne sætning skal så fremstå som konsollens titel. Titlen er som standard programmets absolutte sti på computeren.
Vælg File > New > Project (genvej Ctrl+Shift+N) og dernæst 'Empty Project'. Giv projektet et navn og tryk OK. Du skulle nu gerne se et vindue med titlen 'Solution Explorer', hvori der befinder sig nogle gule mapper. Højreklik på den nederste, 'Source Files', og vælg Add > New item (genvej Ctrl+Shift+A). Vælg 'C++ file (.cpp)', navngiv filen og tryk Add.
Det hvide felt, man skriver koderne i, skulle nu gerne dukke op. Hvis du ligesom mig godt kan lide at have en linjetæller til venstre for koderne, så vælg Tools > Options, udfold 'Text Editor', tryk på 'C/C++' og sæt et hak i 'Line numbers'. Prøv at kompilere og køre denne kode ved at trykke F5 (eller vælge Debug > Start Debugging i menuen).
- #include <windows.h>
-
- int main()
- {
- // Udskriv en besked til brugeren
-
- HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- LPCSTR pMsg = "Indtast en saetning\n";
- WriteConsole(hOut, pMsg, lstrlen(pMsg), NULL, NULL);
-
- // Læs sætningen fra brugeren
-
- const int nMaxChars = 50;
- HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
- TCHAR tcTitle[nMaxChars];
- DWORD dwCharsRead;
-
- ReadConsole(hIn, tcTitle, nMaxChars, &dwCharsRead, NULL);
- tcTitle[dwCharsRead] = 0;
-
- // Udskift konsollens titel med det, brugeren skrev
-
- SetConsoleTitle(tcTitle);
- }
Det første vi gør, er at skaffe en HANDLE, der tillader os at sende output til konsollen. Den skal vi bruge, når vi kalder WriteConsole, som sjovt nok er den funktion, man skriver i konsollen med. Derefter putter vi vores besked i variablen pMsg, som er af typen LPCSTR. 'LPCSTR' står for 'Long Pointer to Const String' og svarer til 'const TCHAR*'. Husker du, hvad jeg fortalte om netop TCHAR? TCHAR kan være både char og wchar_t. Vores streng består eksplicit af chars, så på et UNICODE-system kunne der godt opstå komplikationer. Disse kan undgås ved at bruge makroen TEXT, som, hvis UNICODE er defineret, omdanner en streng til wchar_t*. Nogle gange vælger jeg at smide alle mine strenge i TEXT, men det er helt op til dig. Skulle du vælge at gøre det, bruges den således:
TCHAR* tc = TEXT("Min streng");
Jeg vil ikke bruge den i denne artikel, da jeg ønsker at gøre tingene på simple som muligt.
Nu har vi, hvad vi har brug for, så lad os kalde WriteConsole. Funktionen tager 5 argumenter, hvoraf de sidste to er valgfrie. MSDN definerer funktionen således:
BOOL WINAPI WriteConsole(
__in HANDLE hConsoleOutput,
__in const VOID *lpBuffer,
__in DWORD nNumberOfCharsToWrite,
__out LPDWORD lpNumberOfCharsWritten,
__reserved LPVOID lpReserved
);
Det første argument er vores output-handle. Dernæst kommer vores besked, som vi har defineret som LPCSTR eller const TCHAR*. Hvorfor de bruger VOID* her kan der være mange forklaringer på. Herefter fortæller vi, hvor mange tegn den skal udskrive. Da den skal udskrive alle tegnene, giver vi den strengens længde, som vi får af funktionen lstrlen.
Ønsker vi at vide, hvor mange tegn det lykkedes funktionen at udskrive før den returnerede, kan vi give den en pointer til en variabel af typen DWORD, men vi går ud fra den formår at udskive alle tegn, så vi tildeler den NULL. Den sidste parameter er reserveret og er der kun for 'backward compatibility', så den får også NULL.
'LPDWORD' står for 'Long Pointer to DWORD' og er altså synonym med 'DWORD*'. 'DWORD' svarer til at skrive 'unsigned long'. I Visual C++ kan du se den bagvedliggende type ved at holde musen over typens alternative navn.
NULL er i virkeligheden bare 0 (i modsætning til C#, hvor det rent faktisk er en tom pointer) og kan når som helst erstattes med et nul.
Nu skal vi til at læse det, brugeren indtaster. Til det bruger vi ReadConsole. Da funktionen har brug for at vide, hvor mange tegn vi ønsker at læse, begynder vi med at oprette en konstant. Dernæst skaffer vi en handle på nøjagtig samme måde som før, bortset fra at vi denne gang ønsker en input-handle. tcTitle er et array, der skal indeholde den læste tekst.
ReadConsole afslutter ikke automatisk den indlæste tekst med tegnet \0, hvilket totalt ødelægger strengen, så vi er nødt til at finde ud af, hvor mange tegn der havner i tcTitle. Derfor opretter vi en DWORD, dwCharsRead, som senere vil indeholde antal tegn udskrevet.
ReadConsole ser således ud:
BOOL WINAPI ReadConsole(
__in HANDLE hConsoleInput,
__out LPVOID lpBuffer,
__in DWORD nNumberOfCharsToRead,
__out LPDWORD lpNumberOfCharsRead,
__in_opt LPVOID pInputControl
);
Det sidste argument tager en pointer til en CONSOLE_READCONSOLE_CONTROL-struktur. Ved at bruge en sådan får du adgang til yderligere oplysninger om det læste input, eksempelvis om Ctrl var holdt nede, men det har vi ikke brug for nu, så den får en NULL. Efter ReadConsole returnerer, afgrænser vi strengen i tcTitle med et \0, så den ikke fortsætter for evigt.
Nu har vi både skrevet til og læst fra konsollen. Det eneste, der mangler i vores program, er at udskifte konsollens titel med tcTitle, og intet kunne være nemmere. Vi kalder selvfølgelig bare SetConsoleTitle med tcTitle som argument.
Nu burde du have en lille idé om, hvordan ting gøres i Windows-API'et, og vi kan fortsætte til de lidt større programmer, der involverer vinduer. Dog synes jeg, du bør rode lidt med eksemplet og nogle af de forskellige funktioner. Du kunne blandt andet forhindre konsolvinduet i at lukke med det samme.
Fortsæt til næste side, hvor vi kaster os ud i vinduerne.
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (3)
I må meget gerne rate og kommentere artiklen
En meget behagelig tilgang til GUI i c/c++.. Det var godt, at der var lidt flere eksempler end bare lige at vise et vindue, da der også var hvordan man kunne få noget tekst input, samt hvordan man kunne bruge dette test input i sit program
Endelig en kommentar! Mange tak, Kevin
Du skal være
logget ind for at skrive en kommentar.