Zaawansowane szukanie | Nowości
Newsy | Artykuły | Programowanie: Pascal, Object/Delphi Pascal, C++, PHP, Javascript | Poradniki | Recenzje | FAQ | Mapa

Artykuł: C++ - Aplikacja bez formy, Luna

Jakto bez formy?


Nie będzie to aplikacja całkiem bez formy. Po prostu nie użyjemy gotowej formy z C++ Buildera i żadnych komponentów. Napiszemy wszystko od podstaw. W tym artykule pokażę, że szkoda czasu na pisanie formy, bo nie jest to trudne, ale pisanie "na ślepo" jest syzyfową pracą. Każdy programista C++ powinien jednak umieć sam napisać program od podstaw. Przydaje się to w przypadku, gdy jesteśmy gdzieś dalej i ktoś pyta się jak zrobić aplikację pod Windows, a nie ma C++ Buildera. Wtedy większość kompilatorów powinno przeczytać kod tworzenia formy, bo jest on w miarę przenośny.

Tworzymy API


Trzeba zacząć od stworzenia głównego pliku, który pomoże nam w stworzeniu aplikacji pod środowisko Windows. Tworzymy plik "windows.h", a w nim:
#ifndef _WINDOWS_H
#define _WINDOWS_H
#if defined(__i686__) && !defined(_M_IX86)
#define _M_IX86 600
#elif defined(__i586__) && !defined(_M_IX86)
#define _M_IX86 500
#elif defined(__i486__) && !defined(_M_IX86)
#define _M_IX86 400
#elif defined(__i386__) && !defined(_M_IX86)
#define _M_IX86 300
#endif
#if defined(_M_IX86) && !defined(_X86_)
#define _X86_
#elif defined(_M_ALPHA) && !defined(_ALPHA_)
#define _ALPHA_
#elif defined(_M_PPC) && !defined(_PPC_)
#define _PPC_
#elif defined(_M_MRX000) && !defined(_MIPS_)
#define _MIPS_
#elif defined(_M_M68K) && !defined(_68K_)
#define _68K_
#endif
#ifdef RC_INVOKED
#include <winresrc.h> #else
#ifdef __GNUC__
#if defined(__cplusplus) && !defined(NONAMELESSUNION)
#define _ANONYMOUS_UNION
#endif
#endif
#ifndef _ANONYMOUS_UNION
#define _UNION_NAME(x) x
#define DUMMYUNIONNAME u
#define DUMMYUNIONNAME2 u2
#define DUMMYUNIONNAME3 u3
#else
#define _UNION_NAME(x)
#define DUMMYUNIONNAME
#define DUMMYUNIONNAME2
#define DUMMYUNIONNAME3
#endif
#ifndef _ANONYMOUS_STRUCT
#define _STRUCT_NAME(x) x
#define DUMMYSTRUCTNAME s
#define DUMMYSTRUCTNAME2 s2
#define DUMMYSTRUCTNAME3 s3
#else
#define _STRUCT_NAME(x)
#define DUMMYSTRUCTNAME
#define DUMMYSTRUCTNAME2
#define DUMMYSTRUCTNAME3
#endif
#ifndef NO_STRICT
#ifndef STRICT
#define STRICT 1
#endif
#endif
#include <stdarg.h>
#include <windef.h>
#include <wincon.h>
#include <basetyps.h>
#include <excpt.h>
#include <winbase.h>
#include <wingdi.h>
#include <winuser.h>
#include <winnls.h>
#include <winver.h>
#include <winnetwk.h>
#include <winreg.h>
#include <winsvc.h>
#ifndef WIN32_LEAN_AND_MEAN
#include <commdlg.h>
#include <cderr.h>
#include <dde.h>
#include <ddeml.h>
#include <dlgs.h>
#include <lzexpand.h>
#include <nb30.h>
#include <rpc.h>
#include <shellapi.h>
#include <winperf.h>
#include <winspool.h>
#if defined(Win32_Winsock) || !(defined(__INSIDE_CYGWIN__) || defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(_UWIN))
#include <winsock.h>
#endif
#endif
#endif
#ifdef __OBJC__
#undef BOOL
#endif
#endif
Kod ten w zależności od typu konfiguracji wczytuje ("includuje") wiele plików. Wszystkie te pliki są dużo dłuższe od samego "windows.h". C++ Builder posiada wszystkie potrzebne pliki, lecz jeżeli wystąpią jakieś problemy, można będzie pobrać te pliki z naszego download'u. Nie będę ich omawiał, bo jest to materiał na dużo dłuższy artykuł, a żeby tworzyć wszystkie te pliki, trzeba by poświęcić jeszcze więcej czasu. Napisanie samego API zajęło by nam wtedy nawet tydzień. Każdy kompilator, który wspiera programowanie pod Windows, będzie posiadał te pliki, więc nie ma się tutaj o co martwić.
Teraz uruchamiamy C++ Builder, wybieramy File -≷ New -≷ Unit (tym razem nie wybieramy "Application", aby C++ Builder nie stworzył nam formy). Usuwamy domyślny kod, który wstawia C++ Builder i zapisujemy projekt w tym samym katalogu co "windows.h". Następnym krokiem będzie dołączenie "windows.h" do naszego projektu. Piszemy zatem w oknie kodu:
#include <windows.h>
Zaraz pod tą linijką deklarujemy procedurę Windowsa:
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
Czas teraz napisać główną klasę programu. Kod omawiać będę komentarzami.
char szClassName[ ] = "WindowsApp"; // tworzymy zmienną typu char (jedno słowo), która będzie przechowywała nam nazwę "WindowsApp"
/* Poniżej zadeklarowana zostanie główna klasa Windowsa, a później konfiguracja */
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)
{
	/* Główne odwołania do Windowsa, dzięki czemu będziemy go mogli dalej konfigurować */
    HWND hwnd; // Deklarujemy nazwę formy
    MSG messages;         
    WNDCLASSEX wincl;

/* Możemy teraz konfigurować wygląd naszej aplikacji, edytując poszczególne atrybuty instrukcji "wincl" */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure; 
    wincl.style = CS_DBLCLKS; // Dzięki temu będziemy mogli używać podwójnego kliknięcia
    wincl.cbSize = sizeof(WNDCLASSEX);
/* Poniżej wczytane zostaną domyślne ikony oraz kursor myszy */
    wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL; 
    wincl.cbClsExtra = 0; 
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH); // Tutaj można zmienić kolor tła. Np. LTGRAY to jasny szary, GRAY to ciemny szary

    if(!RegisterClassEx(&wincl)) return 0; // Klasa zostaje rejestrowana
}
Nie zamykamy jeszcze procedury. Teraz trzeba będzie stworzyć już samą formę, która będzie miała nazwę jak już wyżej zadeklarowaliśmy - "hwnd". Posłuży nam do tego funkcja CreateWindowEx, dla której podamy tablicę z wartościami oraz odwołanie do naszej klasy "szClassName".
hwnd = CreateWindowEx(
	   0, 
	   szClassName,  // Nazwa naszej klasy
	   "Aplikacja bez formy",  // Wartość "Caption" dla formy
	   WS_OVERLAPPEDWINDOW, // Domyślne okno
	   CW_USEDEFAULT,  // Pozycja okna (ustawiamy na domyślną
	   CW_USEDEFAULT,  
	   640,  // Wysokość formy
	   480,  // Szerokość formy
	   HWND_DESKTOP,     // Typ otwierania okna
	   NULL,    
	   hThisInstance, 
	   NULL     
);
Zaraz po tym dodajemy funkcję, która pokaże naszą formę "hwnd":
    ShowWindow(hwnd, nFunsterStil);
Następnie tworzymy pętlę, która pokaże wiadomości (komunikaty), kiedy zostanie wywołana:
    while(GetMessage(&messages, NULL, 0, 0)) {
           TranslateMessage(&messages);
           DispatchMessage(&messages);
    }

    return messages.wParam;
}
To by było na tyle z klasą główną. Pozostało jeszcze stworzyć funkcję, która będzie pokazywała komunikaty:
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) 
    {
           case WM_DESTROY:
           PostQuitMessage(0);
           break;
           default: 
           return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}
Zastosowaną funkcję opiszę w innym artykule, w którym napiszę jak tworzyć własne funkcje. To by było na tyle, teraz możemy już skompilować program. Jak widać pokazuje nam się forma w szarym kolorze o wielkości 640x480 pikseli z tytułem "Aplikacja bez formy", którą można dowolnie rozciągać. Jak już mówiłem - nie ma sensu tworzyć dla tej formy komponentów i ustawiać ich "na ślepo" dzięki "Top" i "Left", bo musielibyśmy co chwilę zerkać czy wszystko stoi jak powinno. Proponuję wrócić do programowania obiektowego :)
Końcowy efekt:
Aplikacja "bez formy"

Luna - uzyskujemy nowy wygląd Windows


Nasze aplikacje jak narazie mają standardowe przyciski i inne obiekty. Nie uważam, że są złe, bo podobają mi się, jednak wiele osób pewnie chciałoby, aby ich aplikacja wyglądała ładniej (zależy dla kogo). Tworzymy za tem plik nazwa_programu.exe.manifest, gdzie "nazwa_programu.exe" musi być identyczna z nazwą pliku EXE. Plik ten musi się znajdować w tym samym katalogu co docelowy program. Umieszczamy w nim kod XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="2.0.0.0" processorArchitecture="X86" name="Nazwa naszego programu" type="win32" /> <description>Opis programu</description> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> </assembly>
To wszystko. Teraz uruchamiamy program i widzimy, jak wszystkie komponenty zmienił swój wygląd. U mnie efekt w przypadku naszej "gry strzelanki" jest taki:
Gra strzelanka
Różnica jest tylko taka, że ja używam stylu "Vista".

Następny artykuł będzie niespodzianką :)

Dodano: 7 marca 2006, Wróć do "Gra strzelanka"
Autor: Michał Gacki
Główny programista i założyciel Bil Software.
Informacje o autorze | Kontakt