- GKO, luennot 3.-5.6.2004
- WinAPI (API == Appication Programming Interface)
- Kertaus: tapahtumapohjaiset DOS-ohjelmat
- Milloin puhtaita WinAPI-ohjelmia?
- Maksimaalinen suorituskykytarve (ei "ylimääräisiä" välikerroksia sovelluksen ja Windowsin välillä)
- Windows-kontrollien osalta minimaalinen käyttöliittymätarve
- Pelit (WinAPI+DirectX)
- Käytännössä WinAPI-kutsuja tarvitaan tavallisissa Delphi-ohjelmissakin, kun käytetään Windowsin ominaisuuksia, joita ei ole 'paketoitu' komponenteiksi
- Joskus voi olla jopa helpompaa käyttää suoraa API-kutsua kuin käyttää komponenttia
- Kaikkien Windowsin osa-alueiden ohjelmointi on pohjimmiltaan API-kutsuja funktioihin, jotka sijaitsevat omissa DLL-tiedostoissaan
- Esim (ks. MS SDK):
- ODBC-kutsut tietokantoihin
- ISAPI - web-palvelimen ohjelmointirajapinta
- MAPI -viestijärjestelmä
- Multimedia API (Media Player ym...)
- Setup API (windows installer)
- Sockets (verkko-ohjelmointi)
- Näiden päälle on rakennettu uudemmissa rajapinnoissa käytetyt OLE/COM/ActiveX-komponentit, jotka nekin ovat DLL-tiedostojen (oik. OCX) kaltaisia komponentteja
- DLL-tiedostojen käytöstä oma luento...
- Puhtaan WinAPI-sovelluksen luominen
- C++ Builder:
- Luo uusi konsoliprojekti (console wizard)
- Käännä wizardissa kaikki optiot (use vcl/clx, multi threaded ja console application) pois päältä
- Visual C++
- Luo uusi työtila/projekti (file->new) ja valitse Win32 Application. Huomaa, että projektille tehdään oma hakemisto.
- 'Hello World'-application on toimiva yksinkertainen esimerkki, uutta projektia tehdessä kannattaa valita 'simple win32 application'
- Materiaali: Winohj-moniste, luku 3
- DEF-tiedostoja ei tarvita c++ builderilla eikä MFC-sovelluksissa
- WinAPI-peruskäsitteitä
- Kahva (handle)
- kokonaislukuja, eri resursseille omat kahvansa
- esim. ikkunakahva, tiedostokahva, säiekahva...
- Myös dephissä win-kontrolleista saa kahvan Handle-propertylla. Tällöin VCL-komponentteja voidaan käsitellä suoraan WinAPI-kutsuilla
- Esiintymä (instance)
- Huom. useampikertaisten esiintymien kieltäminen hPrevInstancen avulla ei toimi Win32:ssa (Win95 ja myöhemmät.)
- Syy: jokainen ohjelma ajetaan 32-bittisessä windowsissa omassa koodisegmentissään (vrt. winohj-kirja)
- Uudempi tapa esim. nimettyjen Mutexien avulla, kuvattu Win32 Developer's referencessa (index: winmain)
- Muita lähestymistapoja: ks. WinAPI-linkit
- Viestijono
- Järjestelmä tarjoaa jokaiselle säikeelle oman viestijonon, jossa ovat sovelluksen yksityiset viestit
- Lisäksi globaalissa hardware-jonossa ovat hiiren ja näppäimistön tapahtumat
- Viestisilmukan getmessage (tai peekmessage) -kutsut lukevat kumpaakin jonoa
- Viestisilmukka
- vrt. DOS-autolaskurin viestisilmukka
- jokainen windows-sovellusohjelma sisältää viestisilmukan
- 1. tutkitaan, onko windowsin viestijonossa viestejä (getMessage)
- 2. lähetetään viesti oikealle ikkunalle (dispatchMessage)
- Delphi-ohjelmissa viestisilmukka on piilotettu Application-luokan ProcessMessage-metodiin (Application.Run -metodi kutsuu)
- Säästää koodaajalta saman koodin kirjoittamisen jokaiseen ohjelmaan...
- Ikkunafunktio (window procedure)
- jokainen windows-ikkuna (+dialogi) sisältää ikkunafunktion
- CALLBACK-tyyppi: windows kutsuu ikkunafunktiota, kun ikkuna saa viestin (oik. funktio-osoitin!)
- _export: julistetaan funktio 'ulos' omasta moduulistaan, jolloin sitä voidaan kutsua muista ohjelmista
- Pääikkunalla MainWndProc -funktio
- pääikkunafunktion täytyy sisältää ainakin WM_DESTROY-käsittelijä, jota kutsutaan ohjelmasta poistuttaessa
- Laajemmissa windows-ohjelmissa vaarana, että switch/case-lause kasvaa kohtuuttoman laajaksi
- Apuna omien funktioiden kutsu case-käsittelijässä (vrt. Delphin tapahtumat), oma taulukkoviritys viestien käsittelyyn (vrt. dos-autolaskuri) tai molemmat edelliset kapseloivan sovelluskehyksen (VCL/CLX, MFC, wxWidgets...) käyttö.
- Laiteyhteys (Device Context)
- Joukko ikkunaan liittyviä piirtoattribuutteja (värit, fontit ym) ja piirtofunktioita
- Voidaan ottaa käyttöön tai luovuttaa tarvittaessa
- Delphissä Canvas-luokka kapseloi laiteyhteyden
- Hidas... nopeaa piirtoa varten DirectX
- Lisämateriaali: Delphilinkit
- Esimerkki: whello
- Esimerkki (useamman viestin käsittely): samplew
- Jos ehtii: kolmio ja tabhand
- esikääntäjä: CPP32 tai builderin ponnahdusvalikon preprocess-komento
- includattujen tiedostojen ja laajennettujen makrojen tarkastelu
- Viestinkäsittelystä
- Käyttäjä voi lähettää ja tutkia viestejä manuaalisesti WinAPI-kutsuilla
- PostMessage lähettää viestin jonoon ja palaa välittömästi kutsuvaan funktioon
- Mahdollistaa omien funktioiden 'viivästetyn' suorituksen
- Huom. jos windowsin viestijono on täynnä, viestiä ei lähetetä. Tämän voi tarkistaa funktion paluuarvosta ja tarvittaessa lähettää viesti uudelleen.
- SendMessage lähettää viestin jonoon ja odottaa, kunnes se on suoritettu
- PeekMessage tutkii, onko viestijonossa viestejä ja 'poimii' tarvittaessa viestin. Tällöin käyttäjä voi tehdä useita viestisilmukkaa vastaavia rakenteita ohjlemaan (ks. checker.c)
- Jos viesti poimitaan, on yleensä syytä tehdä normaalit Translate- ja DispatchMessage-kutsut
- Esimerkki viestin lähetyksestä ja käsittelystä Delphissä ja C++ Builderissa: simplemessages
- Käyttäjä voi määritellä omia viestityyppejä WM_USER-arvon yläpuolelle (ks. Win32 SDK: Application-defined messages) tai korttipeliesimerkin hullufinal.pas
- Moniajon mahdollistaminen piirtämisen aikana: checker ja simplet\kolmiom.c
- Idea: käydään tutkimassa viestinkäsittelijässä säännöllisin väliajoin, onko viestijonossa uusia viestejä.
- Moniajoa voidaan Win32:ssa toteuttaa myös säikeillä (tosin VCL-komponenttien piirto ruudulle täytyy tehdä synkronoidusti)
- simplemessages-esimerkkiin lisätty myös äärimmäisen yksinkertaistettu versio checkeriä vastaavasta toiminnasta delphillä
- Säikeistä enemmän TCP/IP-luennolla...
- Useita komponentteja ikkunassa: whello/thello
- ikkunalla olevien komponenttien viestit käsitellään ikkunafunktiossa WM_COMMAND-viestin alla
- MFC
- Materiaali: Olio-ohjelmointi ja C++, APF-ohjelmarunko
- Ks. myös MFC-linkit Delphilinkeistä
- MFC käyttää resurssitiedostoja kuten WinAPIkin, mutta järjestelmäkutsut on kapseloitu luokkiin
- Esim. MFC-luokkahierarkia
- Sovellusluokka peritty CWinApp-luokasta
- MFC:n viestinkäsittely: luodaan MESSAGE_MAP-makroilla metodiosoitintaulukko
- Visual C++ tukee taulukon keskitettyä hallintaa
- Esim: Visual C++ -oletussovellus
- MFC tukee Microsoftin ideaa Asiakirjakeskeisestä käsittelystä ("kaikki" sovellukset ovat asiakirjaeditoreja, jotka OLE:n avulla kommunikoivat keskenään, ks. esim. Visual C++:n oletussovellus)
- Esimerkit mfc-hakemistossa: kolmio ja laskuri
- Resurssit
- Windowsin (Wanha) standarditapa liittää staattista tietoa sovelluksen
- Visual C++:ssa resurssitiedostot _ovat_ käyttöliittymän tallennusformaatti, Borlandin työkaluissa käytetään lähinnä merkkijonojen ja bittikarttojen tallentamiseen
- Voivat sisältää merkkijonoja, bittikarttoja, ikoneita, pikanäppäimiä, menuja ym. komponentteja, dialogeja käyttöliittymineen...
- Käytetään edelleen monikielistämiseen: tehdään useita resurssitiedostoja, joihin sijoitetaan kaikki sovelluksen merkkijonot
- Lisää 'monikieliset ohjelmat'-luennolla...
- Tekstitiedostoja, myös visuaalisia editoreja saatavilla
- RC-lähdekoodi käännetään RES-tiedostoksi ja linkitetään EXE-koodiin
- Käännöksen voi tehdä käsin BRCC-kääntäjällä
- Jos RC-tiedosto on projektissa, se käännetään ja linkitetään automaattisesti
- Huom! jos RC- tai RES- tiedostoa ei ole projektissa, ohjelma ei lähde käyntiin!
- Esimerkki resurssien yhteiskäytöstä VC++:ssa ja BCB:ssä: vctest
- resursseilla tehty autolaskuri: winlaski\reslask
- hakemisto sisältää eri frameworkeillä tehtyjä laskureita
- winlaski\menu5 - kuvilla ja animaatiolla varustettu laskuri
- Tietoa resursseista Win32 SDK -avustuksessa:
- Yleiskatsaus: Microsoft Win32 Programmer's Reference: Resources
- Resurssitiedostojen tarkka syntaksi: Resource Compiler's users guide (tai hakemalla esim: CONTROL - general control. tällöin sivuja pääsee selaamaan)
- MDI, useamman ikkunan käsittely
- Ikkunatyypeistä
- SDI == Single Document Interface, sovelluksessa yksi pääikkuna
- Klassinen Windows-sovellustyyppi
- MDI == Multiple document interface, pääikkuna, joka sisältää lapsi-ikkunoita
- Nykyisissä sovelluksissa suosittu tapa myös koota sovellus "kelluvista" ikkunoista, joita voi dockata ja järjestellä uudelleen.
- Vaarana on, että käyttäjä pääsee 'pilaamaan' käyttöliittymänsä
- Toivottava ominaisuus: ikkunoiden 'lukitseminen'
- WinAPI-kutsut ikkunoiden käsittelyyn
- Modaalinen dialogi luodaan kutsulla DialogBox, tuhotaan kutsulla EndDialog. Modaaliselle dialogille ei tarvitse kirjoittaa viestisilmukkaa
- Modeless-dialogi luodaan kutsulla CreateDialog. Lisäksi vanhempi-ikkunan viestisilmukassa on otettava huomioon dialogit
- Tavalliset ikkunat luodaan CreateWindow-kutsulla
- Delphillä ikkunoita voi käsitellä helposti Show- ja Close-metodeilla. MDI-sovelluksissa lisäksi MDI-ikkunoiden kutsut
- Actionlistissa ja TForm-luokassa on vakiotoiminnot MDI-sovelluksen käsittelyyn (esim. Cascade, Tile, Next/Prev window...)
- Ikkunoita suljettaessa otettava huomioon tuhotaanko vai piilotetaanko ikkuna suljettaessa
- Jos ikkuna tuhotaan ja se halutaan luoda uudelleen, se täytyy luoda konstruktorilla uudelleen
- ikkunan voi tuhota manuaalisesti TFormin release-metodilla
- MDI-sovelluksen voi luoda delphillä: File->New->Other->Projects->MDI Application
- ActionListin ansiosta MDI-perustoiminnot syntyvät lähes ilman koodia, vrt. Visual C++:n MFC Wizard Koodigeneraattori -hirvitys :-)
- Luotaessa MDI-ikkunaan lapsi-ikkunoita dynaamisesti FormStylen oltava fsMDIChild
- Huom. MDI-lapsi-ikkunaa ei voi piilottaa!
- Delphi-MDI -esimerkki
- WinAPI / MFC-esimerkki: vaihtaja
- Delphi-esimerkki: mittakaavamuunnos (vaatii kave-komponenttipaketin)
- Täydennystä
- Simplemessages revisited
- WinAPI-viestien käsittely C++ Builder / VCL -sovelluksessa
- Checkers.c -tyylisen toiminnallisuuden tekeminen Delphillä
- vielä Delphin ja C++:n eroista
- Delphin ja C++:n destruktoreissa on erilainen logiikka
- C++:n luokka 'puretaan' destruktoria kutsuttaessa, ks. C++ ja Olio-ohjelmointi
- Delphissä luokka on kokonainen, kunnes se tuhotaan. Tällöin eri yli- ja aliluokkien destruktorit näkevät saman version luokasta
- Seuraus: erilainen käyttäytyminen virtuaalifunktioiden kutsumisen suhteen
- Esim: Destruct
- C++:n metodiosoitin on Delphin vastaavaa rajoittuneempi
- Delphin metodiosoitin osoittaa suoraan tietyn luokan metodiin ja osoitin voidaan ohjata toisen luokan vastaavaan metodiin
- C++:n metodiosoittimessa täytyy ilmaista luokka, jonka metodiin metodiosoitin osoittaa
- Metodia kutsuttaessa olio täytyy ilmoittaa erikseen
- C++:n metodiosoittimen rajoittuneisuus on Builderissa kierretty epästandardilla __closure -avainsanalla, joka tarjoaa Delphin tapahtumia vastaavan käsittelyn
- Ks. Builderin avustus: keyword extensions
- Seuraus: C++ -builderilla tehty VCL-koodi _ei ole_ standardia C++:aa!
- C++ tukee normaalisti moniperintää, mutta uusien VCL-luokkien luonti moniperinnällä ei ole niiden Delphi-pohjaisuudesta johtuen mahdollista
- ShellExecute, tekniikka sovellusten käynnistämiseen
- Ali-hakemistoon lisätty ExecUnit ja ExecTest-esimerkki