Rozdział 28 Interfejsy API i zestawy SDK Korzystanie z licznych dostępnych podczas programowania w Visual C++ funkcji interfejsów API i zestawów SDK Tworzenie szybkich aplikacji dźwiękowych i graficznych za pomocą interfejsów DirectX ______ _______ Programowanie wysyłania i odbierania poczty w oparciu o archi-tekturę MAPI______________________________ Wykorzystywanie interfejsu MCI do obsługi aplikacji multimedial-nych Krótkie wprowadzenie API i SDK API (od ang. application programming interface, interfejs programowania aplikacji) są to zbiory funkcji przechowywane zazwyczaj w bibliotekach DLL (ang. dynamie link li-braries, biblioteka konsolidowana dynamicznie, .dli) lub statycznych bibliotekach (ang. static libraries, .libs). Funkcje te pomagają implementować w programie inne funkcje użytkowe i dostarczają połączenia między aplikacją a sprzętem przez nią obsługiwanym. Istnieje wiele różnych interfejsów API upraszczających różne aspekty programowania poprzez możliwość odwoływania się do standardowych funkcji API. Poszczególnym interfejsom poświęcone są osobne książki, dlatego w tym rozdziale ograniczymy się do przedstawienia kilku podstawowych przykładów zastosowań funkcji API. Ostatnie postępy w rozwoju technologii OLE i COM sprawiają, że wiele z interfejsów API jest obecnie implementowanych jako obiekty COM, co znacznie ogranicza problemy z zachowaniem zgodności wersji między różnymi bibliotekami .dli.
742 Poznaj Visual C++ 6 SDK (od ang. software development kits, zestawy do budowania oprogramowania) są interfejsami API, wzbogaconymi o dokumentację i przykładowe programy. Zestawy SDK pomagają tworzyć określone rodzaje aplikacji, tak jak Gamę SDK pomaga tworzyć gry, a OLE DB SDK aplikacje baz danych oparte na technologii OLE. Generalnie rzecz biorąc, funkcje API nie są standardowo załączane do bibliotek projektu i wymagają łączenia się z określonymi bibliotekami. Zazwyczaj dołączamy wszystkie prototypy funkcji i definicje makroinstrukcji API potrzebne w programie przez załączenie plik nagłówka .h, specyficznego dla interfejsu API, z którego korzystamy. Zdobywanie interfejsów API i zestawów SDK Nie wszystkie dostępne interfejsy API i zestawy SDK rozprowadzane są z kompilatorem Visual C++, większość jednak można ściągnąć spod adresu internetowego Microsoftu www.microsoft.com, zazwyczaj za darmo. Nawet jeśli potrzebny nam interfejs API lub zestaw SDK jest dostępny w posiadanej przez nas wersji Visual C++, warto sprawdzić, czy nie są już dostępne ich nowsze wersje, i PATRZ TAKŻE Na temat OLE i COM pisaliśmy w rozdziale 25. Tworzenie szybkich aplikacji dźwiękowych i graficznych za pomocą interfejsów DirectX Interfejsy API typu DirectX stworzone zostały, by rozwiązać problem szybkiego dostępu do sprzętu obsługującego obraz i dźwięk. Chociaż system Windows oferuje skomplikowane standardowe funkcje obsługi dźwięku i grafiki, nie są one wystarczająco szybkie i wygodne dla programistów tworzących gry czy programy do prezentacji graficznych. Bardzo długo jedynym sposobem ominięcia tego problemu było napisanie programu w systemie DOS rezygnując tym samym z szeregu usług (takich jak niezależność programu od sprzętu czy informacje na temat konfiguracji), które oferuje system Windows. Microsoft wypełnił tę lukę za pomocą serii interfejsów API zapewniających charakte- rystyczną dla Windows niezależność od wykorzystywanego sprzętu, ale oferujących znacznie szybszy i bardziej bezpośredni dostęp do urządzeń obsługujących dźwięk, obraz i kontakt z użytkownikiem. Te interfejsy API to DirectSound, DirectDraw, Direct3D, DirectPlay, Directlnput i DirectSetup. Łącznie tworzą one zestaw Gamę SDK, który staje się coraz popularniejszy wśród programistów piszących gry i oprogramowanie obsługujące prezentacje graficzne.
Interfejsy API i zestawy SDK 743 Problemy z bardzo szybką grafiką Głównym ograniczeniem dla aplikacji obsługujących animacje komputerowe są możliwości procesora. Aby oszukać ludzkie oko dając mu złudzenie ruchomego obrazu, należy wyświetlać klatki filmu z prędkością około 24 klatek na sekundę. Jedna klatka 256-kolorowego ekranu o rozdzielczości 800x600 to 480 000 pikseli. Aby zmienić kolor każdego piksela 24 razy na sekundę, musimy w ciągu tej sekundy wykonać 11 520 000 operacji. Zdefiniowanie każdego piksela zajmuje około 12 cykli procesora, co daje 138 240000 cykli zegara tylko po to, by odświeżyć w ciągu sekundy zawartość ekranu. We współczesnych procesorach o częstotliwości 266 MHz nawet 50% czasu procesora może być poświęcane operacjom odświeżania ekranu. W pozostałych 50% czasu procesora musimy wykonać wiele skomplikowanych i czasochłonnych operacji, takich jak obliczenia 3D, odwzorowywanie tekstur, skalowanie mapy bitowej i komunikację z użytkownikiem. Interfejsy API korzystają z modelu COM i dlatego odwołania do funkcji grupowane są przez specyficzne interfejsy COM w obiektach COM implementujących te funkcje. Biblioteki DLL niezbędne dla interfejsów API nie są instalowane razem z systemem Windows, są jednak rozprowadzane za darmo i dostępne w kodzie większości współczesnych gier. Można je również ściągnąć z internetowego adresu Microsoftu: www.micro-soft.com/directK. PATRZ TAKŻE Na temat OLE i COM pisaliśmy w rozdziale 25. DirectSound Interfejs API DirectSound służy do bezpośredniego sięgania do karty dźwiękowej w celu wygenerowania dźwięku w oparciu o bufor dźwiękowy (ang. waveform buffer). Bufor ten jest po prostu fragmentem pamięci, który może być odczytywany bezpośrednio przez kartę muzyczną zmieniającą wartości tam zapisanych na dźwięki. Najprostsza technika kodowania dźwięku polega na przypisaniu każdemu z bajtów w buforze cyfry reprezentującej jeden z 256 poziomów amplitudy fali dźwiękowej, który odpowiadać będ/ic następnie odpowiedniemu wychyleniu membrany w podłączonym do komputera głośniku Przypisując kolejnym bajtom wartości, które ułożą się w kształt fali sinusoidalnej otrzymamy czysty dźwięk określonej wysokości. Zmieniając amplitudę fali poprzez wpisanie odpowiednich liczb do bufora możemy modulować kształt fali uzyskując najróżniejsze efekty dźwiękowe. Bufor przechowujący falę, którą słyszymy nazywany jest podstawowym buforem interfejsu DirectSound (ang. primary buffer). Zazwyczaj nie tworzymy bezpośrednio pod-
744 Poznaj Visual C++ 6 stawowego bufora, tylko definiujemy dźwięki zapisane w podrzędnych buforach (ang. secondary buffers), które odegrane razem zostają automatycznie zmiksowane w dźwięk bufora podstawowego. Korzystając z możliwości miksowania dźwięku i kilku podrzędnych buforów możemy uzyskać bardzo skomplikowane efekty dźwiękowe. Na rysunku 28.1 pokazany został efekt zmiksowania dźwięku zapisanego w dwu różnych podrzędnych buforach. Dźwięk bufora przedstawiony na górze jest opadającą sinusoidą (czysty dźwięk), a fala poniżej jest utworzona z losowych wartości dających szum tła (ang. wbite noise) o rosnącej amplitudzie. Fala na samym dole pokazuje zawartość podstawowego bufora będącego efektem jednoczesnego odegrania i zmiksowania dwóch podrzędnych buforów. Efektem jest czysty dźwięk przechodzący stopniowo w szum. Możemy mieszać czyste dźwięki z dźwiękami o falach kwadratowych lub trójkątnych albo z innymi buforami zawierającymi na przykład nagrane fragmenty melodii, aby uzyskać najbardziej niesamowite efekty dźwiękowe. Pierwszą rzeczą, którą należy zrobić, aby móc korzystać z DirectSound APljest utworzenie za pomocą bezpośredniego odwołania do funkcji CoCreateinstance () interfejsu iDirectSound (patrz rozdział 25) lub korzystając ze skrótu, jaki oferuje funkcja Direct-SoundCreate (). Funkcja DirectSoundCreate () Jeśli korzystamy z funkcji DirectSoundCreate (), musimy załączyć do projektu bibliotekę dsound.lib wpisując odpowiednią linię na liście Object/L;ibrary Modliłeś dostępnej na karcie Link okna dialogowego Project Settings. Jeśli natomiast korzystamy z funkcji CoCreateinstance (), biblioteka ta wprawdzie nie będzie nam potrzebna, ale będziemy musieli zadeklarować globalny identyfikator klasy (CLSID) i identyfikator interfejsu (IID). Funkcja DirectSoundCreate () wymaga trzech parametrów: pierwszy z nich jest wskaźnikiem do globalnego identyfikatora GUID definiującego urządzenie (kartę muzyczną), z którego korzystamy. Identyfikator ten można zdobyć za pomocą funkcji zwrotnej (ang. caliback function) DirectSoundEnumerate (). Możemy również przesłać funkcji wartość NULL, aby skorzystać z urządzenia domyślnego. Drugi parametr jest wskaźnikiem do wskaźnika obiektu DirectSoundinterface i służy do przypisywania wskaźnikowi aplikacji nowego obiektu. Trzeciemu parametrowi przypisujemy zazwyczaj wartość NULL, chyba że musimy skorzystać z techniki agregacji COM (ang. COM aggregation). Jeśli funkcji uda się utworzyć interfejs, zwraca ona wskaźnik do interfejsu iDirect-Sound, który jak każdy interfejs COM może być dealokowany (usuwany) z pamięci za pomocą funkcji Relase (). Gdy już mamy interfejs IDirectSound, musimy natychmiast zdefiniować jego poziom współdziałania (ang. cooperative level). Wiele z interfejsów API typu DirectX posiada poziomy współdziałania definiujące jak sprzęt, z którego korzystają, będzie wykorzy-
Interfejsy API i zestawy SDK________________________________745 stywany oraz jak jego czas będzie rozdzielany między różne aplikacje. Poziom współdziałania możemy zdefiniować przyzywając w interfejsie iDirectSoundO funkcję Set-CooperativeLevel (), przesyłając jej definiujący naszą aplikację identyfikator obsługi okna. Funkcji tej musimy również przesłać znacznik definiujący poziom współdziałania. Najczęściej stosuje się znacznik DSSCL_NORMAL umożliwiający urządzeniu podczas obsługi naszego programu pełne współdziałanie z innymi aplikacjami. Teraz możemy utworzyć podrzędne bufory dźwiękowe za pomocą funkcji CreateSo-undBufferO interfejsu iDirectSound. Funkcja CreateSoundBuffer() wymaga trzech parametrów. Pierwszy parametr to struktura DSBUFFERDESC opisująca typ bufora, który tworzymy. Drugi parametr jest wskaźnikiem do wskaźnika interfejsu bufora iDirectSo-undBuffer aplikacji. Trzeciemu parametrowi przesyłamy wartość NULL, chyba że korzystamy z agregacji COM. Struktura DSBUFFERDESC jest definiowana w następujący sposób: typedef struct _DSBUFFERDESC{ DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; LPWAVEFORMATEX lpwfxFormat; } DSBUFFERDESC, *LPDSBUFFERDESC; Zmienne składowe struktury reprezentują: Zmienna dwSize reprezentuje rozmiar struktury (który można znaleźć za pomocą operatora sizeof ()). " Zmienna dwFlags pozwala definiować kilka opcji umożliwiających kontrolę nad różnymi parametrami odgrywania dźwięku, takimi jak dźwięk stereofoniczny czy głośność. Całkiem dobry zestaw standardowych ustawień oferuje znacznik DSBCAPS_CTRLDEFAULT. Zmienna dwBufferBytes informuje, ile bajtów danych może zawierać bufor. Dla bardziej skomplikowanych dźwięków lub efektów potrzebny będzie oczywiście dłuższy bufor, dla krótkich lub powtarzających się dźwięków krótszy. Bufory można odgrywać w pętli powtarzając ich zawartość wielokrotnie, by uzyskać powtarzające się efekty dźwiękowe. Zmienna lpwfxFormat wskazuje strukturę WAVEFORMATEX przechowującą informacje instruujące kartę dźwiękową jak należy dany dźwięk odegrać. Struktura WAVEFORMATEX przechowuje informacje takie jak prędkość, z jaką bufor ma zostać odegrany czy liczba bajtów reprezentujących każdy pojedynczy dźwięk. Zawiera również informacje na temat techniki odgrywania dźwięku, które zależne są już od konkretnej karty muzycznej, chociaż większość kart dźwiękowych korzysta z opisanej wcześniej techniki WAVE_FORMAT_PCM.
746 Poznaj Visual C++ 6 Po zdefiniowaniu podrzędnego bufora możemy zdefiniować jego zawartość przez wpisanie odpowiednich wartości w programie albo załadowanie z pliku .wav. W obu przypadkach dostęp do bufora umożliwia funkcja Lock() interfejsu iDirectSound-Buffer zwracająca adres i rozmiary bufora. Możemy w buforze zapisać odpowiednie liczby reprezentujące dźwięki, a następnie zawezwać funkcję Uniock(), która uwolni bufor. Blokowanie i odblokowywanie bufora Należy starać się nie blokować bufora zbyt długo, bowiem jeśli zdarzy się, że aktualnie odgrywany dźwięk będzie potrzebować zablokowanego bufora i odegra w jego miejsce dźwięki generowane losowo. Niektóre karty dźwiękowe posiadają własną pamięć bufora niedostępną bezpośrednio z przestrzeni adresowej procesora. W tym przypadku funkcje Lock() i unlock() zajmują się również transferowaniem zawartości pamięci pomiędzy buforem karty a pamięcią komputera. Po przypisaniu buforowi odpowiednich reprezentujących dźwięki liczb możemy odegrać go przyzywając funkcję Play () interfejsu iDirectSoundBuffer. Funkcja ta wymaga zdefiniowania trzech parametrów. Pierwszym dwóm parametrom należy przypisać wartość zero. Trzeciemu natomiast znacznik DSBPLAY_LOOPING, jeśli chcemy odgrywać bufor w pętli (wielokrotnie) lub zero jeśli chcemy odegrać go tylko raz. Odgrywanie bufora można przerwać przyzywając funkcję Stop (). Na listingu 28.1 pokazujemy, w jaki sposób za pomocą interfejsu DirectSound można w oparciu o aplikację SDI utworzyć prosty, sterowany klawiaturą syntezator dźwięku. Aplikacja będzie tworzyć i wyświetlać w oknie zawartość dwóch podrzędnych buforów (dwie górne linie, czerwona i zielona widoczne na rysunku 28.1).
Rysunek 28.1. Program SoundYiew wyświetlający zawartość dwóch podrzędnych buforów i dźwięk powstały po ich zmiksowaniu
Interfejsy API i zestawy SDK 747 Kiedy użytkownik wciśnie odpowiedni klawisz, oba bufory są odgrywane dając w efekcie dźwięk, którego fala przedstawiona została na dole (niebieska linia, czego niestety nie widać na rysunku). Wysokość dźwięku (sinusoidalnej fali) definiowana jest w zależności od kodu ASCII klawisza, który został wciśnięty, dzięki czemu dalsze litery alfabetu dają wyższe dźwięki. Oczywiście program ten wymaga karty dźwiękowej. Listing 28.1. LST32_1.CPP - prosty program syntetyzatora miksujący zawartość dwóch buforów po wciśnięciu klawisza klawiatury 1 CSoundView::CSoundView() 2 { 3 m_pDSObject = NULL; 4 m_pDSBuffer = NULL; 5 m_pDSMix = NULL; 6 } 7 8 CSoundView::~CSoundView() 9 < 10 if (m_pDSMix) m_pDSMix->Release(); O 11 if (m_pDSBuffer) m_pDSBuffer->Release(); O 12 if (m_pDSObject) m_pDSObject->Release(); O 13 } 14 15 void CSoundView::PlayFreq(double dFreq) 16 ( 17 if (!m_pDSObject) 18 { 19 // Utwórz obiekt DirectSound 20 if(DS_OK==Direct3oundCreate( 21 NULL,&m_pDSObject,NULL)) 22 { 23 // Ustal poziom współdziałania 24 m_pDSObject->SetCooperativeLevel(m_hWnd, 25 DSSCL_NORMAL) ; 26 27 // Oczyść bufor obiektu DirectSound 28 memset(&m_DSBufferDesc,O,sizeof(DSBUFFERDESC)) ; 29 30 // Zdefiniuj podrzędny bufor 31 m_DSBufferDesc.dwSize = sizeof(DSBUFFERDESC); 32 m_DSBufferDesc.dwFlags = DSBCAPS_CTRLDEFAULT; 33 m_DSBufferDesc.dwBufferBytes = 4096; 34
80 { 81 double dAmplitude = 1.0 + dRange * (double)i; 82 BYTE bl = (BYTE)(127 + (128.0 - dAmplitude) 83 * sin((double)i / (0.147 * dFreq))); 84 BYTE b2 = (BYTE) (rand ()% (int) dAmplitude) 1; 85 *(pBufferl+i) = bl; 86 *(pEnvl+i) = b2; 87 88 BYTE b3 = bl + b2; 89 90 // Namaluj fale @ 91 int x = (int) (dXRange * (double)i); 92 dcClient.SetPixelV(x, 93 (bll) ,RGB(255,0,0) ) ; 94 dcClient.SetPixelV(x,y0ff+64+ 95 (b2l) ,RGB(0,255,0) ); 96 dcClient.SetPixelV(x,yOff*2+' 97 (b3l) ,RGB(0,0,255) ); 98 } 99 100 // Odblokuj bufory i odegraj dźwięk Q 101 m_pDSBuffer->Unlock(pBufferl,dwSizel, 102 pBuffer2,dwSize2); 103 m_pDSMix->Unlock(pEnvl,dwSizel, 104 pEnv2,dwSize2) ; 105 m_pDSBuffer->Play(0,0,0) ; 106 m_pDSMix->Play(0,0,0) ; 107 } 108 } 109 110 void CSoundView::OnKeyDown(UINT nChar.UINT nRepCnt, 111 UINT nFlags) 112 { 113 CYiew::OnKeyDown(nChar, nRepCnt, nFlags); 114 115 // Zagraj nutę o częstotliwości zależnej od wciśniętego klawisza 116 if 117 } O W elegancki sposób usuwamy obiekty COM DirectSound za pomocą funkcji Relase. @ Funkcja Lock() blokuje dostęp do pamięci i zwraca wskaźniki do buforów używanych przez program.
750 Poznaj Visual C++ 6 W oparciu o zapisane w buforach wartości rysujemy odpowiednie fale. O Po odblokowaniu zawartość buforów jest odgrywana. Fragment kodu przedstawiony na listingu 28.1 powinien zostać załączony do klasy widoku zbudowanego za pomocą kreatora CIassWizard szkieletu aplikacji SDI (tutaj nazwanej Sound). Z powodu dodatkowych elementów, które umieszczamy w klasie widoku (w pliku SoundYiew.cpp), należy do pliku nagłówka zawierającego definicję klasy (Sound-View.h) dodać zmienne składowe wykorzystywane na listingu: IDirectSound* m_pDSObject; IDirectSoundBuffer* m_pDSBuffer; IDirectSoundBuffer* m_pDSMix; DSBUFFERDESC m_DSBufferDesc; void PlayFreq(double dFreq); Deklarujemy tutaj zmienne składowe wskaźnika interfejsu potrzebne interfejsowi DirectSound: dwa bufory i strukturę opisującą bufor. Definiujemy również funkcję PlayFreq () wykorzystywaną do implementacji efektów dźwiękowych. Należy również za pomocą kreatora CIassWizard dodać funkcję obsługi komunikatu WM_KEYDOWN. W ten sposób automatycznie utworzymy plik nagłówka i odpowiednie hasła w mapie komunikatów. Aby poinformować kompilator o niezbędnych wskaźnikach interfejsu, strukturze WAVEFORMATEX i wykorzystywanej funkcji sinusoidalnej, należy do pliku definicji klasy dodać następujące dyrektywy łinclude: łinclude "mmsystem.h" łinclude "DSOUND.H" łinclude "math.h" Konstruktor widoku przedstawiony w liniach 1-6 listingu 28.1 inicjuje wskaźnik obiektu DirectSound m_pDSObject i dwa wskaźniki buforów m_pDSBuffer i m_pDS-Object przypisując im wartość NULL. Odpowiadający mu przedstawiony w liniach 8-13 destruktor niszczy obiekty COM przyzywając ich funkcję Relase () usuwając za jej pomocą odwołania do tych obiektów. W liniach 17-49 główna funkcja PlayFreq () wykorzystywana jest do inicjowania obiektu DirectSound i buforów, gdy wzywana jest po raz pierwszy. Inicjacja tych elementów musi być wykonana właśnie tutaj, ponieważ funkcja SetCooperativeLevel () z linii 24 wymaga identyfikatora obsługi istniejącego okna m_hWnd, które jeszcze nie istniało w czasie wykonywania konstruktora. Jeśli obiekt DirectSound zostanie w linii 20 prawidłowo inicjalizowany, za pomocą funkcji CreateSoundBuffer () tworzymy dwa podrzędne bufory zaraz po zdefiniowaniu odpowiedniego opisu bufora m_DSBufferDesc i struktury sWave typu WAVEFORMATEX.
Interfejsy API i zestawy SDK 751 Poziomy współdziałania Istnieją cztery możliwe poziomy współdziałania dla obiektów Direct Sound. Poziom DSSCL_EXCLUSIVE daje aplikacji wyłączny dostęp (jeśli jest pierwszą, która to zadeklaruje) do karty dźwiękowej. Inne aplikacje zostaną odcięte od dźwięku. Poziom DSSCL_NORMAL pozwala innym aplikacjom na jednoczesne korzystanie z karty na równych zasadach. DSSCL_PRIORITY daje aplikacji priorytet w dostępie do zasobów karty dźwiękowej, a poziom DSSCL_WRITEPRIMARY daje aplikacji najwyższy prio- rytet dostępu do podstawowego bufora i blokuje odgrywanie wszystkich podrzędnych buforów (z pozostałych aplikacji). Druga połowa funkcji PlayFreq () definiuje bufory, rysuje fale w oknie widoku i odgrywa dźwięk odpowiednio do wartości częstotliwości zapisanej w parametrze dFreq. W liniach 59-65 inicjowany jest kontekst urządzenia klienta, widok jest oczyszczany i inicjowany obszar roboczy całego widoku. W liniach 68-73 za pomocą funkcji Lock() blokowane są oba podrzędne bufory i pobierane wskaźniki do nich. W liniach 79-98 znajduje się główna pętla, która generuje wartości zapisywane w buforach i przenosi je na malowany w widoku rysunek fal. W linii 83 wyliczamy punkty b l tworzące sinusoidalną falę w oparciu o częstotliwość i pozycję w buforze definiowaną przez licznik pętli i oraz opadającą wartość amplitudy głośności dAmplitude. Uzyskana wartość jest w linii 85 zapisywana w pierwszym z dwu podrzędnych buforów, a w linii 92 wykorzystywana przez funkcję Setpixeiv() do narysowania czerwonego piksela, z których to pikseli powstanie górna fala. Losowa wartość dla szumu zapisywanego w drugim buforze za pomocą zmiennej b2 wyliczana jest w linii 84 w oparciu o funkcję rand () generującą liczby losowe i o odwróconą odwróconą i zmniejszoną o połowę wartość amplitudy głośności dAmplitude, co daje w efekcie narastający szum. Wartość ta jest następnie w linii 86 przypisywana buforowi, a w linii 94 wyświetlana na ekranie jako zielony piksel. Trzecia zmienna b3 odpowiadająca zawartości podstawowego bufora wyliczana jest jako zmiksowana (przez dodanie ich do siebie) wartość zmiennych bl+b2 w linii 88 i wyświetlana na ekranie jako niebieski piksel w linii 96. Miksowanie buforów dźwiękowych Poprzez zmiksowanie dwóch fal odgrywanych jednocześnie z dwóch podrzędnych buforów możemy modulować dźwięk jednej fali za pomocą drugiej. Za pomocą tej techniki możemy połączyć falę o wysokiej częstotliwości z falą o niskiej częstotliwości, otrzymując wibrujący lub falujący dźwięk wynikowy. W liniach 101-103 bufory są odblokowywane za pomocą funkcji Uniock (), a następ nie odgrywane za pomocą funkcji Play () w liniach 105 i 106. W momencie gdy przyzy-
752_____________________________________Poznaj Visual C++ 6 wana jest pierwsza funkcja p l a y (), automatycznie konstruowany jest podstawowy bufor dopasowujący się do wymagań podrzędnego bufora, wykorzystywany do przesyłania informacji do karty muzycznej. Kolejne odwołanie do funkcji Play () sprawi, że drugi podrzędny bufor zostanie zmiksowany z dźwiękiem zapisanym w podstawowym buforze, co da pożądany efekt dźwiękowy. Na koniec możemy jeszcze za pomocą okna dialogowego New Windows Messa-ge/Event handler dodać funkcję OnKeydown () przechwytującą komunikaty o wciśnięciu klawisza klawiatury i przyzywającą funkcję PlayFreq (). Funkcja OnKeydown () przesyła funkcji PlayFreq () jako wartość częstotliwości numer wciśniętego klawisza, tworząc prosty sterowany klawiaturą syntezator dźwięku. Zanim zbudujemy opisaną wyżej aplikację musimy pamiętać, żeby połączyć się z biblioteką dsound.lib wpisując plik dsound.lib na liście Object/Library Modules na karcie Link okna dialogowego Project Settings. Okno to wywołujemy albo wciskając klawisze Alt+F7, albo wybierając w menu Project polecenie Settings. Po zbudowaniu i uruchomieniu aplikacji będziemy mogli wciskając wybrany klawisz klawiatury wyświetlać w oknie rysunek fal dźwiękowych i porównywać go jednocześnie z odegranym przez komputer dźwiękiem. PATRZ TAKŻE Op/s zasad działania OLE i COM znaleźć można w rozdziale 25. DirectDraw Interfejs API DirectDraw możemy wykorzystać do napisania bardzo szybkiej, wolnej od nieprzyjemnego migania obrazu aplikacji graficznej. Tenże interfejs API umożliwia szkieletowi aplikacji synchronizację techniki podwójnego buforowania (ang. double buffeńng), niezbędnej przy tworzeniu gier i oprogramowania do tworzenia animacji wymagających szybkiej grafiki. Pozwala on na całkowitą kontrolę sposobu wyświetlania obrazu w systemie Windows, zmienianie różnych trybów wyświetlania (włączając niesławny tryb ModeX wykorzystywany w grze Doom 2) i zamianę (ang. flip) powierzchni ekranu, dzięki czemu, kiedy ekran wyświetla jeszcze poprzedni obraz, możemy już malować na powierzchni tła następny. Ta zamiana może być synchronizowana z operacjami monitoraw taki sposób, aby zachodziła w momencie, gdy strumień elektronów w monitorze powraca do wyjściowej pozycji, umożliwiając w ten sposób płynne wyświetlanie obrazu bez nieprzyjemnego migotania. Możemy również całkowicie porzucić windowsowy system wyświetlania i wewnątrz zwykłego okna wykorzystać szybszy bezpośredni sposób rysowania. Interfejs DiresctDraw zawdzięcza swoje wysokie możliwości w zakresie rysowania i przenoszenia bitów (ang. bit bliting) dwóm elementom składowym: jednym z nich jest HAL (ang. hardware abstraction layer) a drugim HEL (ang. hardware emulation layer). Kiedy korzystamy z funkcji renderu-jących (ang. rendering) i przenoszących bity powierzchni DirectDraw, przyzywamy HAL.
Interfejsy API i zestawy SDK 753 ModeX ModeX jest nieudokumentowanym trybem karty VGA. Wielu programistów gier było swego czasu zmuszonych do korzystania z trybu co prawda wielokolorowego, ale o stosunkowo niskiej rozdzielczości, aby uzyskać wystarczająco szybkie odświeżanie ekranu niezbędne dla wykorzystywanej w grze animacji. Jednym z lepszych kandydatów był tu tryb VGA 0x13 (w zapisie szesnastkowym) dający rozdzielczość 320x200. Jednak nieudokumentowany tryb ModeX pozwalał osiągnąć rozdzielczość 320x240 pikseli, co tłumaczy jego popularność. W ten sposób otrzymujemy niezależny od karty graficznej mechanizm pozwalający naszemu programowi pracować na wielu różnych kartach graficznych. Jeśli funkcja rende-rująca może być wykonywana przez kartę graficzną, zyskujemy bardzo na szybkości. Jeśli nie, HEL w sposób niezauważalny tworzy niezbędne nam funkcje karty, dzięki czemu nie musimy martwić się o rzeczywiste możliwości naszej karty graficznej. Interfejs API DirectDraw oferuje tylko cztery obiekty COM przedstawione w tabeli 28.1. Oglądając tabelę można zauważyć, że niektóre z interfejsów mają dodaną do nazwy cyfrę 2. Dzieje się tak dlatego, że są to nowsze wersje interfejsów, których wersje wcześniejsze są nadal dostępne dla starszych aplikacji. Możemy również zauważyć, że najważniejszym obiektem jest tutaj DirectDrawSurface. Powierzchnie (ang. surfaces) są tworzone przez ten właśnie główny obiekt DirectDraw, a następnie mogą być odpowiednio przycinane (ang. clipped) przez obiekty DirectDrawCIipper lub korzystać z kolorów obiektów DirectDrawPalette. Tabela 28.1. Obiekty DirectDraw i ich funkcje Obiekt
DirectDraw
Interfejs COM
Jego funkcje
DirectDraw
IDirectDraw2
Zarządza trybami karty, możliwościami urządzenia i tworzy pozostałe obiekty
DirectDrawSurface IDirectSurface2 Zarządza głównymi i podrzędnymi mapami bitowymi, podwójnym buforowaniem, rysowaniem i funkcjami przenoszącymi bity. Może być przycinany przez obiekt DirectDrawCIipper i korzystać z kolorów obiektu DirectDrawPalette IDirectDrawCIipper Zarządza listą informacji definiujących przycinanie powierzchni DirectDrawCIipper IDirectDrawPalette Zarządza paletą kolorów i informacjami odwzorowywania dla kolorów wykorzystywanych przez powierzchnię DirectDrawPalette
754_________________ ___ ______________ Poznaj Visual C++ 6 Gtówny obiekt DirectoryDraw tworzymy korzystając z funkcji biblioteki ddraw.lib, DirectDrawCreate () lub bezpośrednio za pomocą funkcji CoCreateInstance (). Funkcja DirectDrawCreate () wymaga przesłania jej trzech parametrów. Pierwszy parametr jest wskaźnikiem do globalnego identyfikatora GUID, który definiuje wykorzystywany sterownik ekranu. Standardowo przesyłamy tutaj wartość NULL informującą, że powinniśmy korzystać z aktywnego sterownika. Kolejne sterowniki możemy odnaleźć za pośrednictwem odpowiedniej funkcji wyliczeniowej. Drugi parametr jest wskaźnikiem interfejsu iDirectDraw do wskaźnika nowego obiektu. Jeśli skorzystamy z tej metody, powinniśmy następnie użyć funkcji COM Querylnterface (), aby zdobyć wskaźnik do nowszego interfejsu lDirectDraw2. Trzeciemu parametrowi jest normalnie przypisywana wartość NULL, jako że jest on wykorzystywany w agregacji COM. Agregacja COM Agregacja jest techniką COM wykorzystywaną przez komponenty zawierające inne komponenty, aby korzystać z funkcji wewnętrznych komponentów. Zewnętrzny obiekt oferuje programowi klientowi interfejs, który jest w rzeczywistości interfejsem wewnętrznego obiektu. Kiedy klient pyta o wskaźnik do tego interfejsu zewnętrzny komponent wręcza mu bezpośredni wskaźnik do agregowanego wewnętrznego obiektu. Przesyłając wspomniany tu trzeci parametr obiekt DirectDraw pozwala innym obiektom agregować siebie. W starszych wersjach bibliotek DirectX technika agregacji może nie być obsługiwana i przesłanie funkcji DirectDrawCreate () trzeciego parametru zwróci kod informujący o błędzie. Znacznie prościej jest utworzyć obiekt COM za pomocą funkcji CoCreateinstan-ce (), jako że docieramy wtedy bezpośrednio do nowszego interfejsu lDirectDraw2 i nie musimy się łączyć z biblioteką ddraw.lib. Obiekt DirectDraw tworzymy przesyłając funkcji CoCreateInstance () jako identyfikator klasy wartość CLSlD_DirectDraw, a jako identyfikator interfejsu, HD_DirectDraw, łącząc się od razu z nowszym interfejsem. Odpowiednie identyfikatory dostępne są w pliku nagłówka DrawDIg.h. Następnie musimy wezwać funkcję initializef) przesyłając jej identyfikator GUID, który identyfikuje sterownik ekranu (standardowo wpisujemy NULL). Gdy już mamy obiekt DirectDraw, musimy za pomocą funkcji IDirectDraw: :SetCooperativeLevel () zdefiniować jego poziom współdziałania ze sterownikiem. Funkcja ta ma dwa parametry; pierwszy jest identyfikatorem obsługi identyfikującym aplikację będącą właścicielem obiektu, a drugi jest odpowiednim znacznikiem definiującym poziom współdziałania. Wpisując znacznik DDSCL_NORMAL dzielimy się dostępem do .sterownika z innymi aplikacjami, jeśli jednak potrzebna jest nam kontrola nad całym ekranem, musimy użyć znaczników DDSCL_EXCLUSIVE i DDSCL_FULLSCREEN. Aby korzystać z techniki podwójnego buforowania, musimy mieć kontrolę nad całym ekranem.
Interfejsy API i zestawy SDK 755 Możemy teraz utworzyć powierzchnie, na których będziemy tworzyć wyświetlany obraz przywołując funkcję CreateSurface obiektu DirectDraw i przesyłając jej opisującą nową powierzchnię strukturę DDSURFACEDESC. Struktura ta opisuje różne rodzaje powierzchni. Zazwyczaj tworzymy jedną powierzchnie podstawową (ang. primary surface) za pomocą znacznika DDSCAPS_PRIMARYSURFACE i pewną liczbę rezerwowych powierzni-buforów, aby korzystać z techniki przenoszenia bitów lub zamiany stron (podwójnego buforowania). Dla techniki zamiany stron możemy połączyć rezerwowe bufory w tym samym odwołaniu do funkcji CreateSurface () co podstawowy bufor, definiując zmienną zliczania buforów dwBackBufferCount i dołączając znacznik DDSD_BACKBUFFER-COUNT. Możemy również utworzyć kilka obiektów DirectDrawCIipper, aby zdefiniować listę odcinania (ang. clipping list). Obiekty te są bardzo przydatne, kiedy wykorzystujemy DirectDraw na normalnym ekranie Windows, ponieważ możemy utworzyć tę listę w oparciu o okno rodzica, co pozwoli nam uniknąć niepotrzebnego odświeżania powierzchni ekranu poza jego granicami. Obiekty te mogą być tworzone w obiekcie DirectDraw za pomocą funkcji CreateClipper (), a następnie wybierane w utworzonej powierzchni za pomocą jej funkcji SetCIipper (). Cykliczne zmienianie kolorów a animacja Cykliczne zmienianie kolorów może być również wykorzystywane, by za darmo uzyskać dodatkową animację. Zmieniając kolory w palecie kolorów (ang. color pa-lette) możemy dodać monochromatyczne, statyczne pętle animacji tam, gdzie ekran nie musi być odmalowywany. Przykładowo, jeśli zdefiniujemy cztery koncentryczne koła, każde w innym kolorze, możemy następnie zdefiniować trzy z kolorów jako czarne, a jeden jako biały. Biały kolor może być przesuwany przez paletę pomiędzy czterema pozycjami kolorów w każdej kolejnej klatce animacji, co da efekt podświe- tlenia po kolei każdego z kół. Palety umożliwiające cykliczne zmienianie kolorów mogą być tworzone za pomocą funkcji CreatePalette () w głównym obiekcie DirectDraw. Palety mogą być inicjowane zmiennymi COLORREF i wybierane w powierzchni za pomocą funkcji powierzchni Set-PaletteO. Na powierzchni możemy malować za pomocą zwykłych funkcji GDI, blokując ją najpierw, a następnie pobierając odpowiedni kontekst urządzenia za pomocą funkcji powierzchni GetDC (). Kiedy skończymy rysowanie, musimy przyzwać funkcję powierzchni Rela-seDC (), która odblokuje powierzchnię. Zestaw szybkich funkcji służących do przenoszenia bitów, takich jak Bit (), pozwala kopiować powierzchnie, nadawać im różne barwy w oparciu o techniki graficzne, takie jak rozciąganie czy odwzorowywanie tekstury. Podstawowe powierzchnie i ich powierzchnie-burory mogą być zamieniane miejscami za pomocą funkcji Flip (), która może pracować asynchronicznie czekając z wykonaniem operacji na moment, gdy monitor przygotowuje się do wyświetlenia następnej klatki obra-
756_____________________________________Poznaj Visual C++ 6 zu. Efekt ten osiągniemy przesyłając funkcji znacznik polecający czekać jej do tego momentu lub przywołując funkcję WaitForVerticalBlank() obiektu DirectDraw. Inne użyteczne, powiązane z monitorem funkcje to GetMonitorFrequency () i GetScanLine (). Korzystanie z funkcji przenoszących bity i z funkcji GDI Należy uważać, żeby nie przyzywać funkcji przenoszącej bity, gdy mamy aktywny kontekst urządzenia zdobyty za pomocą funkcji GetDC (). Funkcja GetDC () blokuje kontekst urządzenia i nie pozwoli funkcji przenoszącej bity na wykonanie zadania. Dlatego zanim wezwiemy funkcję przenoszącą bity, musimy przywołać funkcję Re- laseDC(). Listing 28.2 prezentuje przykładową aplikację DirectDraw, która wykorzystuje podwójne buforowanie, aby po przejęciu kontroli nad pulpitem Windows wyświetlić szybką, wolną od migotania animację. Nie musi się ona łączyć z biblioteką ddraw.lib, ponieważ do tworzenia obiektu DirectDraw wykorzystuje funkcję CoCreateinstance () i łagodnie przywraca ekran Windows, kiedy wciśniemy klawisz Esc. Przykład ten używa standardowego szkieletu aplikacji opartej na oknie dialogowym (nazwanej Draw). W funkcji Oni-nitDialogO definiowany jest ekran. Następnie w podrzędnym buforze w określonych odstępach czasu malowany jest rysunek. Windowsowe komunikaty czasu odbierane są za pomocą funkcji OnTimer (). Kiedy już rysunek zostanie ukończony, bufory tła i pierwszego planu są zamieniane w momencie, gdy strumień elektronów w monitorze wyłączany jest między kolejnymi klatkami obrazu, tak że nowy rysunek wyświetlany jest natychmiast i bez migotania. Listing 28.2. LST32_2.CPP - obiekt DirectDraw wykorzystany do tworzenia pozbawionej migotania animacji korzystającej z podwójnego buforowania 1 CDrawDlg::CDrawDlg(CWnd* pParent /*=NULL*/) 2 : CDialog(CDrawDlg::IDD, pParent) 3 { 4 //((AFX_DATA_INIT(CDrawDlg) 5 // UWAGA: CIassWizard doda inicjację zmiennych składowych 6 //}}AFX_DATA_INIT 7 // Zauważ, że Loadlcon nie wymaga następującego 8 m_hlcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 9 10 m_pIDraw = NULL; 11 m_pIMainSurface = NULL; 12 13 CoInitialize(NULL); O 14 } 15
760 Poznaj Visual C++ 6 Wskaźnik do podrzędnego bufora możemy pobrać za pomocą funkcji GetAtta- chedSurface (). Funkcję Blt() wykorzystujemy, aby szybko zapełnić powierzchnię określonym kolorem zamiast kopiować w tym celu powierzchnie. Q Za pomocą funkcji matematycznych tworzymy dwa szybko wirujące skomplikowane rysunki. Funkcja Flip() odczekuje z zamianą powierzchni do momentu, gdy ekran zakończy wyświetlanie kolejnej klatki obrazu, by uniknąć migotania wywołanego przez wyświetlanie na ekranie na wpół narysowanych stron. Dodatkowo w kodzie z listingu 28.2 musimy upewnić się, że na początku pliku DrawDIg.cpp załączone zostaną za pomocą dyrektywy ttinclude pliki przedstawione niżej, niezbędne do dostarczenia programowi identyfikatorów GUID i definicji funkcji trygonometrycznych. łinclude łinclude "DrawDIg.h" łinclude "math.h" Definicje interfejsów DirectDraw możemy dostarczyć programowi załączając na początku pliku DrawDIg.h plik nagłówka interfejsu DirectDraw: #include Następujące wskaźniki powinny zostać dodane do pliku DrawDIg.h jako zmienne składowe: IDirectDraw2* m pIDraw; IDirectDrawSurface2* m_pIMain3urface; IDirectDrawSurface2* m_pIFlipSurface; W kodzie z listingu 28.2 wskaźnikom interfejsu przypisywana jest w konstruktorze CDrawDlg (linie 10 i 11) wartość NULL, a w linii 13 za pomocą funkcji Colnitialize () inicjowana jest biblioteka COM. Funkcja oninitDialog () wykorzystywana jest do tworzenia obiektu DirectDraw za pomocą przyzywanej w linii 36 funkcji coCreateinstance (). Proces tworzenia kończymy przyzywając w linii 41 funkcję initialize (), która informuje, że będziemy korzystać w programie z aktywnego sterownika. Następnie w linii 47 ustalamy poziom współdziałania rezerwując sobie wyłączność w korzystaniu z urządzenia i prawa do całego ekranu. Struktura DrawSurfaceDesc typu DDSURFACEDESC deklarowana'jest w linii 50. Definiujemy w niej podstawowy bufor z pojedynczym buforem podrzędnym dla potrzeb podwójnego buforowania. Następnie zadeklarowana struktura DrawSurfaceDesc wykorzy-
Interfejsy API i zestawy SDK 761 stywana jest w linii 59 w tworzącej powierzchnię funkcji CreateSurface (). Wskaźnik do dodatkowej powierzchni zdobywamy w linii 65 za pomocą funkcji GetAttachedSurface (). Na koniec w linii 68 za pomocą funkcji SetTimer () definiujemy licznik czasu (ang. timer) uaktywniający co 50 milisekund funkcję rysująca, której kod znaleźć można w funkcji obsługi OnTimer () w linii 73. Funkcję obsługi OnTime r () komunikatu WM_TIMER dodajemy za pomocą kreatora CIassWizard. Dzięki temu będziemy mieć pewność, że do nagłówka klasy i mapy komunikatów dodane zostaną odpowiednie linie kodu. Po zdefiniowaniu kilku domyślnych wartości przyzywana w linii 86 funkcja Bit () szybko oczyszcza bufor tła, tworząc czarne tło i usuwając poprzednie wykonane na powierzchni rysunki. W linii 92 przyzywamy funkcję GetDC (), aby zdobyć identyfikator obsługi kontekstu urządzenia dla pomocniczej powierzchni. Identyfikator ten jest następnie w linii 97 przypisywany klasie CDC. W liniach 99-126 za pomocą zwykłych odwołań do funkcji GDI i obiektu CPen rysowana jest bardzo szybko jedna klatka animacji. Kontekst urządzenia jest uwalniany w linii 130, a następnie bufory są zamieniane miejscami za pomocą funkcji Flip (), aby wyświetlić nowo narysowaną klatkę animacji. Funkcji przesyłamy znacznik DDFLIP_WAIT polecający jej, aby czekała z wykonaniem operacji do przerwy pomiędzy kolejnymi wyświetlanymi przez monitor klatkami obrazu. Przerwa między wyświetlaniem kolejnych klatek obrazu Kiedy klasyczny (oparty na lampie katodowej) monitor wyświetla obraz przesyłany mu przez kartę graficzną, strumień elektronów wysyłany przez katodę przelatuje ekran linia po Unii, zaczynając od górnego lewego rogu. Elektron uderza w powierzchnię kineskopu powodując świecenie punktu na powierzchni kineskopu. Kiedy strumień elektronów osiągnie prawy dolny róg ekranu promień wodzący kineskopu musi powrócić do prawego górnego rogu, nie podświetlając w trakcie tej operacji punktów na powierzchni kineskopu. W tym momencie strumień elektronów jest na krótką chwilę, pomiędzy kolejnymi klatkami obrazu wyświetlanymi przez monitor wyłączany. Karta graficzna w tym momencie przesyła znacznik informujący, że strumień elektronów został wyłączony. Jest to najlepszy moment, aby zamienić informacje zapisane w pamięci, ponieważ monitor nie wyświetla w tym momencie niczego. Funkcja destruktora z linii 16-25 odtwarza normalny pulpit Windows zmieniając poziom współdziałania i przyzywając funkcję RestoreDisplayMode (), która przywraca normalny tryb wyświetlania. Interfejsy są uwalniane za pomocą funkcji Re l ase (), a biblioteka COM jest automatycznie odłączana. Jeśli wprowadzimy przedstawione tu zmiany do szkieletu aplikacji opartego na oknie dialogowym, będziemy mogli zbudować aplikację bez konieczności dodawania żadnych dodatkowych bibliotek API, korzystając tylko z możliwości, które oferują nam obiekty
762_____________________________________Poznaj Visual C++ 6 COM. Jeśli teraz uruchomimy aplikację, będziemy mogli podziwiać szybką i płynną animację oferowaną przez technikę DirectX. Aplikację możemy wyłączyć w dowolnym momencie wciskając klawisz Esc (będący skrótem do ciągle aktywnego, mimo iż okno aplikacji jest zamknięte, przycisku Cancel). Wciśnięcie klawisza Esc w łagodny sposób przywróci pulpit Windows. PATRZ TAKŻE ^ O technikach COM i OLE pisaliśmy w rozdziale 25. Aby dowiedzieć się więcej na temat kontekstu urządzenia i rysowania za pomocą obiektów i funkcji GDI należy zajrzeć do rozdziału 15. Direct3D Za pomocą interfejsu API Direct3D możemy umożliwić karcie graficznej obsługę grafiki trójwymiarowej. Wspólnie z obiektem DirectDraw interfejs ten oferuje płynną i szybką grafikę trójwymiarową, wykorzystywaną w grach komputerowych i aplikacjach wirtualnej rzeczywistości. Nowe karty graficzne wspomagające grafikę trójwymiarową Ostatnimi czasy pojawiło się na rynku wiele kart graficznych przejmujących część operacji grafiki trójwymiarowej. Specjalistyczne układy scalone karty graficznej pozwalają wykonywać część skomplikowanych obliczeń związanych z grafiką trójwymiarową znacznie szybciej niż programy graficzne. Dodatkowo uwalniana jest w ten sposób także część mocy obliczeniowej procesora, co daje programistom tworzącym gry i programy CAD możliwość zwiększenia ich efektywności. Interfejs Direct3D korzysta z zestawu rysującego złożonego z trzech modułów, które obsługują macierzowe transformacje współrzędnych, efekty oświetlenia dla światła punktowego i oświetlenia biernego. Ostatni moduł odpowiedzialny jest za rastrowanie. Za pomocą tych modułów tworzona jest wynikowa scena. Niektóre z interfejsów Direct3D oferują pełny zestaw funkcji umożliwiających konstruowanie i renderowanie trójwymiarowych widoków, które przypisać można do dwóch kategorii: trybu natychmiastowego (ang. immediate-mode) i trybu opóźnionego (ang. retained-mode). Obiekty trybu natychmiastowego są niskopoziomowymi i szybko działającymi obiektami rysowania w trzech wymiarach (ich interfejsy zostały przedstawione w tabeli 28.2). Na bazie obiektów natychmiastowych zbudowane są liczne obiekty trybu opóźnionego, umożliwiające tworzenie skomplikowanych wysokiej jakości animacji trójwymiarowych.
Interfejsy API i zestawy SDK 763 Interfejs Direct3D Jego funkcje
IDirect3D IDirect3DDevice IDirect3DExecuteBuffer IDirect3DLight IDirect3DMaterial IDirect3DTexture IDirect3DViewport Inicjuje środowisko graficzne i tworzy obiekty Light, Materiał i Viewport. Zarządza konfiguracją umożliwiającą sprzętowi i środowisku uruchomienie możliwości grafiki trójwymiarowej oferowanych przez sprzęt. Interfejs ten możemy utworzyć z powierzchni DirectDraw za pomocą funkcji Querylnterface () Wykonuje bufory i przechowuje informacje o wierzchołkach i instrukcje renderowania dla elementów grafiki trójwymiarowej gotowych do narysowania Konfiguruje dane oświetlenia dla każdego ze świateł określonej sceny Obsługuje właściwości materiału - stopień przezroczystości, odbijanie światła Obsługuje nakładanie na powierzchnie trójwymiarowe mapy bitowej tekstury. Grupuje efekty oświetlenia tekstury i tła w określonym elemencie, który będzie wyświetlony na ekranie. Kilka takich powierzchni ukośnych jest następnie łączonych razem i z informacjami o wierzchołkach, tak aby można je było wyświetlić na ekranie
DirectPlay Interfejs API DirectPlay umożliwia łatwe konstruowanie gier komputerowych umożliwiających kilku graczom wspólną grę przez sieć bez konieczności programowania osobnej obsługi połączeń dla różnego rodzaju połączeń sieciowych. Dwa przynależne tutaj interfejsy lDirectplay2 i iDirectPlayLobby zarządzają wszystkimi aspektami odpowiednio obiektów gry trybu multiplayer i połączeniami między komputerami. Jakie połączenia sieciowe obsługuje DirectPlay? Interfejs API DirectPlay obsługuje grę w komputerach połączonych kablem szeregowym, połączenia TCP/IP sieci lokalnych, połączenia przez modem, Netware i wiele wiele innych połączeń sieciowych. Directlnput Interfejs API Directlnput pozwala na szybszy dostęp do danych wprowadzanych za pomocą myszy i klawiatury niż za pośrednictwem standardowych interfejsów API Win-
764___________________________________Poznaj Visual C++ 6 dows. Obsługuje również pobieranie danych od różnego rodzaju joysticków dostarczając funkcji pozwalających na ich kalibrację. Ten interfejs API jest stosunkowo prosty, oferuje przy tym znaczne możliwości. Składa się z dwóch interfejsów COM. Pierwszy z nich, interfejs IDirectInput, zarządza zainstalowanymi w systemie urządzeniami umożliwiającymi wprowadzanie danych i ich statusem. Umożliwia również tworzenie egzemplarzy interfejsów IDirectInputDevices obsługujących konfigurację i komunikację z konkretnymi urządzeniami. DirectSetup Interfejs API DirectSetup jest prawdopodobnie jednym z najmniejszych interfejsów API. Posiada tylko dwie funkcje: DirectXSetup (), która automatyzuje instalację i konfi-gurowanie wszystkich komponentów DirectX i funkcję DirectXRegisterApplica-tion (), która rejestruje grę jako obiekt DirectPlayLobby, umożliwiający granie w trybie multiplayer. Tworzenie wiadomości i programowanie obsługi poczty za po- mocą MAPI Interfejs Microsoft Messaging API (MAPI) tak naprawdę nie jest interfejsem API, tylko zestawem standardów tworzących architekturę. Posługując się tymi sztywnymi i roz- budowanymi zasadami twórcy oprogramowania nie muszą tworzyć od razu całego opro- gramowania związanego z obsługą poczty, ale mogą ograniczyć się do programowania wybranych fragmentów systemu pocztowego. Część twórców oprogramowania będzie więc tworzyć własne aplikacje klientów (odczytujące i zapisujące pocztę), podczas gdy inni będą tworzyć obiekty oferujące usługi w zakresie transportowania i przechowywania wiadomości lub przechowujące listę adresów w książce adresowej. Każdy z tych komponentów można zaprogramować osobno, a użytkownik może łączyć je ze sobą na różne sposoby wybierając narzędzia do przesyłania poczty, które mu najbardziej odpowiadają. Ta plastyczność systemów pocztowych jest często niezbędna w dużych firmach, które muszą zintegrować ze sobą nowsze i starsze, często bardzo różne systemy pocztowe. Pisząc aplikację MAPI możemy wybierać między dwoma wersjami MAPI, w zależności od tego jak bardzo skomplikowanych narzędzi do wysyłania poczty potrzebujemy. Prostsza wersja MAPI (Simple MAPI) zawiera minimalny zestaw funkcji niezbędny do rejestrowania i utworzenia sesji MAPI, odczytywania i pisania wiadomości oraz rozpatrywania (odnajdywania najbliższego podobnego adresu) adresów w książce adresowej. Jeśli aplikacja, którą tworzymy potrzebuje tylko tych podstawowych funkcji programu pocztowego, lepiej korzystać z prostszej wersji MAPI. Jakiekolwiek dodatkowe funkcje będą wymagały wykorzystania Extended MAPI (rozszerzonego MAPI) implementowanego w pliku mapi28.dll, który dostarczy nam potrzebnych komponentów. Niestety, wersja Extended MAPI jest z konieczności bardzo duża i skomplikowana. Aby z nią pracować,
Interfejsy API i zestawy SDK________________________________765 trzeba dobrze znać zasady programowania COM, ponieważ wersja ta jest implementowana w większej części przez obiektową hierarchię interfejsów i obiektów COM. Extended MAPI SDK i przykładowy program MDBView Jeśli naprawdę potrzebne nam są możliwości Extended MAPI, musimy być przygotowani na ciężką pracę i długą naukę. Będziemy musieli również ściągnąć z sieci MAPI SDK. Ten zestaw SDK zawiera bardzo pouczający przykładowy program MDBVIEW.exe, wielce pomocny przy próbie zrozumienia skomplikowanych zagadnień programowania związanych z rozszerzonym MAPI. PATRZ TAKŻE Na temat OLE i COM pisaliśmy w rozdziale 25. Korzystanie z prostszej wersji MAPI Prostsza wersja MAPI implementowana jest w pliku mapi28.dll, zaś prototypy funkcji i wartości odpowiednich znaczników definiowane są w pliku nagłówka mapi.h. Nasza prosta aplikacja MAPI może zarejestrować się u dostawcy wiadomości (ang. message provider) definiując profil (ang. profile) MAPI lub akceptując profil domyślny, co pozwoli jej rozpocząć sesję pocztową. Profile MAPI możemy skonfigurować za pomocą apletu Maił and Fax w Panelu sterowania (rysunek 28.2). Każdy z profili definiuje szczegółowe informacje na temat przesyłania, wiadomości i programów dostarczających książkę adresową, z których program klient będzie korzystał w momencie zarejestrowania, takich jak Microsoft Exchange czy Lotus Notes. Kiedy już się zarejestrujemy, będziemy mogli wysyłać i odbierać wiadomości i wyrejestrować się po zakończeniu. Aplet Maił and Fax Aplet Maił and Fax w Panelu sterowania może wyglądać inaczej lub mieć inny tytuł niż ten, który przedstawiamy na rysunku, w zależności od aplikacji pocztowej MAPI, którą zainstalujemy na komputerze.
766 Poznaj Visual C++ 6 s
ervices Oelivery] Addresshg 'heioltowinginformation sernice s arę setup in thi$ pro
file;
PersonalA ddi Fersonal Fold Add...
ess Boak ers Remoye
Pro partie s
1 ^
Copy...
About..
JahowProftle s,
l
OK
Caneel
Hełp
Rysunek 28.2. Aplet Maił and Fax w Panelu sterowania w czasie konfigurowania profilu MAPI Podstawowe funkcje MAPI przedstawione zostały w tabeli 28.3. Funkcje te można przywoływać w aplikacji, albo bezpośrednio łącząc się z biblioteką mapi28.1ib, która pomoże połączyć się z biblioteką mapi28.dll, albo korzystając z funkcji LoadLibrary () i GetProc-Address () pozwalających dynamicznie ładować i odłączać bibliotekę DLL. Tabela 28.3. Podstawowe funkcje MAPI Funkcja Opis
MAPILogonO MAPILogoffO MAPISendMail() MAPISendDocuments() MAPIFindNext() MAPIReadMail() MAPISaveMail() MAPIDeleteMaiK) MAPIFreeBufferf) MAPIAddress() MAPIDetails() MAPIResolveName() Rejestruje sesję MAPI przez konfigurację profilu Wyrejestrowuje z sesji MAPI Wysyła wiadomość ze struktury MapiMessage Wysyła zestaw plików dokumentów po sprecyzowaniu ich nazw Znajduje pierwszą lub następną wiadomość zwracając jej identyfikator Wczytuje różne aspekty wiadomości do struktury MapiMessage Zachowuje nową wiadomość w wewnętrznej skrzynce dostarczanej przez dostawcę Usuwa wiadomość zdefiniowaną przez ostatnią funkcję MAPIFindNext () Zwalnia bufory wykorzystywane przez MAPI Wyświetla okno dialogowe z listą adresów umożliwiając użytkownikowi modyfikację zawartości Pokazuje informacje związane z określonym adresem Zmienia niezrozumiałą nazwę odbiorcy na adres pocztowy
f/MKŁ IAIV.t Więcej informacji na temat OLE i COM znaleźć można w rozdziale 25. Dodawanie programu pocztowego MAPI za pomocą kreatora AppWizard Na stronie czwartej kreatora AppWizard (rysunek 28.3) w sekcji What features would like to include? (Jakie dodatkowe możliwości chcesz dołączyć do aplikacji?) znajduje się opcja MAPI (Messaging API) pozwalająca dodać do aplikacji SDI lub MDI możliwości interfejsu MAPI. Opcja ta dodaje do menu File polecenie Send, a do klasy dokumentu następujące hasła mapy komunikatów: ON_COMMAND(ID_FILE_SEND_MAIL, OnFileSendMail) ON_UPDATE_COMMAND_UI(ID_FILE_SEND_MAIL, OnUpdateFileSendMail) MFC AppWizald - Step 4 ol G
IK Chck Baz ( M. ; r n. What fealur&s would you like lo include? | 17 iDockingjcoibal! l F Initia! status bar s ^ Pnff^a aftd ptint pfeview ; F' Conteiit- tereitiye Hglp '. P 30 controls ^ F MAP! (Messaging API) l F Windows Sockets How do you waht your toolbars lo look? y Hennal ^ Inteinet E^plofet ReBafS How many f ileś would yw like on your lecent f ile list? R:ri Advanced... |
|~ Cancel Rysunek 28.3. Dodawanie funkcji interfejsu MAPI do aplikacji SDI za pomocą kreatora AppWizard Jeśli zbudujemy i uruchomimy aplikację już w tym momencie, będzie ona zaopatrzona we wszystkie funkcje niezbędne do wysyłania poczty. Możemy kliknąć polecenie Send w menu File, aby przyzwać okno rejestracji MAPI. Po wybraniu profilu (lub skonfigurowaniu systemu pocztowego) pakiet pocztowy zostanie uruchomiony i będziemy mogli utworzyć i wysłać wiadomość e-mail. Szkielet aplikacji automatycznie serializuje bieżący dokument za pomocą funkcji OnSaveDocument () i dołącza go do nowego e-maila. Jeśli potrzebujemy bardziej rozbudowanych funkcji aplikacji pocztowej, musimy skorzystać z podstawowych funkcji MAPI albo sięgnąć do Extended MAPI. W kodzie z listingu 28.3. pokazujemy, jak za pomocą podstawowych funkcji MAPI implementowanych w aplikacji SDI bazującej na widoku Edit utworzyć klienta pocztowego, który będzie umożliwiał wysyłanie wiadomości za pośrednictwem polecenia Send
768_____________________________________Poznaj Visual C++ 6 menu File i odbieranie ich za pomocą polecenia Receive menu File. Odebrane wiadomości są następnie wyświetlane w widoku Edit. Przykładowy program ładuje plik biblioteki mapi28.dll dynamicznie w konstruktorze dokumentu, rejestruje nową sesję MAPI, a po wszystkim kończy sesję i zwalnia bibliotekę DLL w destruktorze. Najpierw tworzymy zwykły szkielet aplikacji SDI za pomocą kreatora AppWizard, włączając opcję, która umożliwia korzystanie z funkcji MAPI, w celu dodania do aplikacji polecenia Send. Na ostatniej stronie kreatora AppWizard wybieramy widok Edit jako widok bazowy. Za pomocą edytora zasobów dodajemy do menu File polecenie Receive. Przedstawiony niżej kod jest w większości dodawany do funkcji obsługi nowych poleceń w klasie dokumentu. Listing 28.3. LST32_3.CPP - wykorzystanie prostych funkcji MAPI do odczytywania wiadomości w aplikacji SDI korzystającej z MAPI 1 łinclude 2 3 HMODULE g_hMAPI; 4 LHAŃDLE g_hSession; 5 6 LPMAPILOGON g_lpfnLogon; 7 LPMAPILOGOFF g_lpfnLogoff; 8 LPMAPIFINDNEXT g_ipfnFindNext; 9 LPMAPIREADMAIL g_lpfnReadMail; 10 LPMAPIFREEBUFFER g_lpfnFreeBuffer; 11 12 CMailClientDoc::CMailClientDoc() 13 { 14 // ** Dynamicznie załaduj bibliotekę DLL i odpowiednie funkcje 15 g_hMAPI = LoadLibraryf"MAPI28.DLL"); 16 g_lpfnLogon = (LPMAPILOGON) 17 GetProcAddress(g_hMAPI,"MAPILogon"); O 18 g_lpfnLogoff = (LPMAPILOGOFF) 19 GetProcAddress(g_hMAPI,"MAPILogoff") ; 20 g_lpfnFindNext = (LPMAPIFINDNEXT) 21 GetProcAddress(g_hMAPI,"MAPIFindNext") ; 22 g_lpfnReadMail = (LPMAPIREADMAIL) 23 GetProcAddress(g_hMAPI,"MAPIReadMail") ; 24 g_lpfnFreeBuffer= (LPMAPIFREEBUFFER) 25 GetProcAddress(g_hMAPI,"MAPIFreeBuff er"); 26 27 (*g_lpfnLogon)(O,NULL,NULL, @ 28 MAPI_NEW_SESSION | MAPI_LOGON_UI,O,&g_hSession) ; 29 }
770 Poznaj Visual C++ 6 74 strMessage += strFmt; 75 strMessage += !pMessage->lpszNoteText; 76 pSeed = szSeedMessage; 77 stropy(p3eed,pMsg); 78 79 // ** Przypisz wiadomość widokowi Edit 80 pView->SetWindowText(strMessage) ; 81 82 // ** Zwolnij bufor 83 (*g_lpfnFreeBuffer)( (LPVOID)IpMessage) ; 84 } 85 } O Funkcje biblioteki DLL odnajdujemy ręcznie za pomocą funkcji GetProcAdress () @ Program rejestruje MAPI korzystając z domyślnego profilu Funkcja FindNext() odnajduje następną wiadomość w oparciu o wartość przesłaną przez wiadomość poprzednią O Funkcja ReadMail () wczytuje zawartość wiadomości do bufora MapiMessage W pierwszej linii listingu 28.3 załączamy nagłówek mapi.h, który przechowuje prototypy funkcji i używane znaczniki. Konstruktor dokumentu (linie 12-29) ładuje w linii 15 za pomocą funkcji LoadLibraryO plik rnapi28.dll. Następnie za pomocą funkcji Get-ProcAddress () odnajduje adresy funkcji biblioteki DLL zachowując je w globalnych wskaźnikach deklarowanych w liniach 6-10. Na koniec w linii 27 rejestruje nową sesję MAPI zachowując identyfikator sesji w zmiennej g_h3ession. Sprawdzanie wartości zwracanych przez funkcję GetProcAddress () Po udanym załadowaniu biblioteki DLL możemy odnajdywać jej funkcje za pomocą funkcji GetProcAddress (). Funkcja ta zwraca adres funkcji definiowanej jako łańcuch, pod którym występuje w bibliotece DLL. Identyfikator biblioteki DLL również należy przesłać funkcji GetProcAddress (). Jeśli funkcja GetProcAddress () odnajdzie właściwy adres, zostanie on zapisany we wskaźniku funkcji ipfnLogon. Jeśli nie, wskaźnikowi funkcji przypisana zostanie wartość NULL. Należy zawsze sprawdzać, czy funkcji GetProcAddress () udało się zdobyć odpowiedni wskaźnik funkcji. W przeciwnym wypadku program zawiesi się, gdy wyślemy go do zerowego adresu pamięci!
Interfejsy API i zestawy SDK 771 Funkcję obsługi polecenia menu OnFileReceive () dostarczającą aplikacji kod umożliwiający odczytywanie wiadomości należy dodać do dokumentu za pomocą kreatora CIassWizard. W liniach 41 i 42 odnajdujemy za jej pomocą widok Edit, którego identyfikator obsługi okna jest w linii 53 wykorzystywany w funkcji FindNext () do odnajdywania kolejnej oczekującej wiadomości. Bufor szSeedMessage dostarcza funkcji FindNext () informacji, dzięki której może ona odnaleźć następną wiadomość lub pierwszą, o ile wskaźnik pSeed ma wartość NULL. W linii 58 odczytywana jest zawartość wiadomości identyfikowanej przez wskaźnik IpMessage za pomocą odwołania do funkcji ReadMail (). Jeśli wiadomość uda się odczytać, zmiennej uIResult przypisywana jest wartość zero, a wskaźnikowi IpMessage struktura MapiMessage. Szczegółowe informacje na temat wiadomości będzie można następnie wydobywać ze struktury w oparciu o wskaźnik IpMessage (linie 65-75). Kiedy już sformatowany łańcuch strMessage, zostanie zbudowany z zapisanych w strukturze części możemy przypisać wiadomość widokowi korzystając z funkcji set-windowText () (linia 80). Następnie w linii 83 oczyszczamy strukturę za pomocą funkcji MAPiFreeBuffer () przyzywanej za pośrednictwem wskaźnika (zdefiniowanego w linii 24) g_lpfnFreeBuffer. W liniach 76-77 kopiujemy bieżącą wiadomość do bufora szSeedMessage, przygotowując się do kolejnego wezwania funkcji FindNext (), która pobierze następną wiadomość, gdy tylko użytkownik kliknie w menu polecenie Receive. Po zamknięciu dokumentu sesja pocztowa zostaje zakończona w linii 34, a biblioteka .dli jest w linii 35 odłączana za pomocą funkcji FreeLibrary (). Po zbudowaniu i uruchomieniu aplikacji będziemy mogli, tak jak to zostało pokazane na rysunku 28.4, wysyłać i odbierać pocztę za pomocą poleceń Send i Receive z menu File. Jeśli wiadomości do odebrania jest więcej, należy po prostu kolejny raz kuknąć Receive. Gdy będziemy uruchamiać aplikację po raz pierwszy, wyświetlone zostanie okno dialogowe rejestracji profilu pokazujące nasz domyślny profil pocztowy. Kliknięcie OK zatwierdzi profil przedstawiony w oknie.
File Edit "D j Cg| B j Yiew hełp
^om: Microsoft To: New Windows 95 Customer Subject Welcome! Welcome to Microsoft Exchange! Welcome to Microsoft Exchange and the worid ot e-mail. E-mail provides afast and efficientway of communicating with others. There arę many different kinds of e-mail systems. You can use Microsoft Exchange with these systems as a universal lnboxto: Rysunek 28.4. Odczytywanie wiadomości e-mail w widoku Edit aplikacji SDI za pomocą funkcji Simple MAPI
772_____________________________________Poznaj Visual C++6 Biblioteki multimedialne i interfejs MCI Pojawienie się oprogramowania multimedialnego zmieniło pokutujący do niedawna wizerunek komputera PC jako topornej maszyny biurowej. Obecnie na komputerze PC można odgrywać płyty kompaktowe, oglądać telewizję, uruchamiać efekty dźwiękowe i animacje. Dzięki bibliotekom multimedialnym i interfejsowi MCI (od ang. Media Con-trol Interface) możemy bez większych problemów dołączyć wszystkie te możliwości do naszej aplikacji. Interfejs MCI i związana z nim klasa obsługująca interfejs użytkownika MCIWnd pozwalają w prosty i jednolity sposób kontrolować nawet bardzo skomplikowany sprzęt i oprogramowanie multimedialne. Interfejs multimedialny MCI Interfejs MCI dostarcza dwóch systemów przesyłania poleceń (tekstowego i bazującego na strukturach) pozwalających kontrolować z poziomu aplikacji najróżniejsze urządzenia multimedialne. Oparty na tekście system łańcuchów poleceń (ang. Command Strings) pozwala łączyć ze sobą w łańcuchu polecenia sterujące, które są następnie przesyłane do interfejsu MCI za pomocą funkcji mciSendStringO. Tekstowy system definiowania poleceń jest prosty, łatwy w użyciu i co ważne przyjazny, gdy usuwamy błędy programu w procesie debugo- wania. Polecenia MCI Polecenia MCI są zaprojektowane w taki sposób, żeby były maksymalnie ogólne. Interfejs oferuje zestaw bazowych poleceń, które można stosować na każdym urządzeniu. Te bazowe polecenia umożliwiają otwieranie, zamykanie, odczytywanie statusu i możliwości urządzenia, ładowanie danych, zachowywanie danych, odtwarzanie zapisu, zatrzymywanie odtwarzania i nagrywania, a także odszukiwanie określonego momentu odtwarzanego zapisu dla dowolnego urządzenia medialnego. Możemy również definiować polecenia za pomocą zwykłych znaczników i struktur języka C++ w systemie komunikatów poleceń (ang. Command Messages), który wykorzystuje do przesyłania komunikatów do interfejsu MCI funkcję mciSendCommandO. Ten system jest odrobinę szybszy, ponieważ nie wymaga budowania łańcuchów. Jednak oba systemy są podobne i projektując aplikację możemy wybrać ten, który będzie wydawał nam się wygodniejszy. Pierwsze polecenie, które należy wysłać do interfejsu MCI, to polecenie otwarcia konkretnego urządzenia. Możemy to zrobić albo za pomocą funkcji mciSendStringO i łańcucha open, albo za pomocą funkcji mciSendCommandO i znacznika MCI_OPEN. Możemy otworzyć dowolne urządzenie należące do jednego z typów przedstawionych w tabeli 28.4, o ile oczywiście jest zainstalowane w naszym systemie. Niektóre z urządzeń
Interfejsy API i zestawy SDK 773 będą wymagały podłączenia specjalistycznego sprzętu, podczas gdy do innych, takich jak waveaudio czy digitalvideo, w systemie Windows zostało już standardowo zainstalowane odpowiednie oprogramowanie. Tabela 28.4. Typy urządzeń w interfejsie MCI Łańcuch Znacznik Opis urządzenia
waveaudio MCI_DEVTYPE_WAVEFORM_AUDIO sequencer MCI_DEVTYPE_SEQUENCER cdaudio MCI_DEVTYPE_CD_AUDIO dat MCI_DEVTYPE_DAT digitalvideo MCI_DEVTYPE_DIGITAL_VIDEO vcr MCI_DEVTYPE_VCR videodisc MCI_DEVTYPE_VIDEODISC mmmovie MCI_DEVTYPE_ANIMATION overlay MCI_DEVTYPE_OVERLAY scanner MCI_DEVTYPE_SCANNER other MCI DEVTYPE OTHER Nagrywa/odtwarza pliki dźwiękowe .wav Sekwenser MIDI Odtwarza płyty kompaktowe Odtwarza/zapisuje cyfrowe taśmy dźwiękowe Odgrywa cyfrowe pliki filmowe .avi Odtwarza/nagrywa kasety VCR Odtwarza wideodyski Odtwarza animacje Odtwarza w oknie analogowy zapis wideo Skanuje rysunki Urządzenia niezdefiniowane
Tak jak można się spodziewać, polecenie open wymaga paru dodatkowych parametrów, dlatego funkcji mciSendStringO należy po poleceniu open przesłać trzy (oddzielone spacjami) parametry przedstawione poniżej: open Jako parametr nazwa urządzenia można przesłać dowolną nazwę podaną w tabeli 28.4. Parametr znaczniki otwarcia może definiować zastępczą nazwę (alias identyfikujący później określony egzemplarz otwartego urządzenia) i różne specyficzne dla urządzenia opcje. Parametrowi znaczniki powiadamiające można przypisać albo wartość wait informującą, że polecenie powinno czekać na otwarcie urządzenia, albo notify polecające kontynuować wykonanie programu i powiadamiające go, kiedy urządzenie zostanie otwarte. Możemy również polecić funkcji mciSendStringO zwracać komunikat o statusie przesyłając jej bufor zwrotny (ang. retum buffer) wraz z jego rozmiarami. Jeśli chcemy otrzymywać komunikaty powiadamiające, możemy również przesłać identyfikator obsługi okna, które ma je otrzymywać.
774 Poznaj Visual C++ 6 Nazwy urządzeń MCI Interfejs MCI daje się swobodnie rozbudowywać. Nowe urządzenia MCI można do niego dodawać rejestrując odpowiednie sterowniki urządzeń. Odpowiednie hasła mówiące na temat rejestracji urządzeń multimedialnych w Windows 95 można znaleźć w pliku C:\Windows\System.ini w sekcji [mci]. W Windows NT z kolei odpowiednie informacje znajdziemy w folderze HKEY_LOCAL_MACHINE\SOFT- WARE\Microsoft\WindowsNT\CurrentVersion\#SYS...\MCI. Przykładowo, za pomocą przedstawionej niżej funkcji mciSendStringO możemy przesłać interfejsowi MCI łańcuch poleceń polecający mu otworzyć odtwarzacz płyt kompaktowych i w dalszej części programu opisywać go jako mycd: char szRetMsg[80] ; mciSendString("open cdaudio alias mycd wait", szRetMsg,sizeof(szRetMsg),m hWnd) ; Możliwości urządzeń MCI Możemy sprawdzić, czy dane urządzenie MCI oferuje konkretne możliwości (funkcje) korzystając z łańcucha capabilities. Przykładowo, aby sprawdzić, czy czytnik CD oferuje możliwości nagrywania, należy wpisać łańcuch Capabilities cdaudio can record i sprawdzić kod zwrotny informujący, czy tak jest w istocie. Odpowiedni znacznik polecenia to MCI_GETDEVCAPS. Przedstawione tu polecenie zaczeka aż odtwarzacz CD zostanie prawidłowo otwarty, jeśli wszystko pójdzie dobrze zwracając w buforze szRetMsg wartość "l". Jeśli odtwarzacz nie zostanie otwarty, w buforze zostanie zwrócona wartość "Q". Jeśli urządzenia nie uda się otworzyć, zwracana jest struktura MCIERROR, którą można przekształcić w łańcuch komunikatu za pomocą funkcji mciGetErrorString (). Ta sama operacja otwarcia urządzenia za pomocą funkcji mciSendCommand () wymaga przesłania czterech parametrów: identyfikatora urządzenia (nie wykorzystywanego przy otwieraniu) znacznika polecenia (MCI_OPEN) znaczników powiadamiających: albo MCI_WAIT, albo MCI_NOTIFY wskaźnika do struktury MCI_OPEN_PARAMS Funkcja zwróci następnie, podobnie jak to robiła funkcja mciSendString (), strukturę MCIERROR. Aby otworzyć odtwarzacz CD za pomocą funkcji mcisendCominandO, należy przesłać następujący komunikat polecenia:
Interfejsy API i zestawy SDK 775 MCI_OPEN_PARMS myCDOpen = {NULL,O,"cdaudio",NULL,NULL}; errOpen = mciSendCommand(NULL,MCI_OPEN, MCI_OPEN_TYPE|MCI_WAIT,(DWORD)SmyCDOpen) ; Funkcja mciSendCommand () wykorzystuje strukturę MCI_OPEN_PARAMS przesyłaną jako wskaźnik myCDOpen. Struktura ta definiowana jest w następujący sposób: typedef struct { DWORD dwCallback; MCIDEVICEID wDeviceID; LPCSTR !pstrDeviceType; LPCSTR IpstrElementName; LPCSTR IpstrAlias; } MCI_OPEN_PARMS; Struktura MCI_OPEN_PARAMS pozwala przesłać w zmiennej dwCaliBack identyfikator obsługi okna, któremu będą przesyłane komunikaty powiadamiające. Identyfikator otwartego urządzenia jest odsyłany w zmiennej wDeviceID, którą będziemy mogli następnie wykorzystać w kolejnych odwołaniach do tegoż urządzenia. Zmienna IpstrElementName służy do definiowania nazwy pliku, jeżeli otwieramy urządzenie razem z powiązanym z nim plikiem dźwiękowym .wav lub filmowym .avi. Możemy zapisać odpowiedni alias w zmiennej IpstrAlias, aczkolwiek ten krok nie jest niezbędny, jako że urządzenie możemy zawsze zidentyfikować za pomocą zmiennej zawierającej identyfikator urządzenia wDeviceID. Różnych struktur dla różnych komunikatów poleceń MC l_ jest dokładnie tyle, ile jest łańcuchów dla łańcuchów poleceń. Po otwarciu urządzenia możemy za pomocą następnych poleceń odgrywać, zapisywać, zamykać i wykonywać wiele innych poleceń multimedialnych. Część z licznej rzeszy możliwych poleceń została przedstawiona w tabeli 28.5. Każde z poleceń posiada wiele różnych parametrów, zdecydowanie zbyt wiele, abyśmy mogli je tutaj opisywać. Tabela 28.5. Niektóre polecenia MCI Komunikat Łańcuch Opis polecenia
MCI_OPEN MCI_CLOSE MCI_PLAY MCI_RECORD MCI_STOP MCIJ3EEK MCI PAUSE open close play record stop seek pause Otwiera urządzenie Zamyka urządzenie Odtwarza w urządzeniu Nagrywa w urządzeniu Zatrzymuje odgrywanie lub nagrywanie Odszukuje pozycję w materiale (filmie, utworze muzycznym) Pauzuje odgrywanie lub nagrywanie
776 Poznaj Visual C++ 6 MCI_RESUME resume Uruchamia ponownie po pauzie MCI_LOAD load Ładuje plik MCI_SAVE save Zachowuje w pliku MCI_STATUS status Pobiera informacje o statusie MCI_WINDOW window Wyświetla okno wideo dla zapisu filmowego MCI_WHERE where Definiuje pozycję okna Komunikaty powiadamiające MCI Jeśli w poleceniu MCI wykorzystamy znacznik wait lub MCI_WAIT, polecenie nie zwróci aplikacji kontroli, dopóki nie zostanie wykonane do końca. Taka procedura wykonywania poleceń jest bardzo praktyczna w niektórych sytuacjach, jednak jeśli przykładowo wydamy aplikacji odtwarzającej płyty kompaktowe polecenie odtworzenia zawartości kompaktu, aplikacja będzie czekać, dopóki polecenie odgrywania nie zostanie wykonane do końca. To może oznaczać, że na kolejną możliwość kontaktu z aplikacją użytkownik będzie musiał czekać prawie godzinę! Jeśli natomiast nie prześlemy żadnych znaczników powiadamiających ani polecających poleceniu odczekanie aż zadanie zostanie wykonane, polecenie natychmiast przekaże sterowanie z powrotem do aplikacji. Kolejnym rozwiązaniem jest przesłanie do polecenia znacznika MCI_NOTIFY nakazującego mu poinformować aplikację, gdy wykona już swoją funkcję. Aplikacja będzie mogła wtedy wyświetlić odpowiedni komunikat, odłączyć płytę kompaktową lub wykonać inną niezbędną w tym momencie akcję. Większość poleceń MCI może wysyłać komunikaty powiadamiające. Funkcje mciSetYeldProc() imciGetYeldProc() Jeśli polecimy poleceniu odczekiwać z przekazaniem kontroli aplikacji za pomocą znacznika wait, możemy chcieć, aby interfejs MCI przyzywał co pewien czas funkcje zwrotne wykonujące określone operacje lub testy. Możemy robić to za pomocą funkcji mciSetYieldProc (). Funkcja ta wymaga jako pierwszego parametru identyfikatora urządzenia, jako drugiego parametru adresu funkcji zwrotnej, a jako trzeciego parametru zmiennej typu DWORD przechowującej pewne definiowane przez użytkownika dane, które będą przesyłane funkcji zwrotnej. Jeśli funkcja wykona zadanie, zwraca wartość TRUE. Pokrewna jej funkcja mciGetyieldProc () służy do pobierania adresu innej działającej procedury tego typu w zdefiniowanym urządzeniu. Jeśli prześlemy do polecenia znacznik MCI_NOTIFY lub notify, polecenie zwróci aplikacji kontrolę natychmiast i prześle komunikat powiadamiający do pętli komunikatów (ang. message loop) okna, którego identyfikator obsługi przesłaliśmy definiując adresata komunikatu. Tym oknem może być widok, okno dialogowe lub dowolny inny obiekt klasy wywodzącej się z cwnd, który jest powiązany z działającym w programie oknem (możemy
Interfejsy API i zestawy SDK________________________________777 przesłać zmienną składową zawierającą identyfikator obsługi dowolnego legalnego obiektu CWnd). Funkcję obsługi komunikatu MM_MCINOTIFY możemy dodać do aplikacji MFC wpisując do mapy komunikatów Windows makroinstrukcję ON_MESSAGE: BEGIN_MESSAGE_MAP(CMciDlg, CDialog) ON_MESSAGE(MM_MCINOTIFY,OnMCINotify) END_MESSAGE_MAP() Teraz możemy dodać funkcję obsługi, której należy przesłać dwa parametry: pierwszy identyfikujący urządzenie wysyłające komunikat powiadamiający i drugi definiujący kod statusu zwracany przez polecenie. Funkcja ta powinna wyglądać mniej więcej tak: void CMciDlg::OnMCINotify(WPARAM wFlags,LPARAM !DevID) { AfxMessageBox ("Komunikat MCI: Wykonywanie polecenia zostało zakończone!") ; } Zestaw kodów wFlags informujących o statusie polecenia przedstawiliśmy w tabe 28.6. Zmienna l De vi D identyfikuje urządzenie, które wysyła komunikat powiadamiając; Jeśli korzystamy z łańcuchów poleceń i identyfikujemy urządzenia za pomocą aliasóv możemy zdobyć prawdziwy identyfikator urządzenia przyzywając funkcję mciDetDevice (), której przesyłamy alias jako parametr służący nam za nazwę urządzenia. Tabela 28.6. Kody statusu urządzenia w komunikatach powiadamiających Kod statusu polecenia Opis MC I_NOT l FYJSUCCE s S FUL Wykonywane polecenie zakończyło się sukcesem MC I_NOT l FY_FA l LURE w tracie wykonywania polecenia wystąpił błąd MCI_NOTIFY_ABORTED Komunikat powiadamiający poprzedniego polecenia został usunięty przez następne polecenie skierowane do tego samego urządzenia, niekompatybilne z poprzednim MCI_NOTIFY_SUPERESEDED Nowe polecenie zażądało komunikatu powiadamiającego od tego samego urządzenia. Polecenia są kompatybilne i stare polecenie zostało zastąpione przez nowe
778 _______________ Poznaj Visual C++ 6 Sprawdzanie kodów zwrotnych funkcji poleceń Należy zawsze sprawdzać kod zwracany przez funkcję, która domaga się komunikatu powiadamiającego. Jeśli funkcja polecenia zwróci kod informujący o tym, że nie udało jej się wykonać zadania, komunikat powiadamiający nie zostanie wysłany. Może to doprowadzić do tego, że program zawiesi się oczekując komunikatu powiadamiającego, który nigdy nie nadejdzie. Na listingu 28.4 prezentujemy obie formy wysyłania poleceń Command String i Command Message, które wykorzystywane są tam do odtworzenia pięciosekundowego fragmentu zapisu dźwiękowego z kompaktu (pomiędzy 12 a 17 sekundą trzeciej ścieżki utworu). Przedstawiony tam fragment kodu możemy dodać w dowolnym miejscu aplikacji MFC (w przypadku aplikacji opartej na oknie dialogowym fragment ten najlepiej umieścić W funkcji InitDialog () ). Aby załączyć do programu znaczniki i prototypy funkcji interfejsu MCI, należy wpisać w pliku nagłówka następująca dyrektywę i n cłu de: łinclude "mmsystem.h" Aplikacje korzystające z bibliotek MCI powinny łączyć się z plikiem biblioteki winmm.lib. Plik ten należy wpisać w liście Object/L;ibrary Modules na karcie Link okna dialogowego Project Settings. Listing 28.4. LST32_4.CPP - fragment kodu opartego na poleceniach MCI odgrywający pięć sekund muzyki z płyty kompaktowej 1 // Odegraj 5 sekund muzyki z płyty za pomocą łańcucha polecenia 2 char szRetMsg[80]; 3 char szErrorMessage[512]; 4 MCIERROR errOpen; 5 errOpen = mciSendString("open cdaudio alias mycd wait", O 6 szRetMsg,sizeof(szRetMsg),m hWnd); 7 if (szRetMsg[0]!='l') 8 { 9 // Wyświetl komunikat o błędzie podczas otwierania 10 mciGetErrorString(errOpen,szErrorMessage,512) ; 11 AfxMessageBox(szErrorMessage) ; 12 ) 13 else 14 { 15 // Zdefiniuj format wyświetlania informacji o czasie 16 mciSendString("set mycd time format tmsf", 17 szRetMsg,sizeof(szRetMsg),m_hWnd);
Interfejsy API i zestawy SDK 779
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 // Odegraj 5 sekund ze ścieżki 3 poczynając od sekundy 12 mciSendString( "play mycd from 3:0:12:0 to 3:0:17:0 wait", szRetMsg,sizeof(szRetMsg),m_hWnd) ; // ** Zamknij urządzenie mciSendString("close mycd", szRetMsg,sizeof(szRetMsg),m hWnd) ; // Odegraj 5 sekund muzyki z płyty za pomocą, komunikatu polecenia MCI_OPEN_PARMS myCDOpen=(NULL,O,"cdaudio",NULL,NULL}; errOpen = mciSendCommand(NULL,MCI_OPEN, MCI_OPEN_TYPE|MCI_WAIT,(DWORD)SmyCDOpen) ; if (errOpen) { // Wyświetl komunikat o błędzie podczas otwierania mciGetErrorString(errOpen,szErrorMessage,512) ; AfxMessageBox(szErrorMessage) ; } else { // Zdefiniuj format wyświetlania informacji o czasie MCI_SET_PARMS setParams = (NULL, MCI_FORMAT_TMSF,O} ; errOpen=inciSendCommand (myCDOpen.wDeviceID,MCI SET, MCI_SET_TIME_FORMAT,(DWORD)&setParams) ; // Odegraj 5 sekund ze ścieżki 3 poczynając od sekundy 12 MCI_PLAY_PARMS playParams = {NULL, MCI_MAKE_TMSF(3, O, 12, 0), MCI_MAKE_TMSF(3, O, 17, 0)}; mciSendCommand(myCDOpen.wDeviceID,MCI_PLAY, MCI_FROM | MCI_TO l MCI_WAIT, (DWORD)SplayParams) ; // ** Zamknij urządzenie MCI_GENERIC_PARMS genParams = (NULL); mciSendCommand(myCDOpen.wDeviceI D,MCI_CLOSE, NULL,(DWORD)SgenParams) ;
O Łańcuch polecenia open definiuje alias urządzenia wykorzystywany przez następne polecenia.
780 Poznaj Visual C++ 6 @ Znacznik MCI_OPEN komunikatu polecenia odpowiada łańcuchowi open. @ Wartości czasu są dla komunikatów poleceń definiowane za pomocą makroinstrukcji MCI_MAKE_TMSF. Na początku listingu 28.4 w linii 5 przyzywana jest funkcja mciSendString () otwierająca odtwarzacz płyt kompaktowych i nadająca mu alias mycd. Jeśli urządzenia nie uda się otworzyć w liniach 10 i 11, wyświetlany jest odpowiedni komunikat o błędzie. Korzystamy w tym celu z pomocy funkcji mciGetErrorString (), która przekształca kod błędu w dający się zrozumieć łańcuch tekstu. W linii 16 definiujemy format wyświetlania czasu time format jako tmsf informując MCI, jak ma traktować wysyłane później polecenia ustalające nową pozycję po s iti on w odtwarzanym utworze (definiowaną w minutach i sekundach). W linii 19 rozpoczynamy odgrywanie płyty kompaktowej za pomocą polecenia play i definiujemy opcjonalne polecenia from i to pozwalające odegrać fragment utworu między 12 a 17 sekundą trzeciej ścieżki utworu. Znacznik wait poleca funkcji zwrócić aplikacji kontrolę dopiero po odegraniu całego fragmentu. W linii 23 zamykamy urządzenie. Ten sam fragment ścieżki odgrywany jest za pomocą komunikatów poleceń. Urządzenie jest otwierane za pomocą znacznika MCI_OPEN w linii 29. Format czasu definiowany jest za pomocą komunikatu MCI_SET poprzez przesłanie znacznika MCI_SET_TIME_ FORMAT i przypisanie formatu tmsf zmiennej składowej MCI_FORMAT_TMSF struktury setParams. W linii 49 odgrywany jest ten sam co poprzednio fragment ścieżki za pomocą makroinstrukcji MCI_MAKE_TMSF wykorzystywanej do definiowania momentu początkowego i momentu końcowego odgrywanego fragmentu, które w tym celu zapisujemy w strukturze MCI_PLAY_PARAMS. Na koniec w linii 54 urządzenie jest zamykane za pomocą komunikatu MCI_CLOSE. Dodawanie do aplikacji okna MCI Jak dotąd opisywaliśmy interfejs MCI od strony kodu programu, nie wspominając ani słowem o interfejsie użytkownika. Istnieje jednak standardowy interfejs MCI implementowany w klasie MCiWnd. Klasa MCiWnd oferuje wysoko wyspecjalizowaną kontrolkę pozwalającą użytkownikowi na pełną kontrolę nad funkcjami wykorzystywanego urządzenia multimedialnego. Interfejs kontrolki różnić się będzie znacznie w zależności od urządzenia, które obsługuje. Dla urządzenia digi tal video otrzymamy okno i kontrolki umożliwiające odtwarzanie animacji, natomiast dla urządzenia waveaudio kontrolki umożliwiające odgrywanie, przewijanie i zatrzymywanie muzyki zapisanej w pliku .wav.
Interfejsy API i zestawy SDK 781 Okno MCiwnd dodajemy do aplikacji w sposób bardzo prosty. Przykładowo, wpisując na końcu funkcji initDialogO jedno tylko odwołanie do funkcji MCiWndCreate () dodajemy do okna dialogowego kontrolkę obsługującą urządzenie digitalvideo pozwalającą odtwarzać pliki .avi (rysunek 28.5).
OK Cancel
Rysunek 28.5. Okno MCI dołączone do standardowego okna dialogowego za pomocą funkcji MCiWndCreate() Przyzwanie funkcji MCiWndCreate () tworzy okno MCiWnd i inicjuje gotowy do odtworzenia plik .avi zdefiniowany na końcu odwołania: HWND hMCI = MCiWndCreate(m_hWnd, AfxGetApp()->m_hlnstance, MCIWNDF_SHOWALL, "C:\\Vidclip.avi:") ; Pierwszy parametr m_hWnd jest identyfikatorem obsługi okna rodzica (okna dialogo- wego). Drugi jest identyfikatorem egzemplarza obiektu aplikacji. Trzeci parametr pozwala zdefiniować jeden z licznych stylów okna przedstawionych w tabeli 28.7. Ostatni parametr pozwala zdefiniować plik o rozpoznawanym przez MCI rozszerzeniu lub urządzenie MCI, które chcemy otworzyć. Typ wyświetlonej kontro Iki będzie odpowiadać zdefiniowanemu urządzeniu lub rozszerzeniu. Jeśli urządzenie zostanie otwarte, zwracany jest odpowiedni identyfikator obsługi okna MCI (zapisywany w zmiennej hMCi). Jeśli nie, zmiennej hMCI przypisywana jest wartość NULL. Tabela 28.7. Znaczniki funkcji MCiWndCreate Znacznik Opis efektu
MCIWNDF_SHOWALL MCIWNDF_SHOWNAME MCIWNDF SHOWMODE Okno korzysta z wszystkich znaczników stylów typu SHOW Wyświetla nazwę pliku lub urządzenia na pasku tytułowym oknaMCIWnd Wyświetla na pasku tytułowym bieżący tryb urządzenia: odtwarzanie, nagrywanie itp.
782 Poznaj Visual C++ 6
MCIWNDF_SHOWPOS MCIWNDF_RECORD MCIWNDF_NOAUTOSIZEWINDOW MCIWNDF_NOAUTOSIZEMOVIE MCIWNDF_NOERRORDLG MCIWNDF_NOMENU MCIWNDF_NOOPEN MCIWNDF_NOPLAYBAR MCIWNDF_MOTIFYALL MCIWNDF_NO'TIFYMODE MCIWNDF_NOTIFYPLAY [!] MCIWNDF_NOTIFYMEDIA MCIWNDF_NOTIFYSIZE MCIWNDF NOTIFYERROR Wyświetla na pasku tytułowym bieżącą pozycję w odtwarzanym pliku Jeśli urządzenie pozwala na nagrywanie, dodaje odpowiedni przycisk Nie zmienia automatycznie wymiarów okna, gdy zmienią się wymiary obrazu Nie rozszerza rozmiarów obrazu do rozmiarów okna jeśli wymiary okna zostaną zmienione Blokuje wyświetlanie komunikatów o błędach interfejsu MCI Usuwa przycisk rozwijanego menu Uniemożliwia użytkownikowi otwieranie innych plików Ukrywa wszystkie, kontro Iki użytkownika - wszystkim zarządza program Przesyła oknu rodzica wszystkie komunikaty Przesyła oknu rodzica wszystkie komunikaty o zmianie trybu urządzenia Przesyła oknu rodzica komunikaty o pozycji w odtwarzanym mateńale Przesyła oknu rodzica komunikaty o zmianie nazwy pliku lub urządzenia Przesyła oknu rodzica komunikaty o zmianie wymiarów oknaMCIWnd Przesyła oknu rodzica komunikaty o błędach MCI
Zmienianie stylów okna MCI Style już istniejącego okna MCI możemy zmieniać za pomocą funkcji MCiWndChan-geStylesf). Funkcja ta wymaga jako parametrów: identyfikatora obsługi okna MCI, maski bitowej wskazującej zmieniane style i zmiennej z nowym zestawem ustawień dla okna. Powiązana z nią makroinstrukcja MCiwndGetStyles () zwraca bieżące ustawienia znaczników dla zdefiniowanego okna. Gdy okno MCiwnd jest otwarte, możemy przesyłać mu odpowiednie komunikaty kon- trolek za pomocą specjalnie zdefiniowanych makroinstrukcji. Makroinstrukcje te przesyłają komunikaty takie jak MCI_PLAY i inne odpowiadające poleceniom wymienionym w
Interfejsy API i zestawy SDK 783 tabeli 28.5. Makroinstrukcji związanych z komunikatami okna MCiWnd jest bardzo wiele, te które odpowiadają poleceniom z tabeli 28.5, przedstawiliśmy w tabeli 28.8. Tabela 28.8. Makroinstrukcje okna MCiWnd Makroinstrukcja Komunikat Opis MciWndOpenDialog
MCIWNDM OPEN
Otwiera nowy plik lub urządzenie MCI
MciWndOpen
- l
Otwiera nowy plik lub urządzenie MCI
MciWndClose
MCI CLOSE
Zamyka plik lub urządzenie MCI
MciWndPlay
MCI PLAY
Odgrywa plik lub urządzenie
MciWndRecord
MCIRECORD
Nagrywa w urządzeniu
MciWndStop
MCI STOP
Zatrzymuje odtwarzanie lub nagrywanie
MciWndSeek
MCISEEK
Odnajduje pozycję
MciWndPause
MCI PAUSE
Pauzuje odgrywanie
MciWndResume
MCIRESUME
Uruchamia po pauzie
MciWndPutDest
MCI PUT DEST
Ustala pozycję okna wyświetlającego film lub
animację
MciWndPlayFromTo MCI_SEEK, MCIWNDM PLAYTO Odtwarza od miejsca do miejsca najpierw wysyłając komunikat odszukujący pozycję, a potem odgrywając materiał do ustalonej pozycji
Ustalanie bieżącego trybu okna Może się zdarzyć, że po przesłaniu do okna MCI polecenia będziemy chcieli sprawdzić jego bieżący tryb działania (czy odgrywa materiał, czy nagrywa itp.). Służy do tego instrukcja MCiWndGetMode (). Musimy jej przesłać identyfikator obsługi okna oraz wskaźnik do bufora i rozmiary bufora, który będzie przechowywał łańcuch lub strukturę zawierającą informacje o bieżącym trybie. Możliwe tryby okna to: MCI_MODE_NOT_READY, MCI_MODE_OPEN, MCI_MODE_PLAY, MCI_MODE_RECORD, MCI MODĘ PAUSE,MCI MODĘ SEEK,MCI MODĘ STOP. Makroinstrukcje te wymagają również przesłania identyfikatora obsługi okna MCI i w zależności od potrzeb jeszcze kilku innych parametrów. Przykładowo, aby odegrać plik .avi od pozycji pliku l do pozycji 5, należy wpisać: MCIWndPlayFromTo(hMCI,l,5) ;
784 Poznaj Visual C++ 6 Okno MCI możemy zniszczyć korzystając z makroinstrukcji MdWndDestroy i iden- tyfikatora obsługi hMCI: MCIWndDestroy (hMCI) ; Przesyłamy w ten sposób po prostu zamykający okno komunikat WM_CLOSE. Przedstawione tutaj makroinstrukcje możemy wykorzystywać w dowolnej aplikacji oferującej nam legalny identyfikator obsługi okna rodzica. Aby dodać do programu definicje makroinstrukcji i prototypy funkcji, należy załączyć następujący plik nagłówka: łinclude "vfw.h" Aby dodać do programu funkcje API okna MCIWnd, należy do listy Object/Library Modliłeś na karcie Link okna dialogowego Project Settings dodać plik biblioteki vfw28.1ib. Okno to wywołujemy wybierając w menu Project polecenie Settings. PATRZ TAKŻE Okno MCIWnd jako kontrolka ActiveX opisane zostało w rozdziale 9.
Słownik
ActiveX patrz OLE adres bazowy (ang. base address) - adres definiujący początek obszaru zajmo- wanego przez obiekt w pamięci. adres uzupełniający (ang. offset address) również adres przesunięcia; adres wykorzystywany w połączeniu z ad- resem bazowym identyfikujący element składowy (zmienną, funkcję) określonego przez adres bazowy eg- zemplarza obiektu. aktywny (ang. active) - jeden z dwu moż- liwych stanów okna, drugim jest stan nieaktywny. anizotropiczny (ang. anisotrophic) obiekt, element, rzecz mająca w jednym kie- runku inne właściwości niż w pozo- stałych kierunkach. ANSI od American National Standards Institute (amerykańska instytucja normalizacyjna) - w żargonie kom- puterowym standard znaków teksto- wych, w którym każdemu znakowi przypisana jest inna liczba o rozmiarach jednego bajtu. API patrz interfejs API aplikacja MDI - aplikacja oparta na ar- chitekturze dokument/widok pozwa- lająca na jednoczesne otwieranie więcej niż jednego dokumentu, w odróżnieniu od aplikacji SDI, w której jednorazowo otwarty może być tylko jeden dokument.
aplikacja SDI - aplikacja oparta na archi- tekturze dokument/widok pozwalająca na jednoczesne otwieranie, w od- różnieniu od aplikacji MDI, tylko jednego dokumentu, który jednak może być przedstawiany jednocześnie w kilku widokach. AppWizard - kreator, narzędzie progra- mowania umożliwiające automatyczne tworzenie szkieletu projektu Visual C++. architektura dokument/widok - podsta- wowa zasada konstrukcji aplikacji. Patrz również aplikacja MDI i aplikacja SDI. archiwum, plik archiwum (ang. archiye) - plik zawierający dane potrzebne apli- kacji w postaci serializowanej lub w postaci pliku dyskowego. asercja (ang. asertions) - test wykorzysty- wany podczas debugowania programu ostrzegający programistę, że warunek zapisany w makroinstrukcji ASSERT nie został spełniony. Instrukcji ASSERT używa się zazwyczaj do testowania, czy wstępne warunki funkcji są poprawne. aspect ratio - angielski termin oznaczający stosunek długości do szerokości. Również parametr opisujący stosunek długości do szerokości pikseli wy- świetlanych na ekranie monitora. Ma istotne znaczenie, gdy zależy nam na unikaniu zniekształceń obrazu.