Showing posts with label narzędzia. Show all posts
Showing posts with label narzędzia. Show all posts

22/02/2009

Zakamarki Visual Studio (cz. 4)

Home

W czwartym już poście dotyczącym różnych ciekawych funkcji środowiska programistycznego Visual Studio chciałbym przyjrzeć się dokładniej oknu Call Stack, a przy okazji poruszyć kilka innych tematów.

Okno Call Stack, breakpoint'y...

Czym jest breakpoint zapewne każdy programista wie, a jeśli nie to wstyd. Z drugiej strony nie jestem już taki pewny czy każdy programista zna wszystkie możliwości breakpoint'ów w Visual Studio: filtrowanie, warunki, zliczanie, uruchamiania makr czy wstawienie breakpoint'a do metody, której kodu nie znamy (o tym pisałem tutaj). Opisanie tych rzeczy to temat na oddzielny post. W tym miejscu skupmy się na użyciu pułapek w oknie Call Stack.

Po pierwsze umożliwia ono wstawianie breakpoint'ów. W tym celu wybieramy interesującą nas ramkę (metodę), zaznaczamy ją i naciskamy przycisk F9 (albo wywołujemy menu kontekstowe i wybieramy Breakpoint -> Insert Breakpoint). Breakpoint pojawi się zarówno w oknie Call Stack jak i na marginesie edytora kodu. W odwrotnym scenariuszu breakpoint postawiony w kodzie pojawi się w oknie Call Stack dopiero kiedy spowoduje zatrzymanie aplikacji. W każdej metodzie może zostać umieszczony wiele pułapek i trudno by je wszystkie umiescić w oknie Call Stack.

Przy pomocy okna Call Stack można również zarządzać pułapkami czyli określić warunek zatrzymania, filtr, wyłączyć pułapkę itd. W tym celu klikamy prawym przyciskiem wybraną pułapkę, z menu kontekstowego wybieramy Breakpoint, dalej interesującą nas komendę np.: Disable Breakpoint w celu wyłączenia pułapki lub Condition... w celu określenia warunku zatrzymania się pułapki.



Okno Call Stack umożliwia również ustawienie punktu śledzenia (ang. tracepoint). Tracepoint to w gruncie rzeczy specjalny breakpoint, który nie powoduje zatrzymania wykonania ale wypisanie komunikatu do okna Output. W celu ustawienie tracepoint z poziomu okna Call Stack wywołujemy menu kontekstowe i wybieramy Breakpoint -> Insert Tracepoint. Ustawienie punktu śledzienia z poziomu edytora kodu rozpoczyna się od ustawienia zwykłego brakpoint'a. Następnie należy wywołać menu kontekstowe, dalej wybrać When Hit... (sposób ten działa również w przypadku okna Call Stack). W oknie, które pojawi się zaznaczamy pola wyboru jak na rysunku:

Jeśli nie zaznaczymy pola wyboru Continue execution utworzymy zwykły breakpoint, który dodatkowo będzie powodował wypisanie komunikatu do okna Output. Tracepoint'y reprezentowane są przez romby:

W polu tekstowym okna When Breakpoint Is Hit możemy wpisać dowolny tekst, wyświetlić dowolną zmienną lub użyć jednego ze specjalny słów kluczowych. Ale po kolei. Aby wyświetlić zmienną należy wpisać { nazwa }. Słowa kluczowe wpisujemy natomiast po znaku $. Na przykład $PID służy do wypisania identyfikatora procesu. Nie będę omawiał wszystkich słów kluczowych ponieważ, jak widać na powyższym rysunku, ich opis można znaleźć w oknie When Breakpoint Is Hit. Zwrócę tylko jeszcze uwagę na bardzo ciekawą możliwość wyświetlenia stosu przy pomocy słowa kluczowego $CALLSTACK oraz na to, że przy tworzeniu komunikatu jesteśmy ograniczeni tylko do jednej linii.

Run To Cursor

Run To Cursor to bardzo znane polecenie, które umożliwia rozpoczęcie debugowania i wskazanie miejsca w kodzie, w którym debugger ma się zatrzymać (pod warunkiem, że nie zatrzyma się wcześniej z powodu breakpoint'a). Niewiele osób jednak wie, że polecenie to jest dostępna z poziomu okna Call Stack. Pozwala wskazać, do którego miejsca chcemy zwinąć stos. Wystarczy wybrać interesującą nas ramkę/metodę i z menu kontekstowego wybrać tę komendę.

Wartość argumentów

Inną bardzo fajną cechą okna Call Stack jest to, że podaje ono wartości przekazanych do funckji argumentów. Jeśli wartości argumentów nie są widoczne powinniśmy w onie Call Stack wywołać menu kontekstowe, a następnie zaznaczyć Show Parameter Values. No dobrze, w przypadku typów prostych jest to z pewnością przydatne ale co z typami złożonymi. Domyślnie zostanie wyświetlona po prostu nazwa typu ale można to zmienić korzystając z atrybutu DebuggerDisplayAttribute. Dla tych co nie wiedzą jest to jeden z wielu atrybutów, który pozwala na dostosowanie debuggera do naszych potrzeb (kolejny dobry temat na oddzielny post, a nawet kilka :)). Najłatwiej wyjaśnić to na przykładzie. Posłużmy się kodem jak poniżej:
public class Test
{
   private int value;

   public int Value
   {
      get { return this.value; }
   }

   public Test(int value)
   {
      this.value = value;
   }
}
Wartości typu Test zostaną przedstawione w taki o to mało interesujący sposób:

ConsoleApplication.Program.Equals(a = {ConsoleApplication.Program.Test}, b = {ConsoleApplication.Program.Test})

Wystarczy jednak zmodyfikować definicję klasy Test jak poniżej:
[DebuggerDisplay("Value = {value}")]
public class Test
{
...
}
Aby osiągnąć taki efekt:

ConsoleApplication.Program.Equals(a = Value = 1, b = Value = 1

Technika ta działa nie tylko w przypadku okna Call Stack ale również w przypadku innych okien debuggera czyli np.: Quick Watch.

Unwind To This Frame

Unwind To This Frame to jedna z komend dostępna w menu kontekstowym okna Call Stack. W większości przypadków kiedy wywołamy menu jest wyszarzona. Dostępna staje się tylko w przypadku kiedy zostanie rzucony wyjątek. W momencie rzucenia wyjątki środowisko najczęściej zatrzymuje wskazując linię w kodzie, w której wyjątek został wygenerowany (w szczególności może się nie zatrzymać jeśli wyjątek został obsłużony, a my nie mamy zaznaczonej opcji zatrzymywania na wszystkich wyjątkach). W oknie Call Stack ramka, w której został rzucony wyjątek wskazywana jest przy pomocy żółtej strzałki. Jeśli wyjątek został rzucony poza naszym kodem ostatnia ramka wskazująca nasz kod zostanie oznaczona strzałką zieloną .

Kiedy środowiska zatrzyma się już z powodu wyjątku możemy w oknie Call Stack wybrać interesującą nas ramkę, wywołać menu i wybrać komendę Unwind To This Frame. Spowoduje to zwinięcie stosu do wybranej ramki (działanie podobne do Run To Cursor) i umożliwi np.: zdiagnozowanie przyczyny wyjątku, modyfikację zmiennych lokalnych tak aby wyjątek nie został rzucony itd.

Polecenie Unwind To This Frame nie jest dostępne dla wszystkich ramek. Stos można zwinąć do każdej ramki powyżej ostatniej ramki, która wskazuje miejsce w naszym kodzie (pomiędzy żółtą i zieloną strzałką :)). Czyli jeśli wyjątek został rzucony bezpośrednio w naszym kodzie polecenie będzie dostępne tylko dla jednej ramki. Z kolei dla przypadku pokazanego niżej:

System.Xml.Serialization.XmlSerializer.Deserialize(xmlReader, encodingStyle, events)
System.Xml.Serialization.XmlSerializer.Deserialize(stream)
ConsoleTest.Program.Main(args)
...


Stos możemy zwinąć do jednej z metod Deserialize() albo do metody Main() w naszym kodzie.

Omawiając tą funkcjonalność należy koniecznie napisać o exception assistant. Całkiem możliwe, że większość użytkowników Visual Studio nigdy o nim nie słyszała. W sumie nie dziwne ponieważ jest domyślnie włączony (Tools -> Options -> Debugging -> General -> Enable the exception assistant). A do czego służy? Poniżej zamieszczam okienka wyświetlone przez środowisk w momencie rzucenia wyjątku kiedy asystent jest wyłączony (pierwszy obrazek) i kiedy jest włączony (drugi obrazek):



Mam nadzieję, że zalety asystenta są oczywiste :) ale asystent to nie tylko ładne okienko. Jeśli jest włączony w momencie rzucenia wyjątki środowisko wskaże zawsze linię w naszym kodzie nawet jeżeli prawdziwe źródło wyjątku jest gdzie indziej. Dzięki temu możemy po prostu "złapać" żółtą strzałkę, wskazującą w głównym oknie ostatnio wykonaną instrukcję, i przeciągnąć ją kilka linijek wcześniej lub kilka linijek dalej. Innymi słowy możemy ominąć wyjątek lub prześledzić jak został spowodowany. Przy włączonym asystencie polecenie Unwind To This Frame staje się trochę mniej ponieważ przydatne ale czasami możemy chcieć wyłączyć asystenta. W każdym razie dobrze zdawać sobie sprawę z jego istnienia.

Opisane techniki testowałem w środowiskach Visual Studio 2005 oraz Visual Studio 2008.

03/02/2009

Zakamarki Visual Studio (cz. 3)

Home

W trzecim już poście dotyczącym zakamarków Visual Studio, czyli mało znanych funkcji tego środowiska programistycznego, chciałbym napisać o oknie Object Test Bench. Okno to pozwala na szybkie testowanie wywołań metod statycznych i instancyjnych bez potrzeby pisania małych programików testowych. Jeśli po uruchomieniu środowiska nie jest widoczne możemy je wyświetlić wybierając View->Other Widnows->Object Test Bench. Aby wygodnie pracować z tym oknem przyda się również okno Class View (View->Class View), które służy do przeglądania typów zdefiniowanych w naszych projektach oraz w innych pakietach/podzespołach (ang. assembly), do których się odwołujemy. Ważna uwaga! Widok Object Test Bench może zostać użyty tylko i wyłącznie do testowania klas w projekcie oznaczonym jako StartUp oraz w pakietach, do których ten projekt się odwołuje. Co prawda zgodnie z dokumentacją powinniśmy móc testować wszystkie klasy w bieżącym rozwiązaniu (ang. solution) ale niestety nie jest to możliwe.

Dalej będę posługiwał się prościutką, a wręcz naiwną klasą zdefiniowaną poniżej, w celu omówienia zastosowania okna Object Test Bench.
public class Person
{
 private static int count = 0;
 
 private string name;
 private DateTime dateOfBirth;

 public string Name
 {
  get { return name; }
  set { name = value; }
 }

 public DateTime DateOfBirth
 {
  get { return dateOfBirth; }
  set { dateOfBirth = value; }
 }

 public Person()
 {
  Person.count++;
 }

 public Person(string name, DateTime dateOfBirth)
 : this()
 {
  this.name = name;
  this.dateOfBirth = dateOfBirth;
 }

 public int GetAge(bool inDays)
 {
  if(!inDays)
   return DateTime.Now.Year - dateOfBirth.Year;
  else
   return (DateTime.Now - dateOfBirth).Days ;
 }

 public static int GetCount()
 {
  return Person.count;
 }
}
Aby rozpocząć testowanie jakiejś klasy musimy zlokalizować ją w oknie Class View i wywołać dla niej menu kontekstowe. W menu kontekstowym powinny być widoczne dwie komendy:
  • Create Instance - Pozwala wywołać dowolny konstruktor dla danego typu i przypisać mu identyfikator. Stworzony obiekt pojawi się w oknie Object Test Bench. Dla stworzonych obiektów możemy następnie wywoływać metody instancyjne. W ten sposób nie powołamy do życia instancji klasy abstrakcyjnej czy też delegata.
  • Invoke Static Mehtod - Pozwala wywołać wybraną metodę statyczną danej klasy (również klasy abstrakcyjnej).
Polecenia te będą widoczne tylko dla klas z projektu głównego. Oczywiście przed wywołaniem metody statycznej, instancyjnej czy też konstruktora powinniśmy w interesujących nas miejscach w kodzie ustawić breakpointy.

Zacznijmy od wywołania metody statycznej. Po najechaniu w menu kontekstowym na polecenie Invoke Static Mehtod wyświetlone zostaną wszystkie metody statyczne dostępne w tej klasie. Należy wybrać jedną z nich. W przypadku naszej klasy testowej Person możemy wybierać pomiędzy GetCount() oraz dwie metodami odziedziczonymi po klasie Object: Equals(object, object) oraz ReferenceEquals(object, object). Po wybraniu GetCount() pojawi się okno potwierdzające chęć wywołania tej metody. Natomiast w przypadku metod, które posiadają parametry pojawi się okno pozwalające na wyspecyfikowanie wartości tych parametrów. Kiedy klikniemy Ok metoda zostanie wywołana. Na zakończenie pojawi się okno z wynikiem, które powinno wyglądać jak poniżej: Istnieje również możliwość zapisania wyniku wywołania metody. W tym celu powinniśmy zaznaczyć opcję Save return value (w oknie pokazanym powyżej) i nadać identyfikator dla zwracanego obiektu/struktury. Jeśli zdecydujemy się na zapisanie wyniku pojawi się on w oknie Object Test Bench: Stworzone obiekty reprezentowane są w oknie Object Test Bench przez prostokąty. Dla każdego obiektu możemy wywołać menu kontekstowe, które pozwoli nam wybrać metodę instancyjną do wywołania. Spróbujmy wykonać metodę posiadającą parametry np.: CompareTo(int). Po jej wybraniu powinniśmy zobaczyć okno jak poniżej: Warto zauważyć, że jeżeli metoda opatrzona była komentarze to zostanie on przytoczony. Po podaniu wartości parametru aktualnego i wybraniu przycisku Ok, metoda zostanie wywołana, a wynik zostanie przedstawiony w takim samym oknie jak przy wywoływaniu metody statycznej (jego też możemy zapisać).

Przyjrzyjmy się teraz poleceniu Create Instance i stwórzmy instancję klasy Person korzystając z konstruktora dwuargumentowego. Po wybraniu konstruktora powinno pojawić się okno przedstawionej dalej: Po pierwsze powinniśmy podać nazwę dla tworzonego obiektu. W tym miejscu trzeba uważać bo jeśli podanym nazwę, której już użyliśmy zostaniemy o tym poinformowani dopiero po naciśnięciu przycisku Ok, a nie ma możliwości cofnięcia się i naprawienia błędu.

Po drugie, podobnie jak przy wywoływaniu metod, należy podać wartości poszczególnych argumentów. Napiszę o tym troszeczkę więcej. Jeśli chodzi o typy proste nie ma co się rozpisywać. Po prostu wpisujemy liczbę lub ciąg znaków (zamknięty w cudzysłowy) i koniec. Troszeczkę trudniej jest z typami złożonymi. Na rysunku widać, że dla parametru dateOfBirth wstawiłem dziwną wartość dateTime1. Niestety ale w przypadku typów złożonych musimy najpierw stworzyć instancję danego typu, a dopiero potem powołując się na przypisaną danej instancji nazwę, użyć jej jako argumentu wywołania. Nazwa stworzonej instancji pojawi się na rozwijanej liście dla argumentów o odpowiednim typie.

W tym przypadku stworzyłem uprzednio strukturę typu DateTime i nadałem jej nazwę dateTime1. Zacząłem od zlokalizowania typu struktury w drzewie (najwygodniej posłużyć się przyciskiem Search u góry okna Class View). Następnie wybrałem polecenie Create Instance, dalej zdecydowałem się na konstruktor trójargumentowy, jako argumenty wywołania podając rok, miesiąc i dzień mojego urodzenia (wszystkie wartości to typy proste). Na koniec powróciłem do klasy Person i ponownie wybrałem interesujący mnie konstruktor. Jak widać tworzenie obiektów wymagających skomplikowanej inicjalizacji jest trudne i kłopotliwe ale te okno nie do tego służy.

Po stworzeniu instancji klasy Person możemy przystąpić do testowania jej metod, a właściwie jednej metody. Odbywa się to dokładnie w ten sam sposób jak opisałem na przykładzie metody CompareTo(int). Polecam spróbować wywołać metodę GetAge(bool) z parametrem true oraz false oraz sprawdzić jaki wynik zwraca metoda statyczna GetCount() po utworzeniu instancji klasy Person.

Jeszcze dwie uwagi na koniec. Jeśli wprowadzimy jakieś zmiany do kodu to okno Object Test Bench wymusi na nas ponowną kompilację źródeł. Równoznaczne jest to z utratą wszystkich obiektów, struktur, które umieściliśmy wcześniej w tym oknie.

W drugiej części tej serii postów pisałem o oknie Immediate. W ciekawy sposób współpracuje ono z oknem Object Test Bench. Dla przypomnienia w oknie Immediate również możemy wywoływać metody i tworzyć obiekty. Istotne jest to, że obiekty, które stworzymy w tym oknie pojawią się również w oknie Object Test Bench. Tutaj też obowiązuje warunek, żeby użyty typ pochodził z projektu oznaczonego jako StartUp lub z pakietu/podzespołu, do którego tej projekt się odwołuje. Kolejny warunek jaki musimy spełnić (wygląda to na błąd w Visual Studio) jest taki, że okno Object Test Bench nie może być puste w momencie tworzenia obiektu w oknie Immediate. Jeśli będzie tworzone obiekty nie zostaną do niego dodane. Co ciekawe jeśli w pewnym momencie dodamy jakiś obiekt do okna Object Test Bench (z poziomu okna Class View) to nagle pojawią się w nim również obiekty utworzone wcześniej w oknie Immediate.

Warto jeszcze dodać, że obiekty z okna Immediate w oknie Object Test Bench będą miały takie same identyfikatory jak zmienne użyta w oknie Immediate. Czyli wpisanie takiej komendy:
Person person = new Person();
spowoduje dodanie do okna Object Test Bench obiektu o nazwie person

Opisane techniki testowałem w środowiskach Visual Studio 2005 oraz Visual Studio 2008.

23/12/2008

Expression Studio

Home

W zeszłym tygodniu (dokładnie 17 grudnia) miałem przyjemność uczestniczyć w spotkaniu pod tytułem Nowoczesny interfejs użytkownika, oraz aplikacje web’owe - technologie i narzędzia Microsoft zorganizowanym przez firmę Microsoft. Uczestnictwo w tym spotkaniu naprawdę było przyjemnością, szczególnie jeżeli porównam je do Microsoft Technology Summit 2008, które według mnie było organizacyjną porażką, a nawet określił bym je pogardliwym mianem tzw. masówki. Spotkanie zorganizowane było w siedziby firmy Microsoft w Warszawie, a poprowadził je Artur Żarski (Developer Evangelist). W tej mini konferencji uczestniczyło około 60 osób. Prelekcja została przeprowadzono bardzo sprawnie i generalnie była ciekawa. Prelegent omówił pięć narzędzi wchodzących w skład Expression Studio:
  • Expression Media Encoder - obrabianie filmów
  • Expression Media - zarządzanie multimediami
  • Expression Web - projektowanie stron WWW
  • Expression Blend - projektowanie interfejsu użytkownika
  • Expression Designer - tworzenie grafiki wektorowej i rysunków
Dokładną charakterystykę i opis tych produktów znajdziemy na tej stronie.

Dla mnie najbardziej interesujące była cześć poświęcona narzędziom Expression Web oraz Expression Blend dlatego poświęcę im jeszcze kilka słów. Na początek należy stwierdzić, że te dwa narzędzia (zresztą pozostałe trzy podobnie) nie są przeznaczone dla programistów, a przynajmniej nie powinny być używane przez programistów. Nie powinny w tym sensie, że zalecane jest aby interfejsy użytkownika były projektowane przez wyznaczonych do tego projektantów. Oczywiście wiem jakie są realia. Napisałem tutaj o sytuacji idealnej.

Ogólnie zaleca się aby osoby/zespoły pracujące na interfejsem użytkownika były niezależne (odseparowane) względem osób/zespołów tworzących właściwą aplikację. Po pierwsze oba zadania wymagają zupełnie innych umiejętności. Po drugie dzięki takiemu podejściu osiągamy stan, w której projektanci interfejsu użytkownika nie przeszkadzają programistą i vice versa. W szczególności unikamy problemy pod tytułem: Kto zmienił nazwę tej kontrolki? Podobnie jest przy tworzeniu gier komputerowych. Modele postaci, bitmapy tworzone są przez wyspecjalizowanych grafików. Efekt ich pracy otrzymują programiści, którzy ożywiają modele, wyświetlają bitmapy, dodają efekty specjalne: oświetlenie, cienie...

Powstaje pytanie co należy stworzyć najpierw. Moim zdaniem należy pamiętać, że interfejs użytkownika to nie tylko wygląd kontrolek, ikony, kolory itd. ale również ergonomia, odpowiednie rozłożenie kontrolek i organizacja przepływu sterowania. Wydaje się, że elementy te powinny zostać zdefiniowane w pierwszej kolejności tak aby programiści znali je już na samym początku pracy. Unikamy potrzeby przebudowy kodu na późniejszym etapie pracy, a w szczególności unikamy sytuacji, w której nad tym samym elementem interfejsu pracuje równocześnie programista i projektant UI. Dlatego jestem zwolennikiem podejścia, w którym interfejs, a już napewno jego projekt/opis, powstaje najpierw.

Jeśli chodzi o Expression Web to godny zauważenia jest fakt, że Microsoft zauważył PHP i dostarczył narzędzia wspierającego ten język. Zaskoczył mnie natomiast fakt, że w przypadku aplikacji ASP.NET nie jest wspierany model code behind!.

Trochę głupio przyznać ale zaskoczyła mnie również informacja, że Expression Blend to aplikacja komercyjna (czytaj płatna). Do tej pory korzystałem bowiem z darmowej wersji community technology preview i miałem nadzieję, że ten stan utrzyma się w wersji finalnej. Szkoda, bo aplikacja ma ogromne możliwość i umożliwia wyczarowanie naprawdę fajnych efektów przy użyciu XAML'a - niektórych rzeczy nie da się po prostu zrobić czysto programistycznie, no chyba, że ktoś potrafi wyobrazić sobie wszystko w głowie.

24/11/2008

Zakamarki Visual Studio 2005/2008 (cz. 2)

Home

Zapraszam do zapoznania się z kolejną porcją ciekawych i mało znanych funkcji Visual Studio.

Breakpoint w pętli

Bardzo przydaną funkcją jest możliwość postawienia breakpoint'a w definicji pętli for lub foreach. Załóżmy, że mamy taki kod:
for(int i = GetValue(); i < GetLimit(); i++)
{
  ...
}
Domyślne zachowanie środowiska jest takie, że po kliknięciu linii, w której znajduje się początek pętli i naciśnięciu przycisku F9 breakpoint zostanie ustawiony na części inicjalizacyjnej pętli czyli uzyskamy taki efekt:
for(int i = GetValue(); i < GetLimit(); i++)
{
  ...
}
Czasami, a nawet częściej niż czasami chcielibyśmy aby debugger zatrzymał się w części sprawdzającej warunek pętli. Nic prostszego. Wystarczy przesunąć kursor i ponownie nacisnąć F9. Uzyskamy efekt jak poniżej:
for(int i = GetValue(); i < GetLimit(); i++)
{
  ...
}
Oczywiście debugger możemy zatrzymać również w instrukcji interacji. Podobnie możemy postąpić z pętlą foreach.

Testowanie przy pomocy okna Immediate

Ciekawym sposobem na szybkie testowanie metod statycznych jest użycie okna Immediate. Jeśli nie jest ono standardowo widoczne to znajdziemy je w Debug -> Windows. Po pierwsze okno to pozwala w czasie debugowania wywoływać metody obiektów, zmieniać ich właściwości itd. Po drugie, co jest nawet ciekawsze, umożliwia wywołanie metody statycznej kiedy środowisko nie znajduje się w trybie debugowania. Wykonanie takiej operacji spowoduje uruchomienie debugera i o ile, w metodzie statycznej znajduje się breakpoint, jego zatrzymanie. Dzięki temu nie musimy tracić czasu na pisanie krótkich programików tylko po to aby przetestować daną metodę statyczną. Po trzecie co jeszcze ciekawsze okno Immediate pozwala również w podobny sposób testować zwykłe metody klas. Załóżmy, że napisaliśmy klasę Test i zdefiniowaliśmy w niej metodę Fun(). W oknie Immediate może wpisać:
Test t = new Test(); 
t.Fun(); 
Podobnie jak wcześniej. Jeśli w metodzie znajdował się breakpoint debugger zatrzyma się na nim. Jesli nie, metoda zakończy swoje działanie. Oczywiście technika ta pozwala testować tylko stosunkowe proste scenariusze ale tak czy inaczej ułatwia i przyspiesza tworzenie dobrego kodu.

Opisane techniki testowałem w środowiskach Visual Studio 2005 oraz Visual Studio 2008.

20/11/2008

Pożytki płynące z używania Process Monitor'a

Home

W poście tym chciałbym zachęcić do używania darmowego narzędzia Process Monitor, które umożliwia monitorowanie wszelkiej aktywności w systemie operacyjnym dotyczącej zasobów takich jak: pliki, klucze rejestru, połączenia sieciowe itd.

Pożytki płynące z tego narzędzia opiszę na swoim przypadku. Ostatnio napisałem prostą bibliotekę, która parsuje plik Xml, przetwarza jego zawartość przy pomocy transformacji Xsl w celu stworzenia plików Html i finalnie uruchamia kompilator pomocy w celu wytworzenia pliku chm. Biblioteka nie jest skomplikowana i bardzo szybko udało się uzyskać pożądany efekt (prawie). Niestety ale okazało się, że kompilator pomocy nie potrafi przetworzyć wszystkich plików Html i generuje dla nich błąd HHC5003: Error: Compilation failed while compiling... Co ciekawe efekt ten był obserwowany tylko w przypadku programowego uruchomienia kompilatora przy pomocy klasy Process. Przy ręcznym uruchamiania wszystko działało prawidłowo. Po bliższym przyjrzeniu okazało się, że kompilator zgłaszał błędy zawsze dla tych samych plików. W przypadku kiedy lista plików została powiększona błąd zaczął być zgłaszany dla nowych plików! Pliki, które wcześniej zdawały się nieprawidłowe okazywały się nagle jak najbardziej w porządku.

Nie udało mi się znaleźć dokładnych informacji na temat zgłaszanego przez kompilator błędu z wyjątkiem tego, że przyczyną może być brak dostępu do pliku. Postanowiłem więc przyjrzeć się procesowi kompilacji przy pomocy Process Monitor'a. Ponieważ aplikacja w trybie domyślnym wyświetla ogromną ilość danych (setki tysiące wierszy) postanowiłem skupić się tylko na wpisach dotyczących jednego z plików, dla których kompilator zgłaszał błąd. Po włączeniu filtrowania uzyskałem taki wynik:



Na niebiesko oznaczyłem wiersz, w którym widać, że kompilator nie uzyskał dostępu do pliku z powodu błędu SHARING VIOLATION. Błąd ten oznacza, że plik był w tym momencie w użyciu. Ponieważ problematyczny plik Html był generowany w całości przez mój kod mogłem założyć, że błąd tkwi po mojej stronie. Jak się okazało popełniłem akademicki błąd i nie zamykałem strumienia do pliku (do tej pory biję się w pierś). Oczywiście konstrukcja using rozwiązała problem.

Cały czas pozostaje jednak pytanie czemu błąd pojawiał się tylko dla określonych plików. Strumień nie był przecież zamykany dla każdego dynamicznie wygenerowanego pliku Html. W tym przypadku odpowiedź też nie jest skomplikowana - musiał zadziałać mechanizm automatycznego zwalniania pamięci garbage collector.

19/11/2008

Zakamarki Visual Studio (cz. 1)

Home

Visual Studio to potężne narzędzie, o ogromnych możliwościach, które pozwala tworzyć i debugować programy w łatwy i przyjemy sposób. Dobra znajomość swojego środowiska pracy do podstawa dla każdego programisty. Dlatego poniżej zamieszczam opis kilku "zaawansowanych" narzędzi udostępnionych w Visual Studio. Tak naprawdę prezentowane przeze mnie techniki nie są ani trudne, ani skomplikowane w użyciu. Nie ulega jednak wątpliwości, że są stosunkowo mało znane. Działają poprawnie w przypadku języka C# (dla innych języków może być inaczej).

Okienko Find

Z pewnością każdy kiedyś korzystał z tego narzędzia aby szybko wyszukać w kodzie interesującą go frazę. Ale możliwości okienka Find są znacznie większe. Po pierwsze wpisanie do niego nazwy jakieś metody i naciśnięcie przycisku F9 spowoduje wstawienie breakpoint'a do każdej metody o podanej nazwie. Moim zdaniem bardzo przydatna funkcjonalność jeśli mamy w projekcie jedną lub więcej, wielokrotnie przeciążonych metod. Do okienka Find możemy również wpisać nazwę pliku (razem z rozszerzeniem). Teraz jeśli naciśniemy kombinację Ctrl+Shift+G i plik o podanej nazwie istnieje to zostanie otworzony gotowy do edycji. Przydaje się przy dużym drzewie projektów z rozbudowaną hierarchią plików i folderów.

Podglądanie cudzych metod

Przypuśćmy przez chwilę, że pracujemy ze złośliwym programistą, który nie chce udostępnić innym swoich kodów. Przypuśćmy dalej, że w kodzie złośliwego programisty jest generowany wyjątek. Wyjątek ten powstaje głęboko w kodzie złośliwego programisty i oznacza, że do metody został przekazany ciąg znaków o błędnym formacie. Wspomniany ciąg znaków dostarczany jest przez nas i może to być np.: connection string.

Co więcej programista jest tak uparty, że twierdzi, że to nie jego wina oraz, że z pewnością przekazujemy do jego metody złe dane. Co gorsza jest na tyle sprytny, że zabezpieczył swoje biblioteki przed programem takim jak Lutz Roeder’s Reflector. Tak na marginesie to genialne narzędzie, niezbędne w pracy każdego programisty .Net. Należy jeszcze dodać, że ten programista to syn szefa :)

My wiemy swoje i on wie swoje. Nie mamy dostępu to kodu biblioteki złośliwego programisty ale oczywiście znamy stack trace. Znamy więc nazwy kolejno wołanych metod aż do wystąpienia wyjątku. Jedyna nasza szansa to udowodnić złośliwemu programiście, że nasze dane są prawidłowe i to w jego kodzie jest błąd.

I tutaj wkracza do akcji Visual Studio. Okazuje się, że można postawić breakpoint w metodzie, dla której nie mamy kodu źródłowego. Kiedy debugger zatrzyma się będziemy mogli w oknie Locals zobaczyć wartości wszystkich argumentów przekazanych do metody! W celu postawienia takiego breakpoint'u korzystamy z bardzo prostego sposobu: otwieramy okno New Breakpoint, w polu Function wpisujemy nazwę metody, naciskamy przycisk Ok i gotowe. Do okna New Breakpoint dostajemy się z poziomu standardowego okna Breakpoints (Debug -> Windows -> Breakpoints) wybierając przycisk New, a następnie polecenie Brak at Function...

Okno Call Stack

Run To Cursor to bardzo znane polecenie, które umożliwia rozpoczęcie debugowania i wskazanie miejsca w kodzie, w którym debugger ma się zatrzymać (pod warunkiem, że nie zatrzyma się wcześniej z powodu breakpoint'a). Niewiele osób jednak wie, że polecenie to jest dostępna z poziomu okna Call Stack. Pozwala wskazać, do którego miejsca chcemy zwinąć stos. Wystarczy wybrać interesującą nas metodę i z menu kontekstowego wybrać tę komendę. W bardzo podobny sposób można przy pomocy okna Call Stack postawić breakpoint. W tym celu podobnie jak wyżej wybieramy interesującą nas metodę, zaznaczamy ją i naciskamy przycisk F9 (albo wywołujemy menu kontekstowe i wybieramy Breakpoint -> Insert Breakpoint).

Podsumowanie

Mam nadzieję, że opisane przeze mnie narzędzia i techniki okażą się przydatne. W następnej części opiszę kolejną porcję ciekawych funkcji Visual Studio.

Opisane techniki testowałem w środowiskach Visual Studio 2005 oraz Visual Studio 2008.