Programowniae windows petzold Petzold13


Rozdział 12
Schowek
Schowek Microsoft Windows pozwala przenosić dane między programami. Jest
to stosunkowo prosty mechanizm, nie wymagający dużego wysiłku przy two-
rzeniu programów, które pobierają dane ze Schowka i umieszczają je w nim.
Z Windows 98 i Windows NT dostarczany jest program podgląd Schowka, poka-
zujący aktualną zawartość Schowka.
Wiele programów, działających na dokumentach lub innych danych, zawiera
menu Edycja (Edit) z opcjami Wytnij (Cut), Kopiuj (Copy) i Wklej (Paste). Gdy
użytkownik wybiera Wytruj lub Kopiuj, program przenosi swoje dane do Schowka.
Dane mają określony format, zwykle jest to tekst, bitmapa (prostokątna tablica
bitów odpowiadających pikselom wyświetlanego obszaru) lub metaplik (binar-
ny zbiór poleceń rysowania). Gdy użytkownik wybierze w menu opcję Wklej,
program sprawdza, czy w Schowku znajdują się jakieś dane, których może użyć,
i jeśli tak jest, zabiera je do siebie.
Programy nie powinny przenosić danych do lub ze Schowka bez wyraź,nego po-
lecenia użytkownika. Na przykład użytkownik, który wykona w programie ope-
rację Wytnij lub Kopiuj ([Ctrl+X] lub [Ctrl+C]) powinien mieć pewność, że dane
pozostaną w Schowku aż do momentu, gdy uruchomi następną operację Wytnij
lub Kopiuj.
Jak zapewne pamiętasz, menu Edit zostało zaimplementowane w wersjach pro-
gramu POPPAD z rozdziałów 10 i 11. Użyto tam jednak zwykłego wysyłania
komunikatów do kontrolki edycji. W większości przypadków nie jest to wystar-
czające - zamiast tego musisz sam wywołać funkcje przemieszczania danych
Schowka.
W tym rozdziale skoncentrujemy się na przenoseniu tekstu do i ze Schowka.
W kolejnych rozdziałach zobaczysz, jak używa się go z bitmapami (rozdziały 14,
15 i 16) i metaplikami (rozdział 18).
Proste zastosowanie Schowka
Zaczniemy od kodu umożliwiającego przeniesienie danych do Schowka (Cut i Co-
py) i uzyskanie dostępu do danych umieszczonych w nim wcześniej (Paste).
Standardowe formaty danych Schowka
Windows obsługuje różne predefiniowane formaty danych Schowka. Ich identy-
fikatory rozpoczynają się przedrostkiem CD i zdefiniowane są w pliku WINU-
SEft.H.
514 Część I: Podstawy
Poniżej wymienione są trzy typy danych tekstowych, które można przechowy-
wać w Schowku, oraz dodatkowy typ ustawień lokalnych.
ł CF TEXT Ciąg znaków z zestawu ANSI zakończony znakiem NULL i za-
wierający na końcu każdego wiersza znak powrotu karetki i nowego wiersza.
Dane, które mają być przeniesione do Schowka, zapisane są w bloku pamięci
- przenoszony jest uchwyt do tego bloku. (Wkrótce omówimy ten mechanizm).
Blok pamięci staje się własnością Schowka - program, który utworzył ten blok,
nie powinien go więcej używać.
ł CF OEMTEXT Blok pamięci zawierający dane tekstowe (podobny do
CF TEXT), ale używający zestawu znaków OEM. Programy Windows zwy-
kle nie muszą używać tego typu. Staje się on istotny przy wykorzystaniu
Schowka z programami MS-DOS uruchomionymi w oknie.
ł CF-UNICODETEXT Blok pamięci zawierający tekst Unicode. Podobnie jak w
CF TEXT każdy wiersz zakończony jest znakiem powrotu karetki i nowego
wiersza, a znak NULL (dwa bajty zerowe) wskazuje koniec danych. CF UNI-
CODETEXT jest obsługiwany tylko w Windows NT.
ł CF LOCALE Uchwyt do identyfikatora ustawień lokalnych związanych z tek-
stem Schowka.
Dostępne są dwa dodatkowe formaty Schowka, koncepcyjnie zgodne z forma-
tem CF TEXT (tzn. opierają się na tekście), ale ich dane nie są zakończone zna-
kiem NULL - formaty te definiują własny koniec danych. Dzisiaj są one rzadko
używane:
ł CF SYLK Blok pamięci zawierający dane w formacie Symbolic Link Microso-
ftu. Format ten jest używany do wymiany danych między programami Excel, Pr
Chart i Multiplan Microsoftu. Jest to format ASCII, w którym każdy wiersz
zakończony jest znakiem powrotu karetki i nowego wiersza.
ł CF DIF Blok pamięci zawierający dane w formacie Data Interchange Format
(DIF). Jest to format zalecany przez Software Arts do używania przy przeno-
szeniu danych do arkusza kalkulacyjnego VisiCalc, również oparty na ASCII
z wierszami zakończonymi znakiem powrotu karetki i nowego wiersza.
Następujące formaty Schowka używane są z bitmapami, które są tablicami bi-
tów odpowiadających pikselom na urządzeniu wyjściowym. Bitrnapy i związa-
ne z nimi formaty Schowka omówione zostaną dokładniej w rozdziałach 14 i 15.
ł CF BITMAP Bitmapa zależna od urządzenia. Mapa ta jest przenoszona do
Schowka za pomocą jej uchwytu. Program, po przekazaniu uchwytu bitmapy
do Schowka, nie powinien dalej go używać.
, hGl
CF DIB Blok pamięci definiujący bitmapę niezależną od urządzenia, tak jak
opisano to w rozdziale 15. Blok pamięci zaczyna się od struktury informacyj-
nej bitmapy, po której następuje opcjonalna tabela kolorów i bity.
ł CF PALETTE Uchwyt palety kolorów. Typ używany głównie w połączeniu z
CD DIB do definiowania palety kolorów używanej przez bitmapę niezależną
od urządzenia.
Rozdział 12: Schowek 515
Dane bitmapy można także przechowywać w formacie standardu przemysłowe-
go TIFF:
ł CF TIFF Blok pamięci zawierający dane typu Tag Image File Format (TIFF).
Jest to format zaprojektowany przez firmę Microsoft, Aldus Corporation i Hew-
lett-Packard oraz kilku innych wytwórców sprzętu komputerowego. Format
ten opisany jest na stronie internetowej Hewletta-Packarda.
Dostępne są także dwa formaty metaplików, które omówię dokładniej w rozdziale
18. Metaplik jest zbiorem poleceń rysowania przechowanych w formacie binar-
nym:
ł CF METAFILEPICT - obraz typu metaplik oparty na starym formacie obsłu-
gi metaplików w Windows
ł CF ENHMETAFILE - uchwyt rozszerzonego metapliku obshzgiwanego w 32-
bitowych wersjach Windows.
Istnieje także kilka innych formatów Schowka przeznaczonych do różnych za-
stosowań:
ł CF PENDATA - używany w połączeniu z programami obsługującymi pióro
świetlne w Windows
ł CF WAVE - plik dźwiękowy (wave)
ł CF_RIFF - dane multimedialne w formacie Resource Interchange File Format
ł CF HDROP - lista plików używana w połączeniu z programami obshzgują-
cymi funkcje przeciągnij i upuść (ang. drag and drop).
Przydzielanie pamięci
Gdy program przenosi coś do Schowka, musi zarezerwować blok pamięci i prze-
kazać go Schowkowi. Gdy potrzebowaliśmy przydzielić pamięć we wcześniej-
szych programach w tej książce, używaliśmy po prostu funkcji malloc, która do-
starczana jest w standardowej bibliotece C. Ponieważ jednak bloki pamięci prze-
chowywane w Schowku są wspólnie użytkowane przez różne aplikacje urucho-
mione w Windows, funkcja malloc nie jest odpowiednia do tego zadania.
Zamiast tego musimy skorzystać z funkcji przydzielania pamięci zaprojektowa- ,
nych w czasach, gdy system Windows używał 16-bitowej architektury pamięci
w trybie rzeczywistym. Funkcje te są wciąż dostępne i można ich używać, ale rzad-
ko jest to potrzebne.
Chcąc przydzielić blok pamięci za pomocą Windows API, możesz napisać:
hGlobal = GlobalAlloc (uiFlags, dwSize> :
Funkcja pobiera dwa parametry: opcjonalny ciąg flag i rozmiar w bajtach przy-
dzielanego bloku pamięci. Funkcja zwraca uchwyt typu HGLOBAL, nazywany
uchwytem globalnego bloku pamięci lub uchwytem globalnym. Jeśli zwrócona
zostanie wartość NLTLL, oznacza to, że nie ma wystarczająco dużo wolnej pamięci,
aby przydzielić blok.
Mimo że każdy z parametrów funkcji GlobalAlloc jest zdefiniowany nieco inaczej,
obydwa są 32-bitowymi liczbami typu unsigned integer. Jeśli ustawisz pierwszy
parametr na zero, użyjesz w ten sposób flagi GMEM FIXED. W takim wypadku
516 Część I: Podstawy
uchwyt globalny zwrócony przez GlobalAlloc będzie w rzeczywistości wskaźni-
kiem przydzielonego bloku pamięci.
Możesz użyć także flagi GMEM ZERoINIT, jeżeli chcesz, aby każdy bajt w blo-
ku pamięci, był początkowo ustawiony na zero. Flaga GPTR zgodnie z definicją
w plikach nagłówkowych Windows jest połączeniem flag GMEM FIXED
i GMEM ZEROINIT: -
Ildefine GPTR (GMEMFIXED GMEM-ZEROINIT)
Dostępna jest także funkcja realokująca:
hGlobal = GlobalReAlloc (hGlobal, dwSize, uiFlags) ;
Możesz wykorzystać flagę GMEM ZEROINIT, aby wyzerować nowe bajty przy
powiększaniu bloku pamięci.
Poniższa funkcja zwraca rozmiar bloku pamięci: hGl
dwSize = GlobalSize (hGlobal) :
Natomiast ta funkcja zwalnia blok pamięci:
GlobalFree (hGlobal) ;
W 16-bitowych wersjach Windows odradzano używania flagi GMEM FIXED, po-
nieważ system operacyjny nie mógł przenieść bloku w pamięci fizycznej. W wer-
sjach 32-bitowych nie stanowi to problemu, ponieważ flaga GMEM_FIXED zwra-
ca adres wirtualny, dzięki czemu system operacyjny może przenieść blok w pamięci
fizycznej zmieniając tabelę stron. Programistom w 16-bitowych wersjach Windows
zalecano zamiast tego używanie w funkcji GlobalAlloc flagi GMEM MOVEABLE.
(Zauważ, że większość słowników zaleca pisownię "movable" zamiast "moveable"
- ja także ją preferuję w niektórych sytuacjach). W plikach nagłówkowych zdefi-
niowany jest także identyfikator zerujący przenośną pamięć:
Ildefine GHND (GMEM MOVEABLE GMEM ZEROINIT)
Flaga GMEM MOVEABLE pozwala Windows przenieść blok w pamięci wirtu-
alnej. Nie musi to oznaczać, że blok ten zostanie przemieszczony także w pamię-
ci fizycznej, ale może zmienić się adres, którego aplikacja używa do zapisu i od-
czytu z tego bloku.
Mimo że flaga GMEM MOVEABLE była ważna w 16-bitowej wersji Windows,
teraz jest znacznie mniej użyteczna. Jeśli jednak aplikacja często przydziela, re-
alokuje i zwalnia bloki pamięci o różnych rozmiarach, jej wirtualna przestrzeń hGl c
adresowa może stać się pofragmentowana. Z tego powodu może zabraknąć wol-
nych adresów pamięci wirtualnej. Jeżeli napotkasz taki problem, możesz użyć
pamięci przenośnej w pokazany poniżej sposób.
pGlc
Najpierw zdefiniuj wskaźnik (na przykład na typ int) i zmienną typu GLOBAL-
HANDLE:
int * p ; for
GLOBALHANDLE hGlobal ;
Następnie przydziel pamięć, na przykład:
hGlobal = GlobalAlloc (GHND, 1024) ; Glob
Podobnie jak w przypadku każdego innego uchwytu w Windows, nie zastana-
wiaj się, co oznacza ta liczba. Po prostu ją zapisz. Gdy zechcesz uzyskać dostęp
do tego bloku pamięci, wpisz:
Rozdział 12: Schowek 517
p = (int *) GlobalLock (hGlobal) :
Powyższe wywołanie żamienia uchwyt na wskaźnik. Kiedy blok pamięci jest
zablokowany, Windows ustala adres w pamięci wirtualnej. Nie może być on wtedy
przemieszczany. Gdy dostęp do bloku nie będzie już potrzebny, wywołaj:
GlobalUnlock (hGlobal> ;
Instrukcja ta pozwala systemowi Windows swobodnie przemieszczać blok w pa-
mięci wirtualnej. Aby proces ten przeprowadzić w zupełności poprawnie (i zy-
skać uznanie programistów pierwszych wersji Windows), powinieneś zabloko-
wać i odblokować blok pamięci w ciągu trwania jednego komunikatu.
Kiedy chcesz zwolnić pamięć, zamiast ze wskaźnikiem wywołaj GlobalFree
z uchwytem. Jeśli nie masz aktualnie do niego dostępu, użyj funkcji:
hGlobal = GlobalHandle (p) :
Możesz wiele razy zablokować blok pamięci przed jego odblokowaniem. Windows
zarządza licznikiem zablokowań i aby blok mógł być przemieszczony, każda zało-
żona blokada musi zostać wcześniej odblokowana. Gdy system ten przemieszcza
blok w pamięci wirtualnej, nie musi kopiować bajtów z jednego miejsca w drugie -
wystarcza do tego manipulacja na tabelach stron pamięci. W 32-bitowych wersjach
Windows jedynym powodem przydzielania pamięci przenośnej do użytku przez
program jest chęć uniknięcia fragmentacji pamięci wirtualnej. Pamięć przenośną
należy również stosować przy operacjach z użyciem Schowka.
Przydzielając pamięć dla Schowka, powinieneś raczej używać funkcji GlobalAlloc
z flagami GMEM MOVEABLE i GMEM SHARE. Flaga GMEM SHARE powo-
duje, że blok pamięci dostępny jest także dla innych aplikacji Windows.
Przenoszenie tekstu do Schowka
Załóżmy, że chcesz przenieść ciąg znaków ANSI do Schowka. Masz wskaźnik
(nazwany pString) do tego ciągu i chcesz przenieść iLength znaków, które mogą,
ale nie muszą być zakończone znakiem NULL.
Najpierw musisz użyć funkcji GlobalAlloc, aby przydzielić blok pamięci o odpo-
wiednim rozmiarze, w którym zapisany zostanie ciąg znaków. Zostaw przestrzeń
na końcowy znak NULL:
hGlobal = GlobalAlloc (GHND GMEM SHARE, iLength + 1) :
Wartość hGlobal wyniesie NULL, jeżeli nie zostanie przydzielona pamięć dla blo-
ku. jeśli operacja powiedzie się, zablokuj blok, aby uzyskać do niego wskaźnik:
pGlobal = GlobalLock (hGlobal) ;
Skopiuj ciąg znaków do globalnego bloku pamięci:
for (i = 0 ; i < wLength ; i++)
*pGlobal++ = *pString++ ;
Nie musimy dodawać końcowego znaku NULL, ponieważ flaga GHND funkcji
GlobalAlloc zeruje cały blok podczas przydzielania pamięci. Odblokuj blok:
GlobalUnlock (hGlobal) ;
Teraz masz uchwyt bloku globalnego, który odwołuje się do bloku zawierające-
go tekst zakończony znakiem NULL. Aby przenieść ten tekst do Schowka, otwórz
Schowek i wyczyść jego zawartość:
518 Czść I: Podstawy Roz
OpenClipboard (hwnd)
EmptyClipboard () ;
Przekaż Schowkowi uchwyt pamięci używając identyfikatora CF TEXT i zamknij
go:
SetClipboardData (CF TEXT, hGlobal) ;
CloseClipboard () ; T
p e
To wszystko.
Poniżej wymienione są niektóre zasady dotyczące tego procesu:
ł Wywołuj funkcje OpenClipboard i CloseClipboard podczas przetwarzania jed-
nego komunikatu. Nie pozostawiaj Schowka otwartego dłużej, niż jest to po- pGl
trzebne.
ł Nie przekazuj Schowkowi zablokowanego uchwytu pamięci. str
ł Po wywołaniu funkcji SetClipboardData nie używaj więcej przekazanego bloku
pamięci. Nie należy on już do programu i powinieneś traktować jego uchwyt whi
jako nieważny. jeśli wciąż potrzebny jest ci dostęp do tych danych, utwórz ich
kopię lub odczytaj je ze Schowka (w sposób opisany w następnym podrozdzia- G 1 0
le). Do bloku możesz odwoływać się między wywołaniami SetClipboardData C1 0
i CloseClipboard, ale nie używaj uchwytu globalnego, który przekazałeś do funk-
cji SetClipboardData. Funkcja ta również zwraca uchwyt globalny, którego mo-
żesz użyć. Aby uzyskać dostęp do pamięci, zablokuj ten uchwyt. Przed wy-
wołaniem CIoseClipboard odblokuj go. t
Pobieranie tekstu ze Schowka
Pobranie tekstu ze Schowka jest tylko trochę bardziej skomplikowane niż przenie-
sienie do niego tekstu. Najpierw musisz sprawdzić, czy Schowek w ogóle zawiera
dane w formacie CF TEXT. Najprostszym sposobem jest użycie wywołania:
bAvailable = IsClipboardFormatAvailable (CF TEXT) ;
Funkcja ta zwraca wartość TRUE, jeśli Schowek zawiera dane CF TEXT. Użyli-
śmy jej w programie POPPAD2 z rozdziału 10, aby sprawdzić, czy opcja Paste
w menu Edit powinna być udostępniona czy szara. IsClipboardAvailable jest jedną
z kilku funkcji, których możesz użyć bez wcześniejszego otwierania Schowka. Je-
żeli jednak później otworzysz Schowek, aby pobrać tekst, powinieneś ponownie
sprawdzić (używając tej samej funkcji lub jednej z innych metod), czy dane typu
CF TEXT wciąż znajdują się w Schowku.
Chcąc pobrać tekst, najpierw otwórz Schowek:
OpenClipboard (hwnd) ;
Uzyskaj uchwyt globalnego bloku pamięci, odwohxjąc się do tekstu:
hGlobal = GetClipboardData (CF TEXT) ;
Uchwyt ten będzie mieć wartość NULL, jeśli Schowek nie zawiera danych w for-
macie CF TEXT. Jest to jedna z metod sprawdzania, czy zawiera on tekst. Jeżeli
funkcja GetClipboardData zwróci NULL, zamknij Schowek bez wykonywania do-
datkowych czynności.
Uchwyt, który uzyskujesz od funkcji GetClipboardData, nie należy do programu, tylko
do Schowka. Uchwyt ten jest ważny tylko między wywołaniami GetClipboardData
Rozdział 12: Schowek 519
i CloseClipboard. Nie możesz zwolnić go ani zmienić danych, do których się od-
wołuje. Jeśli dostęp do danych będzie ci potrzebny później, powinieneś wykonać
kopię bloku pamięci.
Poniżej pokazana jest jedna z metod kopiowania danych do programu. Po prostu
przydziel wskaźnik do bloku o takim samym rozmiarze jak blok danych Schowka:
pText = (char *) malloc (GlobalSize (hGlobal)) :
Jak pamiętasz, hGlobal był uchwytem globalnym uzyskanym od funkcji GetClipbo-
ardData. Teraz zablokuj uchwyt, aby uzyskać wskaźnik do bloku pamięci Schow-
ka:
pGlobal = GlobalLock (hGlobal) ;
Następnie po prostu skopiuj dane:
strcpy (pText. pGlobal) :
lub użyj prostego kodu:
while (*pTEXT++ = *pGlobal++) :
Odblokuj blok przed zamknięciem Schowka:
GlobalUnlock (hGlobal) :
CloseClipboard () :
Teraz masz wskaźnik nazwany pText, który odwołuje się do kopii tekstu należą-
cej do programu.
Otwieranie i zamykanie Schowka
Schowek może być otwarty jednorazowo tylko dla jednego programu. Celem
wywołania OpenClipboard jest zabezpieczenie jego zawartości przed zmianami,
gdy program go używa. Funkcja OpenClipboard zwraca wartość boolowską wska-
zującą, czy otwarcie Schowka powiodło się. Nie zostanie otwarty, jeśli inna apli-
kacja nie zamknęła go poprawnie. Gdyby wszystkie programy otwierały i zamy-
kały Schowek tak szybko, jak jest to możliwe, w odpowiedzi na żądanie użyt-
kownika, prawdopodobnie nigdy nie doświadczyłbyś trudności związanych z jego
otwieraniem.
Jednak w świecie nieuprzejmych, wielozadaniowych programów, takie proble-
my mogą się pojawić. Nawet jeśli program nie stracił fokusu wejściowego mię-
dzy umieszczeniem czegoś w Schowku a użyciem opcji Paste, nie należy zakła-
dać, że dane umieszczone w nim nadal tam są. W tym czasie dostęp do Schowka
mógł uzyskać proces uruchomiony w tle.
Zwróć także uwagę na bardziej subtelny problem dotyczący okien komunikatu:
jeżeli nie możesz przydzielić wystarczająco dużo pamięci, aby skopiować coś do
Schowka, możesz wyświetlić okno komunikatu. Jeśli jednak nie jest ono modal-
ne w systemie, użytkownik może przełączyć się do innej aplikacji podczas jego
wyświetlania. Powinieneś albo wyświetlać modalne okno komunikatu, albo za-
mknąć Schowek przed wyświetleniem komunikatu.
Trudności możesz napotkać także wtedy, gdy pozostawisz otwarty Schowek i wy-
świetlisz okno dialogowe. Pola edycji w oknach dialogowych wykorzystują Scho-
wek do wycinania i wstawiania tekstu.
520 Część I, Podstawy ' Ro
Schowek i Unicode
Dotąd omawiałem używanie Schowka wyłącznie z tekstem ANSI (jeden bajt na
znak). Format ten jest stosowany po użyciu identyfikatora CF TEXT. Być może,
zastanawiasz się jednak, jak korzystać z identyfikatorów CF OEMTEXT i CF UNI-
CODETEXT.
Mam dobre wiadomości: wystarczy wywołać funkcje SetClipboardData i GetClip-
boardData z wybranym formatem tekstu, a Windows automatycznie wykona
w Schowku potrzebne konwersje. Jeśli na przykład w Windows NT program uży-
wa funkcji SetClipboardData z typem danych Schowka CF TEXT, może także wy-
wołać funkcję GetClipboardData z identyfikatorem CF OEMTEXT. Analogicznie
można na przykład przekonwertować dane typu CF OEMTEXT na CF TEXT.
W Windows NT konwersja może być wykonywana pomiędzy formatami CF UM-
CODETEXT, CF TEXT i CF OEMTEXT. Program powinien wywoływać funkcję
SetClipboardData, używając formatu, który jest dla niego najbardziej wygodny.
Analogicznie funkcja GetClipboardData powinna być wywoływana z formatem
wymaganym przez program. Jak zapewne wiesz, programy w tym podręczniku
są tak napisane, że mogą być kompilowane z lub bez identyfikatora UNICODE.
Jeśli twoje programy używają identyfikatora UNICODE, a CF TEXT jest niezde-
finiowany, powinieneś zaimplementować kod, który będzie wywoływał funkcje
SetClipboardData i GetClipboardData z identyfikatorem CF UNICODETEXT.
Program CLIPTEXT, pokazany na rysunku 12-1, przedstawia jeden ze sposobów,
w jaki można to zrobić.
CLIPTEXT.C
/*
CLIPTEXT.C - Schowek i tekst
(c) Charles Petzold, 1998
*/
ilinclude
#include "resource.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
ili fdef UNICODE
lldefine CF_TCHAR CF_UNICODETEXT
TCHAR sz0efaultText[] = TEXT ("Default Text - Unicode Version") ;
TCHAR szCaption[] = TEXT ("Clipboard Text Transfers - Unicode Version") ;
ilel se
define CF_TCHAR CF_TEXT
TCHAR szDefaultText[7 = TEXT ("Default Text - ANSI Version") ;
TCHAR szCaption[] = TEXT ("Clipboard Text Transfers - ANSI Version") ;
endi f
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
Rozdział 12: Schowek 521
PSTR szCmdLine, int iCmdShow)
(
static TCHAR szAppNameC] = TEXT ("ClipText") ;
HACCEL hAccel ;
HWND hwnd :
I
MSG ms9 '
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW CS VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
k;
F..
if (!RegisterClass (&wndclass))
(
Messa9eBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MBICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, szCaption,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
hAccel = LoadAccelerators (hInstance, szAppName) ;
while (GetMessage (&msg, NULL, 0, 0))
(
if (!TranslateAccelerator (hwnd, hAccel, &msg))
(
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
I I k.
static PTSTR pText ;
BOOL bEnable ;
HGLOBAL hGlobal ;
HDC hdc ;
PTSTR pGlobal ;
PAINTSTRUCT ps ;
RECT rect ;
;, I
switch (message)
.:I
i;, ;
"::
Część I: Podstawy
(ciąg dalszy ze strony 521)
(
case WM_CREATE:
SendMessage (hwnd, WM COMMAND, IDM EDIT RESET, 0) ;
return 0 ;
case WM_INITMENUPOPUP:
EnableMenuItem ((HMENU) wParam, IDM EDIT_PASTE,
IsClipboardFormatAvailable (CF TCHAR) ? MFENABLED : MF GRAYED) ;
bEnable = pText ? MFENABLED : MF GRAYED ;
EnableMenuItem ((HMENU) wParam, IDM_EDIT CUT, bEnable) ;
EnableMenuItem ((HMENU) wParam, IDM_EDITCOPY, bEnable) ;
EnableMenuItem ((HMENU) wParam, IDM EDIT CLEAR, bEnable) ;
break ;
case WM COMMAND:
switch (LOWORD (wParam))
(
case IDM_EDIT_PASTE:
OpenClipboard (hwnd) ;
if (hGlobal = GetClipboard0ata (CF TCHAR))
f
pGlobal = GlobalLock (hGlobal) ;
if (pText)
(
free (pText) ;
pText = NULL ;
}
pText = malloc (GlobalSize (hGlobal)) ;
lstrcpy (pText, pGlobal) ;
InvalidateRect (hwnd, NULL, TRUE) ;
)
CloseClipboard () ;
return 0 ;
case IDM_EDIT_CUT:
case IDM_EDIT COPY:
if (!pText)
return 0 ;
hGlobal = GlobalAlloc (GHND GMEM_SHARE,
(lstrlen (pText) + 1) * sizeof (TCHAR)) ;
pGlobal = GlobalLock (hGlobal) ; //
lstrcpy (pGlobal, pText) ;
GlobalUnlock (hGlobal) ; ir
lli r
OpenClipboard (hwnd) ;
EmptyClipboard () ; //
SetClipboardData (CF TCHAR, hGlobal) ; //
CloseClipboard (> ;
if (LOWORD (wParam) = IDMEDITCOPY)
return 0 ; BEG
// przypadek IDM EDIT CUT zostawiamy nieopracowany
Rozdział 12: Schowek 523
case IDM_EDIT_CLEAR:
if (pText)
(
free (pText) :
pText = NULL ;
1
InvalidateRect (hwnd, NULL, TRUE) ;
I
return 0 :
case IDM_EDIT_RESET:
if (pText)
free (pText) :
pText = NULL ;
pText = malloc ((lstrlen (szDefaultText) + 1) * sizeof (TCHAR)) ;
lstrcpy (pText, szDefaultText) ;
InvalidateRect (hwnd, NULL, TRUE) :
return 0 ;
break ;
case WM PAINT:
hdc = BeginPaint (hwnd, &ps) :
GetClientRect (hwnd, &rect) :
if (pText != NULL)
DrawText (hdc, pText, -1, &rect. DT EXPANDTABS DT WORDBREAK) :
EndPaint (hwnd, &ps) ;
return 0 ;
case WM DESTROY:
if (pText) .
free (pText) :
PostOuitMessage (0) :
return 0 ;
1
return DefWindowProc (hwnd, message, wParam, lParam) :
)
CLIPTEXT.RC (fragmenty)
//Microsoft Developer Studio generated resource script.
inctude "resource.h"
include "afxres.h"
////l//////////////////////////////////////////////////////////////////////
// Menu
CLIPTEXT MENU DISCARDABLE
BEGIN
POPUP "&Edit"
524 Część I: Podstawy
(ciąg dalszy ze strony 523)
BEGIN
MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT
MENUITEM "&Copy\tCtrl+C", IDM EDIT_COPY
MENUITEM "&Paste\tCtrl+V", IDM EDIT_PASTE
MENUITEM "De&lete\tDel", IDMEDIT_CLEAR
MENUITEM SEPARATOR
MENUITEM "&Reset", IDM EDIT RESET
END
END
///////////////////////////////////////////////////////////////////////////
// Accelerator
CLIPTEXT ACCELERATORS DISCARDABLE
BEGIN
"C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
"V", IDMEDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT
"X", IDM EDIT CUT, VIRTKEY, CONTROL, NOINVERT
END
RESOURCE.H (fragmenty)
// Microsoft Developer Studio generated include file.
// Used by ClipText.rc
tidefine IDM_EDIT_CUT 40001
4ldefine IDM_EDIT_COPY 40002
tldefine IDM_EDIT_PASTE 40003
ttdefine IDM_EDIT_CLEAR 40004
tldefine IDM EDIT RESET 40005
Rysunek 12-1. Program CLIPTEXT
Celem jest tutaj uruchomienie wersji Unicode i ANSI programu w Windows NT
i zaobserwowanie, jak Schowek wykonuje konwersje między tymi zestawami
znaków. Zwróć uwagę na instrukcję #ifdef na początku pliku CLIPT'EXT.C. Jeśli
zdefiniowany jest identyfikator UNICODE, wtedy CF TCHAR (nazwa ogólne-
go, utworzonego przeze mnie formatu danych Schowka) równy jest CF_UNICO-
DETEXT. W przeciwnym razie CF_TCHAR zgodny jest z CF TEXT. Wywołania
funkcji IsClipboardFormatAvailable, GetClipboardData i SetClipboardData w dalszej
części programu używają nazwy CF TCHAR do określenia typu danych.
Po uruchomieniu programu (lub gdy wybierzesz opcję Reset z menu Edit) zmienna
pText będzie zawierała wskaźnik do ciągu znaków Unicode "Default Text - Uni-
code version" w wersji Unicode programu lub do ciągu "Default Text - ANSI
version" w drugiej wersji. Możesz użyć poleceń Cut i Copy, aby przenieść ciąg
tekstowy do Schowka, albo poleceń Cut i Delete, aby usunąć ciąg z programu.
Polecenie Paste kopiuje dowolną zawartość tekstową Schowka do pText. Ciąg pText
jest wyświetlany w obszarze roboczym programu podczas przetwarzania komu-
nikatu WM PAINT.
Rozdział 12: Schowek 525
Jeżeli najpierw wybierzesz polecenie Copy z wersji Unicode programu CLIPTEXT,
a następnie polecenie Paste z drugiej wersji, tekst został nie przekonwertowany
z Unicode na ANSI. Analogicznie, jeśli wybierzesz odwrotne polecenia, tekst zo-
stanie przekonwertowany z ANSI na Unicode.
Poza standardowymi zastosowaniami Schowka
Wiemy już, że przeniesienie tekstu ze Schowka wymaga czterech wywołań po
przygotowaniu danych.
OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (iFormat, hGlobal) ;
CloseClipboard () ;
Natomiast uzyskanie dostępu do tych danych wymaga trzech wywołań:
OpenClipboard (hwnd) ;
hGlobal = GetClipboardData (iFormat) ;
[pozostaie wiersze programuJ
CloseClipboard () ;
Możesz wykonać kopię danych ze Schowka lub użyć ich w inny sposób między
wywołaniami GetClipboardData i CloseClipboard. Takie rozwiązanie jest wystarcza-
jące w większości przypadków, ale Schowek można wykorzystać także w spo-
sób bardziej zaawansowany.
Używanie kilku elementów danych
Gdy otwierasz Schowek, aby umieścić w nim dane, musisz wywołać funkcję Emp-
tyClipboard, która powoduje, że Windows zwalnia lub usuwa jego zawartość. Nie
możesz niczego dodać do istniejącej zawartości Schowka. W takim rozumieniu prze-
chowuje on tylko jeden element danych naraz. Pomiędzy wywołaniami EmptyClip-
board i CIoseCliboard możesz jednak wywołać kilka razy funkcję SetClipboardData,
używając za każdym razem różnego formatu danych. Jeśli chcesz na przykład
przechować w Schowku krótki ciąg tekstowy, możesz zapisać go w metapliku
lub bitmapie. W ten sposób udostępniasz ten ciąg nie tylko programom, które
mogą odczytać tekst ze Schowka, ale także tym, które mogą odczytać metapliki
i bitmapy. Oczywiście, programy te nie będą w stanie w prosty sposób rozpo-
znać, czy metaplik lub bitmapa rzeczywiście zawiera ciąg znaków. Jeżeli chcesz
utworzyć kilka uchwytów Schowka, możesz dla każdego z nich wywołać funk-
cję SetClipboardData:
OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboardData (CF_TEXT, hGlobalText) ;
SetClipboardData (CF_BITMAP, hBitmap) ;
SetClipboardData (CF METAFILEPICT, hGlobalMFP ,
CloseClipboard () ;
Gdy w Schowku znajdą się te trzy formaty danych, funkcja IsClipboardFormatA-
vailable zwróci wartość TRUE dla argumentów CF TEXT, CF BITMAP i CF ME-
TAFILEPICT. Program może uzyskać dostęp do tych uchwytów przez wywoła-
nie:
526 Część I: Podstawy
hGlobalText = GetClipboardData (CF TEXT) ;
lub
hBitmap = GetClipboardData (CF BITMAP) ;
lub
hGlobalMFP = GetClipboardData (CF METAFILEPICT) ;
Przy kolejnym wywołaniu EmptyClipboard Windows zwolni lub usunie wszyst-
kie trzy uchwyty używane przez Schowek.
Nie używaj tej techniki, aby umieścić w Schowku różne formaty tekstu, różne
formaty bitmapy lub różne formaty metapliku. Korzystaj tylko z jednego forma-
tu tekstu, jednego formatu bitmapy i jednego formatu metapliku. Jak wspomina-
łem, Windows dokonuje konwersji formatu CF TEXT na CF OEMTEXT i CF UM-
CODETEXT. Wykonywana jest także konwersja między formatami CF BITMAP
i CF DIB oraz między CF METAFILEPICT i CF ENHMETAFILE.
Program może sprawdzić, jakie formaty danych przechowywane są w Schowku,
otwierając go i wywołując funkcję EnumClipboardFormats. Rozpocznij od ustawienia
zmiennej iFormat na 0:
IFormat = 0 ;
OpenClipboard (hwnd) ;
Następnie wykonaj kolejne wywołania EnumCIipboardFormats, zaczynając od war-
tości 0. Funkcja ta zwróci dodatnią wartość iFormat dla każdego formatu prze-
chowywanego aktualnie w Schowku. Gdy zwróci 0, będzie to oznaczało, że prze-
tworzyłeś wszystkie formaty:
while (iFormat = EnumClipboardFormats (iFormat))
ł.
Clogiczny d1a każdej wartoci iFormat]
}
CloseClipboard () ;
Liczbę różnych formatów dostępnych aktualnie w Schowku możesz uzyskać
wywołując:
iCount = CountClipboardFormats () :
Opóźnione przenoszenie
Gdy umieszczasz dane w Schowku, tworzysz w rzeczywistości ich kopię i prze-
kazujesz mu uchwyt globalnego bloku pamięci, zawierającego tę kopię. Dla bar-
dzo dużych elementów danych technika ta powoduje niepotrzebne zużycie pa-
mięci. Jeśli użytkownik nie zamierza umieścić danych w innym programie, pa-
mięć będzie zajęta, aż do momentu, gdy zostaną one zastąpione innymi.
Problemu tego można uruknąć stosując technikę nazwaną opóźnionym przeno-
szeniem, w której program przekazuje dane dopiero wtedy, gdy zażąda ich inna
aplikacja. Zamiast przekazywać systemowi Windows uchwyt danych, możesz
użyć wartości NULL w wywołaniu SetClipboardData:
OpenClipboard (hwnd) ;
EmptyClipboard () ;
SetClipboard (iFormat, NULL) ;
CloseClipboard () ;
Rozdział 12: Schowek 527
Możesz wpisać kilka wywołań SetClipboardData używając różnych wartości iFor-
mat. W niektórych możesz wykorzystać wartość NULL, a w pozostałych - praw-
dziwe uchwyty.
Dotychczasowa część jest prosta, ale w tym punkcie proces staje się bardziej skom-
plikowany. Gdy inny program wywołuje funkcję GetClipboardData, Windows
sprawdza, czy uchwyt danego formatu jest równy NULL. jeżeli tak nie jest, Win-
dows wysyła komunikat do właściciela Schowka (twojego programu) z pytaniem
o prawdziwy uchwyt danych. Program musi wtedy dostarczyć ten uchwyt.
Właściciel Schowka to w rzeczywistości ostatnie okno, które umieściło dane
w Schowku. Gdy program wywołuje funkcję OpenClipboard, Windows zachowu-
je uchwyt wymagany przez tę funkcję. Uchwyt identyfikuje okno, które otwo-
rzyło Schowek. Po odebraniu wywołania EmptyClipboard Windows nadaje temu
oknu status nowego właściciela Schowka.
Program używający opóźnionego przenoszenia musi przetworzyć w procedurze
okna trzy komunikaty: WMRENDERFORMAT, WMRENDERALLFORMATS
i WM-DESTROYCLIPBOARD. Windows wysyła procedurze okna komunikat
WM-RENDERFORMAT, gdy inny program wywołuje funkcję GetClipboardData.
Wartość wParam jest wymaganym formatem. Gdy przetwarzasz komunikat
WM-RENDERFORMAT, nie otwieraj i nie usuwaj zawartości Schowka. Po pro-
stu utwórz globalny blok pamięci dla formatu określonego przez wParam, prze-
nieś do niego dane i wywołaj funkcję SetClipboardData z odpowiednim formatem
i uchwytem globalnym. Musisz oczywiście zachować te informacje w programie,
aby poprawnie skonstruować dane podczas przetwarzania komunikatu
WM-RENDERFORMAT. Kiedy inny program wywołuje funkcję EmptyClipboard,
Windows wysyła do programu komunikat WM DESTROYCLIPBOARD. Mówi
on, że informacje potrzebne do skonstruowania danych Schowka nie są już po-
trzebne. Nie jesteś już jego właścicielem.
Jeśli program zakończy działanie, będąc właścicielem Schowka, a Schowek za-
wiera uchwyty danych o wartości NULL, ustawione przez funkcję SetClipboard-
Data, odbierzesz komunikat WM-RENDERALLFORMATS. Powinieneś otworzyć
Schowek, usunąć jego zawartość, umieścić dane w globalnych blokach pamięci,
dla każdego formatu wywołać funkcję SetClipboardData i zamknąć go. Komuni-
kat WM RENDERALLFORMATS jest jednym z ostatnich, które odbiera proce-
dura okna. Po nim następuje WM-DESTROYCLIPBOARD (ponieważ przetwo-
rzyłeś wszystkie dane) i na koniec zwykły komunikat WM DESTROY.
Jeżeli program może przekazywać do Schowka tylko jeden format danych (na
przykład tekst), możesz połączyć przetwarzanie komunikatów WM-RENDERAL-
LFORMATS i WM RENDERFORMAT.
Kod będzie wtedy wyglądał podobnie do poniższego:
case WMRENDERALLFORMATS :
OpenClipboard (hwnd) ;
EmptyClipboard () ;
, // nie powiodło się
case WM_RENDERFORMAT :
fumieszczenie tekstu w globalnym bloku pamięci]
SetClipboardData (CF TEXT, hGlobal) ;
528 Część I: Podstawy
if (message == WM_RENDERALLFORMATS)
CloseClipboard () ;
return 0 ;
Jeśli program używa kilku typów danych, komunikat WMRENDERFORMAT
należy przetwarzać tylko dla formatu wymaganego przez wParam. Nie należy
przetwarzać komunikatu WMDESTROYCLIPBOARD, chyba że przeszkadza on
programowi uzyskać informacje potrzebne do skonstruowania danych.
Własne formaty danych
Do tej pory zajmowaliśmy się tylko standardowymi formatami danychSchowka
zdefiniowanymi przez Windows. Możesz jednak użyć Schowka do przechowa-
nia własnych formatów danych. Wiele procesorów tekstów używa tej techniki do
przechowania tekstu z informacją o czcionce i formatowaniu.
Na pierwszy rzut oka pomysł ten może wydawać się bezsensowny. Jeśli zada-
niem Schowka jest przenoszenie danych między aplikacjami, dlaczego mieliby-
śmy umieszczać w nim dane wykorzystywane tylko przez jeden program? Od-
powiedź jest prosta: Schowek służy również do przenoszenia danych wewnątrz
jednego programu (lub między kilkoma jego kopiami), który, oczywiście, właści-
wie rozpoznaje swój własny format.
Dostępne jest kilka metod wykorzystania własnych formatów danych. Najłatwiej-
sza używa danych w jednym ze standardowych formatów Schowka (tzn. tekst,
bitmapa lub metaplik), ale mających znaczenie tylko dla twojego programu. W
takim przypadku w wywołaniach SetClipboardData i GetClipboardData używa się
jednej z następujących wartości wFormat: CF DSPTEXT, CF DSPBITMAP,
CF DSPMETAFILEPICT lub CF DSPENHMETAFILE. Litery DSP to skrót od ang.
display (wyświetlanie). Formaty te pozwalają Schowkowi Windows wyświetlać
dane jako tekst, bitmapę lub metaplik.
Jeśli jednak inny program wywoła funkcję GetClipboardData przy użyciu zwykłe-
go formatu CF_TEXT, CF BITMAP, CF DIB, CF METAFILEPICT lub CF_ENH-
METAFILE, nie uzyska danych.
Jeżeli użyjesz jednego z tych formatów, aby umieścić dane w Schowku, musisz
zastosować taki sam format, aby te dane pobrać. Skąd jednak wiemy, czy dane
pochodzą z kopii twojego programu czy z innego programu, używającego jedne-
go z tych formatów? Jednym ze sposobów jest sprawdzenie właściciela Schowka
przez wywołanie:
hwndClipOwner = GetClipboardOwner () :
Następnie możesz uzyskać nazwę klasy okna dla tego uchwytu okna:
TCHAR szClassName [32] ;
Cpozostaie wiersze programu7
GetClassName (hwndClipOwner, szClassName, 32) ;
Jeśli nazwa klasy jest taka sama jak programu, wtedy dane zostały umieszczone
w Schowku przez inną kopię twojego programu.
Innym sposobem jest użycie własnych formatów za pomocą flagi CF OWNER-
DISPLAY. Globalny uchwyt pamięci w funkcji SetClipboardData równy jest NULL:
Rozdział 12: Schowek 529
SetClipboardData (CF OWNERDISPLAY, NULL) ;
Metody tej używają niektóre procesory tekstu, aby wyświetlić sformatowany tekst
w obszarze roboczym podglądu Schowka dostarczanego z Windows. Oczywiście
podgląd Schowka nie wie, że wyświetla sformatowany tekst. Z używaniem flagi
CF OWNERDISPLAY przez procesor tekstu związane jest wyświetlenie obszaru
roboczego podglądu Schowka.
Ponieważ globalny uchwyt pamięci równy jest NULL, program wywołujący funkcję
SetClipboardData z formatem CF OWNERDISPLAY (właściciel Schowka) musi prze-
tworzyć komunikaty opóźnionego przenoszenia wysyłane przez Windows do wła-
ściciela Schowka oraz pięć innych, dodatkowych, które wysyła podgląd Schowka:
ł WM SKCBFORMATNAME Podgląd Schowka wysyła ten komunikat do wła-
ściciela Schowka, aby uzyskać nazwę formatu danych. Parametr lParam jest
wskaźnikiem bufora, a wParam - maksymalną liczbą znaków dla tego bufora.
Właściciel Schowka musi skopiować nazwę formatu Schowka do tego bufora.
ł WM SIZECLIPBOARD Komunikat ten mówi właścicielowi Schowka, że zmie-
nił się rozmiar obszaru roboczego podglądu Schowka. Parametr wParam jest
uchwytem tego podglądu, a lParam - wskaźnikiem do struktury RECT, za-
wierającą nowy rozmiar. Jeżeli struktura RECT zawiera same zera, podgląd
Schowka jest zminimalizowany lub zniszczony. Mimo że można uruchomić
tylko jedną kopię danego podglądu Schowka Windows, inne takie podglądy
także mogą przesłać ten komunikat do właściciela Schowka. Obsługa kilku
podglądów Schowka nie jest dla niego niemożliwa (zważywszy na to, że wPa-
ram identyfikuje poszczególne podglądy), ale nie jest także łatwa.
ł WM PAINTCLIPBOARD Komunikat ten mówi właścicielowi Schowka, aby od-
świeżył obszar roboczy podglądu Schowka. Ponownie wParam jest uchwytem
do okna tego podglądu. Parametr IParam jest uchwytem gtóbalnym do struktu-
ry PAV'TSTRUCT. Właściciel Schowka może zablokować uchwyt i uzyskać go
do kontekstu urządzenia podglądu Schowka za pomocą pola hdc tej struktury.
ł WM HSCROLLCLIPBOARD i WMVSCROLLCLIPBOARD Komunikaty te in-
formują właściciela Schowka, że użytkownik użył pasków przewijania pod-
glądu Schowka. Parametr wParam jest uchwytem okna tego podglądu, mniej
znaczące słowo lParam jest żądaniem przewijania, a bardziej znaczące - po-
zycją miniatury, jeśli mniej znaczące słowo jest równe SB THUMBPOSITION.
Przetwarzanie tych komunikatów może wydawać się niewiele warte. Ma jednak
pewne zalety dla użytkownika: kopiując tekst z procesora tekstów do Schowka,
będzie czuł się pewniej, jeśli zobaczy, że tekst jest sformatowany również w ob-
szarze roboczym Schowka.
Kolejnym sposobem korzystania z własnych formatów danych Schowka jest za-
rejestrowanie własnej nazwy formatu. Nazwę formatu podajesz systemowi Win-
dows, a on zwraca programowi numer, którego używa się jako parametru w funk-
cjach SetCliboardData i GetCliboardData. Programy używające tej metody także
kopiują dane w jednym ze standardowych formatów. Rozwiązanie to umożliwia
podglądowi Schowka wyświetlanie danych w obszarze roboczym (z uniknięciem
kłopotów występujących przy CF OWNERDISPLAY) i pozwala innym progra-
mom kopiować dane ze Schowka.
530 Część I: Podstawy
Załóżmy, że napisaliśmy program do grafiki wektorowej, który kopiuje dane do
Schowka w formacie bitmapy, metapliku i we własnym zarejestrowanym forma-
cie. Podgląd Schowka wyświetli metaplik i bitmapę. Inne programy, mogące od-
czytać metapliki i bitmapy ze Schowka, także obsłużą te formaty. Jeżeli jednak
nasz program potrzebuje odczytać dane ze Schowka, skopiuje je we własnym
formacie, ponieważ zawiera on prawdopodobnie więcej informacji niż bitmapa
czy metaplik.
Program rejestruje nowy format Schowka przez wywołanie:
iFormat = RegisterClipboardFormat (szFormatName) ;
Wartość iFormat mieści się w zakresie od 0xC000 do OxFFFF. Podgląd Schowka
(lub program odbierający wszystkie bieżące formaty w Schowku przez wywoła-
nie EnumClipboardFormats) może uzyskać nazwę ASCII formatu przez wywoła-
nie:
GetClipboardFormatName (iFormat, psBuffer, iMaxCount) ;
Windows kopiuje maksymalnie iMaxCount znaków do psBuffer.
Programiści używający tej metody do kopiowania danych do Schowka mogą
udokumentować nazwę formatu i rzeczywisty format danych. Kiedy program
stanie się populamy, inne programy będą mogły kopiować dane ze Schowka
w tym formacie.
Tworzenie podglądu Schowka
Program powiadamiany o zmianach zawartości Schowka nazywany jest pod-
glądem Schowka. Dostarczany jest z Windows, ale równie dobrze możesz napi-
sać swój własny program. Podgląd Schowka powiadamiany jest o zmianach za-
wartości Schowka przez komunikaty przekazywane do procedury okna pod-
glądu.
Łańcuch podglądu Schowka
Jednocześnie można uruchomić w Windows dowolną liczbę podglądów Schow-
ka i wszystkie z ruch można powiadomić o zmianach jego zawartości. Z perspek-
tywy tego systemu istnieje jednak tylko jeden podgląd Schowka, który nazywam
aktualnym podglądem Schowka". Windows zarządza tylko jednym uchwytem
"
do identyfikacji aktualnego podglądu Schowka i tylko do tego okna wysyła ko-
munikaty, gdy zmieni się zawartość Schowka.Aplikacje tego podglądu stanowią
część łańcucha podglądu Schowka i wszystkie mogą odbierać komunikaty, które
Windows wysyła do podglądu bieżącego. Gdy program zarejestruje się jako pod-
gląd Schowka, staje się podglądem bieżącym. Windows nadaje mu uchwyt okna
podglądu Schowka, który poprzednio był podglądem bieżącym. Program zapi-
suje ten uchwyt. Po odebraniu przez program komunikatu podglądu Schowka,
przesyła go do procedury okna programu, który umieszczony jest jako następny
w łańcuchu tego podglądu.
Rozdział 12: Schowek 531
Funkcje i komunikaty podglądu Schowka
Program może stać się częścią łańcucha podglądu Schowka wywołując funkcję SetC-
lipboardViewer. jeśli podstawowym zadaniem programu ma być działanie jako pod-
gląd Schowka, może wywołać tę funkcję podczas przetwarzania komunikatu
WMCREATE. Funkcja zwraca uchwyt okna podglądu Schowka, który poprzed-
nio był bieżącym. Program powinien zapisać ten uchwyt w zmiennej statycznej:
static HWND hwndNextUiewer ;
Cpozostałe wiersze programuJ
case WM_CREATE :
Cpozostaie wiersze programuJ
hwndNextViewer = SetClipboardViewer (hwnd) ;
Jeżeli twój program jako pierwszy ma stać się podglądem Schowka podczas sesji
Windows, zmienna hwndNextViewer powinna być równa NULL.
Windows wysyła komunikat WM DRAWCLIPBOARD do bieżącego podglądu
Schowka (tzn. ostatniego okna, które zrejestrowało sięjako jego podgląd) za każdym
razem, gdy zmieni się zawartość Schowka. Każdy program w łańcuchu podglądu
Schowka powinien używać funkcji SendMessage, aby przekazać ten komunikat do
następnego jego podglądu. Ostatru program w tym łańcuchu (tzn. pierwsze okno,
które zarejestrowało się jako podgląd Schowka) będzie przechowywać wartość hwnd-
NextViewer równą NULL. Jeśli wartość hwndNextViewer jest równa NULL, program
kończy działanie bez wysyłania komunikatu do innego programu. (Nie pomyl ze
sobą komunikatów WMDRAWCLIPBOARD i WMPAINTCL'BOARD. Komuni-
kat WMPAINTCL'BOARD jest wysyłany przez podgląd Schowka do programów,
które używają formatu CF OWNERDISPLAY. Natomiast komunikat WMDRAWC-
LIl'BOARD jest wysyłany przez Windows do bieżącego podglądu Schowka).
Najprostszym sposobem przetworzenia komunikatu WM DRAWCLIPBOARD
jest wysłanie go do następnego w łańcuchu podglądu Schowka (chyba że war-
tość hwndNextViewer jest równa NULL) i uaktualnienie obszaru roboczego twoje-
go okna:
case WM_DRAWCLIPBOARD :
if (hwndNextViewer)
SendMessage (hwndNextViewer. message, wParam, lParam) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
Podczas przetwarzania komunikatu WMPAINT możesz odczytać zawartość
Schowka używając zwykłych wywołań OpenClipboard, GetClipboardData i CIoseClip-
board.
Gdy program chce usunąć się z łańcucha podglądu Schowka, musi wywołać funk-
cję ChangeClipboardChain. Funkcja ta wymaga uchwytu okna programu, który wy-
cofuje się z łańcucha podglądu Schowka i uchwytu okna następnego w kolejno-
ści jego podglądu:
ChangeClipboardChain (hwnd, hwndNextUiewer) ;
Kiedy program wywohxje funkcję ChangeClipboardChain, Windows wysyła komu-
nikat WM CHANGECBCHAIN do bieżącego podglądu Schowka. Parametr wPa-
ram jest uchwytem okna, które usuwa się z łańcucha (tzn. pierwszym parame-
532 Część I: Podstawy
trem funkcji ChangeClipboardChain), a parametr IParam jest uchwytem okna pod-
glądu Schowka następującego w łańcuchu za podglądem usuwanym (tzn. dru-
gim parametrem funkcji ChangeClipboardChain).
Gdy program odbierze komunikat WM CHANGECBCHAIN, musi sprawdzić,
czy parametr wParam jest równy wartości hwndNextViewer, którą wcześniej zapi-
sałeś. Jeśli tak jest, musisz ustawić hwndNextViewer na IParam. Operacja ta zapew-
nia, że do okna usuwającego się z łańcucha nie zostaną wysłane komunikaty
4VMDRAWCLIPBOARD. Jeżeli wParam nie jest równy hwndNextViewer, a hwnd-
NextViewer nie jest równy NULL, wyślij komunikat do następnego podglądu
Schowka:
case WM_CHANGECBCHAIN :
if ((HWND) wParam = hwndNextViewer)
hwndnNextViewer = (HWND) lParam
else if (hwndNextViewer)
SendMessage (hwndNextViewer, message, wParam, lParam) ;
return 0 ;
W rzeczywistości nie ma potrzeby używania instrukcji else if, która sprawdza,
czy zmienna hwndNextViewer nie zawiera wartości NULL. Wartość NULL dla tej
zmiennej oznaczałaby, że program wykonujący kod jest ostatnim podglądem
w łańcuchu. W takim przypadku komunikat nigdy nie powinien pojawić się tak
daleko.
Jeśli podczas zamykania program nadal znajduje się w łańcuchu podglądu Schow-
ka, powinien zostać z niego usunięty. Można to wykonać przetwarzając komuni-
kat WM DESTROY przez wywołanie funkcji ChangeClipboardChain:
case WM_DESTROY :
ChangeClipboardChain (hwnd, hwndNextViewer) ;
PostOuitMessage (0) ;
return 0 ;
Windows dostarcza także funkcję, która pozwala programowi uzyskać uchwyt
okna pierwszego podglądu Schowka:
hwndViewer = GetClipboardViewer () ;
Zwykle funkcja ta nie jest potrzebna. Jeżeli bieżący Schowek nie jest ustawiony,
funkcja zwraca wartość NULL.
Poniższy przykład ilustruje działanie łańcucha Schowka. Gdy Windows jest uru-
chamiany po raz pierwszy, bieżący podgląd Schowka ma wartość NULL:
Bieżgcy podglgd Schowka: NULL
Program, którego uchwytem okna jest hwndl, wywołuje funkcję SetClipboardVie-
wer. Funkcja zwraca wartość NULL, która staje się w programie wartością hwnd-
NextViewer:
Bieżący podglgd Schowka: hwndl
Następny podglgd za hwndl: NULL
Drugi program, którego uchwytem okna jest hwnd2, także wywołuje funkcję Set-
CipboardViewer. Zwraca ona uchwyt hwndl:
Rozdział 12: Schowek 533
Bieżgcy podglgd Schowka: hwnd2
Następny podglgd za hwnd2: hwndl
Nastgpny podglgd za hwndl: NULL

Trzeci program (hwnd3), a następnie czwarty (hwnd4) również wywołują funkcję
SetClipboardViewer, która zwraca odpowiednio hwnd2 i hwnd3:
I
Bieżgcy podglgd Schowka: hwnd4
Następny podglgd za hwnd4: hwnd3 i :'.
Następny podglgd za hwnd3: hwnd2
Następny podglgd za hwnd2: hwndl
Następny podglgd za hwndl: NULL
Gdy zawartość Schowka zmieni się, Windows wysyła komunikat WM DRAWCLIP-
BOAIZD do hwnd4, hwnd4 wysyła komunikat do hwnd3, hwnd3 - do hwnd2, hwnd2 -
do hwndl, a hwndl kończy ten proces.
Jeżeli hwnd2 zdecyduje usunąć się ze Schowka, wywołując funkcję:
ChangeClipboardChain (hwnd2, krwndl) :
to Windows wyśle do hwnd4 komunikat WM CHANGECBCHAIN z parametrem
wParam równym hwnd2 i parametrem IParam równym 1. Ponieważ następnym
podglądem Schowka za hwnd4 jest hwnd3, hwnd4 wysyła komunikat do hwnd3.
Następnie hwnd3 zauważa, że wParam jest równy następnemu podglądowi
(hwnd2), ustawia swój następny podgląd Schowka na równy IParam (hwndl) i koń-
czy ten proces. Operacja została wykonana. Łańcuch podglądu Schowka wyglą-
da teraz następująco:
Bieżgcy podglgd Schowka: hwnd4 '
Następny podglgd za hwnd4: hwnd3
Następny podglgd za hwnd3: hwndl
Następny podglgd za hwndl: NULL
Prosty podgląd Schowka
Podglądy Schowka nie muszą być aż tak rozbudowane jak ten dostarczany z Win-
dows. Podgląd Schowka może wyświetlać na przykład tylko jeden format da-
nych. Program CLIPVIEW, pokazany na rysunku 12-2, jest podglądem Schowka,
który wyświetla tylko format CF TEXT.
CLIPVIEW.C
i*
CLIPVIEW.C - Prosty podgląd Schowka
(c) Charles Petzold, 1998
*/
ilinclude
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
534 Część 1: Podstawy
(ciąg dalszy ze strony 533)
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
(
static TCHAR szAppName[] = TEXT ("ClipView") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW CS VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITEBRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
(
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MBICONERROR) ;
return 0 ;
)
hwnd = CreateWindow (szAppName,
TEXT ("Simple Clipboard Viewer (Text Only)"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW USEDEFAULT,
CW_USEDEFAULT, CW USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
(
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
return msg.wParam ;
)
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
?
static HWND hwndNextViewer ;
HGLOBAL hGlobal ;
HDC hdc ; .
PTSTR pGlobal ;
PAINTSTRUCT ps ;
RECT rect ;
switch (messa9e)
(
case WMCREATE:
Rozdział 12: Schowek 535
hwndNextViewer = SetClipboardViewer (hwnd) ;
return 0 ;
case WM_CHANGECBCHAIN:
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam ;
else if (hwndNextUiewer)
SendMessage (hwndNextUiewer, message, wParam, lParam) ;
return 0 ;
case WM_DRAWCLIPBOARD:
if (hwndNextViewer)
SendMessage (hwndNextViewer, message, wParam, lParam) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
OpenClipboard (hwnd) ;
ifdef UNICODE
hGlobal = GetClipboardData (CF UNICODETEXT) ;
else
hGlobal = GetClipboardData (CF TEXT) ;
#endif
if (hGlobal != NULL)
pGlobal = (PTSTR) GlobalLock (hGlobal) ;
DrawText (hdc, pGlobal, -1, &rect, DT EXPANDTABS) ;
GlobalUnlock (hGlobal) ;
1
CloseClipboard () ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
ChangeClipboardChain (hwnd, hwndNextViewer) ;
PostOuitMessage (0) ;
return 0 ;
return DefWindowProc (hwnd, message, wParam, lParam) ;
J
Program CLIPVIEW przetwarza komunikaty WMCREATE, WMCHANGECB-
CHAIN, WM DRAWCLIPBOARD i WMDESTROY w omówiony wcześniej spo-
sób. Komunikat WMPAIN'T otwiera po prostu Schowek i używa funkcji GetC-
IipboardData z formatem CF TEXT: Jeśli funkcja zwróci globalny uchwyt pamię-
ci, program CLIPVIEW zablokuje go i użyje funkcji DrawText, aby wyświetlić tekst
w obszarze roboczym.
Podgląd Schowka, obsługujący niestandardowe formaty danych (np. podgląd
Schowka dostarczany z Windows), musi wykonywać dodatkowe zadania, takie
536 Część I: Podstawy
jak wyświetlanie nazw wszystkich formatów dostępnych w Schowku. Można to
zrobić wywołując funkcje EnumClipboardFormats i uzyskując nazwy niestandar-
dowych formatów za pomocą funkcji GetClipboardFormatName. Podgląd Schow-
ka, używający formatu CF OWNERDISPLAY, aby wyświetlić dane, musi prze-
słać następujące cztery komunikaty do właściciela Schowka:
WM PAIN'TCLIPBOARD M VSCROLLCLIPBOARD
WM SIZECLIPBOARD WM HSCROLLCLIPBOARD
Jeżeli chcesz napisać taki podgląd Schowka, musisz uzyskać uchwyt okna wła-
ściciela Schowka, stosując funkcję GetClipboardOwner, i wysłać do tego okna po-
wyższe komunikaty w momencie odświeżania obszaru roboczego podglądu
Schowka.


Wyszukiwarka