Czytając ten rozdział, zapoznasz się ze sposobami konstrukcji algorytmów, realizujących transmisję szeregową w środowisku Windows, które charakteryzuje się pewnymi cechami nie mającymi odpowiedników w MS DOS. Poznanie i umiejętne wykorzystanie tych cech sprawi, iż problem obsługi interfejsów szeregowych z poziomu Windows — uważany powszechnie za trudny — przestanie być dla nas tajemnicą. Pokażemy, w jaki sposób należy tworzyć aplikacje, służące do programowej obsługi łącza szeregowego RS 232C zarówno w C++Builderze jak i w Delphi. W moim odczuciu wśród programistów istnieje zauważalny podział na osoby programujące głównie w Delphi oraz na preferujące Buildera lub ogólnie C++ do Windows. Jednak zdaniem wielu osób uniwersalność jest jedną z tych zalet, jakie powinny charakteryzować programistę. W rozdziale tym przybliżymy Czytelnikowi podobieństwa i różnice w sposobie konstrukcji algorytmów realizujących transmisję szeregową, pisanych w Delphi oraz Builderze.
W dalszej części książki spotkamy się z typami danych, których poznanie i zrozumienie ma kluczowe znaczenie w projektowaniu aplikacji, obsługujących urządzenia zewnętrzne. Zacznijmy od ich przypomnienia. W tabeli poniżej przedstawiono porównanie podstawowych typów zmiennych wykorzystywanych w kompilatorach, które będą dla nas istotne. Większości z nich można używać zamiennie, pisząc zarówno w Delphi jak i w C++Builderze.
Tabela 5.1.
Typy zmiennych stosowanych w Delphi oraz w C++ Builderze
Delphi Rozmiar / Znak / Typ C++ Builder
w bajtach + / -
ShortInt 1 integer signed char
SmallInt 2 integer short
LongInt 4 integer
Byte 1 bez znaku integer unsigned char
Word 2 bez znaku integer unsigned short
Integer 4 integer int
Cardinal 4 bez znaku integer unsigned int
Boolean 1 true/false bool
ByteBool true/false unsigned char
1 bez znaku integer
WordBool true/false unsigned short
2 bez znaku integer
LongBool true/false
4 bez znaku integer
AnsiChar 1 1 znak ANSI character char
WideChar 2 1 znak Unicode character wchar_t
Char 1 bez znaku character char
AnsiString ≈3GB ANSIChar AnsiString AnsiString
String[n] n = 1..255 ANSIChar string SmallString<n>
bajtów
ShortString 255 bajtów ANSIChar string SmallString<255>
String 255 lub ≈3GB ANSIChar AnsiString AnsiString
Single 4 floating point float
number
(liczba zmiennoprzecinkowa)
Double 8 floating point double
Extended 10 floating point long double
Real 4 floating point double
Pointer 4 generic pointer void *
(wskaźnik ogólny, adresowy)
PChar 4 bez znaku pointer to characters unsigned char *
(daleki wskaźnik na C-łańcuch)
PAnsiChar 4 bez znaku pointer to ANSIChar unsigned char *
Comp 8 floating point number Comp
Konstruując nasze programy, będziemy starali się jak najszerzej wykorzystywać standardowe zasoby Windows, w szczególności tzw. interfejs programisty Win 32 API (ang. Application Programming Interface). Jego umiejętne wykorzystanie umożliwi naszym aplikacjom błyskawiczne skonfigurowanie i uzyskanie dostępu do portu komunikacyjnego. Błędnym jest twierdzenie, że sama, nawet bardzo dobra znajomość języka programowania wystarczy, żeby stworzyć poprawnie działający w Windows program. Otóż musimy zdawać sobie sprawę z faktu, o którym często się zapomina — niemożliwe jest napisanie udanej aplikacji, mającej pracować w pewnym środowisku (czytaj — systemie operacyjnym), bez znajomości danego środowiska. Wiele już zostało powiedziane na temat dobrych i złych stron Windows, należy jednak pamiętać, że daje on nam swoją wizytówkę, ofertę współpracy, czyli API. Już nie wystarczy umiejętność wykorzystywania ulubionego kompilatora. Zasoby Delphi czy Buildera połączymy z zasobami systemu operacyjnego, a spoiwem tym będzie właśnie uniwersalne Win32 API. Istnieje wiele warstw API, używanych w zależności od potrzeb. W tym i dalszych rozdziałach zajmiemy się szeroko rozumianą warstwą komunikacyjną.
Win32 API korzysta ze specjalnego systemu nazewnictwa zmiennych, z tzw. notacji węgierskiej wprowadzonej przez Węgra Karoja Szimoni’ego. Zgodnie z nią do rdzenia nazwy zadeklarowanej zmiennej dodaje się przedrostek (ang. prefix). Chociaż istnieją pod tym względem pewne rozbieżności pomiędzy nazewnictwem Microsoftu i Borlanda, to jednak zapis taki bardzo ułatwia szybkie ustalenie roli zmiennej w programie oraz jej typ. W następnych rozdziałach będziemy starali się — wszędzie gdzie jest to możliwe — zachowywać oryginalne, samokomentujące się nazewnictwo Win32 API (większość nazw API będziemy traktować jako nazwy własne). Z doświadczenia wiem, że stosowanie takiej konwencji bardzo pomaga w studiowaniu plików pomocy[1]. Oczywiście, moglibyśmy silić się na oryginalność, wprowadzając własne zmienne, zrozumiałe tylko dla piszącego dany program — wówczas przykłady musiałyby być zapisane jako wręcz humorystyczna mieszanina języków polskiego i angielskiego. Trzeba też przyznać, że byłby to bardzo skuteczny sposób zaciemnienia obrazu API. Zrozumienie znaczenia nazw tam stosowanych okaże się w przyszłości niezwykle cenne, gdyż API można czytać jak książkę. Aby pomóc Czytelnikom, którzy nie zetknęli się dotąd z tymi pojęciami, poniżej przedstawiono ogólne zasady tworzenia niektórych przedrostków.
Tabela 5.2.
Ogólne zasady tworzenia przedrostków wg notacji węgierskiej.
Przedrostek Skrót angielski Znaczenie
a array tablica
b bool zmienna logiczna TRUE lub FALSE (1 lub 0)
by byte unsigned char znak (bajt)
ch char znak
cb count of bytes liczba bajtów
dw double word podwójne słowo
evt event zdarzenie
f flag znacznik
fdw flag of double word zacznik typu dw
fn function funkcja
h handle identyfikator (uchwyt)
i integer typ całkowity 4-bajtowy
id (ID) identification identyfikacja
in input wejście, dane wejściowe
l long int typ całkowity długi 4-bajtowy
lp long pointer wskaźnik typu long int
lpc long pointer to C- string wskaźnik typu long int do C-łańcucha
lpfdw long pointer to flag of dw wskaźnik typu lp do znacznika typu double word
lpfn long pointer to function wskaźnik typu lp do funkcji
n short or int typ krótki lub całkowity
np near pointer bliski wskaźnik
(w środowisku 32-bitowym to samo co lp)
out output wyjście, dane wyjściowe (przetworzone)
p pointer wskaźnik
pfn pointer to function wskaźnik do funkcji
que queue kolejka, bufor danych
s (sz) string łańcuch znaków
st struct struktura
t type typ
u unsigned bez znaku
w (word) unsigned int słowo
wc WCHAR znak zgodny z Unicode
PRZYPOMNIJMY
Fizyczny adres pamięci w PC zaopatrzonym w procesor Pentium może składać się z segmentu i offsetu (tzw. przemieszczenia), określając tym samym możliwość istnienia wskaźników bliskich (ang. near pointer) i dalekich (ang. far pointer). Bliski wskaźnik, stanowiąc 32-bitowy adres efektywny, jest offsetem wewnątrz segmentu. Daleki wskaźnik stanowi 48-bitowy adres zawierając 16-bitowy selektor segmentu i 32-bitowy offset. Dalekie wskaźniki używane są w modelu segmentowym pamięci. Stosując metody zarządzania pamięcią, programy nie adresują w sposób bezpośredni pamięci fizycznej, adresują natomiast jej model. Win32 korzysta z płaskiego modelu pamięci (ang. flat memory model). W modelu tym segmenty mogą całkowicie pokrywać się z zakresami pamięci fizycznej lub z tymi jej adresami, które można mapować (odwzorowywać) do pamięci fizycznej. Wszystkie rejestry segmentowe podczas działania programu zawierają tą samą wartość – selektor odpowiedniego segmentu. W związku z tym wskaźniki stanowią 32-bitowe offsety w 4 GB segmencie (232 @ 4GB).
Windows oferuje nam ponadto kilka typów danych, z których część tylko nieznacznie różni się sposobem zapisu w implementacjach Delphi i Buildera. Typy te mają najczęściej postać struktury lub klasy i są bardzo często wykorzystywane w warstwie komunikacyjnej programów. Tabela 5.3 przedstawia przykładowe, ogólnie stosowane sposoby ich deklaracji.
Tabela 5.3. Typowe sposoby deklaracji stosowane w programach komunikacyjnych
Delphi Znaczenie C++ Builder
dcb : TDCB; struktura kontroli portu DCB dcb;
hCommDev : THANDLE; 32-bitowy identyfikator HANDLE hCommDev;
portu — jednorazowo
nadana wartość
identyfikująca port
Stat : TCOMSTAT; struktura zawierająca COMSTAT Stat;
dodatkowe informacje
o porcie szeregowym
Overlapped : TOVERLAPPED; struktura zawierająca OVERLAPPED Overlapped;
informacje używane przy
transmisji asynchronicznej
...
buszek3320