WinAPI z Assemblerem – Część 1

W poprzedniej części artykułu, stworzyliśmy minimalną wersję programu, która wyświetla okno dialogowe i nie robi nic, czyli można powiedzieć, że po części jest to symulator pracy naszego rządu. Ponieważ od programu wymaga się pewnej responsywności, na rozgrzewkę przerobimy trochę poprzedni przykład tak, żeby w zależności od wyboru użytkownika wyświetlił się odpowiedni komunikat. A na deser podzielimy program na sekcje ;).

Część 0. ukończyliśmy z takim oto kodem:

Zacznijmy od modyfikacji samego okienka, ostatni parametr funkcji MessageBoxA definiuje nam jego wygląd, czyli jaką będzie miał ikonę oraz jakie przyciski będą w nim zawarte. Wartość można podać liczbowo, chociaż nie polecam, gdyż nie daje to żadnej informacji programiście. Stałe zwykle zdefiniowane są w plikach, które dołączamy na początku pliku, w tym przypadku chodzi oczywiście o: ‚win32a.inc’

Na stronie MSDN znajduje się tabelka, z której możemy wybrać interesujące nas parametry, w tym przypadku chcemy okno z pytaniem i 2 przyciski – tak lub nie, zatem będą to wartości MB_YESNO oraz MB_ICONQUESTION. W kodzie te wartości można po prostu do siebie dodać, ponieważ są to zdefiniowane stałe wartości.

Modyfikujemy wywołanie MessageBoxA:

Modyfikacji można poddać też wyświetlany tekst, niech przykład ma trochę sensu ;).

Gdy wszystko zostało poprawnie napisane, po uruchomieniu przykładu powinniśmy otrzymać okienko jak poniżej:

yes_no_message_box

Okienko z dwoma przyciskami

Niby działa, ale nie do końca – przyciski nie wywołują żadnej akcji poza zamknięciem okna, wypadałoby to poprawić, w końcu po coś ten wybór użytkownikowi dajemy ;).

Okienko po zamknięciu zwraca wartość informującą o przycisku jaki został kliknięty, gdy piszemy w C wartość zwracana jest do podanej zmiennej. W assemblerze wartość zwracana przez funkcje WinAPI, zawsze trafia do rejestru EAX, jest to ważna rzecz, którą należy zapamiętać.

W takim razie, zaraz po funkcji wyświetlającej MessageBox, umieścimy blok decyzyjny, gdzie sprawdzimy jaką akcję wykonał użytkownik. Oto nasz przerobiony program:

W zależności od tego jaki przycisk klikniemy, odpowiedź programu będzie jedna z poniższych:

Podział na sekcje

Sprawdźmy najpierw jak obecnie nasz program układa się w pamięci:

Szczególnie interesuje nas kolumna „Section” oraz „Contains”. Pierwsza z nich mówi o tym jak nazywa się sekcja zaś kolejna co się w niej znajduje.

Pierwszy wpis zawiera nagłówek pliku wykonywalnego, tym nie będziemy się zajmować. Drugi – oznaczony jako „.flat” – zawiera cały nasz kod, w jednym miejscu.

„.flat” pochodzi od wyrażenia „Flat Memory Model”, jest to model pamięci, w którym kod programu znajduje się w jednym miejscu, bez podziału na segmenty (segment danych, segment kodu itp.).

Oprócz kodu wykonywalnego, znajdziemy tutaj nasze zmienne i informacje o bibliotekach, które zaimportowaliśmy. Program zostaje załadowany do pamięci w takiej samej postaci, w jakiej go napisaliśmy, można to zobaczyć np. pod debuggerem:

Program załadowany w debuggerze OllyDBG

O ile w małym programie nie robi to większej różnicy, przy większych projektach zaczyna się bałagan, a wiadomo, że gdzie bałagan tam i problemy. Do uporządkowania pamięci przydadzą się sekcje, które zwiększą nieznacznie rozmiar programu, ale dadzą nam możliwość pełnej kontroli nad tym co się w pamięci znajduje. Podzielmy zatem nasz program na 3 sekcje:

  1. „.text” – sekcja kodu, możliwość odczytu i uruchomienia
  2. „.data” – sekcja danych, możliwość odczytu i zapisu
  3. „.idata” – sekcja importu, możliwość odczytu i zapisu

I ponownie podglądnijmy jak to wygląda w rzeczywistości:

No, i teraz wszystko leży na swoim miejscu. Oprócz ładu, uchronimy się przed trudnymi do zidentyfikowania błędami. Od teraz, aż do końca tutorialu, będziemy dzielić program na sekcje. Głównie dlatego, że kodu będzie coraz więcej ;). I na koniec małe „cool story”:

Ostatnio pisząc niedużą aplikację, nie dzieliłem programu na sekcje, nie uznałem tego za konieczne. W trakcie wywoływania funkcji GetThreadContext program zawieszał mi się zgłaszając problem dostępu do pamięci. Około pół dnia zeszło nim doszedłem co wywoływało ten błąd. Cały kod (zmienne również) umieszczony był w jednej sekcji – „.flat” – która posiadała flagę odczytu i uruchomienia, ale nie zapisu. Funkcja, próbując zapisać dane do zmiennej, zgłaszała błąd, ponieważ nie posiadała praw. Co ciekawe, inne funkcje radziły sobie bez problemu. Po wydzieleniu sekcji, wszystko zaczęło działać bezproblemowo.

A w kolejnej części nauczymy się tworzyć prawdziwą aplikację okienkową, dodawać do niej kontrolki oraz przechwytywać zdarzenia.

[guest@itachi.pl:~]$
    Kamil

    haha symulator rządu, że tak powiem, rozjebało mnie to 😀
    zajebisty blog
    strona – XXI wiek

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *