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ć.#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
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>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
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
}
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 );
ShowWindow(hwnd, nFunsterStil);
while(GetMessage(&messages, NULL, 0, 0)) {
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
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;
}
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 strzelankaRóżnica jest tylko taka, że ja używam stylu "Vista".
Następny artykuł będzie niespodzianką :)
Dodano: 7 marca 2006, Wróć do "Gra strzelanka"



















