Win32 perusteet
Mureakuha
Tässä sarjassa käsitellään lähinnä Windowsin graafista puolta eli gdi ja user -kirjastoihin liittyviä toimintoja. Kaikki kohdat, joissa puhutaan C:stä, voidaan tehdä yhtä hyvin C++:lla. Tämä tapa ei kuitenkaan mahdollista - ainakaan helposti - olioiden laajamittaista käyttöä ohjelmassa, johon MFC -osiossa on puolestaan keskitytty.
Windowsin ohjelmointirajapinta (API) sisältää satoja funktioita ja määriteltyjä arvoja. Lisäksi dokumentaatio muuttuu 1 - 3 vuoden sykleissä, joten näiden ulkoamuistamisesta et siis hyödy. Tärkein tavoite on löytää mistä tieto on helposti, nopeasti, luotettavasti ja ymmärrettävästi saatavilla.
Esimerkki tällaisesta voisi olla win32.zip, joka sisältää tärkeimmät toiminnot samassa tiedostossa. Tietoon pitää kuitenkin suhtautua hieman kriittisesti, koska se on peräisin vuoden -95 paikkeilta (Windows perustoiminnot tosin muuttuvat hitaasti - jopa 80 -luvun lopulla tehdyt ohjelmat toimivat vielä tänäkin päivänä). Tuorein tietämys aiheesta löytyy aina Platform SDK -pakettien mukana tulevista ohjetiedostoista.
Sisällysluettelo |
Windows -ohjelmat
Windows -ohjelmat voidaan jakaa seuraaviin ryhmiin:
- konsoliohjelmat (int main(...))
- graafisen käyttöliittymän ohjelmat (int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int))
- kirjastot eli ohjelmien laajennukset (BOOL WINAPI DllEntryPoint(HINSTANCE, DWORD, LPVOID))
- ajurit
Ohjelman todellinen ensimmäinen funktio ei ole sama kuin yllä, vaan C -kirjasto lisää ohjelman alkuun koodin, joka kutsuu C-koodissa näkyvää funktiota. Kääntäjästä riippuen ominaisuus saattaa olla ohitettavissa, jolloin ohjelman (main, WinMain) pääfunktiolla ei ole tunnettuja parametreja eikä voi palata. Tällöin ohjelma on lopetettava käyttäen prosessin lopetuskäskyä.
C/C++ -kääntäjät
Aiemmin ainoastaan Microsoftin ja Borlandin kääntäjillä pystyi tekemään graafisen puolen ohjelmia. Nykyisin kääntäjiä on paljon enemmän:
- MinGW ja Cygwin (GNU) tai graafinen Dev-C++
- LCC-Win32
- Intel C++
- jne.
Kaikissa on kuitenkin oltava mukana Microsoft Platform SDK -paketti (tai vastaava), joka mahdollistaa järjestelmän dll -kirjastojen käytön. Useissa ainakin osa paketista tulee kääntäjän mukana, mutta osassa paketti on itse imuroitava Microsoftilta.
Graafiset ohjelmat
Vaikkakin konsoli- ja graafisestapohjasta voi päätyä samaan lopputulokseen, konsolilla tehdään pääosin komentoriville merkkipohjaisia ohjelmia, joissa ei tehdä kuin yhtä asiaa kerralla. Tässä osassa keskitytään WinMain -alkuisiin ohjelmiin.
Ohjelman vaiheet
Ohjelman alussa siis C -kirjastossa olevat koodit kutsuvat muun muassa:
- GetModuleHandle: ohjelman kahva (hInstance)
- GetCommandLine: osoitin komentorivin parametreihin (lpCmdLine)
- GetStartupInfo: ikkunan näkyväisyys (nCmdShow)
ja seuraavaksi koodaajan kirjoittamaa funktiota:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
Funktion paluuarvona koodissa on syytä olla:
- 0, jos ei käytetä sanomasilmukkaa
- wParam viimeisestä sanomajonon viestistä
Lopuksi C -kirjaston koodi kutsuu ExitProcess -funktiota:
void ExitProcess(uExitCode);
Funktion tehtävänä on lopettaa prosessi ja kaikki sen säikeet. Jos tätä ei käytetä, ohjelma saattaa jäädä pyörimään koneeseen, vaikka WinMainista (eli koko ohjelmasta) olisikin poistuttu. Parametrina funktiolle annetaan lopetuskoodi (käyttäjän päätettävissä).
Esimerkki ohjelmasta, jossa on sanomasilmukka. Seuraavassa ei käytetä sanomasilmukkaa:
#include <windows.h> // otsikkotiedosto //Pääfunktio int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox(0, "Hello World", "Win32", 0); //Viestilaatikko OK -napilla return 0; //Poistutaan ohjelmasta }
Sanomapohjaisuuteen on keskitytty enemmän osassa Win32_sanomat.
Ilman sanomasilmukkaa
Sanomasilmukka ei aina ole koodissa näkyvillä, vaan voi sijaita dll -kirjastoissa. Järjestelmässä olevilla ja resurssieditorilla piirrettävillä ikkunoilla on mahdollista saada helposti ja nopeasti näyttäviäkin ohjelmia. Seuraavassa käydään läpi vaiheet käyttäen neljää työkalua. Kaikki käyttävät c -kielistä pohjaa, mutta koodi voisi yhtä hyvin olla cpp -pohjalla (VC++ 2005:ssä tosin joutuu tekemään pieniä muutoksia).
VC++ 2005 Express
Nämä alkuasetukset on tehtävä VC++ 2005 Express -ilmaisversiolle:
- asenna VC++ Express
- asenna Platform SDK (huomaa Microsoftin sivuilla olevat asennusohjeet)
- asenna VC++ Service Pack
- asenna WindowsTemplateLibrary (tarvitaan täysversioista tulleiden .rc -tiedostojen kääntämiseen)
- asenna ResEd
Muuta VC++:n asetuksista (Tools -> Options...) Projects and Solutions: Include files -kohtaan se WTL:ssä oleva include -hakemisto. Aina kun rc -tiedostossa on viitattu afxres.h, viittaus on muutettava atlres.h -tiedostoon.
Perustetaan projekti
- Dev-C++ 4.9: valitaan File -> New -> Project... -> Windows Application, C Project ja annetaan nimi
- Visual C++ 2005 Express: valitaan File -> New -> Project... -> Win32 Console Application ja annetaan sopiva nimi. Painetaan Next ja asetuksista: Windows application, Empty project ja Finish
- Visual Studio 6: valitaan File -> New... -> Projects -> Win32 Application ja annetaan nimi. Seuraavaksi valitaan 'An empty project'
- Visual Studio 2005: valitaan File -> New -> Project... -> Visual C++ -> Win32 -> Win32 Project ja annetaan nimi ja OK. Asetuksista valitaan 'Windows application' ja 'Empty project'
Tehdään ikkuna
- Dev-C++ 4.9 ja Visual C++ 2005 Express: Käytetään ResEd -sovellusta ja tehdään uusi projekti. Klikataan ylävalikosta Project -> Add Dialog. Syötetään laatikon tyyliksi xStyle -kohtaan esim. 80C80080 ja nimeksi '(NAME)' -kohtaan TEST. Kun tiedosto on tallennettu
- valitaan Dev-C:ssä Project -> Add to Project ja annetaan edellätehty tiedosto
- mennään VC++:ssa 'Solution Explorer' -välilehdellä 'Resource Files' -kansion kohdalle. Tiputetaan tiedosto puuhun tiedostohallinnasta tai valitaan hiiren oikeanpuoleisen napin alta Add -> Existing Item... ja annetaan edellätehty tiedosto
- Visual Studio 6: valitaan File -> New... -> Files -> Resource Script ja annetaan nimi. Sammutetaan tullut ikkuna ja valitaan alalaidan välilehdistä ResourceView (muut ovat ClassView ja FileView). Kansion päällä klikataan hiiren oikeanpuoleisen napin alta Insert... ja valitaan joku Dialogin alta. Varmista, ettei laatikon tyylinä ole Disabled (poista ruksi). Anna nimeksi ID -kohtaan TEST
- Visual Studio 2005: paina Ctrl + Shift + E tai valitse View -> Resource View. Paina hiiren oikeanpuoleista nappia puun otsikon kohdalla (projektin nimi), valitaan Add -> Resource... ja joku Dialogin alta. Varmista, ettei laatikon tyylinä ole ominaisuuksissa Disabled (valitse FALSE). Anna nimeksi ID -kohtaan TEST
Piirrä tämän jälkeen kaksi nappia, joiden ID:t ovat IDOK ja IDCANCEL. Lisää vielä muokkausruutu, jonka ID on IDC_EDIT1.
Jos käytät Dec-C++ tai Visual C++ 2005 Express, niin
- muuta ResEd:ssä nappien:
- IDOK arvoksi '(ID)' -kohtaan 1
- IDCANCEL arvoksi 2
- tee tiedosto Resource.h, jossa on sisältönä edellisessä kohdassa tehdyn .rc -tiedoston #define -alkuiset rivit. Poista rivit joissa on IDOK ja IDCANCEL (tulevat uudestaan windows.h:n mukana)
Lisätään koodi
- Dev-C:ssä pohjatiedosto on jo valmiiksi projektissa
- Visual Studio 6: painetaan työkalurin New Text File -nappia, tallenetaan c -tiedostopäätteellä ja koodissa valitaan hiiren oikeanpuoleisen napin alta Insert File into Project -> (projektin nimi).
- Visual Studio 2005 ja C++ Express: valitaan näkymäksi Solution Explorer ja painetaan hiiren oikeanpuoleista nappia Source Files -kohdassa valiten Add -> New Item.... Valitaan C++ File ja annetaan nimi tiedostopäätteellä c. Valitaan ALT+F7 (projektin ominaisuudet) Configuration Properties -> General -> Character Set -kohdasta Not Set.
Lisätään/muutetaan koodiksi
#include <windows.h> #include "resource.h" //Ikkunoiden tunnukset static HINSTANCE hInst; //Ohjelman esiintymä (osoite) LRESULT CALLBACK MainWnd(HWND, UINT, WPARAM, LPARAM); //Pääohjelma (parametreja ei käytetä) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int i; hInst = GetModuleHandle(0); //Ohjelman osoite talteen //Perustetaan ikkuna i = DialogBoxParam(hInst, MAKEINTRESOURCE(TEST), 0, (DLGPROC)MainWnd, 0); //Ohjelma ei jatka tästä eteenpäin, jos virheitä ei ilmaantunut if(i == -1 || !i){ MessageBox(0, "EI toimi", "Testiohjelma", MB_OK); } MessageBox(0, "Ohjelma loppui", "Testiohjelma", MB_OK); return 0; } //Ohjelman pääikkunan viestit: //Parametrit: hDlg - ikkunan kahva, message - tuleva viesti //wParam ja lParam - viestin parametrit LRESULT CALLBACK MainWnd(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { case WM_INITDIALOG: //Ikkuna on perustettu return 0; case WM_COMMAND: //Kontrollien sanomat if(LOWORD(wParam) == IDOK){ //OK -nappi static char buffer[32]; double p; SendDlgItemMessage(hDlg, IDC_EDIT1, WM_GETTEXT, 16, (LPARAM)(void*)buffer); //Kopioidaan tekstilaatikon sisältö p = atof(buffer); //Annettu teksti luvuksi _gcvt(p*p, 4, buffer); //Luku tekstiksi MessageBox(0, buffer, "Testiohjelma", MB_OK); //Näytetään teksti return 0; //Normaali paluuarvo } else if(LOWORD(wParam) == IDCANCEL){//Cancel -nappi EndDialog(hDlg, wParam); //Tuhoaa valintaikkunan return 0; //Normaali paluuarvo } else return 1; //Ei ole muita kontrollereita case WM_DESTROY: //Ikkuna aiotaan tuhota PostQuitMessage(0); //Lopetetaan säie (ohjelma) return 0; //Normaali paluuarvo default: return 0; //Ei saa käyttää DefWindowProcia ! } }
Visual Studio 2005 ja Express
Jatkoesimerkeissä ja tehtävissä on oletettu, että projektin ominaisuuksista (ALT+F7) Configuration Properties -> General -> Character Set -kohdassa on Not Set. Tämä muutos on pakollinen, jollet halua muuttaa kaikkia merkkijonoja wchar:ksi.

