27 Korzystanie ze zintegrowanego debugera


Rozdział 27
Korzystanie ze zintegrowanego debugera
Sprawdzanie programu w trybie Debug lub optymalizowanie w trybie
Relase_________________________
Śledzenie błędów przeglądając kod programu krok po kroku Śledzenie
okien programów i wysyłanych komunikatów
Usuwanie błędów z programu i narzędzia debugera
Znaczna część pracy nad aplikacją polega na usuwaniu błędów z kodu, czyli debugo-waniu (ang.
debugging) programu. W gruncie rzeczy tworzenie oprogramowania składa się zawsze z trzech etapów:
projektowania, wpisywania kodu i usuwania błędów.
Kompilator Yisuał C++ oferuje szeroki zakres narzędzi do usuwania błędów i rozbudowane
środowisko debugowania. Dzięki temu możemy szybko zidentyfikować problem, zbadać bieżące
wartości zapisane w zmiennych i systematycznie przeglądać zarówno kod napisany przez nas, jak i kod
MFC.
Narzędzia takie jak program Spy++ pozwalają oglądać komunikaty przesyłane między systemem
Windows a aplikacją i śledzić poczynania aplikacji sprawdzając, z których kontrolek i jakich stylów
Windows w danym momencie program korzysta.
Tryby Debug i Relase
Zasadniczo istnieją dwie podstawowe konfiguracje kompilatora: tryb Relase (dla użytkownika) i
tryb Debug (sprawdzania błędów). Konfiguracje te zmieniamy za pomocą polecenia Settings z menu
Project lub kombinacji klawiszy Alt+F7. Pojawi się wtedy przedstawione na rysunku 27.1 okno
dialogowe Project Settings. Podstawowe ustawienia projektu można zmieniać wybierając odpowiednią
opcję na liście kombinowanej ponad lewym panelem. Zmiany tutaj wprowadzone natychmiast znajdują
odbicie w ustawieniach domyślnych wyświetlanych na stronach po prawej stronie okna. Podczas
budowania aplikacji wykorzystywane będą ustawienia związane z jedną z dwu możliwych konfiguracji


716 Poznaj Visual C++ 6
(Relase albo Debug). Możemy również wybrać opcję Ali Configurations, aby jednocześnie
dokonywać zmian w obu konfiguracjach.
Wybieranie konfiguracji projektu
Aktywna konfiguracja projektu jest zestawem instrukcji konfiguracji, które kompilator wykorzystuje
do tworzenia wynikowego programu. Po tym jak zbudujemy aplikację w oparciu o konfigurację Debug
powinniśmy ustawić konfigurację Relase, aby skonstruować mniejszych rozmiarów i szybszą wersję
programu przeznaczoną dla klientów.
Aktywną konfigurację projektu możemy wybrać klikając w menu Bild polecenie Set Active
Configuration. Wyświetlone zostanie okno dialogowe Set Actiye Configu-ration prezentujące listę
możliwych konfiguracji projektu. Odpowiednią konfigurację wybieramy klikając dwukrotnie hasło
Debug lub hasło Relase.
Zarówno konfiguracja Relase, jak i Debug mają zastosowanie przy tworzeniu nowego projektu;
tworzą jednak zupełnie odmienny kod. Tryb Debug tworzy bardzo duży i stosunkowo powolny program.
Dzieje się tak dlatego, że do programu załączone są liczne informacje ułatwiające wykrywanie błędów i
wyłączona jest opcja optymalizacji kodu.




PioiccI Seltings
Bentlfil Debug C/Cł.
Categoir |Geneial
Waming jevel: :;
| Lewi .3
r' Wamings as efrois
Defaug ffito'
mx
Unk | Bemurc. |~[7 _-j
Swt. j
Optimizations:
|DisablelDebugl^ r"
Senerate btowse hfo
|progiam Dalabase tof Edit and Conlini-ie
^j Preptocessof definitiom;' ^ ;,:...
"^IIIEsiNIl^liiII'.'::'
MN32._DEBUG"WINDOWS"AFXDLL..MBCS
P[0ject Sptjons: : ' :.:: '":.:'::.:: .^^;:'!"; . .::?t':^
1/noloao /MOd Ai/3 /em /GX IZ, /Od /D 'WIN32" /D
AJ,:
j"_DEBUQ" /D "_WINDOWS" /D "_AFXDLL" /D
SJ ij"_MBCS" /Fp"Debug/EnoiP[one.pch"
Wstdakth" ^h
nfigurations
JinuriH-upp
li) ErmPione.rc Ę
ErorPromDoc.cpp |3
E[ro[P]on8VieH.cpp , @
Mairfrm.cpp | : B]
SWiK.cfl. B-'s3
HeadelFIes l : |s)
ErołProne.h l ; [g
EnoProneOiic.h : : [B]
EirorProneYiew.h ! , [D
MainFim.h ; ; |s|
Resoufce-h l g)
StltVK.h 1-
l^.jRBrouiceFfcs l lii
ErrorProne,ico





Rysunek 27.1. Strona C/C++ okna dialogowego Project Settings
Kompilując ten sam program w trybie Relase otrzymamy mały i szybki program, ale nie będziemy
mogli śledzić operacji, które wykonuje ani nie będziemy mogli korzystać z komunikatów debugowania
(ang. debugging messages).
W normalnej sytuacji podczas tworzenia aplikacji włączamy tryb Debug, który ułatwia
wyszukiwanie błędów w kodzie. Kiedy aplikacja jest już gotowa, przygotowujemy jej rynkową wersję
uruchamiając tryb Relase, który da nam mały i szybki program przeznaczony dla użytkowników.


Korzystanie ze zintegrowanego debugera 717
Testowanie w trybie Relase
Po zoptymalizowaniu aplikacji w trybie Relase należy konieczne przetestować aplikację przed
rozesłaniem jej do użytkowników. W wyniku przebudowy mogą pojawić się błędy związane z
pozostawieniem części kodu w makroinstrukcjach ASSERT, które zostaną w trybie Relase usunięte, a
ponadto niektóre posunięcia optymalizacyjne same z siebie mogą generować błędy.
PATRZ TAKŻE
Więcej informacji na temat zarządzania projektem i jego konfigurowania znaleźć można w
rozdziale 2.
Opcje trybu Debug i wyświetlane w nim komunikaty ostrzegawcze
Za pomocą karty C/C++ okna dialogowego Project Settings można zdefiniować różne
opcje trybu Debug i określić, jakie komunikaty ostrzegawcze mają być wyświetlane. Okno to
wywołujemy za pomocą polecenia Settings menu Projects (lub wciskając kombinację
klawiszy Alt+F7).
Jeśli z górnej listy kombinowanej Categorv wybierzemy kategorię Generał, na karcie
dostępne będą następujące narzędzia:
" Waming Level. Ta opcja definiuje jak szczegółowe komunikaty ostrzegawcze będą
wyświetlane podczas komplikacji. Możemy tutaj ustawić dowolny z przedstawionych w
tabeli 27.1 poziomów (ang. levels). Domyślnie definiowany jest poziom trzeci, który
zazwyczaj jest wystarczająco czuły, niemniej ostrożniejsi programiści wybierają Level 4,
który oferuje największy zakres komunikatów ostrzegawczych. Level l i opcja None
powinny być wykorzystywane tylko w wyjątkowych przypadkach.
Jeśli wybierzemy Level 4
Na poziomie 4 zauważymy, że nawet kod Microsoftu utworzony przez kreator AppWi-zard
potrafi generować komunikaty ostrzegawcze (niemniej dotyczą one zazwyczaj
niewykorzystywanych parametrów funkcji i mogą być bez obaw ignorowane).
Wamings as Errors. Zaznaczenie tej opcji powoduje, że komunikaty ostrzegawcze są
traktowane jako błędy powodując zatrzymanie kompilatora.
Generale Browse Info. Zaznaczenie tej opcji powoduje, że kompilator generuje infor-
macje pomagające odszukiwać powiązania między funkcjami, klasami i symbolami wy-
świetlanymi w oknie Browse (przeglądarki kompilatora, opisanym dalej). Niestety włą-
czenie tej opcji znacznie wydłuża czas kompilacji szczególnie dla dużych projektów.


718_____________________________________Poznaj Visual C++ 6
' Debug Info. Tutaj możemy zdefiniować poziom generowanych przez kompilator informacji
wykorzystywanych w czasie debugowania (poziomy te opisane zostały w tabeli
27.2).
' Optimizations. W trybie Debug należy pozostawić te opcje wyłączone, ponieważ kolidując z
procesem debugowania wydłużają kompilację. W trybie Relase natomiast możemy wybrać opcję
Maximize Speed przyśpieszającą działanie aplikacji lub opcję Minimize Size redukującą rozmiary
aplikacji (lub pozostawić ustawienie domyślne, które wykorzystuje zalety obu optymalizacji).
Prepiwessor Definitions. Ta opcja pozwala określić definicje zarządzające kompilacją (ang. manifest
definitions). Można użyć tych definicji w połączeniu z poleceniami przetwarzania wstępnego (ang.
preprocessor commands) #ifdef, łelse i łendif, aby kompilować sekcje kodu w ściśle określonej
konfiguracji. W trybie Debug domyślnie wykorzystywana jest definicja _DEBUG. Można w ten
sposób polecić kompilatorowi kompilowanie fragmentu kodu tylko w określonym trybie kompilacji:
int a=b*c/d+e;
łifdef _DEBUG CString strMessage;
strMessage.Format("Result of sum was %d",a);
AfxMessageBox(strMessage) ;
łendif
Okno komunikatu wyświetlane będzie teraz tylko w aplikacji zbudowanej w trybie Debug, a
ignorowane w trybie Relase.
Project Options. Kompilator działa na bardziej podstawowym poziomie niż aplikacje Windows
przekształcając opcje aplikacji Developer Studio w znaczniki wiersza poleceń. Tutaj można
zdefiniować pewne bardziej zaawansowane opcje kompilatora, które nie są dostępne poprzez opcje
interfejsu użytkownika.
Działanie znaczników kompilatora
Każde z ustawień projektu jest tłumaczone na długi ciąg znaczników wiersza poleceń przesyłanych do
działającego na poziomie wiersza poleceń kompilatora. Przykładowo ustawienia definiujące rodzaj
generowanych informacji pomagających w procesie debugowania tłumaczone są na różne opcje /z
wiersza poleceń. Opcja /z d definiuje ustawienie generujące tylko numery linii, opcja /z 7 odpowiada
informacjom kompatybilnym z C 7.0, opcja /z i odpowiada ustawieniu Program Database, a opcja /zł
dołącza tryb Edit and Continue. Jeśli zmienimy ustawienia na liście kombinowanej Debug info,
odpowiednie zmiany pojawią się w polu Project Options.


Korzystanie ze zintegrowanego debugera 719
Tabela 27.1. Poziomy definiujące jakie komunikaty ostrzegawcze kompilatora będą
wyświetlane Poziom Wyświetlane ostrzeżenia
None Nie są wyświetlane żadne komunikaty ostrzegawcze
Level l Tylko najważniejsze komunikaty ostrzegawcze
Level 2 Również mniej ważne ostrzeżenia
Level 3 Standardowy poziom (wszystkie warte rozważenia komunikaty ostrzegawcze)
Level 4 Najczulszy poziom (dla perfekcjonistów)
Tabela 27.2. Opcje definiujące zakres generowanych informacji wspomagających proces
debugowania
Ustawienie Generowane informacje debugowania
None Nie ma żadnych informacji debugowania - zazwyczaj zarezerwowane dla trybu Relase
Linę Numbers Oniy Generuje tylko numery linii ułatwiające orientację w kodzie źródłowym. Czas
kompilacji i rozmiar pliku wykonywalnego nie wydłużają się znacząco
C 7.0-Compatybile Generuje informacje kompatybilne z Microsoft C 7.0. Umieszcza informacje
debugowania w plikach wykonywalnych zwiększając ich rozmiary, pozwalając
jednak na wszystkie operacje debugowania symbolicznego (ang. symbolic
debugging)
Program Database To ustawienie generuje plik z rozszerzeniem .pdb, który przechowuje praktycznie
cały komplet informacji wykorzystywanych w procesie debugowania poza
informacjami Edit and Continue
Program Database To jest domyślne i najczęściej stosowane ustawienie. Tworzy plik .pdb for Edit and
zawierający pełen komplet standardowych informacji oraz informacje Continue potrzebne nowemu
narzędziu Edit and Continue
Tworzenie i korzystanie z informacji programu Source Browser
Do szczegółowego przeglądania kodu źródłowego możemy użyć narzędzia Source
Browser. Narzędzie to jest nieocenione, gdy przeglądamy kod utworzony przez innego
programistę lub po pewnym czasie wracamy do własnego programu.
Aby móc z niego korzystać, należy skompilować aplikację przy ustawionej opcji Ge-
nerate Browse Info (na karcie C/C++ okna dialogowego Project Settings). Aby uruchomić
ten program narzędziowy, należy wcisnąć Alt+F12 lub w menu Tool wybrać polecenie
Source Browser (za pierwszym uruchomieniem program Source Browser zapyta, czy
kompilować tworzone informacje).


720 Poznaj Visual C++ 6
Pierwsze okno dialogowe (rysunek 27.2) programu Source Browser pyta o identyfikator
(Identyfier) elementu, którego szukamy. Identyfikatorem tym może być zarówno nazwa
klasy, jak i nazwą funkcji, struktury czy globalnej lub lokalnej zmiennej występującej w
przeglądanej aplikacji. Po wpisaniu odpowiedniego identyfikatora uaktywnia się przycisk OK
i możemy zdefiniować opcje przeszukiwania dla wprowadzonego identyfikatora.







Identifisr:
CE rro rPro n e Vi ew
jiBte^;t|UflM|fi
B||


Definitions and References File
Outline_________
Deriyed CIasses and Members
Cali Graph Callers Graph
17 (;a.S8 sensitiye
Rysunek 27.2. Okno dialogowe Browse programu narzędziowego Source Browser
Lista Select Ouery poniżej pozwala definiować, jakiego rodzaju dodatkowe informacje
mają być wyszukiwane. Możemy wybrać jedną z poniższych możliwości:
Definitions and References. Ta opcja wyświetla wszystkie pliki odwołujące się do
zdefiniowanego identyfikatora, informując jednocześnie, czy są to odwołania (tam gdzie
identyfikator występuje w kodzie programu), czy definicje (tam gdzie identyfikator jest
definiowany) segregując je tak, jak to widać na rysunku 27.3. Razem z nazwami plików
podawane są odpowiednie numery linii, w których identyfikator występuje. Klikając
dwukrotnie dowolne odwołanie lub definicję wywołamy odpowiedni fragment kodu, który
pojawi się w oknie Deyeloper Studio. Opcja ta przydaje się do odnajdywania wszystkich
miejsc, w których występuje dana zmienna lub funkcja.


ąj Jj :;:::!



IB



rfr/pedef)

Delinitjons:







C;\ProaramFjles\DBVStudio\VCWCLUDE\winus8r.
h(2033)





References: i C:\Program
File8\DevStudio\VC\MFC\indudB\atwin.h(1966) ;
C:\Program
Files\DevSudio\VC\MFC\include\afKWin.h(3254) i:
C:\Program
Files\DBVStudio\VC\MFC\indude\atwin.h(3360) !
C:\Program
Files\DevStudio\VC\MFC\Jnduda\BfC:\Program Files\DevSfadio\VC\MFC\indude\afxwin.h(35)
1) ; C:\Program
Files\DevStudio\VC\MFC\include\afxwin.h(3622) i:
C:\Program
Files\DBvSludio\VC\MFC\indude\afin.h(3671) l
C:\Prograin Files\DevStiidio\VC\MFC\indude\atl, C:\Program
Files\DBvStudio\VC\MFC\indude\atC:\Program
Files\DBi/SE;\L)singVC6\aiap3D\Listings3n\ErrotProne\MainFrm.h(27
) ! E;\UsingVCE\Chap30\l-
istjngs30\ErrorPronB\ErrorProneView.h(30) l:
E:\UsingVC6\aiap30\UstJngs30\EriorProne\MainFrm.cpp(
82) ;
E:\UsJngVCS\Chap30\Lislings30\ErrorPronB\ErrorPronBV
iew.cpp(4?) l:

Rysunek 27.3. Program Source Browser wyświetlający definicje i odwołania do poszukiwanego
identyfikatora


Korzystanie ze zintegrowanego debugera 721
File Outline. Ta opcja wyświetla wszystkie klasy, dane, funkcje, makra i typy zdefiniowane w
podanym pliku (rysunek 27.4). Niektóre z typów, na przykład funkcje, możemy wyłączyć za pomocą
odpowiednich przycisków widniejących u góry okna.
^j _tj Filteron
l CErrorProneDoc::CEno[ProneDocfvoid1





f V CEtrorProneDoc::"CE[TorProneDoc(vQid)
f ,S CEnrorProneDoc::_G9!BaseClass(void)
f :^; CEtTorProneDoc::_GBtBaseMessageMap(void)
d struci AFX_MSGMAP_ENTRY const" const
CEłrorPronel
f 1^! CEnoiPronBDoc:AsserlValid(void[) l'
d |:^: struci CRuntimeClass const CErrorProneDoc-
:classCEffol
f IS1 CEnrorProneDoc::CreateObject(void) l;
f A/l CErrorProneDoc:Dump(das8 CDumpContext i.)
|| f y CErrorPronBDoc::GetM8ssageMBp(void) i f
'S,, CErrorPronBDoc;:GetRuntimBCIass(void)
d IS:,: structAFX_MSGMAP const
CErrorProneDocmessageM4 m new (constant)
l:
f 1^1 CErrorProneDoc::OnNswOocumant(void) II
f ?/: CEiTorProneDoc::SeriBlize(class CArchive 4)
li d^lTHlS.FILE^atiabiB) II


References:
E;\UsingVC6\ChBp30\Listings30\ErrorProne\ErmrP
E:\UsingVC6\ChBp30\Listing530\ErrorProne\ErrorP





Rysunek 27.4. Różne elementy występujące w pliku w oknie programu Source Browser
Base Ciasses and Members. Jest to jedna z najprzydatniejszych opcji programu Source Browser.
Pozwala wyświetlić wszystkie podklasy klasy bazowej oraz zmienne i funkcje składowe dostępne na
każdym poziomie hierarchii (rysunek 27.5). Możemy również ustawić pewne opcje filtrujące, które
sprawią, że program wyświetlać będzie tylko pewne rodzaje funkcji i zmiennych składowych.
CEfrorProneYiew-Base CIasses andMembers B

^j J?j Funnions: A Ali -j Data: A Ali r\

~. JC'EtrorProneView|
1 :-, i
LV|BW
S aCWnd i-J.,;j
CCmdTargBt : 0
CObject ;;

Public:


t V CErorPion8View.:As88rtValid(vo]d) d :S; struct CRuntimeQass const
CErrorProneView::dassCErrorProne^iew : iS5
CErrotProneView.:CreateObject(void) inK CEft(iiPionBVJBW.:Dump(dass
CDumpContexl S.) CErrorProneView:GetDocument(void) ; Sts
CErrorProneView:;GetRunlimeClass(void) l: i S
CErroiProneView;:OnDraw(dass COC") 1: ^1
CErrorPronBVi8w:Pr8CiealBWindow(struct tagCREATESTRUCTA S.) :}
Proleded: ; CErrorProneView:CErrorProneView(void) 3-
CErrorPronBView:GetBaseCtass(-'oni) j
lSiCEr^orPro^BView:LGsflse^BssageMap(vaid) ^CErforProneYieYc.-
GetMessageMaptyoid) d^: struct AF>LMSGMAP const
CErrorProneVi8w:;messag8Map ( M-
CErroiProneVi8w::OnBeginPrinting(dBss COC"stfuct CPrintInto "5 .;]



Definitions: ^



E'\LlsinaVC6\ChBD30\Listings30\ErrorPronB\E]TorPronBViBw.h(12) [



ReferencBe: I
Ę:\UsinqVC6\ChapM\Usfngs30VĘffor!^pne\ĘrrorPfoneyiew,c^^^ ^

Rysunek 27.5. Klasy pochodne klasy bazowej oraz dostępne zmienne i funkcje składowe
Derived Ciasses and Members. Prawie tak samo użyteczna opcja pozwalająca wyświetlić wszystkie
klasy wywodzące się z danej klasy oraz ich własne zmienne i funkcje składowe. Możemy również
wykorzystać tę opcję do przeglądania klas MFC, tak jak to widać na rysunku 27.6, gdzie pokazane
zostały klasy pochodne klasy cwnd.


722 Poznaj Visual C++ 6


-ia( f) Functions: A Ali Ą Data: A Ali j
""l """""' . . . -l . . ^J

.-. U CWnd .t |J CAnimoteOrI f
S 01 CButton !? ' U
CBitmapButton l CJ CComboBox
Da CConfrolBeir l l-
il|CDJBloqBBr|

Public:


f CDialociBar:CDialooBarfvoidl K.


f 1^: CDialogBar:~CDialogBar(void) : f V
CDialogBar:CBlcFixedLayout(intint) d S struct CRuntimeClass const
COialogBar::dassCDialagBar f
CDialogBar::Create(dassOVnd*unsignedinlunsign8dinlun' f
COialogBBr::CłeBte(dBssCWnd''.charconst".unsignedinluns f 1^
CDialogBar::GetRunlimeClass(void) d CDialogBaf.:msizeDefault ^i

l i| CStatusBer i 'L.J CToolBar
S 0 COialog " i (] CEdit SB :]
CFrameWnd ^J



Oelinilions: ilij


C;\Proaram Files\DevStudio\VC\MFC\indude\at

Referencas: ; ;,
CIPrnnrBm FilRc\nouRtlirlinlVf'lMFr'linrlllriRłBtrpł hMRl 2lj

Rysunek 27.6. Klasy pochodne klasy CWnd w oknie programu Source Browser
Cali Graph. Opcja ta wyświetla wszystkie funkcje przyzywane przez dany identyfikator i pliki, w
których są one definiowane i implementowane. W ten sposób możemy stosunkowo szybko śledzić
kolejne czynności programu.
Callers Graph. Opcja lustrzana względem poprzedniej, wyświetlająca wszystkie funkcje
przyzywające podany identyfikator. Za jej pomocą możemy sprawdzić, jakie elementy programu
będą odwoływać się do zdefiniowanej funkcji.
Debugowanie przez sieć oraz opcja Just in time Debugging
Debuger zawiera narzędzia pozwalające sprawdzać program działający na innym komputerze
(nawet poprzez Internet). Jest to szczególnie przydatne, gdy chcemy przetestować aplikację w
środowisku innym niż środowisko komputera, na którym ją pisaliśmy. Aby przeprowadzić taką operację,
musimy mieć w obu komputerach tę samą wersję plików .dli i .exe. Po załadowaniu projektu możemy
rozpocząć proces debugowania na drugim komputerze poprzez wspólny katalog (ang. shared directory),
wpisując w polu Ęxecu-table for debug session ścieżkę i nazwę lokalnego pliku .exe (pole to można
znaleźć na karcie Debug okna dialogowego Project Settings).
Ścieżkę do pliku .exe musimy również wpisać w polu Remote Executable Path and File Name
znajdującym się u dołu strony Debug, pozostawiając jednocześnie puste pole Working Directory.
Teraz możemy uruchomić monitora debugowania (ang. debugger monitor) na drugim komputerze,
przywołując program MCVCMON.EXE i łącząc się z nim za pomocą polecenia Debugger Remote
Connection z menu Build.
W oknie dialogowym Remote Connection możemy wybrać opcję Local w celu debugowania
poprzez wspólny katalog w sieci lokalnej lub Remote do debugowania korzystając z TCP/IP
(odpowiedni adres internetowy definiujemy klikając Settings). W ten sposób połączymy się ze zdalnym
monitorem debugowania, który rozpocznie sesję debugowania na drugim komputerze.


Korzystanie ze zintegrowanego debugera 723
Instalowanie plików zdalnego debugera
Aby uruchomić na innym komputerze monitora debugowania, niezbędne są pliki
MSYCMON.EXE, MSVCRT.DLL, TLNOT.DLL, DM.DLL, MSVCP50.DLL i MSDIS100.DLL. Można je
znaleźć w katalogu . . .\Microsoft Visual Studio\Common\MSDev98\bin.
Opcja Just-in-time debugging pozwala na usuwanie błędów z normalnego programu (nie
uruchomionego w debugerze), który podczas wykonywania sprawia problemy. Jeśli mamy na
komputerze zainstalowany kompilator Visual C++ z uaktywnioną opcją Just-in-time
debugging, program generujący błędy będzie automatycznie ładowany do okna kompilatora
Developer Studio automatycznie pokazując fragment kodu odpowiedzialny za dany błąd.
Najzabawniejsze są sytuacje, gdy programem generującym błąd jest właśnie kompilator
Developer Studio i w systemie otworzone zostaje kolejne okno programu pokazujące błąd w
swoim własnym kodzie. Możliwość usuwania błędów z własnych aplikacji, w momencie gdy
dany błąd się uwidoczni, bywa bardzo przydatna (szczególnie jeśli na przykład demonstrujemy
program szefowi). Opcję tę uruchamiamy w oknie Options za pomocą polecenia Options z
menu Tools. W oknie tym wybieramy kartę Debug i zaznaczamy pole Just-in-time
debugging.
Inną przydatną szczególnie podczas tworzenia aplikacji COM i DCOM opcją dostępną na
tej stronie jest OLE RPC. Opcja ta pozwala przerzucić odwołanie do funkcji do innego
programu lub pliku .dli dzięki czemu jej sprawdzeniem będzie mógł zająć się inny debu-ger.
Gdy proces zostanie zakończony, program powraca do pierwotnego debugera. Opcja ta działa
w sieci.
PATRZ TAKŻE
Więcej informacji na temat COM i OLE znaleźć można w rozdziale 25.
Makroinstrukcje TRACĘ, ASSERT i przeglądanie programu
krok po kroku
Jednym z najbardziej przydatnych narzędzi Visual C++ jest możliwość przeglądania
programu krok po kroku (ang. single stepping). Możemy w ten sposób przeglądać po-
szczególne linie kodu po kolei, tak jak są wykonywane. Możemy również umieścić w kodzie
programu punkty kontrolne (ang. breakpoints) zatrzymujące program w określonym przez nas
momencie. Dzięki temu będziemy mogli spokojnie obejrzeć znajdujący się w tym miejscu
fragment kodu.


724___________________ ____ Poznaj Visual C++6
Warunkowe punkty kontrolne
Możemy dołączyć do punktów kontrolnych warunki sprawiające, że program będzie się zatrzymywał
tylko wtedy, gdy spełniony zostanie związany z punktem kontrolnym warunek na przykład gdy pewne
zmienne osiągną zdefiniowane wartości lub gdy punkt kontrolny zostanie przekroczony określoną
liczbę razy. Warunki te znacznie poszerzają możliwości punktów kontrolnych.
Instrukcje śledzące (ang. tracę) i asercje (ang. assertions) to kolejne narzędzia przydające się przy
usuwaniu błędów z programu. Instrukcje śledzące pozwalają wyświetlać komunikaty i informacje o
aktualnych wartościach zmiennych w momencie, gdy program dociera do takiej instrukcji. Asercje
natomiast zatrzymują program, jeśli warunek definiowany przez daną asercje nie jest spełniony (nie daje
wartości TRUE).
Makroinstrukcja TRACĘ
Umieszczenie makroinstrukcji TRACĘ w różnych miejscach programu pozwala sprawdzać, kiedy
różne fragmenty programu są wykonywane i ewentualnie wyświetlać aktualne wartości
wykorzystywanych w programie zmiennych. Makroinstrukcje TRACĘ wkompi-Iowywane są do kodu
programu w trybie Debug i wyświetlane w oknie Output strony Debug w momencie, gdy program jest
wykonywany przez debuger.
Tworząc wersję programu dla klienta nie musimy przejmować się usuwaniem makroinstrukcji
TRACĘ, ponieważ są one automatycznie oddzielanie od kodu w momencie kompilacji w trybie Relase.
Przesyłając jako pierwszy parametr makroinstrukcji TRACĘ sformatowany łańcuch możemy
wyświetlić prosty komunikat lub aktualną wartość zmiennej. Łańcuch ten musi spełniać takie same
wymagania jak łańcuch przesyłany funkcji printf() lub funkcji CString:: Format (). Możemy definiować
różne kody formatowania (ang. formating codes) definiujące typ zmiennej dołączanej do łańcucha, takie
jak %d, aby wyświetlić liczbę dziesiętną, %x, aby wyświetlić liczbę w kodzie szesnastkowym lub %s,
aby wyświetlić łańcuch. Następnie w porządku odpowiadającym kolejności kodów formatowania
przesyłamy makroinstrukcji odpowiednie zmienne. Przykładowo, następujący fragment kodu:
int nMyNum = 60;
char* szMyString = "This is my String";
TRACĘ("Number -a %d, or %x in hex and my string is: %s\n", nMyNum, szMyString) ;
da w efekcie następujący tekst:
Number = 60, or 3c in hex and my string is This is my String


Korzystanie ze zintegrowanego debugera 725
Na listingu 27.1 makroinstrukcja TRACĘ służy do wyświetlania zawartości tablicy przed i po
sortowaniu za pomocą prostego, ale bardzo efektywnego algorytmu.
Aby przetestować kod przedstawiony na listingu 27.1, należy za pomocą kreatora AppWizard
utworzyć prostą aplikację SDI. Dodajemy po prostu przedstawiony tu fragment kodu do funkcji
składowej OnNewDocument () klasy dokumentu, a następnie wywołujemy go wpisując do funkcji
OnNewDocument () odwołanie do funkcji DoSort ().
Jeśli teraz uruchomimy aplikację w debugerze (klikamy Build wybieramy opcję Start Debug i w
menu, które się pojawi, klikamy polecenie Go), będziemy mogli obejrzeć efekt makroinstrukcji.
Skróty klawiszowe przy debugowaniu
Szybszym sposobem uruchamiania procesu debugowania jest wciśnięcie klawisza F5. W ten sposób
uruchomimy debuger natychmiast, o ile plik wykonywalny jest kompletny. Jeśli nie, pojawi się
komunikat informujący o potrzebie uzupełnienia kodu.
Należy-upewnić się, że okno Output będzie widoczne (klikamy menu Yiew i wybieramy polecenie
Output) przy wyświetlaniu okna Output z karty Debug.
Listing 27.1. LST30_1.CPP - prosta funkcja sortująca wykorzystywana do demonstracji pewnych technik
debugowania





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void Swap(CUIntArray* pdwNumbers,int i) O
{
UINT uVal = pdwNumbers->GetAt(i) ;
pdwNumbers->SetAt(i, pdwNumbers->GetAt(i+1)) ;
pdwNumbers->SetAt(i+l,uVal) ;
}
void DoSort() {
CUIntArray arNumbers;
for(int i=0;i<10;i++) arNumbers.Add(1+rand()%100)
TRACĘ("Before Sort\n"); @
for(i=0;iTRACĘ("[%d] = %d\n",i+1,arNumbers[i]);
BOOL bSorted;
do (
bSorted = TRUE;


726 Poznaj Visual C++ 6
21 for(i=0;i22 {
23 if (arNumbers[i] > arNumbers[i+1])
24 (
25 Swap(&arNumbers,i);
26 bSorted = FALSE;
27 }
28 }
29 } while(!bSorted);
30
31 TRACĘ("After Sort\n"; O
32 for(i=0;i33 TRACĘ("[%d] = %d\n",i+1,arNumbers[i]);
34 }
O Funkcja Swap () zamienia miejscami dwie zmienne zajmujące różne pola tablicy.
@ Makroinstrukcja TRACĘ wykorzystywana jest do wyświetlania zawartości tablicy przed
sortowaniem.
Liczby są sprawdzane parami; jeśli są w złej kolejności, zamieniamy je miejscami.
O Makroinstrukcja TRACĘ wykorzystywana jest do wyświetlania zawartości tablicy po sortowaniu.
W funkcji z listingu 27.1 sortujemy tablice zawierające losowo wygenerowane (w linii 11) liczby (z
przedziału od l do 100). W liniach 13-15 wyświetlamy zawartość tablicy przed posortowaniem za
pomocą instrukcji TRACĘ. W liniach 17-29 sortujemy tablicę zamieniając miejscami te liczby, które są w
złym porządku (za pomocą przyzywanej w linii 25 funkcji SwapO). Implementowana w liniach 1-6
funkcja SwapO pobiera wskaźnik do tablicy i odpowiednią pozycję pola tablicy, a następnie wymienia
liczbę z tej pozycji z następną.
Po posortowaniu tablicy jej zawartość jest w liniach 31-33 ponownie wyświetlana w oknie Output
za pomocą instrukcji TRACĘ.
Efekty obu instrukcji TRACĘ przedstawione zostały w tabeli 27.3.


727
Korzystanie ze zintegrowanego debugera____ Tabela
27.3. Efekt działania programu sortującego


Przed sortowaniem Po sortowaniu
[l]

= 42

[1]

= 1

[2]

= 68

[2]

= 25

[3]

= 35

[3]

= 35

[4]

= 1

[4]

= 42

[5]

= 70

[5]

= 59

[6]

= 25

[6]

= 63

[7]

= 79

[7]

= 65

[8]

= 59

[8]

= 68

[9]

= 63

[9]

= 70

[10]

= 65

[10]

= 79

Algorytmy sortujące
Chociaż przedstawiona tutaj technika sortowania jest stosunkowo prosta i nie komplikuje ilustrującego
wykorzystanie makroinstrukcji TRACĘ przykładu, jest to jednak jeden z najmniej efektywnych
algorytmów sortowania. Jeśli potrzebny nam będzie szybki algorytm sortujący, powinniśmy przyjrzeć
się funkcji qsort () biblioteki czasu wykonania języka C++.
Makroinstrukcje ASSERT i VERIFY
Makroinstrukcja ASSERT służy do sprawdzania, czy zdefiniowany w niej warunek jest
prawdziwy. Makroinstrukcji przesyłany jest jeden parametr, który jest wyrażeniem przyj-
mującym wartość TRUE lub FALSE. Jeśli wyrażenie jest prawdziwe, wszystko jest w porządku.
Jeśli wyrażenie okaże się fałszywe, wyświetlane jest (widoczne na rysunku 27.7) okno Debug
Assertion Failed dające nam do wyboru trzy opcje: Abort, Retry i Ignore. Okno to podaje
również program, plik źródłowy i numer linii, w której znajduje się aser-cja. Jeśli wybierzemy
opcję Abort, proces debugowania zostanie przerwany. Najbardziej przydatną opcją jest Retry,
ponieważ po jej wybraniu kompilator pokazuje fragment kodu z makroinstrukcją ASSERT,
dzięki czemu możemy ustalić, co poszło nie tak. Jeśli znamy przyczyny uruchomienia asercji,
możemy wybrać opcję Ignore, która poleci debugerowi kontynuowanie wykonywania
programu. Należy jednak pamiętać, że może to zaowocować poważnym błędem podczas
dalszego wykonywania programu.


728 Poznaj Visual C++ 6
Microsoft Visual C" Debug Library
Debug Assertion Failed!
Program:
E:\USINGVC6\CHAP30\LISTINGS30\ERRORPRONE\DEBUG\ER
RORPRONE.EXE
File:
E:\UsJngVC6\Chap30\Listings30\ErorProne\ErrorPfoneDoc.
cpp Linę' ĄĄ
For information on howyour progrnm can cause an assertion
failure, see the Visual C++ documentation on asserts,
(Press Rettyto debug the applicotion)
Abort Retty Ignore
Rysunek 27.7. Okno dialogowe Debug Assertion Failed ułatwia wyszukiwanie błędów w programie
Najczęściej makroinstrukcję ASSERT wykorzystujemy, żeby upewnić się, że parametry przesyłane
danej funkcji są poprawne. Przykładowo, możemy dodać do (przedstawionej na listingu 27.1) funkcji
Sort () makroinstrukcję ASSERT sprawdzającą jej parametry:
ASSERT(pdwNumbers) ;
ASSERT(i>=0 && i<10);
W ten sposób upewnimy się, że wskaźnik do tablicy nie ma wartości zero i że pozycja elementu
mieści się w przedziale od O do 9. Jeśli któryś z parametrów nie spełnia tego warunku, wyświetlane jest
okno dialogowe Debug Assertion Failed. W ten sposób możemy wyławiać błędy spowodowane
przesłaniem do funkcji złych parametrów. Sprawdzanie parametrów funkcji za pomocą makroinstrukcji
ASSERT jest bardzo dobrym nawykiem.
Inna makroinstrukcja, ASSERT_VALID, wykorzystywana jest do testowania klas wywodzących się z
klasy cobject (czyli większości klas MFC). Makroinstrukcja ta testuje obiekt i jego zawartość,
sprawdzając jego legalność. Makroinstrukcji przesyłamy wskaźnik do obiektu, który ma zostać
sprawdzony.
ASSERT_VALID(pdwNumbers) ;
Kolejna makroinstrukcja, ASSERT_KINDOF, służy do sprawdzania klasy obiektów wywodzących się z
klasy cobject. Przykładowo, możemy sprawdzić, czy wskaźnik do obiektu widoku należy do właściwej
klasy widoku wpisując:
ASSERT_KINDOF(CYourSpecialView,pYView) ;
Okno dialogowe Assertion Failed jest wyświetlane, kiedy testowany obiekt nie należy do podanego
typu lub któregoś z jego typów pochodnych.


Korzystanie ze zintegrowanego debugera 729
Inne makroinstrukcje ASSERT
Niektóre inne rzadziej wykorzystywane asercje, to ASSERT_POINTER wymagająca przesłania jej jako
parametrów wskaźnika i odpowiedniego typu obiektu przechowującego dane. Makroinstrukcja
sprawdza następnie, czy wskaźnik nie ma wartości NULL, czy obiekt zajmuje legalny adres pamięci i
czy obszar pamięci zajmowany przez obiekt jest legalny dla całego rozmiaru obiektu. Podobna do niej
makroinstrukcja ASSERT_NULL_OR_POINTER akceptuje wskaźnik równy NULL, ale nie obiekty, które
zajmują adres pamięci nielegalny dla danego procesu.
Należy uważać, aby w żadnym wypadku nie umieszczać w makroinstrukcjach ASSERT fragmentów
kodu niezbędnych do działania programu, są one bowiem w trybie Relase usuwane z wersji
przeznaczonej dla klienta. Typowy błąd generowany w ten sposób wygląda tak:
int a = 0;
ASSERT(++a > 0) ;
if (a>9) MyFunc ();
Błędy takie są dość trudne do wyśledzenia. Powyższy fragment kodu zwiększa wartość liczby
całkowitej a w linii z makroinstrukcją ASSERT. Następnie jeśli parametr a jest większy od zera, przyzwana
zostaje funkcja MyFunc (). W trakcie debugowania nie pojawią się żadne błędy. Jeśli jednak
skompilujemy program w trybie Relase i uruchomimy go ponownie, okaże się, że program w tym
momencie padnie, ponieważ operacja ++a jest wykonywana wewnątrz usuwanej w trybie Relase
makroinstrukcji ASSERT.
Problem pozwala rozwiązać makroinstrukcja VERIFY. Makroinstrukcja VERIFY działa tak samo jak
makroinstrukcja ASSERT wyświetlając okno Debug Assertion Failed, jeśli zdefiniowany w nim warunek
okaże się nieprawdziwy. W trybie Relase warunek jest nadal testowany, ale okno z komunikatem o
błędzie nie jest już wyświetlane. Dlatego makroinstrukcje VERIFY należy stosować w odniesieniu do
wyrażeń warunkowych, które muszą być wykonane w programie, a makroinstrukcje ASSERT, kiedy
wyrażenie warunkowe jest nam potrzebne tylko w czasie debugowania programu. Jeśli więc zastąpimy
makroinstrukcję ASSERT makroinstrukcją VERIFY, program będzie działał prawidłowo również po
skompilowaniu w trybie Relase:
VERIFY(++a > 0) ;
Znacznie częściej jednak makroinstrukcja VERIFY wykorzystywana jest do testowania kodów
zwracanych przez funkcje:
VERIFY(MyFunc () !=FALSE);


730 Poznaj Visual C++ 6
Punkty kontrolne i przeglądanie kodu programu krok po kroku
Jedną z najbardziej użytecznych technik odszukiwania błędów w programie jest ko-
rzystanie z punktów kontrolnych (ang. breakpoints) i za ich pomocą przeglądanie kodu krok
po kroku (ang. single stepping) tak jak będzie on wykonywany. Liczba różnych rodzajów
punktów kontrolnych i dostępnych związanych z nimi informacji, które możemy wydobyć z
kodu programu przeglądając go krok po kroku, jest w C++ bardzo duża. Tutaj pokażemy tylko
niektóre możliwości tej techniki.
Kluczowym narzędziem służącym do śledzenia wykonywanego kodu są punkty kon-
trolne. Punkty kontrolne umieszczamy w dowolnych punktach programu, a następnie
uruchamiamy program w debugerze. Kiedy program dotrze do miejsca oznaczonego punktem
kontrolnym, w oknie edytora wyświetlany jest fragment kodu oznaczony punktem
kontrolnym. Możemy teraz przeglądać dalej kod krok po kroku lub wznowić wykonywanie
programu.
Punkt kontrolny dodajemy do programu klikając odpowiednią linię kodu (klikając kursor
edytora w odpowiedniej linii kodu w oknie edytora), a następnie wciskając ikonę punktu
kontrolnego (...) na pasku Build MiniBar (rysunek 27.8) lub wciskając klawisz F9. Bardziej
skomplikowane punkty kontrolne można dodawać lub usuwać za pomocą polecenia
Breakpoints menu Ędit, które wywołuje widoczne na rysunku 27.9 okno dialogowe
Breakpoints. Dodany do kodu punkt kontrolny obrazowany jest małym czerwonym kółkiem
w linii, w której został umieszczony. Punkty kontrolne muszą być umieszczane w liniach kodu
tak jak są one postrzegane przez debuger, dlatego kompilator przesuwa czasem ustawiane
przez nas punkty kontrolne.

2 4
Rysunek 27.8. Dodawanie do programu punktów kontrolnych za pomocą paska narzędziowego Build
MiniBar
1. Compile (Kompiluj); Ctri+F [! F6?]
2. Build (Buduj); F7
3. Stop Build (Przerwij budowanie); Ctri+Break
4. Go (Uruchom); F5
5. Insert/Remove Breakpoint (Dodaj/Usuń punkt kontrolny); F9


Korzystanie ze zintegrowanego debugera 731
Sreakpoints 1:11::1111;? :l^:::-,,.:l:,,jl:lil:::::^l, ^^'^s1;;/'?;!,!,!:::,,:'1"';!!"-
"'^.^^







Location Data Messages

OK

'Breakat

Cancel

|ap30\Usfings30\ErrorFrone\EfiorProneDoc.cpp,}.69 >

Edit Code

Condition Clickthe Condition button i(you



paramelefsforyourbreakpoint.



Sreakpoints:





Bemoue



p;emove Ali









Rysunek 27.9. Dodawanie punktów kontrolnych za pomocą okna dialogowego Breakpoints
Możemy włączać i wyłączać punkty kontrolne wciskając przycisk z ikoną dłoni lub usuwać je
wybierając w oknie dialogowym Breakpoints przycisk Remove lub Remove Ali. Możemy również
pozostawić punkty kontrolne, ale uczynić je nieaktywnymi klikając znak zaznaczenia po lewej stronie
punktu kontrolnego wymienionego w liście u dołu okna Breakpoints. Drugie kliknięcie ponownie
uaktywni punkt kontrolny.
Po rozmieszczeniu w programie punktów kontrolnych możemy uruchomić go w de-bugerze,
klikając po kolei Build, Start Debug i Go. Możemy również skorzystać ze skrótu klikając ikonę Go (...)
na pasku narzędziowym Build Minibar (przedstawionym na rysunku 27.8) lub wciskając przycisk F5.
Program będzie wykonywany normalnie, dopóki nie dotrze do punktu kontrolnego. W tym
momencie program zatrzyma się wyświetlając strzałkę w linii z punktem kontrolnym. Możemy teraz
przyzwać pasek narzędziowy Debug (rysunek 27.10) i rozpocząć przeglądanie kodu programu krok po
kroku.





a
CUIntArray arHiutbers:
!:orTKACE("Before L L or (i c O; i < arHuiii TRACĘ("[%d]
W ^ ^-h & l ^ t1) fl1 {'P 1}
Sd- |;p'(3 E m^]
BOOŁ bSorted;
do {
bSorted
TRUE;
Ec;.r(i'=0;ili (arHuntbers[i] > arHumbers[i+l ])
{T J- Swap (ŁarlIuDłbers. i ) ;
01
MM
bSorted FAISE;





Rysunek 27.10. Debuger zatrzymał program w miejscu oznaczonym punktem kontrolnym
wyświetlając pasek narzędziowy Debug


732 Poznaj Visual C++ 6
Gdy program jest już zatrzymany w debugerze, możemy oglądać zawartość większości
zmiennych po prostu kierując na nie kursor w oknie edytora. Ich zawartość wyświetlana jest na
etykietce ToolTip w miejscu, w którym znajduje się kursor. Bardziej szczegółowe informacje
można uzyskać przeciągając zmienną do okna Watch, które zostanie opisane dokładniej za
chwilę.
Opóźnienia przy wykonywaniu niektórych poleceń
Niektóre z operacji przeglądania programu krok po kroku, takie jak Step Out czy Run to
Cursor, wykonywane są z pewnym opóźnieniem widocznym szczególnie, gdy kompilator
musi po drodze wykonać wielokrotną pętlę. Jest to efektem tego, że debuger musi
zatrzymywać się po każdej wykonanej instrukcji, aby sprawdzić, czy został już osiągnięty
punkt zdefiniowany przez programistę.
Przeglądając program krok po kroku poszczególne posunięcia wykonujemy klikając jeden
z czterech przycisków z ikonami nawiasów paska narzędziowego Debug: (...) lub klikając
menu Debug i wybierając polecenie o odpowiedniej nazwie. Dostępne polecenia
przedstawione zostały w tabeli 27.4.
Tabela 27.4. Polecenia umożliwiające przeglądanie programu krok po kroku


Ikona/Polecenie
Step Into
(Wejdź do)
1^" Step0ver
(Przejdź ponad)
1^ Step Out
(Wyjdź z)
I11 Run to Cursor
(Idź do kursora)
1^- Go
(Wznów)
Klawisz skrótu Efekt polecenia
Fll Debuger wykona bieżącą linię i jeśli kursor znajduje się nad
odwołaniem do funkcji, wejdzie do kodu tej funkcji
F10 Działa podobnie jak poprzednie polecenie z
tym że wykonuje zaznaczoną kursorem funkcję i
zatrzymuje program dopiero po jej wykonaniu
Shift+Fll Debuger wykona do końca funkcję, w której się
znajdujemy, wychodząc do funkcji ją przywołującej
Ctri+FlO Debuger będzie wykonywał program, dopóki nie trafi do
miejsca zdefiniowanego pozycją kursora. Pozycje
ustalamy po prostu klikając linię, do której chcemy
dotrzeć
F5 Program jest wykonywany aż do osiągnięcia następnego
punktu kontrolnego


Korzystanie ze zintegrowanego debugera 733
Shift+FS Przerywamy debugowanie i wracamy do trybu ': Stop Debugging
umożliwiającego edycję kodu
(Przerwij debugowanie)
i'^l CtrI+Shift+FS To polecenie uruchamia program od początku l ' : Restart


(Od początku)
To polecenie przerywa wykonywanie programu
l" Break Execution
(Przerwij)
l"/ Alt+FlO To polecenie pozwala skompilować kod pro-1 Appły Code
gramu po wprowadzeniu niezbędnych popra-Changes ^,g^; kontynuować
debugowanie od miejsca, (Wprowadź zmiany) w którym przerwaliśmy
Za pomocą tych poleceń możemy oglądać, w jaki sposób program jest wykonywany i jak
zmienia się wartość wykorzystywanych w nim zmiennych. Żółte strzałki w oknie Editor
pokazują następną wykonywaną instrukcję.
W dalszej części rozdziału opiszemy niektóre z okien wykorzystywanych w debuge-rze do
obserwowania kodu programu.
Edit and Continue
Nowym bardzo wygodnym narzędziem wprowadzonym w Visual C++ 6.0 jest Edit and
Continue. Narzędzie to ta pozwala zmieniać lub edytować kod programu w miejscu, w którym
zatrzymaliśmy go w debugerze. Po dokonaniu niezbędnej edycji uaktywni się polecenie Appl^
Code Changes z menu Debug (i odpowiadająca mu ikona paska narzę-
l"^
dziowego l ). Możemy teraz wybrać to polecenie (lub kliknąć przycisk paska narzędzi)
i skompilować wprowadzone zmiany, a następnie kontynuować proces debugowania. Dzięki
temu możemy naprawiać błędy w kodzie programu na bieżąco w trakcie debugowania i
kontynuować proces debugowania z tymi samymi wartościami zmiennych co przed
rozpoczęciem edycji. Jest to szczególnie wygodne przy usuwaniu błędów ze skom-
plikowanych, rozbudowanych programów.
Śledzenie zmiennych programu
Okna Watch i Yariables przedstawione zostały na rysunku 27.11. Okna te służą do wy-
świetlania wartości zmiennych w momencie, gdy wykonywanie programu zostanie zatrzymane
w debugerze. Okna te możemy wywołać kukając w menu yiew polecenie Debug
r"'""
Windows lub na pasku narzędziowym wciskając odpowiadające im ikony l


734 Poznaj Visual C++ 6
Mtoinoll Vliiil C<ł Itaeat] lEilolFmneDoc.cpp]


l Fł. yi Vin Insart P.opcI fiebug Imli WirAm Hett
^ESBa ; EfeK " .; 'ciaafi^P -3r
~3
(g S-"' ! "si :, !:)
^-^----^-^-^ai

.. ,^1,,"^1^-1 "..""" ". , d^fca


; TRACE("Before Sorf-n"); ^Oi^i^O;
i.lłl.crMllbt-rs[l]);
; 600L bSorfced; ^ESHSSSSUSmCSSttSEi
? : N? (:::.^:': <> yuprpi)
^o 1 J *-1100 El D 55) Cl





:.
^
""rf

[-; * e CE
rtCTProfleApp ^j :
CEnoFloneAH: :
InHIn.-tónctl) ;
Ot>ł|l[AllJt(] : B-
^CEnotPloneDoc
.:::' ;
^AKtrtVa6dO,
; : Ą ~CEritifProfieD.
DuniriCDitBpC !
^ Or^ewOociiffit : ft
SetiakelCArch - * f1
CE[TnP[weView ,
i r--"-'^





li (arllubers[il > arMutbers [ l+l ] )
0 Svap(&arHunbers. i). bSorted FAŁSE;
i
} Silll!.(lhSorted):
TRACECtfter Sort-o")'
hU ':..';- ,".' " ' " " ":;:':., ^,:,,",/"..1,..,. ^,,:.",,


" i OK.. | ^ Rtt.. J]FtV...







^ Name lYalue :^??^M^.t:|?: j-

-^l Csriexl:j[:ioSo[10

d-

BarKuitbers '' ' : {OTłntirray} ' ' i,
"1-B1 CObiect """ '": {CObject} '"" "-'-- jj ^ HB
pDta" ' '" "7lil>77i| P' nliMiSiz' '13 "" - '"" W || l-:-
nSnaBy ^ P. . .. ., 'f 1



-XS..
Ł


B arNuMbers l{CUIntArraył j 3 Łarituiibera ;
l)ii0064f6c {CBIntżrrsy} "\Q 'ĆObiect ' ' ' :
{CÓbaect}

^i
.......
^


"^''H.nSlEe "' ''10 ' ^ |~
*tnMaxSise 13 ^ :-- R
nGrowBy : 0



..... ^






E aifA*w ii^ia^i,w^i((W^te;s,:ii'teia l:

^vI'^l^./lM^\:W,/m^

:-^
^"^^:^^:::i5?":,,;:<:3
:i

:^;:^ż
;s:

fiM^^laelilitess^^ .^^"Kiias;!:!



BIS!




l 2 3
Rysunek 27.11. Okno Watch wyświetla w trakcie debugowania bieżące wartości zmiennych programu
1. Okno Watch
2. Okno Variables
3. Lista kombinowana Context
Okno Variables wyświetla zmienne lokalne funkcji widniejącej w liście kombinowanej
Context. Aby wybrać bieżącą funkcję, rozwijamy listę. Lista ta zwana stosem przy-zwań
(Cali Stack), pokazuje listę funkcji przywołanych po drodze do miejsca, w którym
zatrzymaliśmy debuger, z aktualnie wykonywaną funkcją na szczycie listy. Jeśli wybierzemy
tutaj inną funkcję, wyświetlone zostaną odpowiednie dla niej zmienne lokalne.
Możemy rozwinąć przedstawione w drzewie wskaźniki do obiektów za pomocą znaku
plus na lewo od ich nazwy. Specjalny wskaźnik this języka C++ ukrywa funkcje składowe
klasy i po otworzeniu pozwala obejrzeć zmienne składowe bieżącego obiektu.
Okno Watch pozwala również na ręczne wpisywanie nazw zmiennych, których wartość
chcemy uzyskać lub przeciąganie ich z okna edytora (po podświetleniu ich za pomocą
myszy).Wartości przedstawionych w oknie zmiennych są wyświetlane tak długo, aż nie
wypadną poza jej zakres kompetencji.


Korzystanie ze zintegrowanego debugera__________________________735
W oknie Watch możemy również zajrzeć do wnętrza prostych przekształceń typów i indeksów
tablic oglądając wartości odpowiednich zmiennych. Kliknięcie prawym klawiszem myszy pozwala
zmieniać formę zapisu zmiennej z dziesiętnej na szesnastkową i na odwrót. Zmienne wyświetlane w
oknach Watch i Variables są uaktualniane na bieżąco w trakcie przeglądania programu, dzięki czemu
możemy śledzić jak program zmienia ich wartości.
Inne okna debugera
Inne okna debugera można wywoływać za pomocą polecenia Debug Windows z menu Yiew,
wybierając odpowiednie okno lub po prostu klikając odpowiadająca mu ikonę na pasku
narzędziowym Debug. Dostępne okna to:
" l" QuickWatch. Jeśli wybierzemy w kodzie programu zmienną i otworzymy okno QuickWatch (na
przykład wciskając Shift+F9), możemy wyświetlić bieżącą wartość wybranej zmiennej. Ten sam
efekt uzyskamy wpisując bezpośrednio nazwę zmiennej i wciskając przycisk Add Watch, dzięki
czemu zmienna pojawi się w oknie Watch.
Wyliczanie wartości wyrażeń za pomocą okna QuickWatch
Okno QuickWatch możemy również wykorzystać jako kalkulator do wyliczania wyników wyrażeń
matematycznych i logicznych, wykorzystując również w obliczeniach bieżące wartości zmiennych. Po
wciśnięciu przycisku Recalculate wynik wyrażenia pojawi się w polu Current Yalue.





F
Registers. To okno wyświetla bieżące wartości przechowywane w rejestrze procesora, co
przydaje się bardzo rzadko, bo tylko w sytuacjach, gdy w programie pojawią się problemy, które
rozwiązać można wyłącznie na poziomie kodu asemblera.
l Memory. Okno Memory wyświetla zawartość komórek pamięci przestrzeni adresowej (ang.
address space) aplikacji. W poszczególnych kolumnach podawany jest adres, wartość szesnastkową
i wartość znakowa każdej ośmiobitowej jednostki pamięci. Wywołując prawym klawiszem myszy
menu skrótów możemy zmienić sposób wyświetlania tak, by przedstawiane były jednostki
rozmiarów Byte, Short lub Long.
1^* Cali Stack. Okno Cali Stack wyświetla listę funkcji, które musiały zostać przyzwane, aby
uruchomić bieżącą funkcję, jak również wartości parametrów przesłanych tym funkcjom. Okno to
pozwala śledzić, w jaki sposób program przywołuje daną funkcję. Klikając dwukrotnie wybraną
funkcję wyświetlimy w oknie edytora miejsce, w którym funkcja została w programie przywołana.


736_____________________________________Poznaj Visual C++ 6
Jeśli kod źródłowy nie jest w danym momencie dostępny przedstawione funkcje przyjmują postać:
KERNEL32! bff88f75( ) Po kliknięciu funkcji otrzymamy jej kod w
kodzie asemblera.
' l^ Disassembly. Przycisk paska narzędziowego (lub opcja menu) Disassembly
pozwala na wybór między wyświetlaniem czystego kodu C++ a kodu C++ przemieszanego z kodem
asemblera. Kiedy kod źródłowy jest niedostępny, wyświetlany jest tylko kod asemblera.
Przeglądanie kodu asemblera
W oknie Disassembly debuger wykonuje krok po kroku, instrukcja po instrukcji kod asemblera. Kod
asemblera jest to (w pewnym uproszczeniu) kod programu w takiej postaci, w jakiej widzi go
komputer. Kod ten zawiera najbardziej podstawowe instrukcje procesora. Przeglądanie krok po kroku
kodu asemblera jest bardzo długą i żmudną pracą, ale czasami to jedyny sposób na ustalenie co w
kodzie programu jest nie tak. Często jest to również jedyna metoda usuwania błędów generowanych
przez kompilator, które mogą się pojawić, gdy kompilujemy szczególnie skomplikowany kod
źródłowy.
Inne narzędzia debugowania
Oprócz narzędzi zintegrowanych z debugerem, w kompilatorze Visual Studio jest jeszcze kilka
niezintegrowanych, ale bardzo przydatnych narzędzi. Można je znaleźć w menu lools.
Narzędzia z menu Tpols umożliwiają śledzenie zachowań pewnych specyficznych elementów takich
jak komunikaty Windows, działające w systemie procesy czy zarejestrowane obiekty OLE, dostarczając
nam dodatkowych informacji, które przydają się w czasie usuwania błędów z aplikacji.
Spy++
Jednym z najbardziej użytecznych narzędzi jest program Spy++. Za jego pomocą możemy ustalić
hierarchiczne zależności między oknami potomnymi a ich rodzicami, pozycję i zestawy znaczników
każdego okna oraz jego klasę bazową. Możemy również śledzić komunikaty przesyłane do danego okna.
Po uruchomieniu program Spy++ wyświetla wszystkie okna działające w tej chwili na pulpicie
Windows, ich okna potomne i bazową klasę każdego z obiektów (rysunek 27.12). Drzewo w oknie
programu Spy++ z rysunku 27.12 zostało przewinięte tak, aby pokazać


Korzystanie ze zintegrowanego debugera 737
okna związane z programem Microsoft Windows CD Player (Odtwarzacz CD). Program
Spy++ wyświetla wszystkie przyciski i listy kombinowane, które są oknami potomnymi
głównego okna programu CD Player.
i3 Spy Ttee Search

^B:::?::S^^.'::::F:^iW^''':'^^::S::i:::i:S::^:;i:::::.:!?E?^''^:''!'?^::::i:?::tiit^^:żi:i:^
:^ ^i^iSiSSt^^S^'''"!^}^!1
View Messeges Window Hełp -)gj xj

n|%'l| a

^



i'-:.:. M
M

i-; '









- n

OOOOOA70-
CD
n000007BC"
1300000514"'
DOOOOOFFB
-

^ay
e
Bu
tt
Butt
c
Butt

" SJECdPlayerClass on

J




Op Oisc i;ia" (Jptions Halp




13000004
n
OOOOOE

A4
"'
Fa
'"

Butt
Butt



>





"'f

J





H<52:23> B

4
4

B
.

>
N

S!




n 000002
0 000007
a 000009

AB'"
Bun B4
Butt
64 ""
Buttc

on ' on
Tolai Ply 52 23 m:s Track 03:50 m;s






in




a 00000544 "|-]<52:23>" SJELEDCIass i n 00000104 "ŁArtIst:" Static ;



13000002

A4""ComboBox



n OOOOOBBO "TItle:" Static

\ \ [30000091 B "New T

tle" SJETextClass









; -n 00000400 "Traci,

k:" Static









^ l n ooooo E

24""ComboBox i

: nOOOOOBE4""ToolbarWlnclow32

n
OOOOO
F
<| W^

BO "" msctlsstatusbar32

^
Ł iŃUM | "

PorHelp. press F1 ^11^^:::::.!::::::::^:. .:^::1::::1it^i:ffi!ll^:^;^.:l^l';::^..::
,1::1::


Rysunek 27.12. Program Spy++ wyświetlający listę okien programu CD Player
W menu Spy programu można znaleźć następujące polecenia:
Messages. Jest to jedna z najbardziej przydatnych opcji, umożliwiająca obserwację
komunikatów wysyłanych do określonego okna (w tym do okien naszej aplikacji). Dzięki
niej możemy również filtrować komunikaty tak, aby nie dochodziły do nas komunikaty
informujące na przykład o poruszeniach myszy.
Aby móc obserwować komunikaty, należy za pomocą polecenia Messages przywołać
pokazane na rysunku 27.13 okno dialogowe Message Options. Przeciągając narzędzie
Finder Tool nad dowolnym działającym w systemie oknem możemy teraz wyświetlać
informacje na jego temat. Program Spy++ podświetla również wybrane okna, dzięki czemu
możemy rozróżnić okno obramowujące od jego okien klientów. Inne strony okna
dialogowego pozwalają definiować opcje filtrujące i opcje regulujące sposób wyświetlania
informacji. Aby zamknąć okno dialogowe Message Options klikamy OK.
Na rysunku 27.14, możemy zobaczyć komunikaty generowane przez pasek narzędziowy
prostej aplikacji SDI. Jak widać, bez włączonych opcji filtrowania otrzymywać będziemy
masę komunikatów informujących o poruszeniach myszy i sprawdzających status kursora.
Możemy jednak również zauważyć znajomy komunikat WM_LBUTTONUP razem z
odpowiednimi parametrami definiującymi pozycję, na której zaszło zdarzenie.


738 Poznaj Visual C++ 6
Message Oplions


ToolbaiWindow32
5W004E
(286.87)-(502.119)2)6x32
FFFC90DB
FFFAODOB
r Ali Windows in System
Windows Messages j Outpu) WindowFindef Tool
1
Saiacted Objęci Window;
0000097C Takt
Drąg the FinderTool overa windowto select (hen
releass the mous@ but!on.
CIass;
Style;
Rect-Thread
ID:
Process ID:
FinderTool' |__| p !HideTŚpy'ti | ;
AdditionalWindows r" Parent f~ Windowa Ol
Same JN'ead
r Oliidren 1" Windows of Same Process
r Save Settings as Defaull
OK Cancsl Hełp





Rysunek 27.13. Okno Message Options programu Spy++ wykorzystywane do definiowania opcji
śledzenia komunikatów okna
" Microsolt Sny" - [Messages (Window 0000097C)]
3l Spy I'6 Sfiarch yiew Massages 'ffindow Uelp
n|%|e| ^ ^ij 8Mx| ł4_
0000097C P WM_MOUSEMOVEfwKeys:MK_LBUTTONxPos:l95yPos:31
0000097CPWM_MOUSEMOVEfwKeys:MK_LBUTTONxPos:196yPos:32
0000097C P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:196
yPos:33 0000097C P WM_TIMERwTimerlD:57345tmprc:00000000
0000097C P WM_TIMERwTimerlD:57345tmprc:00000000 0000097C P
WM_TIMER wTimerlD:57345 tmprc:00000000 0000097C P
WM_LBUTTONUPfwKeys:OOOOxPos:196 yPos:33 0000097C S
WM_CAPTURECHANGED hwndNewCapture:00000000 0000097C R
WM_CAPTURECHANGED
For Hełp, press F1
Rysunek 27.14. Lista komunikatów dla paska narzędziowego w oknie programu
Spy++
Windows. Okno przedstawiające przegląd okien dla pulpitu Windows przedstawione zostało na
rysunku 27.12. Dwukrotne kliknięcie któregokolwiek z okien wyświetli arkusz właściwości
zawierających informacje o położeniu okna i ustawienia wszystkich jego znaczników. Jeśli chcemy
uaktualnić te informacje, musimy w menu Windows wcisnąć polecenie Refresh.
Processes. Ta opcja pozwala nam obejrzeć listę aktualnie działających programów (czyli
wykonywanych przez system procesów). Procesy te można otwierać, aby oglądać wątek (ang.
thread) każdego z nich i wszystkie okna powiązane z danym wątkiem.
T^hreads. Ta opcja oferuje te same informacje co poprzednia, nie podaje jednak poziomu
procesu w hierarchii. Dzięki czemu na jednej liście możemy obejrzeć wątki wszystkich
procesów działające w danym momencie na komputerze i wszystkie przynależne im okna.


Korzystanie ze zintegrowanego debugera 739
Program Spy++ jest zbyt skomplikowany, aby opisać go tutaj dokładnie, jednak jako
narzędzie ułatwiające zrozumienie struktury różnych hierarchii systemu Windows i wysy-
łanych przez system komunikatów jest nieoceniony. Za jego pomocą możemy dowiedzieć się
bardzo wiele po prostu podglądając działanie komercyjnej aplikacji. Program ten jest również
bardzo praktycznym narzędziem pomagającym rozwiązywać pojawiające się podczas
debugowania problemy z komunikatami. Pomaga również dbać o to, aby okna aplikacji
otrzymywały właściwe komunikaty w odpowiedniej kolejności.
PATRZ TAKŻE
Więcej informacji na temat komunikatów Windows znaleźć można w rozdziale 4.
Process Viewer
Więcej informacji na temat wykonywanych w systemie procesów możemy uzyskać
uruchamiając program Process Viewer (PView95.exe). Aplikację tę można uruchomić za
pośrednictwem przycisku Start klikając po kolei Programs (Programy) i Microsoft Visu-al
Studio 6.0 Tools. Process Viewer podaje listę procesów działających w tym momencie w
komputerze i pozwala sortować je przez klikanie nagłówków odpowiednich kolumn listy.
Możemy również wyświetlić wszystkie wątki procesu klikając po prostu odpowiedni proces.
Rysunek 27.15 przedstawia program Process Viewer razem z aplikacją Developer Studio
(MSDEV.EXE) wyświetlający listę wszystkich jej procesów.
Fite
Frocess
aixl
Proces
s

ewerA
pplii
IBfl
fflH

sSimi-.-^.wiiSiSs-f-
^s^t

|Typ9

^::!J:::::^:'-^-^^^^^

l

PVIEW35,
E>E
WNWORD
E".
ERRORP
RO..
Ifteiłła^ga
CDPLAYE
R.E,.

FFFBB
553
FFFD2
617
FFFAO
D...
FFFA4
717
FFF81
94B

Q (Normal) 1 8
(Normal) '.i 8 (Norniol)
1 8 (Normol) 7 B
(Norma)) 1

32-Bll
3;-Bil
32-Bil
32-fiil
32-Bit

C\PROGRAUFILES\DEVSTU
DIO\V
D\MICROSOF\OFFICE',WIN
WOf:^D E...
E;\USINGVC6\CHAP30\USTI
NGS30\... C;\PROGRAM
FILES\DEVSTUDIO\S...
C;\WINDCWS\COPIAYER.EX
E

^

TID | OWBingRID

:.": l Thfsńid
F^iori^lj:^!!!^

1'^h,:"
"

g,,!^:';:!^^!^!^^!::!!!!;^,!^

iil^^

FFFC974]
FFFAF47F
FFFBF09
B
FFFB10D
3
:FFFAAS)
F
FFFW3S7
FFFD7AE
3

FFFA4
717
FFFA4
747
FFFA1
747
FFFA4
747
FFFA4
747
FFFA4
7-17
FFFA4
747

8 (Normal) 7 (Below
Norma!) 9 (Above
Normal) 8 (Normal) 7
(Balów Normol) 6
(NormaO 8 (Norma!)







Rysunek 27.15. Program Process Viewer pokazujący wszystkie wątki procesów aplikacji
MSDEV.EXE
OLE/COM Object Viewer
Program OLE/COM Object Viewer wyświetla listę wszystkich zarejestrowanych w
systemie obiektów OLE/COM, włączając w to kontrolki ActiveX, biblioteki typów, osadzone
obiekty, obiekty automatyzacji i wiele innych.
Możemy również nawet tworzyć egzemplarze zarejestrowanych obiektów i oglądać ich
interfejsy. Program OLE/COM Object Viewer jest szczególnie użyteczny, gdy two-


740 Poznaj Visual C++ 6
rżymy aplikację OLE/COM lub szukamy trudnej do wyśledzenia wśród innych elementów
kontrolki ActiveX.
PATRZ RÓWNIEŻ
Jak wykorzystywać w aplikacjach kontrolki ActiveX pisaliśmy w rozdziale 9.
Jak tworzyć kontrolki ActiveX pisaliśmy w rozdziale 26.
Więcej informacji na temat OLE i COM znaleźć można w rozdziale 25.
MFC Tracer
Za pomocą przedstawionego na rysunku 27.16 programu MFC Tracer możemy zatrzymać
normalne śledzenie wykonywanego programu lub dodać do wtedy wyświetlanych informacji
informacje uzyskane z systemu Windows. Główne okno programu zawiera listę opcji, które
możemy zaznaczyć, aby uzyskać dodatkowe informacje.
Możemy między innymi załączyć informacje na temat komunikatów Windows, ko-
munikatów baz danych, komunikatów OLE i innych informacji przydatnych do odnajdywania
trudnych do wyśledzenia błędów. Komunikaty te generowane są przez kod MFC w zależności
od wybranych znaczników.
Możemy też wyłączyć standardowe informacje generowane przez naszą aplikację,
wyłączając opcję Enable Tracing.
MFC Tracę Options


OK
Cancel
Tracę output will ba ayailabla oniywhiteusingthe debugger i7
^nabietracing; f" Main messoge pump


F OLEtracing
1~ t^atabase tracing
f" MuHip!e applicationdebugging
r" Maicrriessage dispatch r
^M_COMMAND dispatch r
InternetCIientTracitig






Rysunek 27.16. Opcje programu narzędziowego MFC Tracer


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.


Wyszukiwarka

Podobne podstrony:
1 02 Korzystanie z zalet zintegrowanego ¶rodowiska programi
18 Obw M z 2013 r w spr wys stawek opł za korzyst ze rodow
Żądło Jak korzystać ze zdolności parapsychicznych
82 w sprawie opłat za korzystanie ze środowiska
19 Obw M z 2013 r w spr wys stawek należn za korzyst ze ródl dróg wodnych
Jak korzystać ze zdolności parapsychicznych [up by Esi]
zbiorcze zestawienie informacji o zakresie korzystania ze srodowiska wzor od II 05
81 ? ROZ w sprawie opłat za korzystanie ze środowiska
Opłaty za korzystanie ze środowiska
GR 8 Stawki opłat za korzystanie ze środowiska 2011
5 Rozp RM z 2008 r w spr opłat za korzyst ze rodow
Obw M ws stawek opłat za korzystanie ze rodowiska
Jak korzystać ze zdolności parapsychicznych
instrukcja bhp przy korzystaniu ze zjezdzalni

więcej podobnych podstron