24/12/2010

Życzenia świąteczne

Home

Z okazji Świąt Bożego Narodzenia składam czytelnikom i czytelniczkom bloga serdeczne życzenia wszystkiego najlepszego oraz wybiegając trochę w przyszłość życzę dużo szczęścia i pomyślności w nadchodzącym nowym roku.

Pozdrawiam
Michał Komorowski

02/12/2010

Dlaczego należy być upierdliwym...

Home

... kiedy dostaje się jakieś zadanie do wykonania. Albo wręcz bardzo upierdliwym i dopytywać się o wszystkie szczegóły, a przede wszystkim o te, które wydają się nam oczywiste. Odpowiedź brzmi, aby nie tracić czasu. Zasada jest prosta ale pomimo to ciągle o niej zapominam kiedy sądzę, że już wszystko rozumiem i zadanie nie jest skomplikowane. Przykład z życia.

Otrzymałem do wykonania łatwe zadanie, które upraszczając polegało na tym aby, użytkownicy domenowi nie musieli logować się ponownie do aplikacji WWW używanej wewnątrz sieci klienta. Zdecydowałem, że zastosuję Windows Authentication. Testy w środowisku lokalnym przebiegły gładko, a więc pozostało zainstalowanie aplikacji na serwerze testowym. Myślałem, że to już koniec roboty podczas gdy w rzeczywistości zajęło mi to jeszcze dużo, dużo czasu. Z jakichś powodów automatyczne logowanie nie chciało działać na docelowej maszynie. Czemu? Bo nie byłem upierdliwy i nie zadałem jednego pytania:

Czy serwer testowy został dodany do domeny?

Niestety ale przyjąłem, że znajduje się w domenie, do której należą użytkownicy (warunek konieczny automatycznego logowania) i przyczyny kłopotów szukałem wszędzie tylko nie tam gdzie potrzeba. Bądźmy, więc upierdliwi i kiedy dostajemy zadanie pytajmy, pytajmy i jeszcze raz pytajmy.

27/11/2010

Reporting Services i schowane etykiety

Home

Poniższy rysunek zawiera fragment wykresu wygenerowanego przez SQL Server Reporting Services. Wykres ten przedstawia wartość pewnej miary (oś OY) dla różnych kategorii (oś OX). Mniejsza o jaką miarę i kategorię chodzi. Problem polega na tym, że etykiety posiadają tylko niektóre słupki, a nie wszystkie co czyni wykres bezużytecznym. Nie sposób bowiem wydedukować jakie kategorie zostały pokazane np.: pomiędzy JNZ i OR.



Nie zastanawiając się długo postanowiłem, więc dodać etykiety dla wszystkich słupków. Wbrew pozorom znalezienie rozwiązania zajęło mi trochę czasu, a rozwiązanie okazało się mało intuicyjne. Zacząłem od przejrzeniu wszystkich okienek kreatora wykresu i wypróbowaniu różnych przełączników. Niestety bez sukcesu. Następnie zauważyłem, że okno Properties dla osi OX zawiera dużo, dużo więcej właściwości niż jest udostępnione w kreatorze, żeby nie skłamać dobre kilkadziesiąt. Trochę już zniechęcony postanowiłem je przejrzeć i po chwili znalazłem grupę właściwości o nazwach zaczynających się od Label, a wśród nich właściwość LabelInterval ustawioną na wartość Auto.



Zgodnie z nazwą właściwość ta powinna określać krok co jaki będą wyświetlane etykiety. Opis właściwości (Labels interval size) to masło maślane ale co szkodzi sprawdzić. Zamiast Auto wpisałem więc 1. Okazało się to strzałem w dziesiątkę. Po tej niewielkiej zmianie wykres prezentuje się jak poniżej:


25/11/2010

Czemu VB.NET jest be?

Home

Z językiem VB.NET da się pracować, co z powodzeniem czynię od blisko roku, ale niektóre rzeczy doprowadzają mnie do szału. Aby wyjaśnic o co chodzi posłużę się bardzo, bardzo prostym interfejsem pokazanym poniżej:
Public Interface IFun
  Function Fun() As Integer
  Function SuperFun() As Integer
End Interface
Implementacja funkcji Fun oraz SuperFun jest trywialna i wyglądają jak poniżej:
Public Function Fun() As Integer
  Return 0
End Function

Public Function SuperFun() As Integer
  Return 1
End Function
Jeszcze kod, który korzysta z tej implementacji:
Sub Main()
  Dim fun As Fun = New Fun()
  Dim ifun As IFun = fun

  Console.WriteLine(fun.Fun() = ifun.Fun())
  Console.WriteLine(fun.SuperFun() = ifun.SuperFun())
  Console.WriteLine(fun.Fun() = ifun.SuperFun())
  Console.WriteLine(fun.SuperFun() = ifun.Fun())
End Sub
A na koniec pytanie, jaki wynik zostanie wypisany na ekan? Założę się, że nikt się nie zawacha i odpowie:
True
True
False
False
Taki wynik jest oczywiście logiczny i prawidłowy ale nie zawsze, w przypadku VB.NET można uzyskać również taki wynik:
False
False
True
True
Nie trzeba do tego stosować żadnych sztuczek, wystarczy zwykła pomyłka. Pokazując implementację metod Fun oraz SuperFun celowo pominąłem słowo kluczowe Implements, które określa jaki element intefejsu implementuje dana metoda. Kod klasy powinien wyglądać tak:
Public Class Fun
  Implements IFun

  Public Function Fun() As Integer Implements IFun.Fun
    Return 0
  End Function

  Public Function SuperFun() As Integer Implements IFun.SuperFun
    Return 1
  End Function
End Class
Niestety ponieważ metody Fun oraz SuperFun mają takie same sygnatury to nic nie stoi na przeszkodzie aby zamienić miejscami Implements IFun.Fun z Implements IFun.SuperFun i otrzymać coś takiego:
Public Class Fun
  Implements IFun

  Public Function Fun() As Integer Implements IFun.SuperFun
    Return 0
  End Function

  Public Function SuperFun() As Integer Implements IFun.Fun
    Return 1
  End Function
End Class
Mały, wkurzajacy i bardzo trudny do wykrycia błąd wynikający z cech języka VB.NET.

19/11/2010

Moda na chmurę

Home

To, że konferencja MTS 2010 odbyła się w dużym stopniu pod znakiem chmury obliczeniowej nie dziwi mnie. Nie dziwi mnie również, że prasa branżowa, portale społecznościowe, blogi i fora są pełne artykułów i postów na ten temat. Cloud computing to przecież relatywnie nowe i bardzo ciekawe ze względów technologicznych zagadnienie, również dla mnie.

Jestem natomiast zaskoczony, że tematyka chmury obliczeniowej zaczęła bardzo szybko wpływać szerokim nurtem do prasy nie branżowej. W czasie ostatniej wizyty w salonie prasowym w jednym z magazynów przeznaczonych dla menadżerów, którego tytułu niestety nie pamiętam, zobaczyłem artykuł zachwalający zalety chmury. Zaś w magazynie Forbes znalazłem cały, obszerny dodatek poświęcony tylko chmurze! Z ciekawości sprawdziłem czy tematyka chmury była poruszana na portalach takich jak onet.pl czy rp.pl i znalazłem kilka artykułów. Z innych przykładów, koleżanka, która pracuje w instytucji finansowej, powiedziała mi, że dyrektor działu IT zapowiada wykorzystanie tej technologii. Od kolegi z pracy dowiedziałem się natomiast, że jego promotor, który do tej pory nie przywiązywał uwagi do nowinek, stwierdził, że chmura to coś ciekawego.

Chmura bardzo szybko staje się albo już stała się modna, słyszy się o niej prawie wszędzie, a przede wszystkim budzi zainteresowanie biznesu. Z jednej strony niesie to z sobą szanse rozwoju firm, które zainwestują w chmurę oraz kontraktów dla firm IT. Z drugiej strony stare przysłowie mówi, że co nagle to po diable. Nie budzi natomiast wątpliwości fakt, że działy marketingowe dostawców rozwiązań typu cloud wykonały kawał dobrej roboty.

17/11/2010

100 postów

Home

Niby 100 to liczba jak każda inna ale jednak ponad 100 opublikowanych postów skłoniło mnie do spojrzenia wstecz i napisania krótkiego podsumowania. Należy zacząć od tego, że napisanie takiej liczby postów zajęło mi 2 lata co daje średnio 4 posty w miesiącu. Mało to czy dużo? Dla mnie w sam raz. Były okresy, w których publikowałem więcej i takie, w których pojawiał się jeden post w miesiącu. Poruszałem różne tematy, głównie związane z platformą .NET i nowoczesnymi technologiami ale pojawiły się również posty związane z grami komputerowymi, mini recenzje sztuk teatralnych czy na temat inicjatywy Powrót do Ojczyzny. Zawsze starałem się aby to co piszę było dla innych w jakiś sposób wartościowe. Nie mi to oceniać ale wierzę, że mi się udało.

Co zrobiłem w ciągu 2 lat?

W tym czasie blog ewaluował i zmieniał się, domyślny szablon dostosowałem do swoich potrzeb i gustu, dodałem podświetlanie składni przy pomocy syntaxhighlighter'a, chmurę etykiet, QR kod z zakodowanym moim adresem e-mail, sekcję Aktualnie czytam, stopkę z informacją o prawach autorskich, licznik subskrypcji ... by w końcu osiągnąć obecną, odpowiadającą mi w prawie 100% formę. Nauczyłem się jak pisać i redagować posty oraz, że każdy post przed opublikowaniem należy odłożyć i wrócić do niego za kilka godzin lub najlepiej następnego dnia w celu ponownego sprawdzenia. Nieoceniony jest, że kiedy wpiszę w Google swoje imię i nazwisko to mój blog pokazywany jest na jednym z pierwszych miejsc. Moje wpisy pojawiają się wśród pierwszej 10 wyników po wpisaniu takich haseł jak: IntelliTrace, blog programowanie czy visual studio blog (stan na moment pisania tego postu). Linki to mojego bloga można również znaleźć w serwisach dotnetmaniak, dotnetblogs czy dotnetnews. Nie jestem już, więc anonimowy w sieci, a co najważniejsze pokazałem się z dobrej strony, a to było jednym z moich celów kiedy zaczynałem blogowanie.

Co dalej?

Co tu ukrywać blogowanie spodobało mi się. W przyszłości chciałbym przynajmniej utrzymać, a najlepiej zwiększyć średnią 4 postów w miesiącu, oczywiście bez obniżenia poziomu. Będę również starał się zdobyć więcej czytelników i pobudzić ich do komentowania. Po głowie chodzą mi również takie pomysły jak przejście z bloggera na platformę WordPress czy artykuły dwujęzyczne (Pl/En).

Jeśli ktoś z Was ma dla mnie jakieś rady lub uwagi, coś mu się w tym blogu nie podoba albo odwrotnie coś mu przypadło do gustu, a może chciałby więcej lub mniej wpisów na jakiś temat i powinny być dłuższe lub krótsze... to z przyjemnością wysłucham wszystkich głosów.

09/11/2010

Poznaj swój program

Home

Tematykę IntelliTrace poruszałem już kilkukrotnie. Dzisiaj chciałbym powrócić do zagadnienia opisanego w poście Logi IntelliTrace bez tajemnic czyli analizy logu przy pomocy IntelliTrace API. Tym razem napiszę w jaki sposób dostać się do informacji o tym kiedy została wywołana jakaś metoda, jaki wynik zwróciła i jakie były parametry wywołania. Informacje te są prezentowane w oknie Calls View w Visual Studio 2010 ale można je analizować w ograniczonym stopniu. Poniższe informacje przydadzą się każdemu kto będzie chciał na przykład załadować te dane do bazy danych w celu później analizy, na przykład przy pomocy algorytmów odkrywania wiedzy (data mining).

Zacznijmy od wzorca kodu, który posłuży nam do odczytania pliku z logiem:
using (IntelliTraceFile file = new IntelliTraceFile(pathToIntelliTraceLog))
{
  //Kolekcja procesów będzie miała tylko jedną pozycję
  foreach (IntelliTraceProcess process in traceFile.Processes)
  {
    //Przetworzenie kolejnych wątków
    foreach (IntelliTraceThread thread in process.Threads)
    {
      //Tworzymy strumień ze zdarzeniami. Każde zdarzenie odpowiada np.: wywołaniu metody
      Chain chain = thread.CreateThreadChain<ThreadChain>();
  
      EventToken eventToken = chain.FirstValidToken;
      //Przetwarzamy zdarzenie po zdarzeniu
      while (eventToken != chain.AfterLastToken)
      {
        //Pobranie zdarzenia
        IntelliTraceEvent ev = chain.GetEvent(eventToken);

        //To zdarzenie reprezentujące wywołanie metody
        if(ev is MethodEnterEvent)
          ProcessMethodEnterEvent(ev);
        //To zdarzenie reprezentujące zakończenie wywołania metody
        else if(ev is MethodExitEvent)
          ProcessMethodExitEvent(ev);
          
        eventToken = chain.GetNextToken(eventToken);
      }
    }
  }
}
Powyższy kod zawiera dużo komentarzy dlatego nie powinno być trudności z jego zrozumieniem. Chciałbym zwrócić uwagę tylko na jedną rzecz. Lista zdarzeń jakie możemy obsłużyć zawiera znacznie więcej pozycji niż tylko dwie pokazane powyżej (MethodEnterEvent, MethodExitEvent). Niestety ale nie ma dobrej dokumentacji z pełną listą jaki "łańcuch zdarzeń" jakie zdarzenia udostępnia. Pozostaje droga eksperymentalna oraz przyjrzenie się projektowi iTraceReader, o którym już pisałem w poście Logi IntelliTrace bez tajemnic.

Zobaczmy, więc jak wyglądają metody ProcessMethodEnterEvent oraz ProcessMethodExitEvent. Na samym początku należy zamienić zdarzenia MethodEnterEvent oraz MethodExitEvent, które zawierają tylko suchy ciąg bajtów na bardziej przyjazne ResolvedMethodEnterEvent oraz ResolvedMethodExitEvent, z których łatwo wyciągniemy interesujące nas informacje.
private void ProcessMethodEnterEvent(IntelliTraceEvent ev)
{
  ResolvedMethodEnterEvent methodEnterEvent = new ResolvedMethodEnterEvent(CurrentProcess, ev as MethodEnterEvent);
  //...
}
Klasy ResolvedMethodEnterEvent oraz ResolvedMethodExitEvent zawierają właściwość Method, która zwraca obiekt klasy ResolvedMethod. Klasa ta reprezentuje wywołaną metodę i udostępnia następujące bardzo intuicyjne właściwości:
  • ContainingTypeName - Pełna nazwa typu (razem z przestrzenią nazw) w ramach, którego metoda została zdefiniowana.
  • MethodName - Nazwa metody.
  • ParameterNames - Nazwy (ale nie wartości) parametrów.
  • ParameterTypeNames - Pełne nazwy typów parametrów.
  • ReturnTypeName - Pełna nazwa typu (ale nie wartość) zwracanego przez metodę.
Teraz przejdźmy do odczytania parametrów przekazanych do metody przy jej wywołaniu. Do tego celu posłuży nam metoda:

IList<IDataElement> ResolvedMethodEnterEvent.GetParameters()

W celu reprezentowania różnych bytów IntelliTrace API udostępnia interfejs o nazwie IDataElement. IDataElement może reprezentować typy proste i referencyjne. W przypadku typu referencyjnego IDataElement może zawierać elementy podrzędne reprezentujące składowe obiektu, które również będą reprezentowane przez IDataElement. Napisałem może, a nie musi ponieważ IntelliTrace analizuje tylko obiekty pierwszego poziomu. Czyli jeśli parametr jakiejś metody to obiekt klasy A, który zawiera składowe typu string, int oraz wskazanie na obiekt klasy B to IntelliTrace zapisze w logu wartości wszystkich składowych typów prostych obiektu klasy A ale już nie obiektu klasy B. Na temat obiektu klasy B dowiemy się tylko tyle, że jest. Poniżej zamieszczam opis interfejsu IDataElement. Zaznaczam jednak, że tworząc ten opis bazowałem na doświadczeniu i przeprowadzonych eksperymentach dlatego może on zawierać nieścisłości lub błędy:
  • HasChildren - true jeśli mamy do czynienia z typem referencyjnym i jest to obiekt pierwszego poziomu, false w przypadku typów prostych i obiektów zagnieżdżonych.
  • Name - Nazwa parametru, nazwa składowej obiektu, '[Return value]' dla wartości zwracanej przez metodę, this lub base.
  • TypeName - Pełna nazwa typu reprezentowanego bytu.
  • Value - W przypadku typów prostych właściwa wartość, a w przypadku typów referencyjnych nazwę typu. Jeśli wartość nie została zapisana przez IntelliTrace otrzymamy wynik 'unknown'.
  • GetChildren - Metoda, którą możemy wywołać jeśli właściwość HasChildren jest równa true. Metoda ta zwraca elementy podrzędne w stosunku do bieżącego.
Warto również wiedzieć o poniższej metodzie, która służy do pobrania wyniku zwróconego przez metodę:

IDataElement ResolvedMethodExitEvent.GetReturnValue()

Znacznie rzadziej będą nas interesowały parametry typu out, do których uzyskamy dostęp przy pomocy poniżej metody. Niestety ale wygląda na to, że metoda ta zawiera jakiś błąd. Próbowałem wywołać ją kilkukrotnie dla rożnych metod z parametrem out typu string. Za każdym razem kończyło się to błędem. Raz był to wyjątek ArgumentOutOfRangeException innym razem ApplicationException.

IList<IDataElement> ResolvedMethodExitEvent.GetOutputs()

Podsumowując pomimo ograniczeń IntelliTrace API zachęcam do zabawy z logami.

01/11/2010

Biała bluzka

Home

Czy lubię chodzić do teatru? Nawet bardzo, szczególnie na komedie i mam takie szczęście, że do tej pory nie zawiodłem się na żadnym spektaklu na jakim byłem. O dwóch takich kulturalnych wypadkach pisałem już wcześniej w postach Kobieta pierwotna oraz Dzikie żądze. Dzisiaj ponownie chciałbym wrócić do tej tematyki i napisać parę słów o przedstawieniu "Biała bluzka" z bardzo dobrą rolą Krystyny Jandy, granym w teatrze och-teatr.

Dla wielu ta sztuka oparta o opowiadanie Agnieszki Osieckiej to z pewnością przedstawienie kultowe, coś z serii must-see. Ja o niej wcześniej nie słyszałem i szedłem na nią z pewną obawą, a to dlatego, że preferuję przedstawienia śmieszne i przyjemne, a "Biała bluzka" komedią z pewnością nie jest, chociaż momentami śmiałem się. Właściwie to trudno mi zaklasyfikować tą opowieść o niespełnionej miłości do opozycjonisty do konkretnego gatunku. Sztuka nie posiada fabuły w dosłownym tego słowa znaczeniu, brak w niej dialogów, a na scenie widzimy tylko jedną aktorkę wygłaszającą monolog i śpiewającą.

Teraz nie potrafiłbym dokładnie powiedzieć o czym był ten monolog i piosenki. W pamięci zapadło mi natomiast jak dobrze ten monolog został wygłoszony, a przede wszystkim jak rewelacyjnie zostały zaśpiewane piosenki Agnieszki Osieckiej. Przedstawienie trwało około 2 godzin i pomimo minimalistycznej dekoracji, braku nagłych, trzymających w napięciu zwrotów akcji oraz poważnej tematyki nie nudziłem się. Krystyna Janda była po prostu niesamowita, a jej głos, z lekką chrypką zapamiętam na długo i bardzo żałuję, że nie można było kupić nagrania z tymi piosenkami. Wielka szkoda! Inne elementy przedstawienia jak muzyka, nietypowa aranżacja wnętrza również stały na bardzo wysokim poziomie chociaż dla mnie zostały przytłoczone przez kunszt aktorki. Podsumowując: polecam, polecam i jeszcze raz polecam.

28/10/2010

Znikające Activity

Home

Na poniższym obrazku widać dwa schematy pochodzące z rożnych aplikacji, a przedstawiające fragment pewnego procesu biznesowego. Z prawej strony można zobaczyć definicję tego procesu, a z lewej powołaną do życia i uruchomioną jego instancję.



Niestety ale z jakichś powodów instancja różni się od swojej definicji. Pytanie dlaczego? Początkowo myślałem, że to jakiś problem z zapisywaniem lub odczytywaniem definicji/instancji procesu z bazy danych. Pomysł ten jednak szybko odrzuciłem ponieważ gdyby problem tkwił w tym miejscu to odczyt z bazy danych, deserializacja obiektu i w końcu wyświetlenie definicji/instancji procesu w standardowej kontrolce WorkflowView nie udało by się.

W dalszej kolejności zacząłem szukać różnic pomiędzy wspomnianymi aplikacji. Szybko udało się wykryć (dziękuję za pomoc Tomkowi), że kod brakującego Activity znajduje się w bibliotece niedostępnej dla aplikacji. Po skopiowaniu odpowiedniej dll'ki do katalogu z binariami aplikacji problem ustąpił.

Zacząłem się jednak zastanawiać czemu nie został zgłoszony żaden wyjątek informujący o braku dll'ki. Od razu pomyślałem, że ktoś musiał go przechwycić i zgasić. W sumie to się nie pomyliłem. Po włączeniu Managed Debugging Assistant dla wszystkich wyjątków (Debug -> Exceptions) i ponownym uruchomieniu aplikacji asystent pokazał, że wyjątek pojawia się w czasie deserializowania instancji procesu, gdzieś w środku klasy ActivityMarkupSerializer. Pojawienie się wyjątku nie przeszkadza jednak dokończyć deserializacji i zwrócić obiekt reprezentujący proces tylko, że bez kilku Activity.

Można powiedzieć, że ActivityMarkupSerializer jest bardzo sprytny ponieważ potrafi sobie poradzić nawet jeśli brakuje jakiejś biblioteki. Moim zdaniem ta klasa jest jednak za sprytna. Teraz już będę o tym wiedział ale na takim błędzie można stracić mnóstwo czasu.

27/10/2010

OciEnvCreate failed with return code -1

Home

OciEnvCreate failed with return code -1 to błąd na temat, którego można znaleźć w sieci sporo pytań ale mało odpowiedzi. Ja niestety miałem tego pecha i również na niego trafiłem, a w rezultacie straciłem kilka dobrych godzin. Błąd ten może pojawić się przy próbie nawiązania połączenia z bazą danych Oracle. Z informacji jakie znalazłem wynika, że najczęściej pojawia się w trzech przypadkach:
  • Brak zainstalowanych bibliotek klienckich Oracle.
  • Zła wersja zainstalowanych bibliotek klienckich Oracle.
  • Brak uprawnień użytkownika na jakim uruchomiony jest proces roboczy ASP.NET do katalogu z bibliotekami klienckimi Oracle.
Co do dwóch pierwszych scenariuszy to ani nie zaprzeczę i ani nie potwierdzę ponieważ na maszynie, na której napotkałem problem działał już program łączący się z bazą danych Oracle. W moim przypadku błąd pojawił się w usłudze webowej zainstalowanej na IIS'ie. Początkowo myślałem, że chodzi o coś zupełnie innego ponieważ tylko pierwsze odwołanie do wspomnianej usługi kończyło się błędem. Przy każdym następnym odwołaniu Web Service zachowywał się tak jakby działał tylko, że zwracał niepoprawne wyniki. No cóż pewnie ktoś gasił wyjątek! Przy pierwszym odwołaniu do usługi wołana jest natomiast metoda Application_Start zdefiniowana w pliku Global.asax, która w tym przypadku zawierała kod nawiązujący połączenie z bazą danych.

Kiedy już odkryłem pierwotną przyczynę niepoprawnego działania usługi postąpiłem zgodnie ze znalezioną sugestią i nadałem pełne uprawnienia do katalogów z bibliotekami Oracle, wstępnie użytkownikowi Wszyscy. Jednym z tych katalogów był C:\app\Administrator\product\11.1.0\client_1.

Następnie zrestartowałem pulę aplikacji do jakiej została przypisana usługa i sprawdziłem czy to coś zmieniło. Okazało się, że niestety nie. Spróbowałem jeszcze raz i nic. Po jakimś czasie stwierdziłem, że może chodzi o jakieś inne katalogi i zdesperowany nadałem użytkownikowi Wszyscy uprawnienia do całego dysku. Niestety znowu bez sukcesu. W tym momencie przyszło mi do głowy, że może nie wystarczy nadanie uprawnień i zrestartowanie puli aplikacji tylko trzeba dodatkowo uruchomić ponownie komputer, co też zrobiłem. Okazało się to strzałem w dziesiątkę.

Zadowolony z sukcesu postanowiłem tym razem zrobić wszystko po bożemu czyli cofnąć nadanie uprawnień wszystkim do wszystkiego i nadać pełne uprawnienia tylko jednemu użytkownikowi i tylko do jednego folderu, ewentualnie dwóch ale ściśle określonych. Na początek chciałem się jednak upewnić o jaki konkretnie folder chodzi i jakiego użytkownika. Postanowiłem więc wrócić do stanu początkowego kiedy błąd jeszcze występował. Jakie było jednak moje zdziwienie kiedy okazało się, że jest to niemożliwe ponieważ po cofnięciu nadania uprawnień i restarcie maszyny wszystko działa.

Reasumując niestety ale nie potrafię z 100% pewnością stwierdzić komu, do czego i jakie uprawnienia należy nadać. Faktem jest jednak, że instalator Oracle nie do końca poprawnie konfiguruje system w czasie instalacji. Faktem jest również, że nadanie prawdopodobnie pełnych uprawnień, prawdopodobnie do folderów wymienionych powyżej, prawdopodobnie dla użytkownika na jakim działa proces roboczy ASP.NET i prawdopodobnie restart komputera pomaga. Mam nadzieję, że te prawdopodobne stwierdzenie oszczędzi komuś kilka godzin pracy.

14/10/2010

Dziw nad dziwy

Home

Dzisiaj ponownie napiszę o błędzie z serii UFO. Przyjrzyjmy sie przez chwilę dwóm poniższym zapytaniom:

Zapytanie 1
select * 
from tabela1 t1
where exists ( select 1 from tabela2 t2, tabela3 t3
               where t2.kolumna = t3.kolumna and
                     t3.kolumna = t1.kolumna )


Zapytanie 2
select * 
from tabela1 t1
where exists ( select 1 from tabela2 t2, tabela3 t3
               where t2.kolumna = t3.kolumna and
                     t3.kolumna = t1.kolumna ) and 
t1.data = '14-10-2010'


Każdy zapewne zauważył, że drugie zapytanie różni się od pierwszego tylko dodatkowym warunkiem w zapytaniu zewnętrznym. Innymi słowy zapytanie 2 zawęża wynik jaki otrzymany w zapytaniu 1. A teraz pytanie:

Czy zapytanie 2 może zwrócić wiersze, które nie zostaną zwrócone przez zapytanie 1?

Część z was zapewne stuka się teraz w głowę i zastanawia się po co pytam o rzeczy oczywiste. Odpowiedź jest oczywiście oczywista i brzmi NIE ale, o zgrozo, nie dla każdej bazy danych. Niestety, w przypadku Informix'a w wersji 10 w ''sprzyjających'' warunkach odpowiedź będzie brzmiała TAK. Tak wiem, ja też przecierałem oczy ze zdziwienia kiedy koleżanka z pracy pokazała mi wynik dwóch powyższych zapytań.

Niestety dokładnych przyczyn problemu mogę tylko podejrzewać ale znam jego rozwiązanie. Otóż okazało się, że problem ustąpił po przeliczeniu statystyk przy pomocy poniższego polecenia:
UPDATE STATISTICS LOW 
FOR TABLE table_name (column1, column2...) DROP DISTRIBUTIONS
Polecenie to oblicza statystyki na tzw. niskim poziomie czyli przy założeniu, że rozkład wartości w danych kolumnach jest równomierny. Dodatkowo usuwa rozkłady wartości wyznaczane przy obliczaniu statystyk na poziomie średnim lub wysokim. Tak czy inaczej to ewidentny błąd w optymalizatorze zapytań, mechanizmie indeksów czy też statystyk, który obniża zaufanie do bazy danych.

Na koniec podziękowania dla Agnieszki, która pokazała mi ten błąd oraz dla Marcina, który znalazł rozwiązanie.

08/10/2010

MTS, MTS i po MTS'sie

Home

W konferencji Microsoft Technology Summit uczestniczyłem już po raz trzeci i ponownie jestem zadowolony. Pierwsze i najważniejsze prawie wszystkie sesje, które wybrałem wypaliły. Po drugie w tym roku było trochę luźniej i człowiek miał więcej przestrzeni wokół siebie. Udało mi się nawet zjeść obiad kulturalnie przy stoliku, a nie siedząc na ziemi. To oczywiście za sprawą mniejszej, o około 400, liczby uczestników w porównaniu do zeszłego roku. Nie wiem czy to kwestia polityki Microsoftu, wyższej ceny uczestnictwa, znudzenia konferencją... ale uważam, że wyszło to konferencji na plus. A więc, co wyniosłem z tegorocznej konferencji?
  • Bardzo pouczająca była dla mnie krótka rozmowa z Chrisem Grangerem, który pracuje w Microsofcie jako PM i był między innymi odpowiedzialny za nowy edytor czy rozszerzalność w VS 2010. Co w tym dziwnego/wyjątkowego? Chris ma 23 lata i zanim przyszedł do Microsoft nie miał styczności z ich produktami! Zapytałem go w jaki sposób został menadżerem. Odpowiedział, że w sumie to nie wie. Miał pracować jako programista ale został zaproszony na jeszcze jedną rozmowę, po której dostał pracę jako menadżer. Pozostaje tylko pogratulować. Zastanawiam się czy taka sytuacja byłaby możliwa w Polsce i jak zostałoby to odebrane przez zespół, w szczególności przez "starszych" pracowników.
  • Zobaczyłem jednych z najlepszych prelegentów jakich widziałem do tej pory. Mam tutaj na myśli głównie Marka Minasi i Davida Chapella. Tego ostatniego oglądałem niestety tylko kilkanaście minut ponieważ musiałem wyjść w ważnej sprawie.
  • Przekonałem się, że tematyka IT Pro to również coś dla mnie. Do tej pory wybierałem raczej sesje dla programistów, w tym roku zdecydowałem się na większe urozmaicenie i był to strzał w dziesiątkę. Szczególnie chwalę sobie sesję Marka Minasi na temat DNSSEC, po prostu super.
  • Zainteresowałem sie tematyką chmury obliczeniowej. To dziedzina jeszcze bardzo młoda i niedojrzała ale wygląda na to, że jesteśmy na to skazani. Może jeszcze nie teraz, może jeszcze nie za rok ale jak powiedział na swojej sesji John Craddock "Can we afford not to?"
  • O Task Parallel Library słyszałem, czytałem ale jeszcze nie używałem. Dzięki bardzo dobrej sesji Cezarego Nalewajki poznałem jej podstawy i teraz tylko czekam na okazję na jej użycie.
  • Dowiedziałem się o wcześniej nie znanym mi typie projektu w VS 2010, a mianowicie o Data-tier Application, który ułatwia instalowanie i konfigurowanie baz danych. Nowością była dla mnie również protokół OData. To wszystko na sesji Tomasza Wiśniewskiego.
  • Obserwując prelegentów nauczyłem się kilku rzeczy. Na przykład pytanie publiczności o to czy wszystko jasne, czy są jakieś pytania zachęca do ich zadawania. Podobny efekt odnosi dziękowanie za pytania. Podobało mi się również jak prelegent wyjaśnił, że w czasie sesji będzie siedział ponieważ na stojąco trudno kodować itd. Niby to wszystko oczywiste oczywistości ale takie rzeczy łatwo umykają.
  • Utwierdziłem się w przekonaniu, że należy jak ognia wystrzegać się swego rodzaju przerywników, pozwalających zebrać myśli, takich jak słowa OK/Dobrze. Jeden z prelegentów miał z tym problem. Swoją drogą jego sesja była fajna ale mogłaby być jeszcze fajniejsza.
  • Zobaczyłem na żywo jak sprawuje się nowy kontroler do XBox'a czyli Kinect.

23/09/2010

QR kody

Home

QR kod to dwuwymiarowy odpowiednik kodu kreskowego, który ostatnimi czasy staje się coraz popularniejszy. I ja o nim coś tam słyszałem ale do tej pory jakoś nie miałem z nim styczności, aż do dzisiaj.

Szukałem w sieci informacji na temat poleconego stomatologa. Znalazłem je na portalu tupalo czyli numer telefonu, adres, mapkę dojazdu no i jakiś dziwny czarno-biały obrazek. Przyglądam się i czytam, że to QR kod zawierający dane kontaktowe do rzeczonego specjalisty i wystarczy je zeskanować, żeby dodać kontakt do książki adresowej. Myślę sobie super ale ciekawe czy mój Samsung Galaxy GT-I5700 to potrafi. Okazało się, że jak najbardziej tak i już po chwili miałem zainstalowane dwie aplikacji QuickMark oraz BarcodeScanner do skanowania kodów dwu- i jednowymiarowych.

Najpierw przetestowałem skanowanie kodu jednowymiarowego czyli w moim wypadku kodu kreskowego na pierwszej lepszej książce. Wynik pozytywny, numer jaki pojawił się w telefonie zgadza się z numerem pod kodem kreskowym. Dalej coś trudniejszego. Skanuję QR kod wyświetlony na stronie i również sukces, zostaję zapytany czy dodać taki, a taki kontakt do telefonu. Postanawiam kontynuować zabawę i znajduję generator online QR kodów. Poniżej przykład jego działania, czy potrafisz go odczytać?



W postaci QR kodu zakodowałem również swój adres e-mail i umieściłem w lewym panelu bloga, może komuś się przyda. Zabawa, zabawą ale biorąc pod uwagę, że teraz właściwie już każdy posiada telefon komórkowy, bardzo dużo telefon komórkowy z aparatem fotograficznym, a spora grupa telefony, na których z łatwością można instalować nowe oprogramowanie to na rynku pojawia się (albo już sie pojawiła) nowa nisza do zagospodarowania.

14/09/2010

Powrót do Ojczyzny

Home

Powrót do Ojczyzny to nazwa inicjatywy na rzecz ustawy o powrocie do Polski osób deportowanych lub zesłanych za czasów ZSRR. Jeszcze kilka dni temu nie wiedziałem, że taki ruch obywatelski istnieje i niewiele brakowało, a nie złożyłbym swojego podpisu pod projektem ustawy, a potrzeba ich 100 tysięcy aby trafił do sejmu. Do 14 września czyli ostatniego dnia trwania akcji zebrano 93480 głosów czyli bardzo dużo ale niestety za mało. Nowa ustawa jest ważna ponieważ przy obecnych rozwiązaniach prawnych czas oczekiwania na powrót do Polski może sięgać nawet 7-10 lata.

O akcji dowiedziałem się od żony, która zaproponowała, że po pracy przejdziemy się na Krakowskie Przedmieście 64 i złożymy podpisy. Podpis można było oczywiście wysłać pocztą ale nawet polecony nie zostałby doręczony w tak krótkim czasie. Wysiłek nieduży, a można komuś pomóc. Przy okazji postanowiłem spytać kolegów z pracy czy chcą złożyć podpis i w ciągu 2 godzin uzyskałem dodatkowe 12 głosów! Na Krakowskie Przedmieście spotkałem jeszcze sporą grupę ludzi myślących podobnie, część tak jak ja przyniosła listy z podpisami, inni przyszli się podpisać.

Jeśli ustawa wejdzie w życie będzie oczywiście kosztować. Według projektu ustawy potrzebnych będzie rocznie ok. 100 mln. Tu duże pieniądze ale ja wolę aby moje podatki były przeznaczane na takie szczytne rzeczy niż na ... i tutaj każdy może wpisać wiele pozycji w budżecie, które przejadają nasze pieniądze.

A teraz trzymam zaciśnięte kciuki żeby starczyło podpisów, okaże się jutro rano.

Internal .Net Framework Data Provider Error 30

Home

Ostatnio mam szczęście do "dziwnych" błędów. Dwa dni temu pisałem o htmlfile: Access Denied. Przy okazji podziękowania dla Tomka i Procenta za wskazówki jak sobie z nim poradzić. Teraz natknąłem się na błąd z zupełnie innej beczki związany z komunikacją z bazą danych. Zadanie do wykonania: wywołać procedurę składowaną. Zabrałem się do tego jak zawsze czyli nawiązałem połączenie do bazy danych, utworzyłem DbCommand, dodałem parametry, ustawiłem wartości parametrów i wywołałem procedurę. Niestety tym razem zamiast przejść do dalszej części zadania otrzymałem błąd Internal .Net Framework Data Provider Error 30. Konia z rzędem temu kto powie co ten błąd oznacza. Przeglądam kod jeszcze raz ale wszystko wygląda prawidłowo. Zresztą robiłem to już setki razy i działało. Jedyna różnica to taka, że teraz pracuję z bazą danych Oracle, a nie z bazą Microsoftu.

Od czego ma się jednak kolegów? Wysłałem maila z pytaniem czy ktoś już spotkał się z tym problemem i opłaciło się. Szybko uzyskałem wskazówkę, że błąd może być związany z błędną nazwą parametru i okazało sie to, że tak jest. W swoim kodzie użyłem metody, która w zależności od typu bazy danych zwraca odpowiednio przygotowany parametr nazwany. W przypadku Oracle parametry nazwane poprzedzone są dwukropkiem np.: :name, :date ale nie w przypadku przekazywania parametrów do procedury składowanej!!! Wspomniana metoda nie uwzględniała tego i stąd błąd. Inna sprawa to dlaczego sterownik Oracle nie ukrywa tej różnicy przed programistą oraz czemu ten błąd jest komunikowany w taki mało zrozumiały sposób.

Na zakończenie chciałbym podziękować Pawłowi za pomoc w rozwiązaniu problemu.

09/09/2010

htmlfile: Access Denied

Home

FileUploader to kontrolka, która umożliwia wybranie pliku przez użytkownika i przesłanie go na serwer. Po umieszczeniu na stronie renderuje się do pola tekstowego i przycisku Przeglądaj. Ja chciałem uzyskać efekt, w którym kontrolka ta jest ukryta, użytkownik naciska pewien przycisk, wyświetlane jest okienko do wyboru pliku, a następnie plik przesyłany jest na serwer. Zrobiłem to przy pomocy odrobiny JavaScript'u.
    function chooseFile()
    {
        var uploader = document.getElementById("<%= this.Uploader.ClientID %>");
        uploader.click();
    }

    function submitFile() 
    {
        document.forms[0].submit();
    }
Pierwsza funkcja chooseFile znajduje na stronie kontrolkę i wymusza kliknięcie na nią czyli powoduje wyświetlenie okienka do wyboru pliku. Druga funkcja po prostu przesyła stronę na serwer. Kontrolka i przycisk zostały osadzone na stronie w sposób pokazany poniżej:
    ...
    <asp:Button ID="Upload" runat="server" OnClientClick="chooseFile();" Text="Wybierz plik"/>
    ...
    <asp:FileUpload ID="Uploader" runat="server" style="display:none" onchange="submitFile();" />
     ...
Dzięki ustawieniu atrybutu display na wartość none kontrolka renderuje się do kodu HTML nie jest natomiast widoczna dla użytkownika. Po napisaniu kodu uruchamiam stronę, wybieram przycisk Wybierz plik i ku mojemu zadowoleniu pojawia się okienko do wyboru pliku. Wybieram interesujący mnie plik i na tym niestety kończy się zabawa, a pojawia się problem. W linii zawierającej document.forms[0].submit(); raportowany jest błąd htmlfile: Access Denied.

Sprawdzam jeszcze raz te kilka linijek kodu ale wyglądają prawidłowo. No dobrze, a może błąd jest spowodowany tym, że schowałem kontrolkę. Usuwam atrybut style i próbuję jeszcze raz ale błąd znowu się pojawia. Teraz kontrolka jest widoczna, więc sprawdzam co się stanie kiedy nacisnę przycisk Przeglądaj samemu, a nie przy pomocy JavaScript'u. Próbuję i tym razem wybrany plik został przesłany na serwer! Dla pewności powtarzam eksperyment jeszcze raz i ponownie sukces. Innymi słowy błąd pojawia się jeśli kontrolka do wyboru pliku zostanie kliknięta z poziomu kodu.

Więcej pomysłów nie mam, a więc zaglądam do Google i od razu widzę kilku nieszczęśników z podobnym problemem jak mój. Niestety zamiast rozwiązania problemu znajduję tylko informację, że błąd ten związany jest z jakimś mechanizmem bezpieczeństwa, który nie pozwala wysyłać plików bez wiedzy użytkownika. Sprawdzam jeszcze jak sytuacja wygląda w innych przeglądarkach (Opera i Firefox) i błąd się nie pojawia ale to dlatego, że nie działa kod uploader.click(); i okienko do wyboru pliku nie wyświetla się.

Jedyne co mi jeszcze przychodzi do głowy do wypróbowanie innych kontrolek do wyboru plików np.: Talerik'a. Dopóki nie znajdę obejścia problemu będę natomiast stosował tradycyjne podejście do zagadnienia czyli pozwolę użytkownikowi samemu nacisnąć przycisk.

31/08/2010

Jeden duży projekt, czy może wiele małych 2

Home

Publikując artykuł na temat "małych" i "dużych" byłem ciekawy opinii innych ludzi z branży. Pierwszy odzew otrzymałem od kolegi ze studiów Marka Kozłowskiego i za jego zgodą przedstawiam go poniżej.

Marek po przeczytaniu postu wyraził zdziwienie, że taki temat w ogóle pojawił się w rozmowie, a rozbicie kodu na podprojekty jest dla niego czymś naturalnym i oczywistym. Stwierdził również, że nie wyobraża sobie pracy z jednym dużym projektem (Aspekt psychologiczny), na który składa się na przykład 100 plików z kodem zawierających wszystko począwszy od logiki biznesowej, przez konwersję obiektów biznesowych na obiekty bazy danych, po warstwę dostępu do danych oraz interfejsy i implementację obiektów zdalnych. Przytoczył również taki przykład.

Wyobraźmy sobie, że piszemy projekt, który ma realizować funkcjonalność zarządzania magazynem i składa się (w skrócie) z warstwy prezentacji, warstwy logiki biznesowej oraz warstwy dostępu do bazy danych. Jest to prosta aplikacja desktopowa, a implementacje poszczególnych warstw znajdują sie w osobnych projektach (Wymusza poprawną architekturę). Po roku klient prosi o przygotowanie tej samej aplikacji ale w wersji WWW. Co robimy? Budujemy interfejs użytkownika WWW ale dalej korzystamy z tego samej logiki biznesowej i warstwy dostępu do bazy danych (Łatwiejsze użycie tego samego kodu w innym projekcie biznesowym). Innymi słowy bierzemy sobie dwie dll'ki i dodajemy do referencji aplikacji WWW.

Jak by to wyglądało w przypadku jednego dużego projektu? Albo musielibyśmy kopiować kod między projektami co doprowadziło by do redundancji i konieczności poprawiania tych samych błędów w dwóch projektach albo dodalibyśmy do projektu interfejsu WWW referencję do jednej dużej biblioteki albo nawet pliku exe!!!, która zawierać będzie wiele niepotrzebnych rzeczy.

Na koniec Marek słusznie zauważył, że wraz z wprowadzeniem obiektowych języków programowania nastąpił czas tworzenia pakietów, bibliotek, które tworzą sieć powiązań oraz, że jest to wręcz intuicyjne i naturalne.

Opinia Marka jest więc zgodna z moim zdaniem, co mnie bardzo cieszy, ale kij ma zawsze dwa końce. W najbliższej przyszłości postaram się, więc znaleźć i zachęcić do wypowiedzi kogoś z przeciwnego obozu.

15/08/2010

Jeden duży projekt, czy może wiele małych

Home

Kilka dni temu dyskutowałem z kolegą na temat tego czy całość/większość kodu powinna być umieszczona w jednym dużym projekcie (jak On uważa) czy rozłożona pomiędzy mniejsze projekty (jak uważam Ja). Przez projekt rozumiem tutaj jednostkę organizacji kodu np.: csproj w Visual Studio. Duży projekt to dla mnie taki, który zawiera wszystko czyli: implementację GUI, logikę biznesową, interfejsy, struktury danych, klasy dostępu do danych itd. Może się to przekładać na liczbę linii kodu ale nie musi. Dalej, aby odróżnić projekt jako jednostkę organizacji kodu od projektu jako przedsięwzięcia biznesowego będę używał sformułowania projekt biznesowy dla tego drugiego.

Wracając do wspomnianej dyskusji to zakończyła się impasem ponieważ żaden z nas nie zmienił swojego zdania. Spowodowała jednak, że postanowiłem jeszcze raz gruntownie przemyśleć sprawę. W ten sposób powstała poniższa lista zalet i potencjalnych wad małych projektów. Listy te są skonstruowałem w ten sposób, że na początki znajdują się najważniejsze/najpoważniejsze zalety i wady.

Zalety
  • Łatwiejsze użycie tego samego kodu w innym projekcie biznesowym. Przy dużych projekcie również jest to możliwe ale oznacza dodanie referencji do wielu innych niepotrzebnych w danym kontekście rzeczy czyl bałagan.
  • Wymusza poprawną architekturę. Na przykład jeśli GUI, logika biznesowa i dostęp do bazy danych znajdują się w innych projektach to projekt z GUI będzie miał referencję na projekt z logiką biznesową ale nie na odwrót ponieważ referencje cykliczne nie są dozwolone.
  • Ułatwia dalszy rozwój. Wyobraźmy sobie sytuację, w której projekt biznesowy jest na etapie stabilizacji i zbliża się termin oddania. Z drugiej strony istnieje konieczność dalszego rozwijania jakiejś jego części. Po wdrożeniu klient zapewne będzie zgłaszał błędy. Po jakimś czasie może pojawić się potrzeba złączenia (merge) dwóch (lub więcej) ścieżek rozwoju czyli przeniesienia poprawek błędów z wersji produkcyjnej na rozwojową i dodanie nowych funkcji z wersji rozwojowej do wersji produkcyjnej. W przypadku małych projektów łatwiej zorientować się co się zmieniło, co trzeba przenieść, a co nie, czy merge spowoduje jakieś błędy itd.
  • Aspekty psychologiczne. Nie przytłacza liczbą plików i folderów. Łatwiej zorientować się "o co biega" - łatwiej jest pracować z małym projektem odpowiedzialnym za jedną konkretna rzecz niż z dużym odpowiedzialnym za dziesiątki różnych rzeczy.
  • Łatwiejsze utrzymanie testów jednostkowych. W przypadku podejścia, w którym testy jednostkowe są umieszczane w innym projekcie niż testowany kod będzie mieli kilka małych projektów z testami jednostkowymi. W podejściu przeciwstawnym w danym projekcie będziemy mieli ograniczoną liczbę testów dotyczących tego jednego projektu. Należy to przeciwstawić dużym projektom gdzie powstanie nam albo kolejny duży projekt na testy jednostkowe albo bardzo duży projekt zawierający wszystko plus jeszcze testy jednostkowe tego wszystkiego.
  • Prostsze i łatwiejsze w utrzymaniu pliki konfiguracyjne. Ma to znaczenie jeśli używamy technologii wymagających wielu plików konfiguracyjnych, najczęsciej dokumentów XML np.: Spring.
  • Krótsza kompilacja. Jeśli nie zmieniły się interfejsy to można skompilować pojedynczy, mały projekt.
  • Wykonywanie brancha małego projektu trwa szybciej
Wady
  • Defragmentacja pamięci. Pisałem o tym już wcześniej. Problem polega na tym, że przy ładowaniu do pamięci moduły nie są ustawiane jeden po drugim ale są umieszczane w "losowo" wybranych miejscach co powoduje poszatkowanie pamięci. W większości wypadków nie jest to problemem ale na przykład przy alokacji dużej bitmapy potrzebny jest ciągły obszar pamięci. W wyniku defragmentacji system będzie dysponował dostatecznie dużą ilością pamięci ale nie w jednym kawałku. Problem nie występuje na systemach 64-bitowych, które są coraz powszechniejsze.
  • Dłuższe uruchamianie VS. Jeśli utworzymy Solution i dodamy do niego kilkadziesiąt projektów to jego otwieranie będzie trwać długo. Z drugiej strony czy aby na pewno praca z kilkudziesięcioma projektami ma sens?
  • Konieczność zarządzania dużą liczbą referencji. Każdy lub prawie każdy projekt będzie miał kilka lub więcej referencji do innych projektów. Zgadzam się, że może to być problem. Z drugiej strony pracowałem przy rozwijaniu naprawdę dużego systemu składającego się z kilkunastu podsystemów, każdy z kilkunastoma małymi projektami z czego część była współdzielona i radziliśmy sobie.
  • Trudniejsza instalacja. Wynika to z dużej ilości bibliotek dll, które powstają w wyniku kompilacji wielu małych projektów. Mogą również wystąpić konflikty wersji. Podobnie jak wyżej zgadzam się, że jest to możliwe ale podobnie jak wyżej przeciwstawiam tej wadzie swoje doświadczenie, które mówi co innego.
  • Dłuższa kompilacja całego systemu. Zgadzam się, przy wielu małych projektach kompilacja wydłuży się i to znacznie. Jednak i tutaj dołożę swoje trzy grosze. Jak często istnieje potrzeba przekompilowania całego systemu? Jeśli w danym momencie pracujemy z kilkoma konkretnymi projektami to po co wykonywać build wszystkich pozostałych? Ma to sens, jeśli zostały zmienione klasa, struktury lub interfejsy używane w wielu innych projektach.
  • Problemy z konfiguracją tych samych rzeczy w różnych projektach. Przy odpowiedniej architekturze systemu i zastosowaniu odpowiednich wzorców projektowych (singleton, fabryka) nie jest to dla mnie żaden problem.
  • Zwiększony czas uruchamiania aplikacji. Tak ale o ułamki sekund.
Ponieważ w powyższych wyliczeniach powoływałem się na to, że coś trwa tyle, a tyle przytoczę bardzo fajne zestawienie porównujące czasy kompilacji, ładowawania solution'a przez Visual Studio itd. dla różnej liczby projektów, które pojawiło sie w dyskusji na portalu stackoverflow - odpowiedź autorstwa jerryjvl'a.

Reasumując jestem przekonany, że zalety dzielenia kodu na małe projekty przeważają potencjalne wady. Co więcej uważam, że problemy z małymi projektami wynikają głównie ze złego podejścia i przyjęcia nieodpowiednich rozwiązań takich jak: budowanie wszystkich projektów zamiast kilku wybranych lub z dogmatycznego trzymania się małych projektów czyli bezrefleksyjnego tworzenia małego projektu na wszystko co się da. Co za dużo to jednak nie zdrowo :)

Na koniec jedna uwaga. Na początku projektu biznesowego może się wydawać, że lepiej trzymać wszystko w dużym projekcie bo tak prościej, bo kodu mało. Ale o ile nie jest to rzeczywiście malutki projekt biznesowy to szybko okaże się, że podzielenie kodu na mniejsze projekty będzie wymagać tyle pracy, że nikomu nie będzie się tego chciało zrobić.

12/08/2010

SlickRun

Home

Dwa dni temu kolega polecił mi program SlickRun, który spodobał mi się tak bardzo, że nie wiem jak radziłem sobie bez niego. SlickRun umożliwia tworzenie poleceń/komend, przez twórców zwanych "magicznymi słowami", do uruchamiania aplikacji, wyświetlania katalogów i stron WWW. Niby nic wielkiego ale równocześnie po zainstalowaniu programu w prawym dolnym rogu ekranu pojawia sie malutkie okienko (niezauważalne w codziennej pracy), do którego możemy wpisywać te komendy. Co ważne SlickRun jest na tyle mądry, że potrafi podpowiadać komendy, a więc nawet nie trzeba znać ich pełnych nazw. Teraz zamiast tworzyć skróty na pulpicie lub każdorazowo przeszukiwać dysk w poszukiwaniu potrzebnego katalogu mam kilkanaście intuicyjnych komend. Dla mnie super program, którego potrzebowałem nie zdając sobie z tego sprawy :)

09/08/2010

Logi IntelliTrace bez tajemnic

Home

IntelliTrace to jedno z najciekawszych narzędzi jakie pojawiło się w Visual Studio 2010. Dla tych, którzy jeszcze go nie znają w skrócie służy do nagrywania działania programu w celu późniejszej jego analizy. Nagrywane są wywołania metod oraz tzw. zdarzenia diagostyczne czyli ważne punkty w historii działania programu np.: nawiązanie połączenia z bazą danych, wykonanie zapytania do bazy danych, załadowanie modułu czy uruchomienie wątku. Visual Studio 2010 udostępnia kilka sposobów ich analizowania.

Listę wszystkich zarejestrowanych zdarzeń znajdziemy w oknie IntelliTrace Events View. Możemy je przefiltrować na podstawie wątku w jakim wystąpiły lub kategorii do jakiej należą. W oknie Calls View zobaczymy natomiast zdarzenia wplecione pomiędzy wywołania metod. Na tej podstawie łatwo zorientować się co doprowadziło do ich wygenerowania. Możliwości są więc całkiem spore ale jednak ograniczone. Z pomocą przychodzi IntelliTrace API, które pozwala na programową analizę logów IntelliTrace (*.iTrace).

Do czego może się to przydać? Do głowy przychodzi mi kilka pomysłów. Pierwszy z brzegu to program to graficznej wizualizacji zdarzeń. Dalej analiza logu pod kątem poprawności działania programu na podstawie analizy częstotliwości występowania poszczególnych typów zdarzeń. Sądzę, że w podobny sposób można analizować wydajność aplikacji. Może się również zdarzyć, że dane prezentowane przez Visual Studio będą dla nas zbyt ubogie i jedynym wyjściem będzie napisanie własnego analizatora.

Aby skorzystać z interfejsu programistycznego IntelliTrace powinniśmy zacząć od dodania do projektu referencji do biblioteki Microsoft.VisualStudio.IntelliTrace.dll. Znajdziemy ją w standardowym oknie Add Reference w zakładce .NET. Właściwą analizę logu rozpoczynamy od jego wczytania przy pomocy klasy IntelliTraceFile.
using (IntelliTraceFile file = new IntelliTraceFile(pathToIntelliTraceLog))
{
...
}
Klasa ta ma tylko jedną interesującą właściwość o nazwie Processes. Przy jej pomocy uzyskujemy dostęp do listy procesów, dla których log zawiera jakieś zdarzenia. W praktyce lista ta będzie zawierać tylko jedną pozycję. Proces reprezentowany jest przez klasę IntelliTraceProcess, która jest bardziej interesująca. Dzięki niej możemy dowiedzieć się o modułach załadowanych do procesu (właściwość Modules), uruchomionych wątkach (właściwość Threads) czy środowisku w jakim został uruchomiony proces (właściwość SystemInformationEvent). Wątki reprezentowane są przez klasę IntelliTraceThread.

Najważniejsze jest to, że klasy IntelliTraceProcess oraz IntelliTraceThread pozwalają na dobranie się do zdarzeń IntelliTrace. Służą do tego odpowiednio metody IntelliTraceProcess.CreateProcessChain oraz IntelliTraceThread.CreateThreadChain. Pierwszej z nich użyjemy jeśli interesują nas "łańcuchy zdarzeń" globalne dla całego procesu, a drugiej jeśli "łańcuchy zdarzeń" specyficzne dla danego wątku. Druga kategoria jest mniejsza i zawiera tylko trzy pozycje: ThreadChain, ThreadStreamChain oraz ThreadCheckpointChain. Warto zaznaczyć, że na tym poziomie wszystko jest zdarzeniem czyli zarówno wspomniane zdarzenia diagnostyczne jak i wywołania metod będą obsługiwane w taki sam sposób. Różnica polega tylko na innej klasie zdarzenia. Poniżej przykład wywołania metody IntelliTraceThread.CreateThreadChain, które zwróci łańcuch ze zdarzeniami odpowiadającymi min.: wywołaniu metod:
Chain chain = thread.CreateThreadChain<ThreadChain>();
Teraz nie pozostaje nic innego jak odczytać i przetworzyć kolejne zdarzenia:
EventToken eventToken = chain.FirstValidToken;
while (eventToken != chain.AfterLastToken)
{
  IntelliTraceEvent ev = chain.GetEvent(eventToken);

  //...

  eventToken = chain.GetNextToken(eventToken);
}
Klasa IntelliTraceEvent jest klasą bazową dla wszystkich typów zdarzeń, a więc aby uzyskać dokładniejsze dane należy wykonać rzutowanie na jeden z wydziedziczonych z niej typów.

Warto zwrócić uwagę na jedną szczególnie interesująca klasę z perspektywy analizy logu. Mam tutaj na myśli klasę DiagnosticStreamChain przy pomocy, której uzyskamy dostęp do tzw. zdarzeń diagnostycznych. Przykładem zdarzenia diagnostycznego jest wspomniane wcześniej wykonanie zapytania do bazy danych, naciśnięcie przycisku, wypisanie czegoś na konsolę czy przeładowanie strony w aplikacji ASP.Net. Zdarzeń tych jest dużo, a pełną ich listę znajdziemy w ustawieniach Visual Studio: Tools->Options->IntelliTrace->IntelliTrace Events. Możliwość analizy tego typu zdarzeń jest tym ciekawsza jeśli uwzględnimy możliwość rozszerzania tej listy o swoje własne zdarzenia! Zainteresowanych odsyłam do moich dwóch wcześniejszych tekstów Własne zdarzenia IntelliTrace oraz Własne zdarzenia IntelliTrace 2 .

Użycie IntelliTrace API nie jest skomplikowane ale nasuwa się pytanie:

Gdzie znaleźć dokumentację, szczegółowy opis poszczególnych klas reprezentujących zdarzenia, "łańcuchy zdarzeń" itd.?

Niestety ale dostępna dokumentacja na ten temat jest bardzo uboga. Znajdziemy w niej co prawda listę typów zdarzeń czy "łańcuchów" ale bez jakiegokolwiek opisu. Chyba, że opisem można nazwać coś takiego: "Describes a DiagnosticEvent." (dla klasy DiagnosticEvent) albo coś takiego: "Describes the ThreadCreateEvent." (dla klasy ThreadCreateEvent). Dla mnie to masło maślane. Konie z rzędem temu kto domyśli się na podstawie dostępnych informacji czym na przykład różnią się klasy ThreadChain oraz ThreadStreamChain? Można do tego dojść analizując kod biblioteki przy pomocy reflektora lub eksperymentalnie ale wymaga to dodatkowego wysiłku.

W zgłębianiu IntelliTrace API pomocny okaże się natomiast bardzo fajny projekt o nazwie iTraceReader, który znajdziemy tutaj. iTraceReader to nakładka na surowe IntelliTrace API, która ułatwia wykonanie wielu czynności. Zapoznając się z tym projektem można nauczyć się bardzo wielu rzeczy. Wszystkich, których ten temat zaciekawił zachęcam do bliższego przyjrzenia się.

28/07/2010

Używanie IntelliTrace poza Visual Studio 2010!

Home

W oficjalnej dokumentacji IntelliTrace można przeczytać, że narzędzie to jest dostępne tylko i wyłącznie z poziomu środowiska Visual Studio 2010. To bardzo źle ponieważ z góry przekreśla użycie IntelliTrace do diagnozowania błędów u klienta. Wyobraźmy sobie, że dostajemy zgłoszenie trudnego do powtórzenia błędu. Czy nie byłoby wspaniale uruchomić aplikację w środowisku produkcyjnym, pod kontrolą IntelliTrace i w razie wystąpienia błędu poprosić klienta o przekazanie nam plików z logami (*.iTrace). Nie wszystko jest jednak stracone. Pomimo, że nie jest to oficjalnie wspierane można uruchomić IntelliTrace z poza Visual Studio 2010!

Na początek należy odpowiedzieć na pytanie czym jest IntelliTrace: wątkiem działającym w ramach procesu Visual Studio 2010, niezależnym procesem, a może jeszcze czymś innym? Odpowiedź na to pytanie można uzyskać przy pomocy programu Process Explorer. Na poniższym obrazku widać fragment drzewa procesów dla węzła devenv.exe (czyli dla Visual Studio).



Pod nim znajdziemy węzeł odpowiadający debug'owanemu programowi oraz węzeł IntelliTrace, a w jego właściwościach ścieżkę z jakiej jest uruchamiany (VS_2010_INSTALL_DIR to katalog instalacyjny środowiska) np.:

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\IntelliTrace.EXE

Reasumując IntelliTrace to niezależny proces, który komunikuje się z Visual Studio przy użyciu jakiegoś mechanizmu IPC (ang. Inter-process communication ). Jeśli spróbujemy go uruchomić z linii poleceń na ekran zostanie wypisana lista dostępnych komend. Z kilku dostępnych interesująca jest komenda launch, która zgodnie z podanym opisem uruchamia podaną aplikację i rozpoczyna jej monitorowanie. Po wpisaniu w linię poleceń: IntelliTrace help launch uzyskamy bardziej szczegółowe informacje na jej temat.

Z dostępnych parametrów najważniejsze to /logfile (w skrócie /f) przy pomocy, którego wskazujemy docelowy plik z logiem oraz /collectionplan (w skrócie /cp) przy pomocy, którego wskazujemy plik z planem działania, konfiguracją IntelliTrace. Skąd go wziąć? Domyślny plik używany przez Visual Studio 2010, a który jest dokumentem XML, znajdziemy w lokalizacji:

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\en\CollectionPlan.xml

Mając już wszystkie elementy układanki, spróbujmy uruchomić jakiś program pod kontrolą IntelliTrace ale bez pomocy Visual Studio. Poniżej pokazano takie przykładowe wywołanie. W miejsce LOG_PATH należy wstawić ścieżkę do wyjściowego pliku z logiem (najlepiej nadać mu rozszerzenie iTrace), w miejsce COLLECTION_PLAN_PATH ścieżkę do pliku CollectionPlan.xml (może nazywać się inaczej i mieć inne rozszerzenie). PROGRAM_PATH to oczywiście ścieżka do programu jaki chcemy monitorować.

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\IntelliTrace.EXE launch /f:LOG_PATH /cp:COLLECTION_PLAN_PATH PROGRAM_PATH

Po wykonaniu tego polecenia we wskazanym przez nas katalogu pojawi się plik z logiem. Jeśli będzie miał rozszerzenie iTrace to po dwukliku uruchomi się Visual Studio 2010. W tym momencie czeka nas jednak niemiła niespodzianka ponieważ okaże się, że plik z logiem nie zawiera żadnych informacji tak jakby program nie był monitorowany.



Na brakujący element układanki naprowadzi nas ponownie Process Explorer, który umożliwia podejrzenie parametrów wywołania programu. Interesuje nas oczywiście proces IntelliTrace. Jeśli odczytamy jego parametry wywołania to dowiemy się, że został uruchomiony z komendą run, a nie launch. Różnica polega na tym, że run w przeciwieństwie do launch uruchamia tylko proces monitorujący i nie wskazuje konkretnej aplikacji do monitorowania. To jaka aplikacja będzie monitorowana zależy od Visual Studio 2010. Kolejna, ważna dla nas różnica to wartość parametru /cp, który nie wskazuje na domyślny plan wykonania ale na jakiś "dziwny" plik np.:

C:\Users\user\AppData\Local\Microsoft\VisualStudio\10.0\TraceDebugger\Settings\frueouq2.vfs

Jest to plik tymczasowy, a jego nazwa generowana jest w sposób losowy. Jeśli spróbujemy go otworzyć to okaże się, że bardzo przypomina domyślny plik CollectionPlan.xml. Dokładniej mówiąc schemat XML jest taki sam, inne są natomiast wartości atrybutów, liczba węzłów itd. czyżby brakujący element układanki? Tworzymy, więc kopię tego pliku, wskazujemy go w parametrze \cp i ponownie wydajemy wcześniej pokazane polecenie. Tym razem po otworzeniu wynikowego pliku z logiem zobaczymy listę monitorowanych wątków, drzewo wywołań itd.



Pełny sukces? Niestety nie do końca. Po bliższym przyjrzeniu się zauważymy, że mamy dostęp tylko do drzewa wywołań, a brakuje informacji o zdarzeniach diagnostycznych (ważne punkty w historii wykonania programu np.: wykonanie zapytania do bazy danych). Tego ograniczenia nie udało mi się jeszcze ominąć. Przypuszczam, że przy uruchamianiu IntelliTrace przez Visual Studio 2010 do programu przekazywane są jakieś dodatkowe opcje. Być może do rejestrowania zdarzeń potrzebne jest Visual Studio, a logger IntelliTrace nie potrafi tego robić?

Najważniejsze jest jednak to, że już teraz IntelliTrace można uruchomić niezależnie od środowiska programistycznego, chociaż z pewnymi ograniczeniami. Biorę to za dobrą monetę i mam nadzieję, że pełne wsparcie dla uruchamiania IntelliTrace z poza Visual Studio jest w planach Microsoft'u i pojawi się, jeśli nie z którymś service pack'iem to w kolejnej edycji Visual Studio.

Artukuł opublikowałem również w serwisie software.com.pl.

20/07/2010

Android 2.1

Home

Pierwszy post po długiej przerwie spowodowanej urlopem :) będzie trochę z innej beczki niż zwykle, a będzie dotyczył aktualizacji systemu Android do wersji 2.1 na telefonie Samsung Galaxy GT-I5700. Postanowiłem o tym napisać ponieważ spędziłem nad tym trochę czasu, na różnych forach można znaleźć sporo pytań o to, a okazało się, że sprawa jest dość prosta. Ja napotkałem dwa główne problemy:
  • Problem z wykryciem telefonu, po podłączeniu go do komputera przez kabel USB, przez oprogramowanie Samsung PC Studio.
  • Zawieszenie się telefonu w czasie aktualizacji.
Pierwszy problem rozwiązuje się właściwie sam. Jakiś czas temu pojawiła się aktualizacja aplikacji Samsung PC Studio. Po uruchomieniu programu otrzymamy komunikat z informacją o możliwość pobrania i zainstalowania uaktualnienia. Po zakończeniu procesu problem z wykrywaniem telefonu powinien zniknąć (u mnie ustąpił). Jeśli PC Studio cały czas uparcie nie widzi telefonu należy sprawdzić czy telefon ma włączone Debugowanie USB. Opcję tą znajdziemy tutaj: Ustawienia -> Aplikacje -> Programowanie -> Debugowanie USB.

Drugi problem polegał na tym, że po rozpoczęciu aktualizacji telefon w pewnym momencie wyłączył się, a przy ponownym uruchomieniu zawiesił na czarnym ekranie z napisem Samsung. Po przeczekaniu kilkunastu minut straciłem cierpliwość i odłączyłem (wbrew zaleceniom) telefon od komputera oraz go wyłączyłem wyjmując baterie (również niezalecane). Kolejne uruchomieniu telefonu odbyło się szczęśliwie bez kłopotów ale system oczywiście nie został uaktualniony.

Po chwili przypomniałem sobie, że używam dobrze znanej wielu osobom aplikacji APNdroid dzięki, której telefon nie połączy sie z netem poprzez sieci 2G/3G/EDGE bez naszej wiedzy (tak na marginesie polecam). Standardowo dostęp do sieci mam wyłączony i miałem niejasne przeczucie, że tutaj tkwi rozwiązanie problemu. Moje przeczucie okazało się trafne. Po włączeniu dostępu do sieci 2G/3G/EDGE i ponownym rozpoczęciu procesu aktualizacji wszystko się udało.

Na zakończenie kilka uwag. Przed rozpoczęciem aktualizacji warto wykonać kopię zapasową danych na telefonie. Pytanie o to czy chcemy wykonać taką kopię pojawi się w kreatorze aktualizacji. Po drugie zwracam uwagę, że odłączania telefonu od komputera w czasie aktualizacji jest niebezpieczne i może doprowadzić do utraty danych lub uszkodzenia telefonu. Ja byłem zmuszony to zrobić i się udało ale w żadnym stopniu nie zalecam powielania tej procedury. Najlepiej zrobić wszystko od początku do końca prawidłowo :) Na koniec przypominam, że po wykonaniu aktualizacji nie będzie już można korzystać z PC Studio do komunikacji z telefonem. Zamiast tej aplikacji należy pobrać nową o nazwie KIES

27/06/2010

Słów kilka o ASP.NET, IIS, corflags i opcjach kompilacji

Home

Jakiś czas temu przenosiłem aplikację ASP.NET z środowiska developerskiego do testowego i jak często bywa w takich sytuacjach migracja nie obyła się bez pewnych kłopotów. Przy próbie uruchomienia aplikacji użytkownik otrzymywał informację o tym, że nie udało się załadować jednej z bibliotek. Po chwili zauważyłem, że bezpośrednim winowajcą był wyjątek BadImageFormatException. Z podobnym problem już się spotkałem dlatego szybko skojarzyłem, że przyczyną problemu może być próba załadowania 32 bitowej biblioteki do procesu 64 bitowego (środowisko testowe było 64 bitowe). Dla przypomnienia nie jest możliwe aby proces 64 bitowy korzystał z bibliotek 32 bitowych i na odwrót.

Przyjrzałem się więc bliżej kłopotliwej bibliotece przy pomocy programu corflags (pisałem już o nim w tym poście) i okazało się, że ma ona ustawiona flagę 32BIT wskazującą, że biblioteka może być uruchamiana tylko w trybie 32 bitowym. Sprawdziłem również inne biblioteki ale nie miały ustawione tej flagi. W tym momencie wszystko było już jasne, aplikacja ASP.NET hostowana była przez 64 bitowy serwer i w związku z tym nie mogła skorzystać z biblioteki 32 bitowej. Wyczyściłem flagę, również przy pomocy programu corflags i zgodnie z oczekiwaniami problem ustąpił.

Następnie postanowiłem wyjaśnić przyczynę czemu jedna biblioteka miała ustawioną flagę 32BIT. W tym celu przyjrzałem się ustawieniom projektu w Visual Studio i okazało się, że opcja Platform target ustawiona jest na x86 zamiast na Any CPU jak w innych projektach. W tym momencie przypomniałem sobie, że sam to zrobiłem żeby móc używać funkcji Edit and Continue, a po skończeniu pracy zapomniałem przywrócić odpowiednią konfigurację.

Na koniec pozostaje wyjaśnić czemu aplikacja działała w 64 bitowym środowisku developerskim, a w testowym już nie. Otóż, w środowisku testowym pula aplikacyjna w jakiej została umieszczona aplikacja miała wyłączoną flagę Włącz aplikacje 32 bitowe, która umożliwia hostowanie takich aplikacji na 64 bitowym serwerze IIS. Flaga ta dostępna jest w zaawansowanych ustawieniach puli aplikacji w grupie Ogólne. W środowisku developerskim używałem natomiast serwera zintegrowanego z Visual Studio. Nie jestem tego pewien ale prawdopodobnie ma on domyślnie włączoną tą flagę albo środowisko samo określa czy ta flaga ma być włączona w zależności od ustawień uruchamianych projektów.

Reasumując warto znać narzędzie corflags, opcję konfiguracji projektów Platform target oraz przełącznik Włącz aplikacje 32 bitowe .

16/06/2010

Własne zdarzenia IntelliTrace 2

Home

Dzisiaj, zgodnie z wcześniejszą obietnicą, chciałem pokazać w jaki sposób zdefiniować nowe zdarzenie diagnostyczne dla naszej własnej metody. W tym celu użyję bardzo prostej klasy pokazanej poniżej:

namespace SmallTest
{
    public class Fun
    {
        public void Hello(int count, string msg)
        {
            for (int i = 0; i < count; ++i)
                Console.WriteLine(msg);
        }
    }
}

Przyjmijmy, że klasa ta znajduje się w projekcie o nazwie SmallTest po skompilowaniu, którego powstanie plik SmallTest.dll. Skoro mamy już z czym pracować przystąpmy do modyfikacji pliku ColllectionPlan.xml. Cały proces będzie bardzo podobny do tego co pokazałem w poprzednim poście. Zaczynamy od znalezienia węzła o nazwie ModuleList i umieszczamy pod nim węzeł wskazujący na naszą bibliotekę:

  <ModuleSpecification Id="SmallTest">SmallTest.dll</ModuleSpecification>

Co istotne biblioteka nie musi być podpisana, ani znajdować się w jakimś konkretnym katalogu. Następnie tworzymy nową kategorię dla zdarzeń. W tym celu pod węzłem Categories powinniśmy umieścić pokazany poniżej węzeł XML. Jeśli nie chcemy tworzyć nowej kategorii możemy pominąć ten krok.

  <Category Id="my" _locID="category.My">My</Category>

Na koniec najważniejsza rzecz, zdefiniowanie nowego zdarzenia. Postępujemy dokładnie tak samo jak przy definiowaniu zdarzenia dla metody platformy .NET. Potrzebny kod XML został pokazany poniżej. Umieszczamy go pod węzłem DiagnosticEventSpecifications. Jeśli w poprzednim kroku nie tworzyliśmy nowej kategorii wartość węzła CategoryId powinna odpowiadać nazwie innej, już istniejącej kategorii.

    <DiagnosticEventSpecifications>
      <DiagnosticEventSpecification>
        <CategoryId>my</CategoryId>
        <SettingsName _locID="settingsName.Fun.Hello">Fun.Hello</SettingsName>
        <SettingsDescription _locID="settingsDescription.Fun.Hello">Fun.Hello</SettingsDescription>
        <Bindings>
          <Binding>
            <ModuleSpecificationId>SmallTest</ModuleSpecificationId>
            <TypeName>SmallTest.Fun</TypeName>
            <MethodName>Hello</MethodName>
            <MethodId>SmallTest.Fun.Hello(System.Int32, System.String):System.Void</MethodId>
            <ShortDescription _locID="shortDescription.Fun.Hello">Fun.Hello({0},{1})</ShortDescription>
            <LongDescription _locID="longDescription.Fun.Hello">Fun.Hello(count={0},msg={1})</LongDescription>
            <DataQueries>
              <DataQuery index="1" maxSize="0" type="Int32" _locID="dataquery.Fun.Hello.count" _locAttrData="name" name="count" query="" />
              <DataQuery index="2" maxSize="256" type="String" _locID="dataquery.Fun.Hello.msg" _locAttrData="name" name="msg"  query=""></DataQuery>
            </DataQueries>
          </Binding>
        </Bindings>
      </DiagnosticEventSpecification>

Szczegółowy opis znaczenia poszczególnych węzłów XML użytych w tej definicji podałem w poprzednim poście, a więc nie będę go tutaj powielał. Uruchamiany Visual Studio i jeśli nie popełniliśmy żadnego błędu możemy już korzystać z nowego zdarzenia. W celu sprawdzenia czy wszystko jest w porządku najpierw zaglądamy do Tools->Options->IntteliTrace->IntteliTrace Events. Powinniśmy zobaczyć nową kategorię, a po jej rozwinięciu nowe zdarzenie:



Teraz możemy stworzyć nowy projekt konsolowy i dodać referencję to biblioteki SmallTest. Kiedy to zrobimy, nie pozostaje nic innego jak utworzyć obiekt klasy Fun i wywołać metodę Hello:

            Fun f = new Fun();
            f.Hello(2, "Hello world!");

Po skompilowaniu i uruchomieniu programu pod kontrolą debugger'a i z włączonym mechanizmem IntteliTrace w oknie z zarejestrowanymi zdarzeniami zobaczymy nasze zdarzenie:



W następnym poście pokażę w jaki sposób analizować wystąpienia zdarzeń w sposób programowy.

11/05/2010

Własne zdarzenia IntelliTrace!

Home

IntelliTrace, znane również pod nazwą historycznego debugger'a, to narzędzie jakie pojawiło się w Visual Studio 2010, a które stanowi rozwinięcie "tradycyjnych" debugger'ów o możliwość nagrywania historii wykonania programu w celu jej późniejszej analizy. Post ten rozpoczyna serię dotyczącą tej technologii, a w której chcę opisać zaawansowane techniki użycia IntelliTrace.

IntelliTrace posiada dwa tryby pracy: podstawowy oraz rozszerzony. W trybie podstawowym stan programu zapisywany jest w momencie wystąpienia tzw. zdarzeń diagnostycznych. W trybie rozszerzonym rejestrowane są dodatkowo wywołania metod, dostęp do właściwości itd. Zdarzenia diagnostyczne to ważne punkty w historii wykonania programu np.: wykonanie zapytania do bazy danych, dostęp do pliku, wystąpienie wyjątku, zatrzymanie programu na pułapce i wiele innych. Poniższy rysunek pokazuje penel z listą dostępnych zdarzeń. Możemy się do niego dostać wybierając Tools->Options->IntelliTrace->IntelliTrace Events.



Większość zdarzeń związana jest z wywołaniem określonych metod np.: ExecuteReader. Zdarzenia tego rodzaju zdefiniowane są w pliku CollectionPlan.xml. Istotne jest to, że plik ten można modyfikować poprzez zmianę istniejących zdarzeń albo dodawanie właśnych. Plik ten znajduje się w lokalizacji:

VS_2010_INSTALL_DIR\Team Tools\TraceDebugger Tools\LANGUAGE\CollectionPlan.xml

VS_2010_INSTALL_DIR to katalog instalacyjny Visual Studio 2010. Wartość LANGUAGE zależy od używanego przez nas języka. Domyślnie po zainstalowaniu środowiska zostanie utworzony katalog en. Jeśli jednak pracujemy z językiem polskim musimy samemu utworzyć katalog o nazwie pl i skopiować do niego plik CollectionPlan.xml. Jest to konieczne jeśli chcemy wprowadzić jakieś zmiany do podstawowego zestawu zdarzeń. Przed przystąpieniem do pracy zalecam utworzenie kopii zapasowej tego pliku.

Modyfikację ColllectionPlan.xml pokażę na przykładzie dodania nowego zdarzenia do kategorii Console. Kategoria ta zawiera zdarzenia skojarzone z wywołaniem metody Console.WriteLine. Z niewiadomych powodów nie przewidziano zdarzenia dla metody Console.Write. Naprawmy to! Poniżej zamieściłem XML stanowiący definicję nowego zdarzenia dla tej metody:

      <DiagnosticEventSpecification enabled="false">
        <CategoryId>console</CategoryId>
        <SettingsName _locID="settingsName.Console.Write">Write (1 args)</SettingsName>
        <SettingsDescription _locID="settingsDescription.Console.Write">Console Output with a String passed in.</SettingsDescription>
        <Bindings>
          <Binding>
            <ModuleSpecificationId>mscorlib</ModuleSpecificationId>
            <TypeName>System.Console</TypeName>
            <MethodName>Write</MethodName>
            <MethodId>System.Console.Write(System.String):System.Void</MethodId>
            <ShortDescription _locID="shortDescription.Console.Write.String">{0}</ShortDescription>
            <LongDescription _locID="longDescription.Console.Write">Console Output "{0}".</LongDescription>
            <DataQueries>
              <DataQuery index="0" maxSize="256" type="String" name="value" _locID="dataquery.Write.value" _locAttrData="name" query=""></DataQuery>
            </DataQueries>
          </Binding>
        </Bindings>
      </DiagnosticEventSpecification>

Węzeł Category określa kategorię do jakiej zostanie dodane nowe zdarzenie. SettingsName definiuje nazwę zdarzenia jaka będzie widoczna w ustawieniach VS 2010. Kolejny węzeł o nazwie SettingsDescription ma podobne znaczenie. Zawiera opis zdarzenia jak będzie wyświetlany dla użytkownika. Poniżej zamieścilem obrazek pokazujący w jaki sposób środowisko zinterpretuje te parametry.



Dalsza część definicji zdarzenia jest ważniejsza. Przede wszystkim zawiera ona dokładną specyfikację metody czyli nazwę modułu w jakim znajduje sie jej kod (węzeł ModuleSpecificationId), nazwę klasy w jakiej została zdefiniowana (węzeł TypeName), nazwę metody (węzeł MethodName) oraz pełną sygnaturę metody (węzeł MethodId). Jeśli podamy błędne dane nasze zdarzenie nie zadziała dlatego trzeba być uważnym.

Większą swobodę dają nam węzły ShortDescription oraz LongDescription definiujące opis podstawowy/skrócony i rozszerzony zdarzenia. O co dokładnie chodzi? Zostało to pokazane na poniższym obrazku przedstawiającym zdarzenia zarejestrowane przez IntelliTrace w czasie monitorowania programu.



Opis skrócony zdarzenia widoczny jest zawsze, podczas gdy opis rozszerzony dopiero po kliknięciu zdarzenia. Opis te mogą zawierać dowolne napisy, a także odwoływać się do wyniku zwróconego przez metodę, do wartości parametrów wywołania czy też do składowych klasy. O tym jakie informacje są dostępne dla zdarzenia decyduje zawartość węzła DataQueries. Może on zawierać dowolną liczbę podwęzłów DataQuery. Każdy podwęzeł o takiej nazwie definiuje coś w rodzaju zapytania, które zwróci np.: wartość parametru wywołania metody. Aby odwołać się do jakiegoś zapytania używamy składni {index} gdzie index to numer zapytania w numeracji od zero. Znaczenie poszczególnych atrybutów jest następujące.

  • index Przyjmuje odpowiednio wartości: -1 gdy odwołujemy się do wyniku zwracanego przez metodę, 0 gdy odwołujemy się do obiektu na rzecz, którego została wywołana metoda lub do pierwszego parametru jeśli to metoda statyczna, 0,1,2... gdy odwołujemy się do kolejnych parametrów wywołania metody.
  • maxSize Określa maksymalny rozmiar wyniku zwróconego przez zapytanie. Jeśli zostanie przekroczony to wynik zostanie obcięty.
  • type Typ wyniku. Na przykład jeśli pobieramy wartości parametru wywołania metody, który jest typu String to wartość tego atrybutu powinna być równa String.
  • name, _locID, _locAttrData Z mojego doświadczenia wynika, że można tu wpisać co kolwiek. Osobiście jednak wypełniając te atrybuty wzoruję się na innych definicjach zdarzeń.
  • query Atrybut używany kiedy chcemy odwołać się do składowej obiektu. W najprostszej postaci zawiera po prostu nazwę składowej.
Skoro już wiemy jak zbudowana jest definicja zdarzenia umieśćmy ją w pliku CollectionPlan.xml jako dziecko węzła DiagnosticEventSpecifications. Teraz konieczne jest ponowne uruchomienie VS 2010 i już możemy cieszyć się nowym zdarzeniem. Nic trudnego, prawda?

Na koniec ostrzeżenie. Przy dodawaniu nowych zdarzeń należy zachować umiar. Domyślny zestaw zdarzeń został dobrany tak aby nie wpływać na wydajność monitorowanego programu. Jeśli dodamy zdarzenie dla metody wywoływanej bardzo często wpłynie to negatywnie na jego wydajność podczas pracy z IntelliTrace.

W następnym poście na temat IntelliTrace pokażę jak dodać zdarzenie związane z naszą własną metodą, w jaki użyć węzłów DataQuery aby odwołać się do składowej obiektu oraz jak analizować wystąpienia zdarzeń w sposób programistyczny.

07/05/2010

ShowDialog i zwalnianie zasobów

Home

TestForm form = new TestForm();

if (form.ShowDialog() == DialogResult.OK)
{
  ...
}
Czy powyższy króciutki fragment kodu powodujący wyświetlenie okna dialogowego jest poprawny? Niestety, jeszcze do niedawna powiedziałbym bez mrugnięcia oka, że oczywiście tak. Niestety ponieważ ta odpowiedź jest niepoprawna. Do tego aby ten kod był poprawny brakuje niewielkiej ale za to bardzo istotnej rzeczy i zostało to pokazane poniżej:

using(TestForm form = new TestForm())
{
  if (form.ShowDialog() == DialogResult.OK)
  {
    ...
  }
}
Zamiast klauzuli using można oczywiście wywołać Dispose. Dlaczego jednak jawne zwolnienie zasobów jest w ogóle potrzebne? Czy to oznacza, że za każdym razem kiedy chcemy wyświetlić jakieś okno musimy pamiętać o klauzuli using?

Odpowiedź brzmi nie, jest to konieczne tylko jeśli wyświetlamy okno dialogowe. W takim wypadku po jego zamknięciu nie następuje wywołanie metody Close, która zwolni za nas zasoby. Dzięki temu możemy użyć tego samego okna ponownie. Nie jest to żadna wiedza tajemna i można o tym przeczytać w dokumentacji metody ShowDialog:

Unlike modeless forms, the Close method is not called by the .NET Framework when the user clicks the close form button of a dialog box or sets the value of the DialogResult property.

Kolejny raz przekonuję się, że nawet jeśli czegoś używamy bardzo często i jest to dla nas coś prostego i oczywistego to nie znaczy to, że robimy to na 100% dobrze :)

10/04/2010

.NET Reflector Pro

Home

.NET Reflector Pro to najnowsza odsłona dobrze znanego programistą .NET narzędzia, firmy redgate, ktora pojawiła się na rynku 15 lutego. Do tej pory nie miałem okazji się z nią zapoznać ale ostatnio nadrobiłem zaległości i jestem zachwycony nowymi możliwościami programu. Co mi się tak spodobało?

Otóż .NET Reflector Pro to plugin integrujący się z środowiskiem Visual Studio, który pozwala na dekompilację pakietów C#/VB w locie, a następnie ich debugowanie! Innymi słowy jeśli korzystamy z bibliotek jakiegoś zewnętrznego dostawcy ale nie posiadamy jej źródeł, a z jakiegoś powodu chcemy ją zdebugować postępujemy następująco:
  • Wybieramy polecenie Choose Assemblies To Debug z menu .NET Reflector w Visual Studio.
  • Zaznaczamy interesujące nas pakiety.
  • Klikamy OK.
  • Rozpoczynamy debugowanie, a niewidoczny do tej pory kody stoi dla nas otworem. Możemy stawiać pułapki, podglądać wartości zmiennych itd.
Proste, prawda? Bardzo lubię takie narzędzia, minimum wysiłku i maksimum korzyści. Niestety ale plugin jest płatny - można jednak testować go przez okres 14 dni (do pobrania tutaj). Szczerze polecam!

03/04/2010

Po konferencji

Home

Od mojej sesji na temat IntelliTrace podczas Warszawskich Dni Informatyki minęły już prawie trzy tygodnie. Pora odpowiedzieć na kilka pytań: Jak było? Jak spisali się organizatorzy? Czy jestem zadowolony ze swojej prezentacji? Co wypadło dobrze, a co źle?
Zacznę od tego, że organizatorzy czyli Student Partners z warszawskich uczelni stanęli na wysokości zadania i przygotowali w pełni profesjonalną konferencję. Wydarzenie było dobrze rozreklamowane i frekwencja dopisała. Zadbano o profesjonalne nagłośnienie. Prelegenci mogli spokojnie przygotować się do wystąpienia w zarezerwowanym dla nich pokoju. Nie chodzili też głodni i spragnieni, uczestnicy zresztą również nie ;) Każdy dostał materiały konferencyjne, w tym pendriv'a. Można było wygrać laptopa, a w czasie przerw pograć na konsoli XBOX. Zorganizowano też konkurs Guitar Hero oraz przygotowano ankiety.

Mam tylko kilka uwag ale niewielkiego kalibru. Wolałbym aby impreza odbyła się bliżej centrum albo jakiejś stacji metra ale zdaję sobie sprawę, że to nie taka prosta sprawa. Po drugie obsługa konferencji mogłaby rozpoznawać prelegentów i nie kazać się im rejestrować :) Dziewięć osób to nie jest znowu taka duża liczba. Największe zastrzeżenie mam do rzutnika, który przynajmniej podczas mojej sesji nie spisał sie dobrze i jakość obrazu była słaba.

Jak oceniam swoją sesję? W skrócie jestem zadowolony. Przed konferencją byłem trochę zdenerwowany ale zaprocentowało dobre przygotowanie i wcześniejsze przećwiczenie prezentacji. Dzięki temu kiedy zacząłem mówić nerwy odpuściły i poczułem się swobodniej. Plan prezentacji, układ i treść slajdów miałem w głowie i nie musiałem zastanawiać się kiedy co powiedzieć. Sądzę, że mówiłem głośno i wyraźnie. Udały się również wszystkie zaplanowane pokazy IntelliTrace, Visual Studio 2010 i przygotowanych programów. W czasie i po sesji uczestnicy zadawali pytania.

Oczywiście zdaję sobie sprawę z kilku niedociągnięć. Co mi nie wyszło? Po pierwsze po konferencji zwrócono mi uwagę na błąd ortograficzny na jednym ze slajdów. Na swoją obronę mogę powiedzieć, że Power Point też go nie zauważył. Po drugie nie ustrzegłem się błędu w wymowie słowa debugger oraz zapomniałem powiedzieć o jednej czy dwóch rzeczach. Nagrania swojej sesji jeszcze nie widziałem ale zdaję sobie sprawę, że pomimo wszystko byłem zdenerwowany - zdarzyło mi się zaciąć czy powiedzieć coś niezrozumiale. Powinienem również zachowywać się swobodniej.

Podsumowując konferencję oceniam na bardzo mocne 5, brawo dla organizatorów. Swojej sesji nie podejmują się oceniać ale chętnie wysłucham waszych opinii i uwag.

Chciałbym również podziękować żonie za pomoc w przygotowaniach do konferencji, cierpliwe wysłuchanie i cenne uwagi.

02/03/2010

Warszawskie Dni Informatyki 2010

Home

Zapraszam na konferencję Warszawskie Dni Informatyki 2010 oraz na moją sesję podczas, której opowiem, a przede wszystkim pokażę jedną z najciekawszych nowości w Visual Studio 2010 - IntelliTrace!


21/02/2010

World of Goo

Home

Rozgłos i zainteresowanie zdobywają dwa rodzaje gier komputerowych. Te, które są tworzone przez wielkie studia developerskie i mają budżety sięgające dziesiątek milionów dolarów oraz te tworzone w małych studiach, stosunkowo niewielkim nakładem pieniędzy ale nowatorskie, przyciągające nietypowym pomysłem i podejściem do tematu. Te drugie przeważnie nie zachwycają grafiką czy rozmachem ale potrafią wciągnąć na długie godziny. Na taką grę natknąłem się ostatnio.

Word of Goo czyli świat glutów ;) to logiczno, zręcznościowa gra, w której ogromne znaczenie odgrywają prawa fizyki. Grę charakteryzuje specyficzne poczucie humoru i nietypowa oprawa graficzna, a rozgrywka jest naprawdę wciągająca. W skrócie polega na budowaniu różnorakich konstrukcji (mostów, wież itp.) z tytułowych glutów. Może i brzmi to trochę dziwnie ale daje bardzo dużo radości.

Nie chciałbym sie tutaj rozpisywać ponieważ w sieci można znaleźć niejedną dobrą recenzję tej gry. Od siebie serdecznie polecam ten tytuł każdemu kto lubi trochę pomyśleć, ceni innowacyjność i lubi dobrą zabawę. Grę można kupić już za 20 zł!

18/02/2010

Enterprise Library, Informix i CLOB

Home

Data Access Application Block to część Enterprise Library oparta o ADO.NET odpowiedzialna za komunikację z bazą danych. Jedną z bardzo miłych cech tego modułu jest to, że w dużym stopniu ukrywa przed programistą rodzaj bazy danych z jakim pracuje. Uprasza to pisanie aplikacji kompatybilnych z wieloma bazami danych.

Oczywiście nie wszystko jest takie różowe o czym przekonałem się ostatnio. W tej chwili pracuję głównie z bazą danych Informix firmy IBM i Data Access Application Block radził sobie z nią całkiem dobrze ale do czasu kiedy postanowiłem wykorzystać typ bazodanowy CLOB służący do przechowywania dużych obiektów znakowych.

Nie wnikając w szczegóły zapis danych do kolumny o typie CLOB nie udał mi się w ogóle, a odczyt częściowo :) Częściowo ponieważ mogłem odczytać tylko fragment danych. Nie wykluczam możliwości, że mogłem popełnić jakiś błąd ale nie chcąc tracić czasu problem rozwiązałem wykorzystując sterownik ADO.NET firmy IBM przeznaczony tylko do obsługi Informix'a. Biblioteka ze sterownikiem nazywa sie IBM.Data.Informix.dll i instalowana jest razem z Client SDK. U mnie znajdowała się w lokalizacji C:\Program Files (x86)\Informix\Client-SDK\bin\netf20. Model pracy z klasami z tej biblioteki jest prawie taki sam jak z standardowymi klasami ADO.NET. Mamy więc klasę IfxCommand, która dziedziczy z DbCommand, IfxConnecton, która implementuje interfejs IDbConnection itd. Różnice są niewielkie i dotyczą rzeczy specyficznych dla Informix'a czyli np.: obsługi typu CLOB.

Wykorzystanie biblioteki rozwiązało oczywiście mój problem. Sądzę, że wybór IBM.Data.Informix.dll jest dobrym pomysłem dla każdego kto ma zamiar pracować z bazą danych firmy IBM. Nie ma jednak róży bez kolców. Użycie rozwiązania właściwego dla konkretnej bazy danych zamiast Enterprise Library wpływa negatywnie na przenoszalność aplikacji, a własciwie ją uniemożliwia. Oczywiście klasy specyficzne dla Informix'a można odpowiednio przykryć tak aby ewentualna podmiana była łatwiejsza ale nie jest to równoznaczne z użyciem Data Access Application Block.

Reasumując jeśli chcę zachować przenaszalnośc aplikacji będę musiał zastanowić się jak dostosować tą bibliotekę do współpracy z Enterprise Library.

14/02/2010

Pobranie fabryki klasy COM...

Home

Niedawno zostałem poproszony o pomoc w rozwiązaniu problemu z aplikacją ASP.Net do zarządzania bazą danych, który polegał na pojawianiu się poniższego błędu przy jej uruchamianiu:

"Pobranie fabryki klasy COM dla składnika o identyfikatorze CLSID {10020200-E260-11CF-AE68-00AA004A34D5} nie powiodło się z powodu następującego błędu: 80040154."

Błąd ten oznacza tyle, że w systemie nie ma zarejestrowanej klasy COM o podanym identyfikatorze. Ponieważ miałem pod ręką drugi komputer postanowiłem sprawdzić co kryje się za tajemniczym numerem CLSID. W tym celu uruchomiłem edytor rejestru (regedit) i przeszukałem gałąź HKEY_CLASSES_ROOT\CLSID w poszukiwaniu pasującego identyfikatora. Szczęście uśmiechnęło się do mnie i szybko znalazłem odpowiedni klucz z ścieżką do biblioteki dll:

C:\Program Files\Microsoft SQL Server\80\Tools\binn\SQLDMO.DLL

Dalej poszło już prosto. SQLDMO czyli SQL Distributed Management Objects to zbiór obiektów umożliwiających wykonywanie różnych czynności administracyjnych, które standardowo instalowane są razem SQL Server Client Utilities. Ponieważ nie miałem możliwości zainstalowania tego zestawu narzędzi postanowiłem po prostu skopiować wspomnianą bibliotekę i zarejestrować ją przy pomocy polecenia regsvr32. W rozwiązaniu problemu przydatny okazał sie również ten artykuł pomocy technicznej Microsoft. Dzięki niemu dowiedziałem się, że oprócz biblioteki Sqldmo.dll potrzebny jest też plik zasobów Sqldmo.rll. W artykule wymieniono również listę innych bibliotek związanych z modelem obiektów SQL-DMO. Okazało się jednak, że na problematycznym komputerze brakuje tylko tej jednej biblioteki. Po jej zarejestrowaniu aplikacja ASP.Net zaczęła działać.