28/12/2009

BadImageFormatException i TFS

Home

Przed paroma dniami zainstalowałem na swoim komputerze najnowszą wersję serwera TFS czyli Microsoft Visual Studio Team Foundation Server 2010 Beta 2. Instalacja przebiegła szybko i bezproblemowo z dokładnością do tego, że wcześniej musiałem jeszcze zainstalować SQL Server 2008 oraz, co trochę dziwne dla mnie, najnowszy Service Pack dla systemu Windows Vista.

Kiedy instalacja dobiegła końca uruchomiłem konsolę Team Foundation Administration Console, dalej użyłem kreatora konfiguracji i pomyślałem, że będę mógł się cieszyć nową zabawką. Niestety próba podłączenia się do serwera, czy to z poziomu VS 2010 czy to z poziomu programu Microsoft Test and Lab Manager, kończyła się błędem Unable to connect to Team Foundation Server.

Próbując rozwiązać problem dotarłem do systemowego logu zdarzeń. Tam znalazłem kilka podejrzanie wyglądających zdarzeń, których źródło nazywało się TFS Services. Aby upewnić się co do ich pochodzenia zatrzymałem pulę aplikacyjną Microsoft Team Foundation Server Application Pool, do której należą usługi TFS. Odczekałem chwilę i ponownie ją uruchomiłem. Zaglądam ponownie do logu zdarzeń i znajduję serię 3 komunikatów, których czas wystąpienia zgadzał się z czasem uruchomienia puli aplikacyjnej. Dwa pierwsze komunikaty miały źródło o nazwie TFS Services, a trzeci ASP.NET 4.0.21006.0. W trzecim komunikacie znalazłem interesującą informację o wystąpieniu nieobsłużonego wyjątku BadImageFormatException.

Jeszcze chwila szukania i znajduję rozwiązanie. Okazało się, że błąd był spowodowany nieprawidłową konfiguracja wspomnianej puli aplikacyjnej Microsoft Team Foundation Server Application Pool. Miała ona włączoną opcję Włącz aplikacje 32 bitowe podczas gdy ja korzystam z wersji 64 bitowej. Po wyłączeniu tej flagi problem ustąpił i wszystko zaczęło działać.

23/12/2009

Życzenie świąteczne

Home

Wszystkim czytelnikom i czytelniczkom składam życzenia wesołych i radosnych Świąt Bożego Narodzenia.

Serdecznie pozdrawiam
Michał Komorowski

18/12/2009

"Dzikie żądze"

Home

W pracy komputer, na studiach doktoranckich komputer... Nie ulega wątpliwości, że dużą część dnia spędzam wpatrzony w monitor. Dlatego tak bardzo lubię wyjść z żoną na miasto, a jedną z naszych ulubionych rozrywek jest dobra komedia w teatrze.

Przedwczoraj odwiedziliśmy ponownie teatr "Bajka" na deskach, którego można teraz oglądać brytyjską sztukę "Dzikie żądze". Opowiada ona o małżeństwie Państwa Griffin. On bogaty przemysłowiec, ona lekko znudzona. Aby ratować małżeństwo postanawiają na nowo rozbudzić w sobie żądzę... Więcej z fabuły nie zdradzę, dodam tylko, że jest nieprzewidywalna, pełna zwrotów akcji i po prostu przezabawna. Przez większość przedstawienia nie robiłem nic innego tylko się śmiałem. W pewnym momencie nawet aktorzy nie mogli się powstrzymać od śmiechu. Byłbym jednak niesprawiedliwy gdybym nie powiedział, że bardzo szybko się powstrzymali.

Zaskakujące było też zakończenie wieczoru. Jeden z aktorów zaprosił na scenę młodą parę, która wygrała bilety w konkursie radiowym żeby zrobić sobie z nimi zdjęcie. Jak się okazało był to tylko "niecny" wybieg. Gdzieś znalazły się kwiaty i za chwilę chłopak na kolanach prosił swoją wybrankę o rękę :) Brawo za odwagę!

W teatrze "Bajka" po raz kolejny spędziłem wspaniały wieczór i z czystym sumieniem mogę go polecić każdemu. Teatr naprawdę nie gryzie.

11/12/2009

Ciekawe zgłoszenie błędu

Home

Jest to druga, poprawiona wersja tego postu. Za wcześniejsze pomyłki przepraszam.

W poście tym chciałbym opisać interesujący błąd. Wszystko zaczęło się od zgłoszenia od klienta dotyczącego problemów z wydrukami. Nie wchodząc w szczegóły okazało się, że cały problem sprowadza się do utworzenia dostatecznie dużej bitmapy. Co jednak ciekawe analiza pokazała, że system dysponuje znaczną ilością wolnej pamięci (ok 1.5 GB) podczas gdy do utworzenia bitmapy potrzebne było "raptem" kilkaset megabajtów. Tutaj dodam, że mówimy o systemie 32 bitowym.

Z pomocą przyszedł tutaj program vnmap, który służy do analizy pamięci wirtualnej i fizycznej procesu. Pokazał on, że proces rzeczywiście dysponuje znaczną ilością pamięci ale największy ciągły jej obszar to tylko 200 MB. Do zaalokowania bitmapy potrzeba natomiast właśnie ciągłego obszaru pamięci. Nie dotyczy to zresztą tylko bitmap, podobny problem możemy wystąpić przy ładowaniu bibliotek dll.

Taką sytuację nazywamy defragmentacją pamięci. Co było jej przyczyną? Zgodnie z tym co pokazał wspomniany program vnamp pamięć w dużym stopniu była poszatkowana przez biblioteki dynamiczne. Nie bez znaczenia jest tutaj fakt, że rozpatrywany przypadek dotyczył dość dużego systemu zbudowanego z wielu modułów.

Problem próbowałem zaleczyć przy użyciu narzędzia rebase.exe, które służy do ustawienia preferowanego adresu pod jaki ma zostać załadowana dll'ka. Testy niestety pokazały, że to nic nie daje.

Pytanie co jest przyczyną takiego położenia bibliotek w pamięci? Tutaj nie pozostaje mi nic innego jak rozłożyć ręce. Wcześniej byłem przekonany, że jest to związane z mechanizmem bezpieczeństwa, który losowo rozrzuca biblioteki po pamięci. Zwrócono mi jednak uwagę, że taki mechanizm (ASLR) pojawił się dopiero w Windows Vista. Sprawa jest więc otwarta. Jakieś pomysły?

Jak sobie z tym poradzić? Generalnie jednoznacznej odpowiedzi nie ma, ja znam trzy podejścia. Po pierwsze przejście na system 64 bitowy rozwiąże problem ale nie jest to zawsze możliwe. Po drugie można próbować wyeliminować konieczność alokacji tak dużej bitmapy ale może to być bardzo trudne. Można też użyć przełącznika /3GB, który pozwala procesom użytkownika użyć 3 GB pamięci wirtualnej zamiast domyślnych 2 GB ale nie jest to zalecane rozwiązanie.

Na zakończenie chciałbym podziękować koledze Damianowi z pracy, który analizował zgłoszenie klienta i podsunął mi pomysł na ten post.

30/11/2009

Uwierzytelnienie, ASP.NET i IIS

Home

Post ten postanowiłem napisać kiedy zorientowałem się, że kilku kolegów poświęciło sporo czasu aby rozwiązać problem z uwierzytelnieniem usługi sieciowej zainstalowanej na IIS'ie podczas gdy nie posiadali dokładnej wiedzy jak ten mechanizm działa. Problem udało się rozwiązać ale jestem przekonany, że można to było zrobić mniejszym nakładem czasu. Na początku zaznaczę również, że wpis ten dotyczy IIS w wersjach wcześniejszych niż IIS 7.

Jak wszyscy wiemy aplikacje ASP.NET i usługi sieciowe (ang. Web Services) hostowane są na serwerze IIS. Do swojego działania wymagają one dostępu do różnych zasobów. Aby możliwe było określenie czy aplikacja lub usługa posiadają uprawnienie do jakiegoś zasobu muszą one posiadać jakąś tożsamość czyli być uwierzytelnione (ang. authentication). Powstaje pytanie jaką tożsamość posiada aplikacja ASP.NET lub usługa sieciowa?

Na początek przyjrzyjmy się poniższemu scenariuszowi:
  • Użytkownik uruchamia przeglądarkę internetową i wpisuje adres aplikacji.
  • Serwer IIS otrzymuje żądanie i w zależności od ustawień żąda lub nie uwierzytelnienia.
  • Serwer IIS sprawdza czy użytkownik uwierzytelniony (bądź anonimowy) może uzyskać dostęp do żądanego zasobu.
  • Żądanie przekazywane jest do obsługi do silnika ASP.NET.
  • Aplikacja w zależności od ustawień ponownie żąda lub nie uwierzytelnienia się użytkownika.
  • Aplikacja obsługuje żądanie w razie potrzeby legitymując się pewną tożsamością.

Poziom IIS

Na poziomie IIS domyślnie włączone jest uwierzytelnienie anonimowe czyli innymi słowy brak uwierzytelnienia. W takim wypadku zgodnie z domyślnymi ustawieniami przyjmuje się, że użytkownik uwierzytelnił się jako IUSR_machinename. Dostępne są oczywiście inne tryby uwierzytelnienia np.: uwierzytelnienie zintegrowane z Windows (ang. Integrated Windows Authentication) czy uwierzytelnienia podstawowe (ang. Basic) gdzie hasło przekazywane jest czystym tekstem.

Uwierzytelnienia na poziomie IIS jest potrzebne aby stwierdzić czy użytkownik może żądać danego zasobu znajdującego się na serwerze. Zasobem tym może być aplikacja ASP.NET ale również inne rzeczy np.: obrazki, archiwa itd. Uwierzytelnienia na tym poziomie konfigurowne jest przy pomocy aplikacji administratora IIS: Control Panel -> Administrative Tools -> Internet Information Services.

Poziom silnika ASP.NET

Następnie mamy uwierzytelnienia na poziomie silnika ASP.NET. W tym przypadku można włączyć uwierzytelnienie anonimowe, zaimplementować cały mechanizm samemu czy użyć uwierzytelnienia Windows. Należy jednak zaznaczyć, że nie należy mylić uwierzytelnienia Windows na poziomie ASP.NET z uwierzytelnienia zintegrowanym z Windows na poziomie IIS.

Włączenie uwierzytelnienia Windows na poziomie ASP.NET oznacza, że silnik ASP.NET będzie widział użytkownika pod tożsamością wyznaczoną przez IIS. Innymi słowy IIS uwierzytelni użytkownika i przekaże jego tożsamość do ASP.NET. Metoda jaka zostanie użyta przez IIS do uwierzytelnienia zależy od ustawień. W szczególności, tak jak napisałem wyżej, może to być uwierzytelnienia zintegrowane z Windows ale również podstawowe czy inne. W przypadku braku uwierzytelnienia do silnika ASP.NET zostanie natomiast przekazana tożsamość anonimowa. Uwierzytelnienia na tym poziomie konfigurowane jest przy pomocy pliku web.config w sekcji <authentication>.

Tożsamość silnika ASP.NET

Teraz dochodzimy do setna sprawy czyli z jaką tożsamością działa silnik ASP.Net. Odpowiedź: „To zależy” :) Już odpowiadam ale zanim przejdziemy dalej należy jeszcze wyjaśnić czym jest impersonacja. Otóż impersonacja to mechanizm pozwalający przyjąć cudzą tożsamość i wykonywać w jej kontekście różne działania.

A więc jeśli wyłączona jest impersonacja (ustawienia domyślne) to silnik ASP.NET w przypadku systemów Windows Server 2000 i Windows XP działa na koncie o nazwie ASPNET, a przypadku Windows Server 2003 używa konta Network Service. Natomiast jeśli impersonacja jest włączona ASP.NET działa z tożsamością uwierzytelnionego użytkownika nawet jeśli jest to tożsamość anonimowa. Za te kwestie odpowiada sekcja <identity>.

Sprawa wygląda trochę inaczej w przypadku IIS 7, który posiada mocno zmienioną, a właściwie zupełnie inną architekturę w stosunku do IIS 6 i wcześniejszych. W szczególności w IIS 7 nie ma podziału na uwierzytelnienia na poziomie serwera i ASP.NET. Serwer IIS7 może być również konfigurowany z poziomu sekcji <system.WebServer> w pliku web.config. To jednak temat na kolejny wpis.

17/11/2009

SharpPcap

Home

WinPcap to dobrze znane wielu osobom narzędzie umożliwiające przechwytywanie i analizę ruchu sieciowego, jego generowanie oraz wiele innych rzeczy. W oparciu o WinPcap została napisana inna bardzo przydatna aplikacja WireShark. Narzędzi te są bezwątpienia bardzo przydatne ale co jeśli chcemy samemu zaimplementować algorytm analizujący ruch sieciowy. Dodatkowo najlepiej by było zrobić to w C# i nie babrać się w mechanizmach interoperacyjności z kodem niezarządzanym, bawić z atrybutem DllImport itd. Oczywiście chcemy również skorzystać z możliwości WinPcap.

Dokładnie w takiej sytuacji znalazłem się ostatnio. Wypróbowałem kilka rozwiązań ale szeroki uśmiech na mojej twarzy wywołała biblioteka SharpPcap. Została napisana w oparciu o WinPcap ale ukrywa przed użytkownikiem wszelkie odwołania do niezarządzanego kodu. Do tej pory przetestowałem tylko jej podstawowe funkcje ale wydaje się działać bardzo dobrze, a pracuje się z nią przyjemnie.

Zastanawiałem się nad zamieszczeniem tutaj jakiegoś przykładu użycia ale jestem zwolennikiem zasady "Po co robić coś co zostało już zrobione". Zainteresowanych zachęcam więc do pobrania biblioteki i zapoznania się z załączonymi przykładami, które są dobrze skomentowane. .

09/11/2009

Jeszcze o rysowaniu wykresów

Home

Kilka miesięcy temu pisałem o komponencie do rysowania wykresów firmy Xceed. Teraz postanowiłem przyjrzeć się dwóm innym komponentom: Komponent pierwszy jest do pobrania za darmo ze strony Microsoftu. Za drugi trzeba już płacić ale producent udostępnia wersję demonstracyjną.

Najpierw zainstalowałem komponent firmy Dundas. Od razu spodobał mi się program pokazujący przykłady różnych wykresów wraz z kodem. Dobre wrażenie sprawił również kreator zintegrowany z VS do konfigurowania kontrolki. Ogólnie pierwsze wrażenie bardzo pozytywne. Na dalszy ogień poszło rozwiązanie Microsoftu. W porównaniu do poprzednika od razu rzucił mi się w oczy brak kreatora i programu demonstracyjnego (można go pobrać oddzielnie tutaj).

Następnie zaczynam przyglądać się interfejsowi programistycznemu. Patrzę, patrzę i mam wrażenie deja vu. Pomimo pewnych różnic oba produkty mają bardzo, bardzo podobne API. Chwila zastanowienia i wpisuję w Google ''Microsoft Chart Control vs. Dundas Chart Control''. Pierwszy link i wszystko staje się jasne. Począwszy od roku 2007 Microsoft posiada prawa do własności intelektualnej firmy Dundas, a przejawem tego jest komponent Microsoft Chart Controls for Microsoft .NET Framework 3.5 oparty o kod Dundas Chart for .NET.

Podsumowując można za darmo pracować z bardzo fajną kontrolką pamiętając jednak o tym, że produkt Microsoftu posiada do pewnego stopnia ograniczoną względem oryginału funkcjonalności. Po drugie firma Dundas udziela wsparcie tylko na produkty zakupione bezpośrednio od nich. Nie jestem również pewny czy nowe usprawnienia, które wprowadzają programiści z Dundas są również dostępne w wersji Microsoftu (pewnie nie).

02/11/2009

Problem z SqlDependency. Czyżby?

Home

Post ten dotyczy mechanizmu query notification, który pozwala na otrzymywanie powiadomień o zmianach w bazie danych dotyczących wybranych wierszy. Funkcjonalność ta jest dostępna na poziomie programistycznym między innymi przez łatwą w użyciu klasę SqlDependency (Jest to opakowanie na klasę SqlNotificationRequest). W Internecie można znaleźć bardzo dużo przykładów użycia tej klasy nie będę, więc powielał tego co zostało już napisane. Chciałbym natomiast zwrócić uwagę, że chociaż warunkiem koniecznym użycia powiadomień jest użycie SQL Server'a w wersji 2005 lub późniejszej to nie jest to warunek wystarczający.

Wszystko zaczęło się od tego, że postanowiłem przyjrzeć się dokładnie temu mechanizmowi. Do testów wybrałem chyba dobrze znana bazę Northwind. Bardzo szybko udało mi się stworzyć testową aplikację ale po jej uruchomieniu okazało się, że powiadomienia nie są generowane albo aplikacja ich nie otrzymuje. Kod sprawdziłem kilka razy, dla pewności przejrzałem kilka opisów w sieci i nic.

W końcu postanowiłem wykorzystać kody pokazane w jednym z tutoriali, wraz z użytą w tam bazą danych. Po chwili okazało się, że działa. Zmodyfikowałem, więc swoją aplikację aby używała właśnie tej bazy danych. Chwila niepewności, uruchamiam i również działa.

Dochodzę do wniosku, że nie ma mocnych, problem musi tkwić gdzieś w bazie danych. Porównuję konfigurację obu baz danych i znajduję winnego - tryb kompatybilności. Okazało się, że po zainstalowaniu baza Northwind ma ustawiany tryb kompatybilności na SQL Server 2000, zamiast SQL Server 2005. Mała, głupia sprawa, a można stracić trochę czasu.

26/10/2009

CodeBehind i CodeFile

Home

Jakiś czas temu pisząc prostą aplikacje WWW utworzyłem z rozpędu projekt typu Web Application zamiast Web Site. Zanim się zorientowałem popełniłem już trochę kodu stwierdziłem więc, że nie będę pisał go od początku. Usunąłem projekt z solution, wykasowałem plik z rozszerzeniem csproj i skorzystałem z polecenia Add -> Existing Web Site.... Wszystko wydawało się w porządku do momentu kiedy spróbowałem skompilować aplikację. W efekcie otrzymałem komunikat jak poniżej:

Could not load type 'PageName'.

Przy drugiej próbie kompilacji otrzymałem taki sam błąd kompilacji. Patrzę i patrzę w kod strony i nic. Przecież jeszcze 5 minut temu kompilowało się, czary? Oczywiście, że nie. Po chwili przypominam sobie o jednej drobnej różnicy. W przypadku projektów typu Web Application w dyrektywie @Page używa się atrybutu CodeBehind, a w przypadku Web Site'ów atrybutu CodeFile. Niby szczegół ale jeśli się o nim zapomni może popsuć trochę krwi.

19/10/2009

Trochę o zwalnianiu zasobów

Home

Każdy dobry programista wie, że po skończeniu pracy z obiektem klasy implementującej interfejs IDisposable należy wywołać metodę Dispose (jawnie bądź nie jawnie). Dlatego kiedy ostatnio zobaczyłem kod, w którym programista beztrosko raz po raz tworzy ikonę, a następnie radośnie o niej zapomina powodując wzrost liczby obiektów GDI przez usta przeszły mi dość niecenzuralne słowa. Oczywiście od razu poprawiłem kod w mniej więcej taki sposób:
using(Icon icon = GetIcon())
{
   ...
}
Nic prostszego można powiedzieć. Jednak przy następnym uruchomieniu aplikacji ku mojemu zdziwieniu liczba obiektów GDI znowu zaczęła rosnąć. Zaglądam, więc do metody GetIcon. A tam widzę coś takiego:
return Icon.FromHandle(handle);
Nie kojarząc za bardzo metody FromHandle zaglądam do dokumentacji, a tam jest napisane coś takiego:

When using this method you must dispose of the resulting icon using the DestroyIcon method in the Win32 API to ensure the resources are released.

Kolejny mój krok to oczywiście sprawdzenie czy wywołanie DestroyIcon działa. Metodę tą należy zadeklarować w następujący sposób:
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet=CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);
Jej użycie oczywiście rozwiązało problem. Co dociekliwsi mogą jeszcze zapytać czemu wywołanie Dispose nie wystarcza. Sprawa jest dość ciekawa. Okazuje się, że klasa Icon używa wewnętrznie DestroyIcon do zwalniania zasobów ale tylko i wyłącznie wtedy kiedy jest właścicielem tych zasobów to jest kiedy sama je zaalokuje. W momencie tworzenia ikony przy pomocy uchwytu dostarczonego z zewnątrz trzeba samemu zadbać o jego zwolnienie.

Reasumując w opisanym przypadku dwóch programistów zrobiło dwa poważne błędy. Pierwszy zapomniał o zwolnieniu zasobów, a drugi o dokładnym przeczytaniu dokumentacji. Metoda GetIcon powinna zostać napisana tak aby korzystający z niej programista nie musiał posiadać wiedzy "tajemnej" aby dobrze jej użyć.

11/10/2009

Kontrolki ASP.NET i zdarzenia

Home

Dzisiaj napiszę o rzeczy bardzo prostej ale, o której jednak zdarzyło mi się zapomnieć przez co zmarnowałem trochę czasu. Sytuacja miała miejsce kiedy pracowałem nad kontrolkę, która na swoim interfejsie publicznym między innymi udostępniała zdarzenia SelectionChanged. W kodzie wyglądało to jakoś tak:
...
public event EventHandler SelectionChanged;
...
Po jej napisaniu zabrałem się do testowania i jedną z rzeczy jaką chciałem sprawdzić było to czy zdarzenie jest generowane w odpowiednim momencie. Umieściłem więc kontrolkę na stronie w taki sposób:
...
<cc1:MyControl id="Control1" runat="server" SelectionChanged="Control1_SelectionChanged" />
...
Uruchamiam stronę i coś nie działa. Po krótkiej chwili dochodzę do wniosku, że coś musi być nie tak z zdarzeniem. Stawiam, więc w kodzie pułapkę i odświeżam stronę. Chwila debugu i konsternacja. Kod działa prawidłowo ale w momencie kiedy następuje próba wygenerowania zdarzenia okazuje się, że SelectionChanged równa się null.

Zaczynam sprawdzać czy nigdzie nie zrobiłem literówki itd. Uruchamiam kod ponownie i ciągle to samo. W końcu przypominam sobie, że aby deklaratywnie podczepić się pod zdarzenie kontrolki trzeba użyć przedrostka On. Kod powinien więc wyglądać jak poniżej:
...
<cc1:MyControl id="Control1" runat="server" OnSelectionChanged="Control1_SelectionChanged" />
...

05/10/2009

Raportowanie

Home
Ostatnio poznałem nieznany mi wcześniej, a prosty sposób tworzenia raportów i zapisywania ich do formatu PDF czy Excel. Działa on zarówno w kontekście aplikacji ASP.NET jak i w aplikacjach Windows Forms i innych. Mam tutaj na myśli klasy z przestrzeni nazw Microsoft.Reporting.WebForm (w przypadku aplikacji stacjonarnych chodzi o przestrzeń Microsoft.Reporting.WinForms).

W przestrzeni Microsoft.Reporting.WebForm znajdziemy wiele rzeczy, najważniejsze to po pierwsze kontrolka ReportViewer do prezentowania raportów. Po drugie klasy LocalReport oraz ServerReport służące odpowiednio wykonywaniu raportów lokalnie oraz zdalnie na serwerze. Źródłem danych dla raportów może być oczywiście relacyjna baza danych ale również obiekty biznesowe. Definicja raportu to dokument XML stworzony przy pomocy języka RDL (ang. Report Definition Language).

Zanim przeję dalej podam założenia/wymagania jakimi się kierowałem:
  • Raporty chcę zapisywać do formatu PDF i Excel.
  • Proces generowania raportu ma odbywać się lokalnie bez połączenia z bazą danych.
  • Dane mam zapisane w obiekcie klasy DataTable.
Po pierwsze musiałem znaleźć sposób dynamicznego generowania definicji raportów. Język RDL nie wygląda na trudny ale nie zmienia to faktu, że go nie znam. Po krótkich poszukiwaniach napotkałem na ten artykuł Lesson 4: Creating Code to Generate the Report Definition File. Zaprezentowany tam kod tworzy plik z definicją raportu jako dane wejściowe przyjmując: listę pól/kolumn, zapytanie do bazy danych oraz connection string. Jak widać kod jest przydatny ale nie do końca ponieważ nie odpowiada postawionym wymaganiom. Jak sie jednak okazało konieczne modyfikacje były bardzo proste. Pomijając zmianę argumentów wejściowych metody, delikatną modyfikację logiki wystarczyło, że jako zapytanie do bazy danych oraz connection string przekazałem pusty ciąg znaków String.Empty. Cała metoda jest dość długa ale w gruncie rzeczy nie zawiera niczego skomplikowanego. Dopowiem tylko, że liczba kolumn generowanego raportu zależy od liczby kolumn w przekezanym do metody obiekcie klasy DataTable. Pełny kod został pokazany poniżej.

Pokaż/Ukryj kod

Proces generowania raportu wynikowego w żądanym formacie również jest prosty. Poniższy fragment kody generuje raport w formacie PDF. Żeby wytworzyć arkusz programu Excel to metody Render należy przekazać ciąg znaków "Excel". Zmienna reportPath powinna zawierać ścieżkę do pliku z definicją raportu stworzonego przez pokazaną wcześniej metodę. Zmienna dataTable to z kolei tabela z danymi do zapisania w żądanym formacie. Powinna to być ta sama tabela, która została przekazana do metody GenerateRdl.
...
FileStream fs = File.OpenWrite( reportPath);
GenerateRdl(fs, dataTable);

LocalReport lr = new LocalReport();
lr.ReportPath = reportPath;
lr.DataSources.Add(new ReportDataSource("DummyDataSet", dataTable));

string deviceInfo = "<DeviceInfo><SimplePageHeaders>True</SimplePageHeaders></DeviceInfo>";
string mimeType = null;
string encoding = null;
string ext = null;
string[] streamids = null;
Warning[] warnings = null;
byte[] bytes = lr.Render("PDF", deviceInfo, out mimeType, out encoding, out ext, out streamids, out warnings);
...

15/09/2009

GridView oraz puste źródło danych

Home

Programistom używającym kontrolki GridView na co dzień znany jest zapewne fakt, że w przypadku pustego źródła danych kontrolka nie generuje żadnego widocznego markup'u. W szczególności nie będą widoczne nagłówki kolumn czy wiersz dodający.

Kiedy w wyszukiwarce wpiszemy hasło Show GridView if datasource is empty otrzymamy oczywiście mnóstwo rozwiązań tego problemu. Niestety pośród nich nie znajdziemy, a przynajmniej ja nie znalazłem, satysfakcjonującej odpowiedzi dotyczącej źródła danych typu ObjectDataSource. Nie będziemy natomiast osamotnienie jeśli używamy SqlDataSource. Ale co jeśli nie chcemy, nie możemy lub najzwyczajniej w świecie nie chce nam się zmieniać używanego typu źródła danych. Ja zastosowałem rozwiązanie, które opisałem poniżej.

Dla ustalenia uwagi załóżmy, że metoda dostarczająca danych wygląda następująco:
public static IEnumerable GetData()
{
  return _data;
}
Zacznijmy od zmodyfikowania tej metody w ten sposób aby zawsze zwróciła niepustą kolekcję:
public static IEnumerable GetData()
{
  if(_data.Count == 0)
  {
    _data.Add(new TestClass());
  }
  
  return _data;
}
Oczywiście teraz wszystko zadziała z wyjątkiem tego, że na kontrolce pojawi się jakiś "dziwny", sztuczny obiekt. Można temu jednak zaradzić zmieniając lekko definicję TestClass:
public class TestClass
{
  ...
  public bool IsFake
  {
    get; set;
  }
  ...
}
Przy okazji zmodyfikujemy ponownie metodę GetData:
public static IEnumerable GetData()
{
  if(_data.Count == 0)
  {
    _data.Add(new TestClass() { IsFake = true; });
  }
  
  return _data;
}
Ostatni element rozwiązania do podczepienie się pod zdarzenie OnRowDataBound kontrolki GridView w celu sterowania widocznością wierszy:
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
  TestClass ts = e.Row.DataItem as TestClass;
  if(ts != null && ts.IsFake)
  {
    e.Row.Visible = false;
  }
}
Rozwiązanie można jeszcze rozszerzyć o usunięcie sztucznego obiektu z kolekcji w momencie kiedy pojawią się "prawdziwe" dane ale moim zdaniem nie jest to konieczne.

04/09/2009

Trochę na temat oferty ESRI

Home

Tak jak obiecałem w ostatnim poście teraz poświęcę trochę czasu na omówienie oferty firmy ESRI, która dla kogoś nie związanego na co dzień z GIS'ami może wydać się niezrozumiała i zagmatwana i taka mówiąc szczerze jest. Sądzę, że takie zestawienie może być ciekawe i przydatne dla kogoś stawiającego pierwsze kroki w świecie geograficznych systemów informacyjnych (wiem to z własnego doświadczenie) i nie tylko. A więc zaczynamy.

Po pierwsze wyróżnijmy dwie kategorie produktów. W pierwszej umieścimy gotowe, pudełkowo aplikacje, a w drugiej rozwiązania przeznaczone dla programistów. W pierwszej znajdziemy:
  • ArcGIS Desktop. Flagowa aplikacja, a raczej zestaw aplikacji ESRI pozwalających na przeglądanie i analizowanie zgromadzonych danych geograficznych, zarządzanie nimi, edytowanie, tworzenie i tysiące innych rzeczy. Najważniejsze składowe pakietu ArcGIS Desktop to aplikacja administracyjna ArcCatalog, właściwa aplikacja do oglądania i przetwarzania danych czyli ArcMap oraz ArcGlobe służąca do pracy z danymi trójwymiarowymi. W obecnej chwili ArcGIS Desktop dostępne jest w trzech wersjach różniących się między sobą zakresem funkcjonalności. Są to odpowiednio: ArcView, ArcEditor oraz ArcInfo.


  • ArcView. To nie tylko nazwa jednej z wersji ArcGIS Desktop ale również nazwa starej aplikacji będącej w prostej linii jej poprzedniczką.


  • ArcGIS Server. Dedykowany do zastosowań GIS'owych serwer aplikacyjny. Dostarcza bogaty zestaw usług webowych takich jak: geokodowanie, lokalizowanie... oraz umożliwia tworzenie własnych. Pozwala na publikowania w sieci danych geograficznych oraz zawiera kilka platform do tworzenia mapowych aplikacji WWW i mobilnych. ArcGIS Server sprzedawany jest w wersjach: Basic, Standard oraz Enterprise. ESRI stworzyła również zestaw specjalizowanych rozszerzeń dla serwera np.: do analiz i modelowania 3D.


  • ArcPad. Dedykowana dla urządzeń mobilnych aplikacja o dużych możliwościach konfiguracyjnych pozwalających na dostosowanie do konkretnych potrzeb. Również aplikacja wykonująca synchronizację pomiędzy urządzeniem mobilnych, a centralną bazą danych.


  • ArcSDE. Nakładka na relacyjną bazę danych, uruchamiana jako usługa, umożliwiająca przechowywanie w niej danych przestrzennych.


  • ArcGIS Explorer. Prosty klient dla ArcGIS Server umożliwiający przeglądanie danych geograficznych dostępnych w sieci, na przykład w serwisie ArcGIS Online. Ta aplikacja jest w całości darmowa.


  • ArcReader. Aplikacja o podobnej funckjonalności co ArcGIS Explorer ale służąca do przeglądania danych przestrzennych stworzonych w ArcGIS Desktop i opublikowanych przy pomocy aplikacji ArcPublisher. Również darmowa.


  • ArcIMS. W pewnym uproszczeniu poprzednik ArcGIS Server. ESRI zaleca użycie następcy.


  • ArcGIS for AutoCAD. Darmowe narzędzia zapewniające interoperacyjność pomiędzy rozwiązaniami ESRI i AutoCAD.


  • ArcPublisher. Rozszerzenie ArcGIS Desktop o możliwość publikowania map w sieci.
Ofertę dla developerów tworzą natomiast:
  • MapObjects. Stare API programistyczne umożliwiające tworzenie okienkowych aplikacji mapowych. Technologia używana coraz rzadziej.


  • ArcObjects. Zestaw komponentów COM w oparciu, o które zostały stworzone prawie wszystkie produkty z oferty ESRI: ArcGIS Desktop
  • , ArcGIS Server, ArcGIS Engine itd.

  • ArcGIS Engine. Platforma programistyczna (COM, .NET, Java i C++) oparta o ArcObjects służąca tworzeniu aplikacji okienkowych oraz do dostosowywania aplikacji z pakietu ArcGIS Desktop do swoich potrzeb.


  • ArcGIS Mobile. Platforma programistyczna (C#, Java) do tworzenia aplikacji mobilnych sprzedawana razem z ArcGIS Server w wersji Enterprise.


  • Web ADF (Application Development Framework). Platforma programistyczna do tworzenia aplikacji WWW sprzedawana razem z ArcGIS Server w wersji Standard. Dostępna dla .NET i Java . W przypadku .NET oparta oczywiście o ASP.NET Ajax.


  • ArcGIS Web Mapping APIs. Zestaw interfejsów programistycznych/bibliotek do tworzenia aplikacji mapowych w WPF/Silverlight, JavaScript i Flex darmowych do niekomercyjnego użytku. W porównaniu do Web ADF ma bardzo ograniczone możliwości.
Przegląd ten pomimo, że dość długi nie jest kompletny. Zawiera jednak większość, a z pewnością najważniejsze z produktów z oferty firmy ESRI. Można również spierać się co do użytego przeze mnie podziału. Z mojego doświadczenie wynika jednak, że z tej gromady Arc'ów może być trudno wyłowić to co jest przydatne dla programisty.

03/09/2009

ESRI WPF/Silverlight API

Home

ESRI to lider światowego rynku systemów informacji geograficznej (ang. GIS). Lider przez duże L - Na całym świecie z rozwiązań ESRI korzysta 300 tyś instytucji w tym 2/3 firm z listy Fortune. ESRI to taki Microsoft w świecie GIS'ów :). Czym jednak są systemy GIS'owe?

GIS to w bardzo dużym skrócie system służący do wprowadzania, przechowywania, przetwarzania, analizowania i wizualizowania danych przestrzennych. Upraszczając jeszcze bardziej, a wręcz trywializując chodzi tu po prostu o mapę ;) Niektórzy zapewne pomyślą coś w rodzaju: "Czym ty zawracasz głowę człowieku, przecież jest już Google Maps". Nie obrażając nikogo Google Maps w porównaniu z produktami ESRI to zabawka dla dzieci, którą poważni ludzie się nie zajmują. Patrząc na to z innej strony oferta ESRI (w jednym z kolejnych postów postaram się ją przybliżyć) jest dużo, dużo, dużo bogatsza. Poza tym jest skierowana do zupełnie innego rodzaju odbiorców, którzy poza wizualizacją danych geograficznych chcą je analizować i przetwarzać w celu usprawnienia swoich procesów biznesowych.

Czemu jednak w ogóle o tym piszę? Otóż rozwiązania ESRI są generalnie bardzo drogie i dostępnie dla niewielkiego grona specjalistów, a co z tym związane mało znane. Ostatnio dotarłem jednak do darmowego (dla celów niekomercyjnych) API dla WPF/Silverlight umożliwiającego tworzenie całkiem fajnych aplikacji. Tworzone aplikacje możemy zasilić danymi udostępnianymi przez ESRI poprzez ArcGIS Online. Zasoby udostępnione publicznie nie są tak duże jak w przypadku Google ale można już coś zdziałać przy ich pomocy. Możliwe jest również wykorzystanie map publikowanych przez Microsoft na Bing Maps.

Potrzebne biblioteki można pobrać tutaj, a dokumentację znajdziemy tutaj. Początkowo chciałem opisać prosty przykład użycia ale zrezygnowałem z tego kiedy zobaczyłem tą stronę. Znajdziecie tam kilkanaście bardzo prostych, poglądowych aplikacji. Polecam.

26/08/2009

Przykład tego jak nie należy pisać kodu

Home

Czasami jak patrzę na niektóre kody to zastanawiam się o czym myślał piszący je programista. Ostatnio natknąłem się na kod mniej więcej jak poniżej (zmieniłem nazwy zmiennych, metod itd.). Pomińmy jaka jest jego logika i gdzie został użyty. Proponuję natomiast przyjrzeć się przez chwilę temu fragmentowi i spróbować odpowiedzieć na pytanie co jest nie tak.
...
for (int i = 0; i < values.Length; i++)
{
  if (values[i] != null)
  {
    if (hash[values[i]] != null)
      Process(hash[values[i]]);

    if (hash[values[i]] != null)
    { 
      Process(values[i]);
      Process(values[i], hash[values[i]]);
      hash.Remove(values[i]);
    }
  }
}
...
Mnie uderzyła duża i zupełnie zbyteczna liczba odwołań do zmiennych values (tablica) i hash (Hashtable). Odwołanie do tej pierwszej występuje 8 razy, a do drugiej 5 razy! Po chwili pracy i wprowadzeniu bardzo prostych poprawek kod wygląda tak:
...
for (int i = 0; i < values.Length; i++)
{
  string val = values[i];
  if (val != null)
  {
    object obj = hash[val];
    if (obj != null)
      Process(obj);

    if (obj != null)
    {
      Process(val);
      Process(val, obj);
      hash.Remove(obj);
    }
  }
}
...
Odwołanie do zmiennych values oraz hash występuje tylko 1 raz. Czemu o tym piszę? Odpowiedź jest tak prosta jak wprowadzone poprawki: wydajność. Dodam jescze, że kod ten można jeszcze ulepszyć. W tym poście skupiam się jednak na tej jednej rzeczy.

W tym przypadku poprawiona wersja działa jakieś 3x/4x razy szybciej! Przyznam jednak, że całościowy efekt tej optymalizacji jest niezauważalny z dwóch powodów. Po pierwsze długość używanej w kodzie tablicy i liczba elementów w tablicy hashującej jest generalnie niewielka. Przeważnie nie przekracza kilkudziesięciu, może kilkuset elementów. W takim scenariuszu czas wykonania tego kodu nie przekracza milisekundy. Po drugi częstotliwość użycia tego kodu jest niewielka.

Z drugiej jednak strony programista, który napisał omawiany kod stosuje (zastosował) takie konstrukcje w wielu miejscach. Być może wywołuje jedną, ciężką, metodę wielokrotnie zamiast zapamiętać jej wynik wywołania na później. Przykładów takich można mnożyć.

Reasumując należy uczyć się na cudzych błędach i strzec się takich konstrukcji.

18/08/2009

Ciekawy przypadek optymalizacji

Home

Ostatnio zajmowałem się optymalizacją aplikacji mapowej do planowania i inwentaryzacji sieci telekomunikacyjnych. Problem polegał na tym, że przy dużej liczbie warstw aplikacja zaczynała działać powoli. Warstwa to taki pojemnik na dane tego samego rodzaju np.: warstwa kabli, warstwa wzmacniaczy itd.

W określonych scenariuszach liczba takich warstw mogła sięgać nawet kilku tysięcy. Zacząłem od wrzucenia aplikacji do profilera (AQTime). Jak to często bywa w takich sytuacjach okazało się, że czas pożera kilka metod, które same z siebie wykonują się bardzo szybko ale są wołane bardzo dużo razy - proporcjonalnie do liczby rastrów. W rzeczywistości dojście to tego o jakie metody chodzi nie było oczywiście takie proste ale nie będę zanudzał szczegółami.

Po pierwsze postanowiłem ograniczyć liczbę wywołań tych metod. Niestety okazało się, że z różnych powodów nie da się tego zrobić. W drugim podejściu postanowiłem, więc ograniczyć czas potrzebny na ich wykonanie. Co ciekawe okazało się, że metody te pozornie prawie nic nie robią czyli zawierają tylko odwołania do właściwości Count czy też indeksera jakiejś kolekcji.

Zanim przejdę dalej wyjaśnię, że jako platformę mapową używam rozwiązań firmy ESRI opartych o technologię COM. W związku z tym konieczne jest użycie mechanizmów interoperacyjności pomiędzy kodem zarządzanym i niezarządzanym. W omawianym przypadku wspomniana kolekcja nie była kolekcją zarządzaną ale wrapper'em na obiekt COM. Postanowiłem, więc zrezygnować z odwołań do tej kolekcji i użyć dobrze znanej klasy List<T>. W praktyce utworzyłem instancję listy zarządzanej istniejącą równolegle do kolekcji niezarządzanej. Elementy do obu kolekcji dodawane są równocześnie, podobnie usuwane. Nie jest to problem ponieważ czynność ta wykonywana jest jednorazowo.

Postępując podobnie w kilku innych miejscach, to znaczy eliminując odwołania do wrapper'ów do obiektów COM, zmniejszyłem czas wykonywania niektórych czynności o połowę! Wydaje mi się, że to niezły wynik szczególnie, że dalsze optymalizację cały czas są możliwe. Wniosek z tego taki, że czasem warto wprowadzić redundancję aby zyskać na wydajności.

14/08/2009

Site, Site Collection, IIS Web Site...

Home

Oj dużo miałem ostatnio na głowie: ślub, wesele, podróż poślubna :) ale pora wrócić do blogowania. Do tej pory nie miałem do czynie z WSS (Windows SharePoint Services) ale los chciał, że przyjdzie mi poznać również tą technologię Microsoftu. Przygodę w świecie Sharepoint'a postanowiłem zacząć od znalezienia i zebrania serwisów, blogów i książek dotyczących tego zagadnienia. Jak się szybko okazało pierwszym problemem okazała się terminologia. Doświadczony developer WSS skwitowałby to pewnie ironicznym uśmieszkiem ale dla mnie terminologia WSS jest co tu dożo mówić 'zakręcona'. Jedną z przyczyn jest zapewne fakt, że jako developer ASP.Net widząc słowo site od razu przychodzi mi do głowy projekt typu Web Site, podobnie Web Application to również dla mnie typ projektu VS. W nomenklaturze WSS terminy te przyjmują natomiast inne znaczenia. Postanowiłem więc opisać pojęcia, które wprawiły mnie w taką konsternację.
  • IIS Web Site - Punkt wejściowy do serwera IIS. Domyślny site (Default Web Site) oczekuje na żądania HTTP na porcie 80. Nie mylić z projektem typu Web Site w VS. Projekt typu Web Site po zainstalowaniu na IIS widoczny jest najczęściej jako katalog wirtualny.
  • Web Application - IIS Web Site przygotowana do pracy z WSS. Nie mylić z projektem typu Web Application w VS. Projekt typu Web Application po zainstalowaniu na IIS widoczny jest najczęściej jako katalog wirtualny.
  • Virtual Server - W WSS 2.0 oznacza to samo co Web Application.
  • Virtual Directory - Katalog wirtualny. Tworzy podprzestrzeń adresów URL w ramach IIS Web Site w jakiej został utworzony. Katalogi wirtualne mapują się na fizyczne lokalizacje na dysku.
  • Site Collection - Podstawowa jednostka organizacyjna służąca do grupowania i zarządzania site'ami. Web Application może składać się z wielu takich kolekcji. Na poziomie programistycznym reprezentowana przez klasę SPSite.
  • Site - W WSS 3.0 to porostu pojemnik na jakąś zawartość: listy, biblioteki dokumentów czy też inne pojemniki. Każdy site musi należeć do jakiejś kolekcji. Na poziomie programistycznym reprezentowany przez klasę SPWeb. W WSS 2.0 termin ten oznacza to samo co Site Collection. Stąd w WSS 3.0 klasa reprezentująca kolekcję site'ów nazywa się SPSite, a nie SPSiteCollection.
  • Web - Stary termin z WSS 2.0 oznaczający to samo co site w WSS 3.0. Stąd w WSS 3.0 klasa modelująca site nazywa się SPWeb, a nie SPSite.
  • Top-level Site - Główny pojemnik (site) w ramach kolekcji pojemników (site collection).
  • Root Web - W WSS 2.0 oznacza to samo co Top-level Site.

22/07/2009

Czemu należy używać właściwości SyncRoot?

Home

SyncRoot to właściwość zdefiniowana na poziomie interfejsu ICollection służąca do synchronizowania operacji wykonywanych na kolekcjach przy pomocy słowa kluczowego lock lub jawnie przy pomocy monitora. Czemu jednak należy używać tej właściwości zamiast instancji kolekcji, czyli czemu zalecany jest taki kod:
lock(list.SyncRoot)
{
   ...
}
A nie taki?
lock(list)
{
   ...
}
Kiedy postawiłem sobie to pytanie okazało się, że odpowiedź nie jest dla mnie oczywista. Wizyta w dokumentacji MSDN nic nie pomogła bo udzielone wyjaśnienie należy do grupy tych, które stają się jasne i oczywiste jak już znasz prawidłową odpowiedź ;).

Spędziłem trochę czasu zastanawiając się nad tą kwestią oraz szperając po sieci i doszedłem do następujących wniosków. Należy używać właściwości SyncRoot ponieważ zapewnia ona centralny, dobrze określony, bezpieczny punkt synchronizacji dla kodu zewnętrznego używającego kolekcję jak i dla kodu wewnętrznego kolekcji. Inaczej mówiąc zapewnia to, że każdy kod używa tego samego obiektu do synchronizacji. Bezpieczny w tym sensie, że bardzo łatwo można stwierdzić kto i kiedy uzyskuje dostęp do obiektu synchronizacyjnego - wystarczy ustawić pułapkę w getter-ze właściwości. Synchronizacja bezpośrednio przy użyciu instancji kolekcji czy też na obiekcie this może natomiast doprowadzić to bardzo trudnych do wykrycia zakleszczeń ponieważ nie będzie można łatwo określić kto i w jakim momencie blokuje obiekt. Z drugiej strony jeśli taka semantyka SyncRoot jest z jakiegoś powodu niepożądana kolekcje wydziedziczone z kolekcji bazowej mogą dostarczyć własnej implementacji tej właściwości.

17/07/2009

$find vs $get

Home

Ostatnimi czasy pracowałem nad webową aplikacją mapową, która bardzo silnie wykorzystuje ASP.NET Ajax, a więc nie obyło się bez napisania "kilku" linii kodu w JavaScript. Przy okazji implementowania jednej z funkcjonalności musiałem odwołać się do kontrolki mapowej. Niewiele myśląc napisałem mniej więcej taki kod:
...
var map = $get(MapControlId);
...
Kontrolka oczywiście została znaleziono. Zgodnie z dokumentacją powinna udostępniać funkcję toMapPoint konwertującą współrzędne ekranowe na mapowe. Niestety ale okazało się, że taki kod nie działa ponieważ znaleziona kontrolka nie udostępnia takiej metody.
...
var res = map.toMapPoint(x, y);
...
Moment konsternacji, dokumentacja mówi jedno, a życie pokazuje drugie. Chwila szukania w Internecie i rozwiązanie problemu jak zwykle okazało się trywialne. Zamiast funkcji $get należy użyć funkcji $find. Czym się różnią? Pierwsza z nich to po prostu skrót do użycia dobrze znanej funkcji getElementById służącej do wyszukiwania elementów DOM o podanej wartości atrybutu id.

Funkcja $find służy natomiast do wyszukiwania komponentów o stanowi skrót dla wywołania findComponent. Komponent to moduł programowy udostępniający jakąś funkcjonalność. Komponenty dzielimy na trzy kategorie. Po pierwsze mamy komponenty niewizualne takie jak zegar (ang. timer). Pozostałe dwa typy komponentów w ASP.NET Ajax to kontrolki czyli komponenty wizualne (elementy DOM) oraz zachowania (ang. behaviours), które rozszerzają możliwości istniejących już elementów DOM. Metoda, której potrzebowałem użyć była oczywiście zdefiniowana na komponencie, a nie na elemencie DOM.

03/07/2009

'ltr' -> 'rtl'

Home

Wszyscy jesteśmy przyzwyczajeni do pisania i czytania od lewej do prawej. Przyjmujemy jako pewnik, że menu Start znajduje się w lewym dolnym rogu ekranu, krzyżyk służący do zamykania okna w prawym górnym rogu okna, a krzyżyki służące do rozwijania węzłów w drzewie umieszczane są z lewej strony węzłów itd. Dlaczego o tym piszę? Niedawno zajmowałem się przystosowaniem aplikacji WWW do kultur o orientacji od prawej do lewej. Na warsztat wziąłem kulturę hebrajską. Zainstalowałem odpowiedni pakiet do Windows XP i po przełączeniu ustawień regionalnych i językowych opadła mi szczęka :) Wszystko zostało odwrócone: ikony na pulpicie, położenie poleceń w menu, przycisk Backspace działa jak Delete, po naciśnięciu strzałki w prawo kursor przesuwa się w lewo, menu start jest w prawym dolnym rogu, strzałka do rozwijania list z lewej strony itd. Mały przykład poniżej:



Wracając jednak do aplikacji WWW. Okazuje się, że przestawienie aplikacji w tryb Right to Left jest banalnie proste i sprowadza się do dodania atrybutu dir o wartości 'rtl' na przykład do tagu html. Całą resztą zajmie się przeglądarka (testowałem w IE7 oraz Operze). Poniżej porównanie prostego układu w dwóch orientacjach. Jak widać układ z prawej strony stanowi lustrzane odbicie tego z lewej.



W dużej, komercyjnej aplikacji po zmianie orientacji zauważyłem niewielkie błędy ale można je z pewnością łatwo wyeliminować. W każdym razie wkładając minimum wysiłku można bardzo prosto przełączyć aplikację WWW w tryb od prawej do lewej.

18/06/2009

Xslt jako język programowania

Home

Ostatnio sporo czasu poświęcam poznawaniu nowych narzędzi w Visual Studio 2010 czyli pożytecznej zabawie. Przy tej okazji uczę się również czasami czegoś na temat starszych wersji tego środowiska. Na przykład niedawno zajmowałem się narzędziami wspierającymi pracę z szeroko pojętym Xml'em, a w szczególności z transformacjami Xsl. Obok rzeczy wręcz oczywistych takich jak podświetlenie składni, podpowiadanie - IntelliSense znajdziemy również: profiler, debugger czy podgląd wyniku działania transformacji. Co ciekawe narzędzia te, oprócz profilera, znajdują się również w Visual Studio 2005/2008. Muszę przyznać, że do tej pory nie zdawałem sobie sprawy z tego, że Visual Studio traktuje Xslt jako "normalny" język programowania. Co tu dużo mówić, człowiek uczy się całe życie.

Dla tych co żyli w niewiedzy tak jak ja kilka zdań na ten temat. Zaczynamy od otworzenia pliku z definicją transformacji (z rozszerzeniem *.xslt) albo od dodania go do projektu. We właściwościach otwartego dokumentu (zakładka Properties) mamy do wypełnienia dwa pola: Input oraz Output. W pierwszym wskazujemy dokument Xml do przekształcenia, a w drugim plik wynikowy.



Analogicznie można zacząć od otworzenia pliku Xml. W tym przypadku na zakładce Properties będziemy mieli do wypełninia pola: Stylesheet oraz Output. W pierwszym wskazujemy plik z transformacją, a znaczenie drugiego jest takie same jak wcześniej.

W momencie kiedy mamy otworzony plik z transformacją albo plik Xml na pasku menu pojawi się element o nazwie XML.



Z tego menu może po pierwsze wybrać polecenie Show XSLT Output, które odpali transformację i pokaże nam jej wynik. Dużo ciekawsze jest jednak polecenie Debug XSLT, które umożliwia śledzenie wykonania transformacji.

Pułapki umieszczamy w kodzie transformacji dokładnie w taki sam sposób jak w kodzie programu napisanego w C#. Co więcej pułapki możemy też postawić w dokumencie Xml. Funkcji tej można użyć kiedy interesuje nas ściśle określony węzeł w dokumencie i chcemy aby debugger zatrzymał się kiedy będzie przetwarzany. Bardzo przydatne jest to, że na bieżąco możemy obserwować jak generowany jest plik wynikowy. Kod transformacji lub dokument Xml możemy teoretycznie modyfikować w czasie działania transformacji ale nie odniesie to żadnych skutków.



W Visual Studio 2010 pojawiła się jeszcze możliwość profilowania transformacji (w menu XML dodane zostało polecenie Profile XSLT). Możemy sprawdzić, wykonanie której część kodu transformacji zajmuje najwięcej czasu itd. Okno z raportem z wynikami profilowania zostało przedstawione poniżej:


04/06/2009

Bardzo użyteczne narzędzie do pracy z WMI

Home

Przeglądając dzisiaj fora internetowa, dotyczące platformy .NET, w odpowiedzi na jedno z zadanych pytań znalazłem wzmiankę o bardzo użytecznym narzędziu WMI Code Creator v1.0. Narzędzie to pozwala na wygenerowanie kodu używającego WMI (ang. Windows Management Instrumentation) do wykonywania różnego rodzaju zadań zarządzania: odczytywanie danych, oczekiwanie na zdarzenia WMI czy wywoływanie metod z klas WMI.

WMI nie jest za pewne narzędziem używanym w codziennej pracy ale mogącym się czasem przydać. Przy pomocy tej technologii możemy dobrać się do szczegółowych inforamacji na temat BIOS'u, procesora, dysków i innych urządzeń, procesów, usług oraz najróżniejszych ustawień systemu operacyjnego. WMI posługuje się dobrze znanymi pojęciami takimi jak: przestrzenie nazw, klasy, metody, zdarzenia i właściwości. Przestrzenie nazw zawierają klasy dotyczące poszczególnych obszarów zarządzania. Klasy modelują różne byty w tych obszarach. Właściwości tych klas to różne parametry konfiguracyjne. Na przykład dla klasy modelującej procesor możemy odczytać jego rodzinę, producenta czy liczbę rdzeni. Dla klasy modelującej proces została zdefiniowana metoda pozwalająca go zamknąć. Przykładem zdarzenia na jakie można oczekiwać jest natomiast zmiana statusu usługi.

Z poziomu platformy .NET do komunikacji z WMI służą klasy z przestrzeni nazw System.Management. Ich użycie nie jest bardzo skomplikowane, główna trudność polega na mnogości przestrzeni nazw i klas WMI. Nie sposób tego spamiętać. Tutaj z pomocą przychodzi wspomniane narzędzie. Przy jego pomocy możemy wybrać interesującą nas przestrzeń, potem klasę i jej właściwości, a następnie wygenerować kod w jednym z trzech języków: C#, Visual Basic .NET, Visual Basic Script, który odczyta parametry jakie wybraliśmy, wywoła metodę lub będzie oczekiwał na zdarzenia WMI. Co bardzo przydatne WMI Code Creator v1.0 pozwala podejrzeć wartości wybranych właściwości.

02/06/2009

Problemy z instalatorem

Home

A problem has been encountered while loading the setup components. Canceling setup

Komunikat ten, objaśniający bardzo dokładnie przyczynę błędu ;), napotkałem próbując doinstalować do Visual Studio 2008 Team System komponenty związane z obsługą języka C++. Rozwiązanie problemu okazało się trywialne ale dojście do niego zajęło mi trochę czasu.

Rozwiązywanie problemu rozpocząłem od sprawdzenia czy w katalogu z plikami instalacyjnymi znajdują się inne pliki z setup w nazwie lub podobne i czy można je uruchomić. Kiedy to nie pomogło skopiowałem cały katalog z instalatorem i potrzebnymi plikami na dysk lokalny przyjmując, że problem może wynikać z tego, że uruchamiam go z dysku zdalnego. Niestety ale to również nie pomogło.

Zacząłem szukać pomocy u pana Google. Natrafiłem na forum, na którym radzono aby po prostu odinstalować środowisko i wszystkie jego komponenty, a następnie zainstalować je jeszcze raz. Pewnie by podziałało ale nie chciałem poświęcać cennego czasu na ponowne instalowanie wszystkiego czego potrzebuję.

Po dalszych poszukiwaniach znalazłem wskazówkę mówiącą abym poszukał w katalogach tymczasowych logów instalatora i zobaczył co się w nich znajduje. Rada była o tyle mało użyteczna, że nie zawierała informacji gdzie dokładnie szukać plików i jak się nazywają. Uruchomiłem więc Process Monitor i zacząłem przeglądać jakie pliki otwiera instalator VS. W moim przypadku nazywały się: dd_install_vs_vstdcore_90.txt oraz dd_error_vs_vstdcore_90.txt i znajdowały się w katalogu:
C:\Documents and Settings\username\Local Settings\Temp
W jednym z plików znalazłem wpisy zawierające tekst ERRORLOG EVENT, coś w tym rodzaju:
[06/02/09,12:25:45] setup.exe: ***ERRORLOG EVENT*** : ISetupModule::SetManager() failed in ISetupManager::LoadSetupObjectGuid() : vs_setup.dll
Nie zastanawiając się długo wrzuciłem treść loga do Googla i jak to zwykle otrzymałem dużoooooooo wyników. Jedna z pierwszych stron, zresztą forum MSDN, dotyczyła VS 2005 ale wyglądała obiecująco. Wyczytałem na niej, że problem ustępuje po wyłączeniu antywirusa, a dokładniej programu firmy Kaspersky. Korzystam co prawdy z produktu konkurencji ale stwierdziłem, że nie zaszkodzi spróbować. Niestety ale i to rozwiązanie okazało się chybione. Kiedy już prawie straciłem nadzieję kilka pozycji niżej na liście wyników znalazłem ten post. Żeby nie przedłużać rozwiązanie problemu to:

W przypadku Visual Studio 2008 z zainstalowanym service pack'iem pierwszym (nie wiem jak z kolejnymi) instalator należy uruchamiać z poziomu okna Add or Remove Programs w panelu sterowania.

Data Binding i dobre praktyki programistyczne

Home

The data source for GridView with id 'GridView1' did not have any properties or attributes from which to generate columns. Ensure that your data source has content.

Czy spotkaliście się z takim błędem pomimo, że byliście pewni, że poprawnie zasilacie kontrolkę lub powiązane z nią źródło danych? Jeśli tak to problem był związany z użytymi strukturami danych. Przykładowy kod, który spowoduje powyższy błąd został przedstawiony poniżej. Zacznijmy od prostej klasy, którą chcemy zaprezentować na kontrolce GridView.
public class Data
{
  public int Id;
  public string Name;
}
Fragment kodu strony:
...
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
...
Kod zasilający kontrolkę jest równie prosty, dla uproszczenie nie korzystam ze źródła danych:
...
this.GridView1.DataSource = new Data[] { new Data { Id = 1, Name = "1" }, new Data { Id = 2, Name = "2" } };
this.GridView1.DataBind();
...
Uważny czytelnik mógł zauważyć, że w klasie Data użyłem publicznych pól składowych zamiast właściwości. I tu leży pies pogrzebany. Pola składowe klasy nie są automatycznie wykrywane i obsługiwane przy dowiązywaniu kontrolki (ang. binding). Innymi słowy pola trzeba opakować we właściwości. Nie wiem czy to przeoczenie w implementacji czy celowe działanie (biorąc pod uwagę treść komunikatu to drugie) ale w każdym razie ograniczenie to wspiera dobre praktyki programistyczne. Poprawny kod klasy Data powinien wyglądać tak:
public class Data
{
  public int Id { get; set; };
  public string Name { get; set; };
}

25/05/2009

Konkurs Code Camp 2009 - podsumowanie

Home

Na podsumowanie konferencji Code Camp 2009 w Warszawie z mojej perspektywy nadejdzie jeszcze czas. Najpierw chciałbym napisać o konkursie, który udało mi się zorganizować przy tej okazji. W skrócie konkurs składał się z dwóch etapów. Pierwszy etap (tzw. konkurs skojarzeniowy) odbył się w tygodniu poprzedzającym konferencję i polegał na odgadnięciu nazwy miejsca (kraju, miasta, wyspy itd.) na podstawie zdjęć, obrazków czy też opisu słownego. Wszystkie te miejsca miały tą wspólną cechę, że ich nazwy zostały użyte jako nazwy kodowe różnych produktów firmy Microsoft. Drugi etap, konkurs kartkowy, odbył się w czasie konferencji i polegał na odpowiedzeniu na 5 testowych pytań. Odpowiadając na pytanie nie można było korzystać z komputera, Internetu czyli trzeba było liczyć tylko na własną głowę.

Jak wyszło? Mówiąc szczerze nie jest do końca zadowolony. Frekwencja co tu dużo mówić nie dopisała. W konkursie wzięło udział tylko 6 osób. Chociaż z tego co dowiedziałem się od bardziej doświadczonych kolegów to i tak całkiem dobry wynik :)

Jeśli chodzi o konkurs skojarzeniowy to okazał się on dość łatwy i generalnie uczestnicy nie mieli problemów z odpowiedzeniem na pytania. Z konkursem kartkowym było już gorzej, poprawnych odpowiedzi było zdecydowanie mniej. Wydaje mi się, że było to spowodowane dwoma czynnikami. Po pierwsze pytania mogły być zbyt szczegółowe ale ja nie jestem tutaj obiektywny. Po drugie, konkurs był przewidziany na raptem 10 - 15 minut. Najwyraźniej nie udało mi się dostosować poziomu pytań do tak krótkiego czasu. Muszę o tym pamiętać i następnym razem przetestować pytania na znacznie większej grupie ludzi niż to uczyniłem.

Poniżej zamieszczam pytania i odpowiedzi do obu konkursów:

Konkurs skojarzeniowy


Odpowiedzi

  1. Dublin - Opis
  2. Hawaii - Visual Studio
  3. Astoria - ADO.NET Data Services
  4. Kilimanjaro - Sql Server 2010/Dynamics CRM 4.0
  5. Orleans - Opis
  6. Geneva - Opis
  7. Paris - Opis
  8. Ibiza - Sync Framework
  9. Oslo - Opis
  10. Quebec - Windows Embeded 2010
Do trzeciego pytani wkradł się błąd. Pytałem w nim o miasto na wschodnim wybrzeżu podczas gdy chodziło o zachodni brzeg USA. Z tego powodu zaliczałem również inne odpowiedzi na przykład Boston czyli Visual Studio 97.

Konkurs kartkowy

1. Mamy definicje dwóch klas jak poniżej:
class Base
{
   public virtual void Fun(int x)
   {
      Console.WriteLine("Base.Fun(int x)");
   }
}

class Derived : Base
{
   public override void Fun(int x)
   {
      Console.WriteLine("Derived.Fun(int x)");
   }

   public void Fun(object o)
   {
      Console.WriteLine("Derived.Fun(object o)");
   }
}
Co wypisze na ekran podany kod:
Derived d = new Derived();
d.Fun(1);
  1. Base.Fun(int x)
  2. Derived.Fun(int x)
  3. Derived.Fun(object o)
  4. Wywołanie metody spowoduje wyjątek
2.
Nullable<int> nullable = new Nullable<int>();
Type t = nullable.GetType();
Jaki rodzaj wyjątku zostanie rzucony po uruchomieniu tego kodu?
  1. Żaden
  2. InvalidOperationException
  3. NullReferenceExcpetion
  4. IndexOutOfRangeException
  5. ArgumentNullException
3. Załóżmy, że mamy zdefiniowaną zmienną o nazwie variable. Kiedy poniższy kod nie spowoduje rzucenia wyjątku:
variable = null;
variable.ToString();
4. Jaki będzie wynik wykonania poniższego kodu:
string s1 = new String(new char[] { 'a' });
string s2 = new String(new char[] { 'a' });
bool result = Object.ReferenceEquals(s1, s2);
Console.Write(result);

s1 = new String(new char[0]);
s2 = new String(new char[0]);
result = Object.ReferenceEquals(s1, s2);
Console.Write(result);

s1 = "Ala ma kota";
s2 = "Ala ma" + " kota";
result = Object.ReferenceEquals(s1, s2);
Console.Write(result);

s1 = "Ala ma kota";
string temp = "ma";
s2 = "Ala " + temp  + " kota";
result = Object.ReferenceEquals(s1, s2);
Console.Write(result);
  1. True, False, True, Talse
  2. False, False, True, False
  3. False, True, True, False
  4. False, False, False, False
  5. True, True, True, False
5. Jaki będzie wynik wykonania poniższego kodu:
delegate void Printer();
...
List printers = new List();
for (int i = 0; i < 4; i++)
{
  if (i % 2 == 0)
  {
      printers.Add(delegate { Console.Write(i); });
  }
  else
  {
     int j = i;
     printers.Add(delegate { Console.Write(j); });
  }
}

foreach (Printer printer in printers)
   printer();
  1. 0, 1, 2, 3
  2. 4, 4, 4, 4
  3. 4, 1, 4, 3
  4. 3, 2, 1, 0
  5. Inny

Odpowiedzi

  1. C - Derived.Fun(object o);

    W przypadku wywołania derived.Fun(1) zostanie wywołana metoda Derived.Foo(object). Metody zdefiniowane bezpośrednio na poziomie klasy wywołującej mają zawsze priorytet nad metodami zdefiniowanymi na poziomie klasy bazowej nawet jeżeli zostały przedefiniowane w klasie pochodnej. Jest tak ponieważ przy szukaniu metody najlepiej pasującej do wywołaniu, spośród wszystkich metod kandydujących odrzucane są metody zdefiniowane poza typem najbardziej wydziedziczonym. W wyniku tego kroku ze zbioru mogą zostać usunięte metody wirtualne.

  2. C - NullReferenceException

    Uruchomienie tego kod zakończy się rzuceniem wyjątku NullReferenceException w linijce z wywołaniem GetType. Typ Nullable<T> dostarcza własną implementacja ToString oraz GetHashCode ale nie GetType. Wywołanie GetType prowadzi więc do przekształcenia wartości Nullable<int> w referencje do obiektu (opakowywanie), a ponieważ w tym przypadku Nullable.HasValue jest równe false na stos odkładany jest null.

  3. Jeśli variable jest typem Nullable<T>

  4. C - False, True – bug?, True – optymalizacja kompilatora, False

  5. C - Domknięcie - Jak to działa?

19/05/2009

Name mangling, UniqueID, ClientID oraz ID

Home

Name mangling, po polsku maglowanie nazw, to w kontekście stron wzorcowych (ang. master pages) proces podmieniania identyfikatorów kontrolek przy generowaniu strony wynikowej (ze strony wzorcowej i strony z właściwą zawartością), a celem tej operacji jest zapewnianie, że identyfikatory będą na pewno unikalne w obrębie strony. Technicznie operacja ta sprowadza się do połączenia identyfikatora kontrolki z identyfikatorem kontenera w jakiej kontrolka została umieszczona, a dokładniej jego UniqueID. Operacja ta jest potrzebna ale stwarza też problemy i prowadzi niestety do błędów. Zacznę od opisu scenariusza:
  • Utworzyłem stronę wzorcową.
  • Utworzyłem stronę z zawartością.
  • Do strony z zawartością dodałem kontrolki GridView oraz SqlDataSource.
  • Skonfigurowałem połączenie z bazą danych.
  • Uruchomiłem stronę i GridView wyświetlił dane ze wskazanej tablicy.
  • Dodałem do strony kilka pól tekstowych i przycisk.
  • Dodałem metodę obsługującą naciśnięcie przycisku:
    protected void Button_Click(object sender, EventArgs e)
    {
     SqlDataSource.Insert();
    }
    
  • W kodzie strony deklaratywnie zdefiniowałem parametry wejściowe dla zapytania wstawiającego dane do bazy:
      <asp:SqlDataSource ID="SqlDataSource" runat="server" 
        ConnectionString="<%$ ConnectionStrings:Database %>" 
        SelectCommand="SELECT [Kolumna_1], [Kolumna_2], [Kolumna_3] FROM [Tabela]"
        InsertCommand="INSERT INTO users ([Kolumna_1], [Kolumna_2], [Kolumna_3]) VALUES (@Param_1,@Param1_2,@Param_3)">
      <InsertParameters>
        <asp:FormParameter Name="Param_1" FormField="PoleTekstowe_1" />
        ...
      </InsertParameters>
      </asp:SqlDataSource>
    
    W powyższym kodzie PoleTekstowe_1 to identyfikator pola tekstowego będącego źródłem danych dla parametru Param_1.
Mając gotową stronę uruchomiłem aplikację, wypełniłem pola tekstowe, nacisnąłem przycisk i otrzymałem wyjątek z komunikatem: Cannot insert the value NULL into column 'Kolumna_1'. Sprawdzam czy nie zrobiłem jakiejś literówki ale wszystko wygląda prawidłowo. Chwila zastanowienia i przypominam sobie, że przecież używałem już podobnego kodu i nie miałem problemów. Główkują dalej i zaczynam patrzeć podejrzliwie na użycie stron wzorcowych. Przenoszę więc cały kod do 'zwykłej' strony, odpalam i wszystko działa. W tym momencie przypominam sobie o Name mangling ale przecież nie zrezygnuję z tego powodu ze wszystkiego co dają strony tego typu.

Żeby nie przeciągać problem rozwiązałem rezygnując z deklaratywnego definiowana parametrów dla źródła danych na poziomie strony na rzecz zdefiniowania ich w kodzie. Oczywiście to nie wszystko. Sztuczka polega na tym, żeby przy podawaniu identyfikatora kontrolki, która będzie źródłem danych dla parametru należy użyć właściwości Control.UniqueID:
protected void Page_Load(object sender, EventArgs e)
{
   this.SqlDataSource.InsertParameters.Add(new FormParameter("Param_1", this.PoleTekstowe_1.UniqueID));
   this.SqlDataSource.InsertParameters.Add(new FormParameter("Param_2", this.PoleTekstowe_2.UniqueID));
   this.SqlDataSource.InsertParameters.Add(new FormParameter("Param_3", this.PoleTekstowe_3.UniqueID));
}
Control.UniqueID zawiera już przemaglowany identyfikator (globalnie unikalny w obrębie strony) i w związku z tym źródło danych nie ma problemu ze znalezieniem odpowiedniej kontrolki.

Dociekliwi mogą powiedzieć, że jest jeszcze właściwość Control.ClientID. Od razu mówię, że jeśli użyjemy tej właściwości to błąd również wystąpi. Na pierwszy rzut oka może wydawać się to dziwne ponieważ ClientID zawiera już zmieniony identyfikator. Różnica polega na tym, że do oddzielenie poszczególnych składowych identyfikatora ClientID używany jest inny separator niż przy UniqueID. Poniżej przykład dwóch identyfikatorów dla tej samej kontrolki:

UniqueID: ctl00$ContentPlaceHolder1$Nazwa
ClientID: ctl00_ContentPlaceHolder1_Nazwa

Dlaczego jednak użyto innych separatorów. Zacznijmy od tego, że aby proces podmieniania nazw był odwracalny ASP.NET musi umieć wydzielić z wygenerowanego identyfikatora jego składowe czyli identyfikatory kolejnych kontenerów i na końcu kontrolki. To jest przyczyna, dla której wprowadzono separatory. Separatorem powinien być oczywiście znak, który nie może wystąpić w bazowym identyfikatorze, w tym przypadku '$' ale można to zmienić.

Tutaj dochodzimy do momentu, w którym potrzebujemy z jakiegoś powodu odwołać się do wyrenderowanej kontrolki z poziomu JavaScript'u. Oczywiście potrzebujemy jakiś identyfikator i tu pojawiaja się problem. Otóż co zrobić jeśli jako separator użytu znaku niedozwolonego w identyfikatorach przez JavaScript? Problem ten postanowiono obejść wprowadzając ClientID i używając innego dozwolonego separatora czyli podkreślnika '_'. Reasumując UniqueID (renderuje się do atrybutu name tagu HTML) identyfikuje globalnie kontrolkę na potrzeby silnika ASP.NET, a ClientID (renderuje się do atrybutu id tagu HTML) na potrzeby JavaScript'u

11/05/2009

Visual Studio 2010

Home

Kilka dni temu, 6 maja, byłem na forum architektów, na którym między innymi pokazano wersję pre-beta Visual Studio 2010. Mówiono również o innych rzeczach ale część dotycząca nowej wersji tego środowiska była, przynajmniej dla mnie, zdecydowanie najciekawsza. Być może ta zasługa, jak zawsze rewelacyjnego Tomasza Kopacza ale nowe funkcjonalności wyglądały naprawdę bardzo obiecująco. Pozwolę sobie wymienić niektóre z nich:
  • UI oparte w całości o WPF
  • Dodano nowy sposób wyszukiwania w IntelliSense. W obecnej chwili po wpisaniu na przykład frazy Add IntelliSense poda wszystkie typy, metody itd. zaczynające się od tej frazy. W VS 2010 poda wszystkie typy, metody itd. zawierające podaną frazę czyli Add, AddTo ale również TestAdd, SuperAddTo.
  • Podgląd hierarchii wywołań pokazujący co wywołuje dana metoda i dalej co wywołują te metody itd.
  • Dynamiczne generowane diagramy sekwencji na podstawie kodu.
  • Generowanie szablonów klas. Na przykład piszemy:

    MyClass c = new MyClass();
    
    Załóżmy, że MyClass nie istnieje. W VS 2010 będziemy mogli wygenerować szablon dla tej klas. Podobny mechanizm będzie dostępny dla metod.
  • Transformacje konfiguracji. Tworzymy szablon konfiguracji i definiujemy przy pomocy specjalnego języka jak ten szablon ma zostać podczas kompilacji przekształcony w zależności od jakichś parametrów.
  • Dużo narzędzi do modelowania: integracja z Visio, powiązanie modelu z kodem na przykład po to aby kontrolować jak przebiegają prace - jakie komponenty zostały już zaimplementowane. Dalej kontrola poprawności kodu na podstawie modelu np.: czy nie ma zależności pomiędzy warstwami prezentacji i danych.
  • Lepsze narzędzia do pracy z aplikacjami wielowątkowymi. Profiler pokazujący jak działa nasza aplikacja wielowątkowa, który wątek generuje największe obciążenie, jak rozkłada się obciążenie na rdzenie/procesory. Narzędzia do radzenia sobie z zakleszczeniami.
  • Impact Analysis - Powiązanie testów z metodami, których dotyczą. Po modyfikacji metody wiemy, które testy należy powtórzyć.
  • Narywanie testów UI aplikacji okienkowych (WPF, Win Forms) - narzędzie oparte o Microsoft UI Automation. Testy takie można było pisać do tej pory tylko ręcznie.
Na koniec zostawiłem sobie dwa hity:

Historyczny debugger!!! Podczas debugowania będzie można podejrzeć jak zmieniał się stan aplikacji w różnych chwilach czasowych.

Nagrywanie stanu aplikacji!!! Przy pomocy odpowiedniego narzędzia będzie można zrzucić stan aplikacji (u klienta) do pliku i potem wczytać go do VS 2010 i rozpocząć debugowanie (potrzebny .NET 4.0).

Zaznaczam, że nie wiem jak te i inne rzeczy będą finalnie zapakowane w pudełka i sprzedawane. Z ciekawostek dodam, że Microsoft używa VS 2010 od początki roku 2008, a prace nad VS 2012 trwają już od roku :). Niby to nic dziwnego bo jak lepiej przetestować środowisko niż korzystając z niego, a prace nad tak złożonym programem muszą trwać. Ale jak sobie pomyślę, że ludzie z Microsoftu pracują teraz z/nad zabawkami, które poznam za parę lat, a zacznę z nimi pracować pewnie jeszcze później to co tu dużo mówić, ja też tak chcę!!!.

07/05/2009

"Autentykacja" - Czy takie słowo istnieje?

Home

Nie jestem purystą językowym ale niektóre rzeczy mi przeszkadzają i uważam, że powinno się mówić, a szczególnie pisać po polsku, a nie po "polskiemu". Właśnie jestem po lekturze kilku dokumentów dotyczących bezpieczeństwa, zarządzania użytkownikami oraz uprawnieniami, w których nagminnie używano słowa autentykacja. Niby wiadomo, że chodzi o proces uwierzytelniania (ang. authentication) ale takiego słowa nie ma po prostu w języku polskim. Jeśli ktoś nie wierzy to zapraszam do słownika języka polskiego, dostępny również on-line. Pół biedy kiedy używamy tego sformułowania w luźnej rozmowie ale słowo pisane jest bardziej wrażliwe na takie neologizmy, dziwne sformułowania itd.

06/05/2009

Dopowiedzenie

Home

Mam niewielką uwagę w kontekście postów Czemu zdarzenia nie działają??? oraz Czemu zdarzenia nie działają??? (część 2). Główne ich przesłanie:

W przypadku dynamicznego tworzenia kontrolek, czy to bezpośrednio czy to przy okazji użycia kontrolek data bound zawsze należy pamiętać aby dynamiczne kontrolki były tworzone nie tylko przy inicjalnym odwołaniu do strony ale również przy kolejnych.

Jest cały czas aktualne przy czym chciałbym dodać jedną rzecz. Otóż zapomniałem napisać, że w przypadku dowiązywania kontrolek do źródeł danych (na przykład do ObjectDataSource) nie trzeba się tym przejmować. Jest tak ponieważ źródła danych same dbają o to aby kontrolka została zasilona danymi w odpowiednim momencie. W szczególności nie trzeba jawnie wołać metody DataBind.

05/05/2009

Jak rozpoczęła się moja przygoda z Delphi

Home

Los chciał, że od jakiegoś czasu do moich obowiązków dołączyła potrzeba utrzymywania i pielęgnacji komponentu drzewo-listowego (kontrolka pozwalająca na wyświetlanie danych hierarchicznych i listowych z wieloma kolumnami) napisanego w Delphi. Długi broniłem i zapierałem się nogami ale w końcu musiałem się poddać. Dodam, że moja wiedza na temat Delphi była mniej niż skromna. Była bo teraz jest po prostu skromna :).

Plusem całej sytuacji jest to, że kod, który otrzymałem do pielęgnacji został dobrze wytestowany, komponent zachowuje się bardzo stabilnie, jednym słowem zgłoszeń błędów jest mało.

Można zapytań po co utrzymywać stary komponent skoro można napisać nowy albo jeszcze lepiej wybrać i zakupić jeden z istniejących na rynku. Z przepisaniem problem jest taki, że potrzeba by pewnie jednego osobo-roku zdolnego programisty aby to przepisać na .NET (tyle mniej więcej zajęło napisanie starego komponentu) plus dużo czasu na wytestowanie i stabilizację. Ameryki nie odkryję jeśli powiem, że znalezienie takiej ilości zasobów na w gruncie rzeczy niekomercyjny projekt to trudna sprawa.

Jeśli chodzi o zakupienie komercyjnego rozwiązania to pomysł dobry ale zawsze jest jakieś ale. Osobiście zapoznałem się i przebadałem pod względem wydajności, funkcjonalności itd. około dziesięciu rozwiązań dostępnych na rynku. Niektóre komponenty mają naprawdę duże możliwości. Problem polega na tym, że pomimo wszystko nie pokrywają wszystkich cech komponentu stworzonego na wewnętrzne potrzeby firmy, w której pracuję. Nie wchodząc w szczegóły firmowy produkt potrafi naprawdę dużo. Sytuacja bez wyjścia? Tak i nie. Tak, ponieważ z pewnością nie uda się znaleźć rozwiązania w 100% pokrywającego wymagania. Nie, ponieważ w obecnej chwili firma rozwija swoje produkty w .NET, a używanie starego komponentu wymaga użycia ActiveX z czego raz po raz wynikają jakieś problemy. Nie wspominając już o konieczności utrzymywania starego kodu.

Reasumując moja przygoda z Delphi jeszcze potrwa i mam nadzieję, że się czegoś nauczę. Na razie dzięki porównaniu IDE dla Delphi z Visual Studio zdałem sobie sprawę jak duży postęp dokonał się jeśli chodzi o zintegrowane środowiska programistyczne. Z drugiej strony porównanie czasu kompilacji dużego projektu napisanego w Delphi do projektu napisanego w .NET wypada bardzo znacząco na korzyść tej pierwszej technologii. Niestety podobnie jest z szybkością uruchamiania się aplikacji.

04/05/2009

Skrzydła furii

Home

Wiele osób, w tym i ja, spędziło długie godziny swoich młodzieńczych lat grając w takie gry jak Wings of fury. Kilka kolorów na krzyż, idea prosta ja budowa cepa, a grywalności tyle, że można by podzielić pomiędzy kilkadziesiąt współczesnych tytułów i jeszcze by zostało. Kiedy, więc przed weekendem majowym natknąłem się na sequel tej gry stworzony przez polskich programistów - Wings of fury 2 moje serce przyspieszyło.

Na szczęście, po za poprawioną grafiką, dużo się nie zmieniło. Znowu latamy w lewo i w prawo, zrzucam bomby, odpalamy rakiety, startujemy i lądujemy na lotniskowcu itd. Jednym słowem to co tygryski lubią najbardziej.

Jest trochę bugów, które utrudniają grę (problemy ze sterowaniem samolotu, menu gry słabo działa) ale da się z nimi grać. Najgorsze, że pomimo niewielkich wymagań występują problemy z uruchomieniem gry. W moim przypadku gra nie chciała działać na dwóch z trzech testowanych komputerów.

Tytuł jest w pełni darmowy i co dla mnie najciekawsze został napisany w C# na silniku MOGRE. Jest to zarządzana nakładka na silnik OGRE, a jego możliwości wydają się bardzo duże. Zaciekawiło mnie to tyle, że przymierzam się do zrobienia czegoś przy jego pomocy. Co z tego wyjdzie czas pokaże.

Jako podsumowanie mogę powiedzieć, pomimo pewnych zastrzeżeń, brawo dla autorów.

28/04/2009

Słów kilka o terminologii

Home

Ostatnio odniosłem wrażenie, że jest spora grupa osób, która dość niechlujnie używa terminów szyfrowanie (deszyfrowanie) i kodowanie (dekodowanie). Prowadzić to może natomiast do niezrozumienia u rozmówcy lub czytelnika.

Kodowanie to proces przekształcenie danych wejściowych do innej, bardziej pożądanej i użytecznej w danym kontekście postaci. W jednym przypadku użyteczne będą to dane skompresowane, w innym zapisane przy użyciu odmiennego alfabetu, a w jeszcze innym zaszyfrowane. Zgadza się, szyfrowanie jest specyficzną formą kodowania. Podstawowa różnica pomiędzy szyfrowaniem, a innymi formami kodowania polega natomiast na tym, że szyfrowanie wymaga podania klucza. Zarówno w przypadku innych form kodowania jak i szyfrowania algorytm może być dobrze znany i dostępny publicznie. Tylko, że nie znając klucza, a znając algorytm szyfrujący nie zdziałamy dużo. Pomijam tutaj sytuację kiedy algorytm został skompromitowany (złamany) lub kiedy użyty klucz jest zbyt słaby, łatwy do odgadnięcia.

Pojęcie kodowania jest, więc bardzo ogólne. Podsumowując mówiąc - te dane są zakodowane - tak naprawdę nie przekazujemy żadnej informacji, a już na pewno nie o tym, że dane zostały zaszyfrowane - utajnione.

27/04/2009

"Kobieta pierwotna"

Home

"Kobieta pierwotna" to tytuł naprawdę świetnej komedii, która miałem przyjemność zobaczyć wczorajszego wieczoru w teatrze "Bajka". W roli tytułowej występuje Hanna Śleszyńska, a przedstawienie zostało wyreżyserowane przez Arkadiusza Jakubika. Innych aktorów nie wymieniam bo ich nie było. Na scenie można jeszcze było zobaczyć Pana X czyli szmacianą kukłę reprezentującą mężczyznę :) Ci z was, którzy w tym momencie zaczynają podejrzewać o czym jest ta sztuka zapewne mają rację. W kilku słowach: o stosunkach damsko-męskich i ogólnie międzyludzkich, o poszukiwaniu mężczyzny, z którym kobieta mogłaby spędzić całe życie, o naszych (czytaj mężczyzn) wadach.

Nie trudno domyśleć się również, że przedstawienie miało postać monologu. Ale jakiego monologu, w czasie półtorej godziny więcej śmiałem się niż nie! Dowcipy, kąśliwe i ironiczne uwagi pod kątem mężczyzn wywoływały salwy śmiechu na widowni i bynajmniej nie tylko damskiej części publiczności. Hanna Śleszyńska była po prostu przezabawna, potrafiła improwizować, bardzo szybko nawiązała kontaktu z publicznością. W pewnym momencie aktorka zeszła nawet ze sceny i rozpoczęła dowcipny dialog z jednym z widzów (dowcipny choć trochę sprośny, więc nie będę cytował).

Podsumowując lekka i przyjemna rozrywka najwyższych lotów skłaniająca jednak do refleksji plus świetna, wręcz genialna rola Hanny Śleszyńskiej. Jednym słowem z czystym sumieniem polecam. Na zakończenie dodam jeszcze pesymistyczną uwagę, że kultura w naszym społeczeństwie najwyraźniej ginie skoro ludzie nie potrafią przyjść punktualnie do teatru, przeszkadzając sobie, a przede wszystkim innym.

23/04/2009

Steve Ballmer - spotkanie ze społecznością

Home

Dzisiejszego przedpołudnia udałem się na spotkanie z prezesem Microsoftu chociaż mówiąc szczerze miałem mieszane uczucia co do niego. Czemu? Po prostu nie chciałem tracić czasu na zobaczenie może śmiesznego ale medialnego przedstawienia, z których Steve Ballmer słynie. Z perspektywy mogę jednak w pełni szczerze powiedzieć, że było warto.

Spotkanie było co prawda krótkie, ale na dobrego showmana zawsze dobrze popatrzeć. I nie chodzi bynajmniej o to, że Steve Ballmer zrobił coś dziwnego czy bardzo ekscentrycznego. Wchodząc do sali miał na szyi biało-czerwony szalik z napisem Polska co wywołało gromkie brawa. Przemawiając zachowywał się na luzie, mówił konkretnie i zwięźle. Nawiązując do obecnej sytuacji ekonomicznej, czytaj kryzysu, powiedział że co prawda rynek się skurczył ale wbrew pozorom jest to doskonały moment na wprowadzanie nowych produktów i innowacji. Krótko mówiąc z IT jest całkiem dobrze :) Zachęcał do wysyłania do niego wiadomości z sugestiami dotyczącymi produktów Microsoftu. Na pytania społeczności odpowiadał w sposób błyskotliwy, żartował (chociaż mógł się do nich zapewne przygotować ponieważ były autoryzowane).

Tak czy inaczej występy publiczne to z pewnością jego żywioł i jest w tym naprawdę dobry. A mistrza przy pracy zawsze ogląda się z przyjemnością bez względu na czym ta praca polega.

17/04/2009

Bug w Visual Studio?

Home

W poście pisałem o bardzo ciekawej i użytecznej funkcjonalności Visual Studio umożliwiającej testowanie kodu bezpośrednio z poziomu środowiska bez potrzeby pisania programów testowych. Dzisiaj w pracy chciałem skorzystać z tej możliwości, a dokładniej chciałem z poziomu okna Class View utworzyć obiekt i umieścić go w oknie Object Test Bench. Służy do tego polecenie menu kontekstowego Create Insance. Wybrałem konstruktor domyślny i ku mojemu zdziwieniu otrzymałem okienko błędu, którego nigdy wcześniej nie widziałem, z mniej więcej takim komunikatem:

The type name 'ClassName' does not exist in the type 'Test.Test'

Po bliższym przyjrzeniu okazało się, że błąd spowodowany jest tym, że klasa obiektu, który chciałem utworzyć znajduje sie w przestrzeni nazw, która zawiera inną klasę o takiej samej nazwie jak nazwa przestrzeni np.:

namespace Test
{
  public class Test
  {
  }
}
Podobny błąd występuje również przy próbie wywołania metody statycznej z poziomu okna Class View (polecenie Invoke Static Method). Aby pozbyć się problemy wystarczy zmienić nazwę przestrzeni lub klasy. Co ciekawe wygląda na to, że występowanie problemu związane jest typem projektu. Ja zauważyłem go dla projektu konsolowego. Dla projektu typu Class Library już nie. Na sieci znalazłem informacje, że taki komunikat może pojawiać się również w innych scenariuszach dla projektów innego rodzaju. Dodam jeszcze, że błąd zaobserwowałem w Visual Studio 2008.

11/04/2009

Życzenia

Home



Życzę wszystkim wesołych i spokojnych Świąt Wielkiej Nocy spędzonych z najbliższymi.

Serdecznie pozdrawiam
Michał Komorowski

09/04/2009

Visual Studio i za długie ścieżki

Home

Możliwe, że to znany problem ale ja natknąłem się na niego ostatnio. Otóż siedzę sobie i koduję, oczywiście używając Visual Studio i w pewnym momencie zdecydowałem, że potrzebuję z bazy kodów (aktualnie używam Visual Source Safe) pobrać nowy projekt. Sprawa prosta, wybrałem polecenie Add -> Existing Project..., wyszukałem interesujący mnie projekt, wybrałem Ok i... Visual Studio kaput. Bez żadnego ostrzeżenia, komunikatu środowisko zostało zamknięte. No cóż, myślę sobie, spróbuję jeszcze raz. Za drugim, trzecim razem dokładnie to samo. Sprawa dziwna bo inne projekty pobierają się bez problemu. Zaczynam się zastanawiać i nagle przebłysk, że chyba słyszałem o czymś takim, o jakimś problemie z długością ścieżek. Kilka eksperymentów i okazało się, że problem występuje jeżeli pełna ścieżka do pliku z projektem umieszczonym w bazie kodów przekracza 120 znaków. Jak sobie z tym poradzić. Nie pozostaje nic innego jak pobrać projekt na dysk przy pomocy Visual Source Safe Explorer i wczytać go z tej lokalizacji. Dalej Visual Studio już sobie poradzi.

Velocity - co nowego?

Home

Dwa dni tempu pojawiła się wersja CTP 3 Velocity. Ponieważ w kilku postach wspominałem o tej technologii pozwalającej na rozproszone cache'owanie danych chciałbym w kilku słowach napisać o najważniejszych nowościach względem CTP 2.
  • Mechanizm powiadamiania - W tej chwili możliwe jest reagowanie kiedy jakiś region (grupa logicznie powiązanych z sobą obiektów umieszczonych w cache'u) jest dodawany, aktualizowany lub usuwany z cache'a. Mechanizm powiadamiania umożliwia również automatyczne unieważnianie cache lokalnego. Cache lokalny utrzymywany jest po stronie klienta i zawiera kopie danych pobranych z klastra. Do tej pory dane zgromadzone w tym cache'u były unieważniane dopiero po upływie określonego czasu. W tej chwili zostaną unieważnione tak szybko jak cache lokalny otrzyma powiadomienie.
  • Poprawienie wydajności.
  • Poprawienie bezpieczeństwa - Usługa gospodarza hosta działa teraz z uprawnieniami konta (mniejszymi niż dotychczas): Network Service.
  • Nowe opcje zarządzania klastrem - Wraz z CTP 3 rolę zarządcy klastra może pełnić instancja SQL Server'a. Wcześniej za zarządzanie klastrem byli odpowiedzialni tylko tzw. główni gospodarze (ang. lead host). Równocześnie usunięto możliwość przechowywania konfiguracji klastra w pliku XML.
  • Ulepszenia programu instalacyjnego - Wprowadzono możliwość automatycznej instalacji oraz zwiększono zakres dostępnych w czasie instalacji opcji.
  • Nowe API - Ta zmiana nie cieszy mnie za bardzo ale nic nie poradzę. Ogólnie zmieniło się nazewnictwo klas, delegatów, kolejność parametrów w metodach itd. czyli w sumie całkiem sporo.