27. Zprávy Windows I
  1. Další aplikace ukazuje jak vytvořit obsluhu pro zprávu Win32. Tento příklad používá makra mapování zpráv definovaná v SYSDEFS.H k obsloužení zprávy WM_GETMINMAXINFO. Začneme vývoj nové aplikace. Rozměry formuláře změníme na 350 x 350. Na formulář umístíme čtyři komponenty Label (vždy dvě vedle sebe) a změníme texty levých komponent na Šířka a Výška. Texty pravých komponent změníme na 350. Dále vytvoříme obsluhu události OnResize formaláře. Obsluha bude obsahovat příkazy:

  2. Label2->Caption = IntToStr(Width);
    Label4->Caption = IntToStr(Height);
    Když nyní aplikaci spustíme a měníme velikost formuláře, pak zobrazené texty nás informují o rozměrech formuláře.  My ale požadujeme, aby rozměry formuláře bylo možno měnit pouze v nějakém intervalu, např. mezi 300 a 400 body.
    Zpráva WM_GETMINMAXINFO je zaslána oknu, když je měněna velikost nebo pozice okna. Aplikace může použít tuto zprávu k přepsání minimální a maximální implicitní velikosti a pozice okna. Parametrem zprávy je ukazatel na strukturu MINMAXINFO, která obsahuje implicitní hodnoty okna. Tyto implicitní hodnoty můžeme přepsat. Struktura MINMAXINFO je deklarována takto:
    typedef struct tagMINMAXINFO {
        POINT ptReserved;
        POINT ptMaxSize;
        POINT ptMaxPosition;
        POINT ptMinTrackSize;
        POINT ptMaxTrackSize;
    } MINMAXINFO;
    Složky struktury mají tento význam:
     
    Složka Význam
    ptReserved Rezervováno
    ptMaxSize Specifikuje maximalizovanou šířku (point.x) a maximalizovanou výšku (point.y) okna.
    ptMaxPosition Specifikuje pozici levého okraje maximalizovaného okna (point.x) a pozici horního okraje maximalizovaného okna (point.y).
    ptMinTrackSize Specifikuje minimální šířku (point.x) a minimální výšku (point.y) okna.
    ptMaxTrackSize Specifikuje maximální šířku (point.x) a maximální výšku (point.y) okna.

    Vytvoříme tedy veřejnou virtuální metodu formuláře nazvanou RestrictSize s následujícím obsahem:
    void __fastcall TForm1::RestrictSize(TMessage& Msg)
    {
         ((POINT far *)Msg.LParam)[3].x = 300;
         ((POINT far *)Msg.LParam)[3].y = 300;
         ((POINT far *)Msg.LParam)[4].x = 400;
         ((POINT far *)Msg.LParam)[4].y = 400;
         TForm::Dispatch(&Msg);
    }
    a budeme se snažit namapovat zprávu WM_GETMINMAXINFO na naši metodu RestrictSize, která zajistí, že šířka a výška formuláře se musí pohybovat mezi 300 a 400. Pokud formulář přijme zprávu WM_GETMINMAXINFO, pak naše metoda změní informace zprávy o maximálním a minimálním rozměru okna a voláním Dispatch je tato upravená zpráva předána současné obsluze události objektu formuláře. Následuje výpis deklarace typu formuláře, ve kterém je uvedeno požadované mapování:
    class TForm1 : public TForm
    {
    __published: // IDE-managed Components
            TLabel *Label1;
            TLabel *Label3;
            TLabel *Label2;
            TLabel *Label4;
            void __fastcall FormResize(TObject *Sender);
    private: // User declarations
    public:  // User declarations
            virtual __fastcall TForm1(TComponent* Owner);
            void virtual __fastcall RestrictSize(TMessage& Msg);
    // Toto mapování mapuje zprávu WM_GETMINMAXINFO na funkci RestrictSize
    BEGIN_MESSAGE_MAP
    MESSAGE_HANDLER(WM_GETMINMAXINFO,TMessage,RestrictSize)
    // Zde lze přidat další mapované zprávy.
    END_MESSAGE_MAP(TForm)
    };
    Nyní, když aplikaci přeložíme a spustíme, vidíme, že velikost okna lze měnit pouze v zadaných mezích. Obdobně je možno provést mapování dalších zpráv Windows. K mapování jsou použita makra BEGIN_MESSAGE_MAP, MESSAGE_HANDLER a END_MESSAGE_MAP.

  3. Další aplikace, se kterou se nyní seznámíme, je aplikace zobrazující seznam procesů běžících na našem počítači a umožňující zrušení vybraného procesu. Jedná se opět o hotovou aplikaci. Aplikaci si stáhněte a vyzkoušejte jak pracuje. Nyní se podíváme jak tato aplikace je vytvořena. Nejprve začneme jednoduššími činnostmi. Voliče ve spodní části formuláře pouze určují barvu textu v komponentě ListBox. Obsluha události OnClick pro všechny čtyři voliče je tvořena příkazy:

  4. TRadioButton *rbp =(TRadioButton*) Sender;
    ListBox1->Font->Color=rbp->Font->Color;
    Obsluhu události OnTimer časovače (pohybuje šipkou na panelu nástrojů) tvoří (i je globální proměnná deklarovaná na začátku zdrojového souboru formuláře):
    switch(i){
      case 0:{
        Image1->Picture->Bitmap->LoadFromResourceName(0,
                                     (AnsiString)"BITMAP_5");
        Image1->Refresh();
        i=1;
        return;
      }
      case 1:{
        Image1->Picture->Bitmap->LoadFromResourceName(0,
                                     (AnsiString)"BITMAP_2");
        Image1->Refresh();
        i=2;
        return;
      }
      case 2:{
        Image1->Picture->Bitmap->LoadFromResourceName(0,
                                     (AnsiString)"BITMAP_3");
        Image1->Refresh();
        i=3;
        return;
      }
      case 3:{
        Image1->Picture->Bitmap->LoadFromResourceName(0,
                                     (AnsiString)"BITMAP_4");
        Image1->Refresh();
        i=4;
        return;
      }
      case 4:{
       Image1->Picture->Bitmap->LoadFromResourceName(0,
                                     (AnsiString)"BITMAP_1");
        Image1->Refresh();
        i=0;
        return;
      }
    }
    Obsluha volby File | Exit je tvořena příkazem
    PostQuitMessage(0);
    Funkce API PostQuitMessage předává Windows požadavek na ukončení. Parametr funkce určuje ukončující kód aplikace. Funkce zasílá zprávu WM_QUIT frontě zpráv vlákna. Když vlákno získá zprávu WM_QUIT ze své fronty zpráv, ukončí svůj cyklus zpráv a vrací řízení Windows.
    Volba Help | About zobrazí okno s informacemi o programu. Obsluha této volby nepotřebuje žádné vysvětlení.
    Obsluha stisku levého tlačítka na paletě nástrojů je tvořena příkazy (tato obsluha je vyvolána i při volbě File | List Pids a File | Refresh List):
    long lp=0;
    ListBox1->Enabled=true;
    ListBox1->Clear();
    EnumWindows((WNDENUMPROC)EnumProc,lp);
    SpeedButton2->Enabled=true;
    SpeedButton3->Enabled=true;
    V této obsluze je volána funkce EnumWindows. Popis této funkce bude uveden později. Obsluhu stisku druhého tlačítka tvoří (ListBox je vyprázdněn a zakázána dvě tlačítka):
    ListBox1->Clear();
    SpeedButton3->Enabled=false;
    SpeedButton1->Enabled=false;
    Obsluhu stisku třetího tlačítka tvoří příkazy (je zde volána obsluha stisku prvního tlačítka a poslední tlačítko zakázáno):
    SpeedButton4Click(0);
    SpeedButton1->Enabled=false;
    Obsluha události OnShow formuláře tvoří příkazy (proměnná stat je statická proměnná s počáteční hodnotou 1; je deklarována na počátku programové jednotky formuláře):
    if(stat){
      stat= 0;
      ListBox1->Items->Add((AnsiString)"Click on 'pid' Tool button above");
    }
    Tato obsluha po prvním zobrazení formuláře zobrazí v ListBox text Click on 'pid' Tool button above. Nyní se již věnujeme funkci EnumWindows. Tato funkce prochází všechna okna nejvyšší úrovně (nezabývá se podřízenými okny) na obrazovce a předává madlo každého z nich zpětně volané funkci. EnumWindows pokračuje až do posledního okna nebo dokud zpětně volaná funkce vrací false. Funkce má dva parametry. Prvním je ukazatel na zpětně volanou funkci a druhý určuje hodnotu, která bude předána zpětně volané funkci.
    V našem příkladě je funkcí EnumWindows zpětně volána následující funkce:
    bool __stdcall EnumProc(/*HWND*/void * hWnd,/*LPARAM*/long/*lp*/)
    {
       unsigned long* pPid;       //LPDWORD
       unsigned long result;      //DWORD
       void *hg;                  //HGLOBAL
       unsigned long id;
       if(hWnd==NULL) return false;
       hg = GlobalAlloc(GMEM_SHARE,sizeof(unsigned long));
       pPid = (unsigned long *)GlobalLock(hg);
       result = GetWindowThreadProcessId(hWnd,pPid);
       if(result){
          char title[110];
          char className[95];
          char totalStr[256];
          GetClassName(hWnd,className,95);
          GetWindowText(hWnd,title,110);
          id=*pPid;
          itoa(id,totalStr,10);
          strcat(totalStr,"\t");
          if(title){
             strcat(totalStr,title);
             strcat(totalStr,"\t");
          }
          strcat(totalStr,className);
          Form1->ListBox1->Items->Add((AnsiString)totalStr);
       }
       else{
          GlobalUnlock(hg);
          GlobalFree(hg);
          return false;
       }
       GlobalUnlock(hg);
       GlobalFree(hg);
       return true;
    }
    Naše funkce vytváří seznam spuštěných procesů. Pokuste se pochopit, jak tato funkce pracuje. V našem programu zbývá ještě obsluha události OnClick okna seznamu, kterou tvoří příkazy:
    SpeedButton1->Enabled=true;
    StatusBar1->SimpleText=
      "Select 'Kill Selected PID' to terminate the process";
    a obsluha stisku posledního tlačítka (zrušení vybraného procesu). Obsluhu stisku posledního tlačítka tvoří:
    AnsiString str;
    char *tmp;
    int i;
    i= ListBox1->ItemIndex;
    if( i != -1){
      AnsiString s;
      tmp = new char[100];
      s=ListBox1->Items->Strings[i];
      strcpy(tmp,(char*)s.c_str());
      tmp=strtok(tmp,"\t");
    }
    int id=atoi(tmp);
    delete[] tmp;
    HANDLE ps = OpenProcess(1,false,id);
    if(ps){
      if(!TerminateProcess(ps,-9)){
        ShowMessage((AnsiString)"Could not end process specified!");
      }
      else{
        ShowMessage((AnsiString)"Process successfully terminated!");
      }
    }
    else{
      ShowMessage((AnsiString)"Could not open process requested!");
    }
    Pokuste se pochopit, jak tato obsluha pracuje.
27. Zprávy Windows I