Win32 perusteet

Mureakuha

Loikkaa: valikkoon, hakuun

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 !
  }
}

Kuva:Win32_perusteet_esimerkkiohjelma.png

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.

Henkilökohtaiset työkalut