Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

  • Grupy offline w czasach zarazy

    Kontynuując myśl napoczętą w poprzednim wpisie przeniosę się ze swoimi rozważaniami na grunt grup offline. I już na wstępie wysunę śmiałą tezę, że obecna sytuacja gospodarcza na świecie sprzyja ruchom społecznościowym, w tym grupom offline.

    Pomijając moje osobiste przemyślenia dotyczące 'kryzysu', czy też 'Kryzysu', spróbuję przejść od razu do namacalno-zauważalnych skutków.

    W mediach pojawiają się informacje dotyczące zamrażania rekrutacji, a także likwidacji stanowisk pracy. Wszyscy czytali o cięciach Microsoftu, choć pojawiają się również głosy optymistyczne, wedle których sektor IT w ogóle nie zostanie dotknięty przez recesję. Jestem przekonany, że rzadko kto z branży uwierzy w to, że jego nic nie dotknie, tym bardziej, że na forach pojawiają się wątki, z których jasno wynika, że nie jesteśmy grupą specjalnie uprzywilejowaną, a może tylko w nieznacznie lepszej sytuacji.

    I tak jak ponad dwa lata temu mówił Peter Schiff (Gutek, dzięki za link!), ja też jestem zdania, że trzeba się z obecną sytuacją pogodzić i ją po prostu przyjąć. Rynek pracy się zepsuje, co do tego nikt chyba nie ma najmniejszych wątpliwości. A w takiej sytuacji najlepsze, co możemy zrobić, to starać się o to, aby w potencjalnej rozmowie rekrutacyjnej móc się jak najlepiej zaprezentować. Firmy się reorganizują, restrukturyzują i następują wewnętrzne ruchy robaczkowe. To jest czas na inwestowanie w siebie, poszerzanie istniejących i zdobywanie nowych umiejętności. Zarówno tych technicznych, jak i miękkich - związanych z pracą w grupie, prowadzenia prezentacji, czy sprawnego formułowania myśli na piśmie. I wracając do mojej poprzedniej notki - gdzie szybciej, taniej i skuteczniej można to osiągnąć, jak nie właśnie w ramach spotkań grup offline??? Niektórzy już to dostrzegają i coraz cieplej patrzą na swój udział w spotkaniach grup, czy zaangażowaniu się w powstanie nowych.

    Trzeba pamiętać również, że grupy offline i społeczności online to miejsca, gdzie ludzie kontaktują się ze sobą i zawierają bliskie znajomości. Bardzo cenne znajomości, bo dzięki nim grupowicze poszerzają swoją sieć potencjalnych pracowników / pracodawców / współpracowników, czyli miejsca, w którym przyjdzie im żyć i funkcjonować. "Liga rządzi, liga radzi, liga nigdy cię nie zdradzi!".

    I właśnie te aspekty działania grup są obecnie bardzo atrakcyjne dla każdego z branży. Dla każdego, kto zastanawia się nad swoją przyszłością.

    Jednak grupy to nie tylko sami ludzie. To także cele, jakie stawiają przed sobą grupowicze do realizacji, a tu nierzadko zaczynają pojawiać się pieniądze. I w tym miejscu dotykamy kolejnej sfery związanej z działalnością grup - sponsoring.

    Nie będę zbyt odkrywczy, gdy stwierdzę, że sponsorzy zdecydowanie bardziej wolą wspierać grupy swoimi produktami, niż przekazując bezpośrednio pieniądze. W czasach kryzysowych prawda ta osiąga swoje drugie i trzecie dno - następuje całkowity odpływ gotówki ze strony sponsorów. I uważam, że to również można wykorzystać z powodzeniem dla rozwoju grupy! To jest właśnie ten moment, kiedy możemy postarać się o sponsoring produktowy, który w dłuższej perspektywie może zaowocować wsparciem finansowym dla grupy. Firmy dobrze wiedzą, że dołek kiedyś się skończy i wtedy lepiej być dobrze osadzonym w środowisku, niż zaczynać wszystko od zera. A licencje na soft można wykorzystać w czasie spotkań grupy, podnosząc w ten sposób wartość takich meetingów w oczach grupowiczów.

    Zamiast zapraszać na spotkanie prezentera z innego kraju i opłacać jego T&E, można pokusić się o zorganizowanie sesji live meeting, a w przyszłości poprosić o wizytę. Sponsorzy mają swoje listy wybranych ze społeczności specjalistów, których chętnie oddadzą na pożarcie grupom offline. Oczywiście swoich pracowników również chętnie podeślą - w końcu to ich najcenniejszy nabytek, który na pewno wspomni o swoich produktach.

    Idąc dalej tym tokiem rozumowania, grupy powinny odpowiadać na obecne potrzeby i dać szansę nowym grupowiczom na szybkie dołączenie do społeczności i zaoferować pomoc w realizacji swojej ścieżki rozwojowej. Zarówno udostępniając możliwości prowadzenia prezentacji, jak i angażując coraz to nowe osoby w rozmaite działania grupy. To czas na rozwój grup i budowanie silnego zaplecza prelegenckiego, z którym można próbować sił na wielu różnych imprezach tak krajowych jak i zagranicznych. To czas na budowanie silnie zintegrowanej społeczności, w której "it's all about people".

    Szukasz swojej grupy? Ludzi podobnych do Ciebie, z którymi chcesz pogadać, wymienić się doświadczeniami, pośmiać i zaprzyjaźnić? W tej chwili najszybciej osiągniesz swój cel odwiedzając witrynę grup offline skupionych wokół technologii firmy Microsoft.

    I nie, nie jest to płatna reklama grup. Sam mam wiele zastrzeżeń do sposobu działania grup oraz udziału firmy Microsoft w całej tej zabawie, ale jedno wiem na pewno: to właśnie dzięki grupom poznałem wiele wyjątkowych osób, z którymi naprawdę dobrze spędza się czas. I nikt mi tego nie odbierze. Ludzi i wiedzy tam zdobytej.

    opublikowano 17 lutego 2009 01:58 przez mgrzeg | 1 komentarzy
    Filed under:
  • Pro publico bono.NET - tak czy nie?

    Czy zastanawialiście się kiedykolwiek nad tym, co popchnęło was do napisania pierwszego postu na forum, pierwszego komentarza na czyimś blogu, pierwszego udziału w spotkaniu grupy offline? A co was potem podkusiło, żeby pomóc komuś rozwiązać problem odpowiadając na pytanie na forum, lub co was skłoniło do napisania pierwszej notki na blogu, poprowadzenia prezentacji na spotkaniu grupy, albo zorganizowania pierwszego spotkania grupy offline w waszym mieście? Nie, nie zamierzam szukać odpowiedzi na te pytania. Zakładam, że wszystko (albo przynajmniej część, a reszta pozostaje kwestią czasu) to macie za sobą i zastanawiacie się nad swoją obecną sytuacją: warto, czy nie?

    Oczywiste jest, że z punktu widzenia aktywisty jest on do przodu. No bo przecież:
    - prowadząc blog rozwija swoje umiejętności pisarsko - edytorskie. Pogłębia wiedzę, zawiera wirtualne znajomości, poszerza swoje horyzonty;
    - odpowiadając na forum znowu poszerza swoją wiedzę, gruntuje ją. Krąg jego e-znajomych jest coraz większy. Uczy się w klarowny sposób przekazywać istotę problemu i cierpliwie odpowiada na pytania, nierzadko zadawane przez osobę, która nie wie o co pyta;
    - organizując życie społeczności offline rozwija swoje umiejętności interpersonalne: pracuje w grupie, współpracuje ze sponsorami oraz rozwiązuje wewnętrzne problemy i konflikty. Dodatkowo zahacza o prowadzenie finansów, marketing grupy, reklamę w serwisach społecznościowych i innych miejscach publicznych. Nie wspominając o prowadzeniu strony internetowej, mailingów i całej reszty, którą aktywista musi sobie jakoś zorganizować;
    - prowadząc prezentację uczy się panować nad stresem, poszerza swoją wiedzę, doskonali umiejętności wyrażania własnych myśli oraz walczy z charakterystycznym dla osób o inklinacjach technicznych introwertyzmem. Otwiera się na innych, przełyka krytykę.

    A to i tak tylko część 'bonusów'. Aktywista czasem coś wygra: a to dostanie myszkę z klawiaturą, a to książkę, a to MVP...

    I wszystko pięknie, tylko od czasu do czasu pracodawca, lub partner życiowy spogląda przez ramię i pyta: a co to ma wspólnego z twoją pracą, lub odpowiednio - naszym życiem? Przecież czas jaki się poświęca na tego typu działalność nie bierze się z powietrza. Suma godzin podzielonych na jedzenie, sen, pracę, zabawę z dziećmi, sprawy rodzinne, sklepy.... oraz działalność 'pro publico bono.net' jest stała i = 24h na dobę ziemską. Oczywiście, zawsze można poszukać tu i ówdzie wolnych zasobów czasowych: a to snu trochę uszczknąć, a to dzieci przegonić, a to szybciej wygenerować kawałek aplikacji...

    Zaraz, zaraz! Jak to 'szybciej wygenerować kawałek aplikacji'? Czyżby aktywista był wydajniejszy od nie-aktywisty? Lub też Wydajnosc(Aktywista(t-1 dzień)) < Wydajnosc(Aktywista(t)), dla każdego t? Być może. Ale jeśli nawet tak jest, to i tak 'zaoszczędzony' czas aktywista przeznacza na samorozwój. I czy nie jest tak, że leniwy, acz cwany i inteligentny programista szybciej znajdzie sposób na przyspieszenie sobie pracy, niż 'aktywista', który co i rusz będzie szukał 'lepszych i jeszcze wspanialszych' rozwiązań? Lenistwo motorem postępu?

    W kategoriach ogólnoludzkich aktywista zawsze jest wygrany. Dzięki jego pomocy x osób rozwiązało swoje problemy. Dzięki zorganizowanym przez niego spotkaniom y osób wymieniło się doświadczeniami i być może rozpoczęło współpracę zawodową. Zmienił nieodwracalnie otaczający go świat. Zrobił duży krok w nieznane, gdzie przyszłość jest znacznie słabiej przewidywalna.

    Spytacie: po co o tym piszę? Ano kategorie ogólnoludzkie nie wystarczają. Jeszcze pare miesięcy temu Gael Fraiteur próbował wespół z Mauricem de Beijer przekonać mnie, że organizowanie darmowych imprez nie ma większego sensu (Maurice pomaga przy organizacji SDE i SDC). Znam wiele osób, które koszta osobiste związane z działalnością pro publico bono musiały położyć na szali razem z potencjalnymi zyskami, czy to wynikającymi z potrzeb altruistycznych, czy to zimnej kalkulacji potencjalnych zysków i niestety... zakończyły, lub zamierzają zakończyć swoją działalność 'charytatywną'. 'Dlaczego?' - spytacie. Odpowiedź nie jest prosta, a wręcz bardzo złożona i często indywidualna. Wypalili się, dostali po plecach, odechciało im się, przenieśli aktywność na inny poziom, lub przedmiot zainteresowań... ale też zmienili swoje priorytety życiowe, oddali pole innym... co przypadek, to przyczyna. Statystycznie tylko 1% populacji jest aktywny. Reszta - nie. I bez współpracy aktywistów, studnia z nimi szybko wyschnie. Niepokoi mnie ten ruch odpływowy, choć nie wszystko rozumiem i wiem. Wiem jednak, że w światku aktywistów, który znam, brakuje 'superintegratora', osoby, za którą wszyscy pójdą i dzięki której cała działalność będzie miała większy wykop i sens. A może nie jest potrzebna taka osoba?

    Zatem - być czy nie być aktywistą? Hę? Jak widzicie, ja wybrałem :)

    opublikowano 16 lutego 2009 19:07 przez mgrzeg | 0 komentarzy
    Filed under:
  • CreateInstance vs CreateInstance<T>

    W swoich potyczkach dosyć często mam do czynienia z COM-ami, przy tworzeniu instancji których równie często sięgam po Activator.CreateInstance. I pewnie nie byłoby w tym nic odkrywczego ani niezwykłego, gdyby nie pewna obserwacja.

    Weźmy dla przykładu kawałek kodu, w którym tworzymy instancję klasy TestClass korzystając z wersji genericsowej i zwykłej:

    class TestClass
    {
     public TestClass()
     {
     }
    }

    class Program
    {
     const Int32 CYCLES = 100000;
     static void Main(string[] args)
     {
      CreateInstanceGenericTest();
      CreateInstanceNonGenericTest();
     }

     private static void CreateInstanceNonGenericTest()
     {
      using (OperationTimer ot = new OperationTimer("CreateInstance NON Generic"))
      {
       TestClass t = null;
       for (int i = 0; i < CYCLES; i++)
       {
        t = (TestClass)Activator.CreateInstance(typeof(TestClass));
       }
      }
     }

     private static void CreateInstanceGenericTest()
     {
      using (OperationTimer ot = new OperationTimer("CreateInstance Generic"))
      {
       TestClass t = null;
       for (int i = 0; i < CYCLES; i++)
       {
        t = (TestClass)Activator.CreateInstance<TestClass>(); //jedyna różnica
       }
      }
     }
    }

    po wykonaniu którego dostajemy czasy:

      ,375 seconds CreateInstance Generic
      ,574 seconds CreateInstance NON Generic

    Czyli wszystko mniej-więcej zgodnie z oczekiwaniami. Wersja genericsowa nieznacznie sprawniejsza (powiedzmy dwukrotnie).

    Dokonajmy jednak prostej modyfikacji. Zmieńmy mianowicie modyfikator dostępu dla klasy TestClass z internal na public, pozostawiając resztę bez zmian:

    public class TestClass
    {
     public TestClass()
     {
     }
    }

    a otrzymamy następujące czasy:

      ,372 seconds CreateInstance Generic
      ,035 seconds CreateInstance NON Generic

    Jak widać powyższa zmiana zupełnie nie wpłynęła na wersję genericsową, natomiast spowodowała prawie 20x przyspieszenie wersji niegenericsowej, wobec czego wersja nie używająca generics jest teraz o rząd wielkości szybsza od odpowiednika genericsowego. Wow!

    Ale o so chodzi?! :) Mam nadzieję, że ktoś wie jaka idea przyświecała takiemu rozróżnieniu i wytłumaczy mi to łopatologicznie :)

    PS. Pomocnicza klasa OperationTimer mierząca czas wykonania kodu wewnątrz bloku using jest żywcem ściągnięta z książki 'CLR via C#' Jeffreya Richtera i opiera się na klasie Stopwatch z System.Diagnostics.

    PS2. W przypadku zagnieżdżenia klasy TestClass niezależnie od wybranego modyfikatora dostępu wyniki są takie jak w przypadku pierwszym, czyli wersja genericsowa szybsza.

    opublikowano 12 lutego 2009 01:57 przez mgrzeg | 4 komentarzy
    Filed under: ,
  • Konferencja szuka sponsora, sponsor szuka konferencji :)

    W tych ciężkich czasach każdy stara się liczyć każdy grosik, bo nigdy nie wiadomo, kiedy dopadną go czarne macki kryzysu. Jednak to również dobry czas, żeby inwestować we własną markę i myśleć o przyszłości, w której rozpoznawalność i pozytywna aura mogą mieć decydujące znaczenie.

    Zachęcam wszystkie firmy oscylujące wokół technologii microsoftowych do wspierania inicjatyw typu konferencja C2C, na którą trafia wyjątkowa grupa pasjonatów i entuzjastów wspomnianych technologii. Niewielka - relatywnie - suma wydana na wsparcie cateringu, czy nadruku koszulek z logiem firmy i konferencji, lub pozwalająca na zarejestrowanie webcastu z sesji, to w miarę prosty sposób, żeby zaistnieć w umysłach wielu osób jako patron społeczności oraz dbający o rozwój i wiedzę pracodawca. Doświadczenia grup offline pokazują, że odwiedzający je członkowie nierzadko szukają nowej pracy poprzez znajomości zawarte na grupie, trafiając często do firm o rozpoznanej nazwie.

    A zatem - jeśli chcesz, by logo Twojej firmy znajdowało się obok nazwisk tak znanych postaci, jak Udi Dahan, Julia Lerman, czy Ingo Rammer, które doskonale się indeksują i szybko prowadzą na stronę C2C, lub też chcesz, aby niemal cała .NETowa społeczność w Polsce nosiła koszulki z Twoim logo, a po niezwykle udanej konferencji uczestnicy zaśpiewali 'Niech żyje nam <Twoja firma>!!!', chwaląc pod niebiosa Twój wkład w to niezwykłe wydarzenie, to naprawdę nic prostszego!

    Wystarczy jeden mały kroczek, a Aneta i Arek na pewno odpowiedzą na wszystkie pytania i rozwieją wszelkie wątpliwości! :)

    Jeśli jeszcze się wahasz, to przeczytaj list intencyjny Microsoftu dla C2C. Potem zajrzyj na stale uzupełnianą stronę z sesjami oraz zapoznaj się z ubiegłoroczną edycją oraz echem, jakim konferencja odbiła się po polskich społecznościach microsoftowych.

    Porozmawiaj ze swoimi kolegami z pracy, uderzcie potem wspólnie do swoich przełożonych - przecież Ty też możesz zrobić coś pozytywnego i pomóc organizatorom C2C, a nic nie ryzykujesz! Jeśli nie musisz do nikogo uderzać, bo sam podejmujesz istotne decyzje - zastanów się nad przyszłością swojej firmy, może ten ruch zaowocuje w sposób dla Ciebie nieprzewidywalny, a na pewno pozytywny, przy niewielkim koszcie.

  • Nowości z podwórka i okolic

    Kilka rzeczy wydarzyło się w ostatnim czasie. Kolejne właśnie się dzieją, a jeszcze następne zbliżają się wielkimi krokami. Jedziemy!
    UPDATED!

    1. Początek stycznia. Nominacje i renominacje MVP.
      Do programu dołączyli Szymon Kobalczyk, Damian Widera i Marcin Goł. Fajnie znać chłopaków osobiście :). Pierwszy to szwagier Marcina, blogera zinowego. Drugi - bez żadnych koligacji rodzinnych ze znanymi mi blogerami, po prostu sam jest blogerem na zinie :D. Trzeci - lider warszawskiej grupy PLSSUG, którą prowadzi po innym blogerze zinowym, Pawle ;). No i renominacje: razem z innymi chłopakami z zina (poza Mateuszem Kierepką, autorem tekstów do obu numerów zina): Procentem, Gutkiem i Kubą Binkowskim, dostaliśmy pozytywne cenzurki i jedziemy następny rok na tym samym wózku.
    2. Konferencje: VCL, 4Developers, C2C, TechFest.
      VCL: to tak naprawdę pierwsza całodniowa konferencja organizowana siłami warszawskich grup ITPro, z dużym zaangażowaniem ze strony wielu osób (w tym m.in. Łukasza Foksa, z-ce rednacza WSS oraz lidera WGUiSW). Gdybym miał więcej wolnych dni, to na pewno bym się tam pojawił (impreza odbędzie się we wtorek, 17 lutego).

      4Developers: organizowana w Krakowie przez fundację PROIDEA, z patronatem KGD, to miejsce, gdzie będzie można posłuchać o ciekawych rzeczach. A o ciekawych rzeczach mówią ciekawi ludzie. Polecam oczywiście prezentacje chłopaków z zina: Szymona i Marcina, a na dokładkę Mateusza. Przy okazji warto zajrzeć na prezentację Basi Fusińskiej, która dzielnie walczyła w konkursie SI w ramach ścieżki .NET C2C. Dodatkowo polecam prezentację (a przynajmniej zajrzenie na nią) niezwykle charyzmatycznego Jacka Laskowskiego, którego można złapać w czasie spotkań WJUG. Poza Jackiem swoją prezentację będzie miał również inny waleczny WJUG-owicz - Waldemar Kot.

      C2C: organizowana siłami wielu grup offline. Niektórzy z nich, to blogerzy zina, z czego na główną uwagę zasługuje Arek, który zdaje się pociąga większość sznurków przy organizacji tej imprezy. W drugiej połowie stycznia zorganizowany został konkurs Speaker Idol, w którym w szrankach stanęli m.in. wspomnieni przy okazji 4Developers: Szymon i Basia, a który wygrał Piotrek Leszczyński - najnowsza postać na zinie. Poza Piotrkiem swoją prezentację będzie miał jeszcze jeden bloger zina: Marek, a to i tak tylko odnośnie ścieżki .NET. I pewnie pojawiłoby się na ścieżce .NET więcej prelegentów zina, ale przecież trzeba dać dojść do głosu gwiazdom zagranicznym, które w tym roku będą błyszczeć szczególnie jasnym blaskiem w czasie konferencji!!! W ramach ścieżki SQL pojawi się m.in. Marek, wymiatacz bazodanowy z zina, w niektórych momentach posiłkujący się językiem rosyjskim w stopniu dla mnie niepojętym. Poza nim zdaje się jeszcze jedna z postaci z zina, ale to chyba nic oficjalnego i lepiej nic więcej nie będę pisał.

      TechFest: Michał Jagieła szykuje imprezę z ogromnym rozmachem i szczerze trzymam kciuki za powodzenie tego wydarzenia. Pojawi się tam plejada polskich prelegentów (w tym również blogerów z zina ;)), ale także zagraniczni goście, żeby wspomnieć tylko o sławnym i równie kontrowersyjnym Ayende Rahien.
    3. Krążą plotki, że w okolicach początku marca po Polsce będzie się tułał niejaki Raymond Lewallen. Zawita do kilku miast:
      Kraków - sobota, 7 marca
      Katowice - poniedzialek
      Warszawa - wtorek
      Poznań - środa
      Toruń - czwartek
      Gdańsk - piątek
      gdzie na pewno opowie coś ciekawego. Co? A o tym możecie zdecydować, odwiedzając stronę swojej grupy na
      ms-groups.pl i głośno krzycząc o czym chcielibyście posłuchać. Ugośćcie strudzonego wędrowcę, a odpłaci Wam się pięknym za nadobne :). Nie jest wielką tajemnicą, że zine.net wzoruje się w dużej mierze na codebetter.com i IMO wizyta każdego z bloggerów tego wyjątkowego sajtu to niewątpliwe wydarzenie, na którym warto być.
    4. Ziemek Skowroński, barwna postać developers.pl oraz zina, po moich marudzeniach, a pewnie i nadejściu własnej potrzeby integracji z innymi podobnymi sobie, wziął się za grupy offline. I tak, jeśli będziecie 26 lutego w okolicach Gloucester (czyt. Gloster), to zajrzyjcie koło 18.00 w to przytulne miejsce, a będziecie świadkami inauguracji grupy GL.NET, prowadzonej właśnie przez Ziemka. Zapowiadają się ciekawe prezentacje, więc jak tylko możesz, to po prostu wpadnij.
    5. ITBlogs.pl
      Niezwykle ciekawa i warta szerszego rozpropagowania inicjatywa, prowadzona przez Karola Stilgera oraz innych znanych itprosów. Trzymam kciuki i życzę sukcesu! Już jest tam parę fajnych blogów, w tym Bloggers Underground, blog imprezy około konferencyjnej :). Bracia i siostry itprosi, łączcie się na ITBlogs.pl!!! :)
    6. WG.NET
      Wciągam się ponownie w Warszawską Grupę .NET. Liczę na to, że wspólnie z Arkiem oraz innymi chłopakami z zina (ale nie tylko!!!) zrobimy parę fajnych rzeczy i to nie wysiłkiem jednej osoby. To jednak temat na osobny post i być może szerszą dyskusję.
    7. Rok temu się nie udało, ale w tym roku wybieram się na MVP Global Summit, gdzie mam nadzieję zahaczę o
      Party with Palermo
      i liczę na kilka pogaduch z różnymi ludźmi. Być może uda się załatwić jakąś fajną sesję LM dla wg.net, albo prelegenta na jakąś konferencję? Zobaczymy...
      Jak macie jakieś pytania, lub sugestie, które chcielibyście przekazać któremuś z MVP, którzy licznie przybywają na Summit, to chętnie przekażę :)

    To tyle z 'nowości', zdecydowanie wartych moim zdaniem dostrzeżenia. Na pewno coś pominąłem, ale liczę na to, że znajdzie się dobra dusza, która mi to wskaże ;)

    opublikowano 4 lutego 2009 19:44 przez mgrzeg | 5 komentarzy
    Filed under: , ,
  • [UPDATED] Zabawy z LSA - wydłubywanie haseł usług + tool.

    Tekst ten dedykuję Grzesiowi Tworkowi i Pauli Januszkiewicz, których tegoroczna sesja na MTS natchnęła mnie do tego, żeby opisać parę poruszonych tam tematów na blogu.

    Na pewno wielokrotnie zastanawialiście się nad tym, w jaki sposób zapisać jakąś tajemną informację tak, żeby nikt niepowołany nie mógł się do niej dobrać. Weźmy dla przykładu automatyczne logowanie do systemu.

    Winlogon jakiego nie znamy?

    W ramach swojego wsparcia technicznego, Microsoft proponuje rozwiązanie polegające na edycji zawartości klucza rejestru, a mianowicie HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon i ustawieniu:

    • DefaultUserName - nazwa użytkownika, dla którego ma zachodzić automatyczne logowanie;
    • DefaultPassword - hasełko użytkownika wpisanego w DefaultUserName;
    • AutoAdminLogon - 1 oznacza, że ma zachodzić automatyczne logowanie, 0 - nie.

    I wszystko w porządku, gdyby nie to, że hasełko w DefaultPassword jest wpisane jawnym tekstem i dostępne bez większego problemu dla każdego, kto może odczytać zawartość tego klucza. Bezpieczeństwo takiego rozwiązania pozostawię każdemu do samodzielnej oceny. Moment! - ktoś może powiedzieć - przecież jest bezpieczniejsze rozwiązanie!

    A i owszem!

    Podczas swojego startu, proces Winlogon sprawdza zawartość wspomnianego wyżej klucza i na jego podstawie podejmuje decyzję o automatycznym logowaniu. Jednak chwilę wcześniej Winlogon sięga do LSA po DefaultPassword i jeśli coś dostanie w odpowiedzi, to nie omieszka z tego skorzystać, zamiast wpisu w rejestrze. Prawdę powiedziawszy, Microsoft zaleca tę właśnie metodę jako sposób ochrony hasła DefaultPassword.

    LsaStorePrivateData - odsłona pierwsza

    Jednak co to znaczy, że Winlogon sięga do LSA po DefaultPassword? Otóż upraszczając, wywoływana jest funkcja LsaRetrievePrivateData z biblioteki advapi32.dll z próbą odczytania wartości Sekretu dla klucza 'DefaultPassword'.

    Sekretu? A cóż to takiego???

    Podsystem Lsass udostępnia mechanizm umożliwiający zapisywanie wrażliwych danych do 'zasobnika LSA' w postaci zaszyfrowanej oraz na żądanie dostęp do tych danych. I tak: żeby zapisać interesujące nas dane, wystarczy użyć funkcji LsaStorePrivateData, gdzie w postaci parametrów przekazujemy parę klucz - wartość, czyli np. 'DefaultPassword' i 'Pa$$w0rd', natomiast do wyłuskania zapisanych danych należy skorzystać ze wspomnianej wcześniej funkcji LsaRetrievePrivateData z parametrem określającym klucz.
    Microsoft podzielił dane, które możemy w ten sposób zapisywać na 4 grupy:

    • dane lokalne - nazwy z tej grupy rozpoczynają się prefiksem 'L$'. Dane lokalne można odczytać tylko na komputerze, na którym te dane są przechowywane. Dodatkowo wpadają tu dane, których nazwy zaczynają się od '$machine.acc', 'SAC', 'SAI', 'SANSC', żeby wymienić tylko kilka;
    • dane globalne - tu nazwy rozpoczynają się od prefiksu 'G$'. Dane globalne utworzone na kontrolerze domeny są replikowane na pozostałe kontrolery domeny;
    • dane komputera - ich nazwy rozpoczynają się od 'M$'. Zgodnie z dokumentacją dostęp do nich ma wyłącznie system operacyjny. Do tej grupy trafiają również nazwy zaczynające się od 'NL$', lub '_SC_';
    • dane prywatne - te nie wymagają żadnego przedrostka. Są dostępne z zewnętrznych komputerów, ale nie są replikowane w domenie.

    Warto zwrócić uwagę na dane komputera (machine data), które możemy zapisywać korzystając z LsaStorePrivateData, natomiast nie możemy odczytywać przy wykorzystaniu LsaRetrievePrivateData.

    Poniższe fragmenty kodu pokazują, jak korzystać z obu funkcji przy użyciu P/Invoke. Jest to właściwie przeniesienie przykładowego kodu do zmiany DefaultPassword do środowiska .NET. Można również skorzystać z przykładowego kodu do funkcji LsaRetrievePrivateData w ramach pinvoke.net.

    public static string RetrieveData(string secretName)
    {
      string secretValue = "";
      long retcode = 0;
      IntPtr zero = Marshal.AllocHGlobal(0);
      Win32.LSA_UNICODE_STRING systemName = new Win32.LSA_UNICODE_STRING();
      IntPtr policyHandle = IntPtr.Zero;
      Win32.LSA_OBJECT_ATTRIBUTES objectAttributes = new Win32.LSA_OBJECT_ATTRIBUTES();
      objectAttributes.Length = 0;
      objectAttributes.RootDirectory = IntPtr.Zero;
      objectAttributes.Attributes = 0;
      objectAttributes.SecurityDescriptor = IntPtr.Zero;
      objectAttributes.SecurityQualityOfService = IntPtr.Zero;
      retcode = Win32.LsaNtStatusToWinError(Win32.LsaOpenPolicy(ref systemName, ref objectAttributes, (int) Win32.LSA_AccessPolicy.POLICY_CREATE_SECRET, out policyHandle));
      if (retcode == 0)
      {
        IntPtr secretData;
        Win32.LSA_UNICODE_STRING[] lsa_unicode_stringArray = new Win32.LSA_UNICODE_STRING[] { new Win32.LSA_UNICODE_STRING() };
        lsa_unicode_stringArray[0].Buffer = Marshal.StringToHGlobalUni(secretName);
        lsa_unicode_stringArray[0].Length = (ushort)(secretName.Length * 2);
        lsa_unicode_stringArray[0].MaximumLength = (ushort)((secretName.Length + 1) * 2);
        Win32.LsaRetrievePrivateData(policyHandle, lsa_unicode_stringArray, out secretData);
        if (secretData != IntPtr.Zero)
        {
          Win32.LSA_UNICODE_STRING lsa_unicode_string2 = (Win32.LSA_UNICODE_STRING)Marshal.PtrToStructure(secretData, typeof(Win32.LSA_UNICODE_STRING));
          secretValue = Marshal.PtrToStringAuto(lsa_unicode_string2.Buffer);
        }
        Win32.LsaClose(policyHandle);
      }
      Win32.FreeSid(zero);
      return secretValue;
    }

    public static long StoreData(string secretName, string secretData)
    {
      long retcode = 0;
      IntPtr zero = Marshal.AllocHGlobal(0);
      Win32.LSA_UNICODE_STRING systemName = new Win32.LSA_UNICODE_STRING();
      IntPtr policyHandle = IntPtr.Zero;
      Win32.LSA_OBJECT_ATTRIBUTES objectAttributes = new Win32.LSA_OBJECT_ATTRIBUTES();
      objectAttributes.Length = 0;
      objectAttributes.RootDirectory = IntPtr.Zero;
      objectAttributes.Attributes = 0;
      objectAttributes.SecurityDescriptor = IntPtr.Zero;
      objectAttributes.SecurityQualityOfService = IntPtr.Zero;
      retcode = Win32.LsaNtStatusToWinError(Win32.LsaOpenPolicy(ref systemName, ref objectAttributes, (int) Win32.LSA_AccessPolicy.POLICY_CREATE_SECRET, out policyHandle));
      if (retcode == 0)
      {
        Win32.LSA_UNICODE_STRING[] lsa_unicode_stringArray = new Win32.LSA_UNICODE_STRING[] { new Win32.LSA_UNICODE_STRING() };
        lsa_unicode_stringArray[0].Buffer = Marshal.StringToHGlobalUni(secretName);
        lsa_unicode_stringArray[0].Length = (ushort)(secretName.Length * 2);
        lsa_unicode_stringArray[0].MaximumLength = (ushort)((secretName.Length + 1) * 2);
        Win32.LSA_UNICODE_STRING[] privateData = new Win32.LSA_UNICODE_STRING[] { new Win32.LSA_UNICODE_STRING() };
        privateData[0].Buffer = Marshal.StringToHGlobalUni(secretData);
        privateData[0].Length = (ushort)(secretData.Length * 2);
        privateData[0].MaximumLength = (ushort)((secretData.Length + 1) * 2);
        Win32.LsaStorePrivateData(policyHandle, lsa_unicode_stringArray, privateData);
        Win32.LsaClose(policyHandle);
      }
      Win32.FreeSid(zero);
      return retcode;
    }

    Do tego niezbędne importy na potrzeby P/Invoke

    internal static class Win32
    {
      [DllImport("advapi32")]
      public static extern IntPtr FreeSid(IntPtr pSid);
      [DllImport("advapi32.dll")]
      public static extern uint LsaClose(IntPtr ObjectHandle);
      [DllImport("advapi32.dll")]
      public static extern uint LsaNtStatusToWinError(uint status);
      [DllImport("advapi32.dll")]
      public static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, int DesiredAccess, out IntPtr PolicyHandle);
      [DllImport("advapi32.dll")]
      public static extern uint LsaRetrievePrivateData(IntPtr PolicyHandle, LSA_UNICODE_STRING[] KeyName, out IntPtr PrivateData);
      [DllImport("advapi32.dll", SetLastError = true)]
      public static extern uint LsaStorePrivateData(IntPtr PolicyHandle, LSA_UNICODE_STRING[] KeyName, LSA_UNICODE_STRING[] PrivateData);

      public enum LSA_AccessPolicy : long
      {
        POLICY_AUDIT_LOG_ADMIN = 0x200L,
        POLICY_CREATE_ACCOUNT = 0x10L,
        POLICY_CREATE_PRIVILEGE = 0x40L,
        POLICY_CREATE_SECRET = 0x20L,
        POLICY_GET_PRIVATE_INFORMATION = 4L,
        POLICY_LOOKUP_NAMES = 0x800L,
        POLICY_NOTIFICATION = 0x1000L,
        POLICY_SERVER_ADMIN = 0x400L,
        POLICY_SET_AUDIT_REQUIREMENTS = 0x100L,
        POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x80L,
        POLICY_TRUST_ADMIN = 8L,
        POLICY_VIEW_AUDIT_INFORMATION = 2L,
        POLICY_VIEW_LOCAL_INFORMATION = 1L
      }

      [StructLayout(LayoutKind.Sequential)]
      public struct LSA_OBJECT_ATTRIBUTES
      {
        public int Length;
        public IntPtr RootDirectory;
        public Win32.LSA_UNICODE_STRING ObjectName;
        public uint Attributes;
        public IntPtr SecurityDescriptor;
        public IntPtr SecurityQualityOfService;
      }

      [StructLayout(LayoutKind.Sequential)]
      public struct LSA_UNICODE_STRING
      {
        public ushort Length;
        public ushort MaximumLength;
        public IntPtr Buffer;
      }
    }

    I przykładowe użycie może wyglądać następująco:

    static void Main(string[] args)
    {
      StoreData("alamakota", "ala ma kota");
      string s = RetrieveData("alamakota");
      Console.WriteLine(s);
    }

    Uzbrojeni w maszynkę do odczytywania i zapisywania sekretów możemy już swobodnie ustawiać sobie DefaultPassword i usunąć odpowiedni element w kluczu Winlogon w rejestrze. Nareszcie bezpieczni!

    Lsa(Store | Retrieve)PrivateData internals

    Po pierwszej euforii nadchodzi chwila refleksji: 'A gdzie właściwie zapisywane są te dane?'. W dokumentacji do obu funkcji, Microsoft twierdzi, że:

    "The data stored by the LsaStorePrivateData function is not absolutely protected. However, the data is encrypted before being stored, and the key has a DACL that allows only the creator and administrators to read the data."

    Z jednej strony mamy zaufanie do systemu, że zadba o nasze dane, z drugiej - okazuje się, że wcale nie jest tak różowo. Otóż okazuje się, że dane przekazane przez LSASS zapisywane są w rejestrze, w gałęzi HKLM\Security\Policy\Secrets. Standardowo tylko system ma dostęp do tego klucza, więc jeśli chcecie się do niego dobrać, to musicie to zrobić w kontekście konta systemowego. W systemach przed Vistą najprościej skorzystać z polecenia systemowego at:

    >at 10:23 /interactive regedit.exe

    albo, już niezależnie od systemu i jeszcze prościej, z sysinternalsowego psexec:

    >psexec -i -s regedit.exe

    Przykładowa zawartość sekretu zapisanego wcześniej opisanymi metodami wygląda tak, jak to przedstawia Rysunek 1.

    Rysunek 1. Przykładowy sekret :)

    Przeglądając zawartość klucza HKLM\SECURITY\Policy\Secrets możemy znaleźć wiele podkluczy, których nazwy zaczynają się od L$, M$, ale także w formacie SCM:{GUID}, o których nie będę się rozpisywał.
    Naszą uwagę przykują jednak klucze o nazwach zaczynających się na _SC_.

    Sekrety usług

    Jeśli postanowimy, żeby któraś z usług uruchamiana była z konta innego niż systemowe, to zostaniemy poproszeni o wprowadzenie nazwy użytkownika i hasła, w ramach którego usługa ma startować. Jest to jednorazowa czynność i musimy tylko pamiętać o tym, że gdy zmienimy hasło dla tego użytkownika, to musimy ponownie odwiedzić zakładkę 'Logowanie', gdzie ponownie będziemy musieli wprowadzić nasze dane. Zapewne zastanawialiście się kiedyś, gdzie przechowywane są te informacje i czy można się do nich jakoś dobrać. No właśnie, ciekawe, nie? :)

    Weźmy dla przykładu usługę Telnet (jeśli jeszcze ją mamy w systemie, a w przypadku braku, możemy wybrać sobie inną ofiarę, której zepsucie nic nam nie zaszkodzi ;)) i ustawmy logowanie z dowolnego konta.

    Rysunek 2. Zakładka logowanie dla usługi Telnet.

    Zajrzyjmy teraz na chwilę do książki Marka Russinovicha i Davida Solomona o internalsach windowsów, a dokładniej do rozdziału poświęconemu uruchamianiu usług. Interesuje nas w szczególności fragment:

    "SCM [Service Control Manager] zapisuje dane o usługach uruchomionych na koncie innym niż systemowe poprzez wywoływanie funkcji Lsass LsaLogonUser. Funkcja LsaLogonUser standardowo wymaga hasła, ale SCM sygnalizuje dla Lsass, że hasło jest przechowywane 'sekretnie' w kluczu HKLM\SECURITY\Policy\Secrets w rejestrze. [..] Kiedy SCM wywołuje LsaLogonUser, ukreśla typ logowania jako logowanie usługi, tak by Lsass szukał hasła w podkluczu Secrets o nazwie _SC_<nazwa usługi>. SCM poleca Lsass, by ten przechowywał hasło logowania w sekrecie, podczas gdy SCP konfiguruje informacje logowania usługi." [2]

    Aha! Tu Cię mamy!

    Mr Jingle podpowiada mi w tym momencie, że czuje już zapach hasła.

    Po modyfikacji ustawień logowania dla usługi Telnet, w sekretach w rejestrze pojawił się dodatkowy podklucz, _SC_TlntSvr. To by potwierdzało słowa Wielkich Autorów, więc nie czekając długo na dalsze zachęty, łapię szybko za kod do wydłubywania sekretów i dopisuje w mainie krótką linijkę, dla testu:

    static void Main(string[] args)
    {
      string s = RetrieveData("_SC_TlntSvr");
      Console.WriteLine(s);
    }

    Kompiluję, uruchamiam i... nic :(. Po prostu pusta linia. No tak, przecież sekrety zaczynające się od _SC_ należą do kategorii systemowych, a te można tylko zapisywać, odczytywanie nie działa. Upewniam się jeszcze, czy sam mogę utworzyć sekret systemowy i później go odczytać:

    static void Main(string[] args)
    {
      StoreData("M$alamakota", "ala ma kota");
      string s = RetrieveData("M$alamakota");
      Console.WriteLine(s);
    }

    i oczywiście znowu puściutko. Dla pewności sprawdzam jeszcze wcześniejszy kod i przy zapisie do 'alamakota', odczyt działa prawidłowo. A więc tak, jest bezpiecznie!

    W tym momencie Mr Jingle trąca mnie nosem. 'Nie, to nie może być takie proste!' - odpowiadam. Sięgam jednak po otwarty regedit, eksportuję do pliku zawartość klucza HKLM\SECURITY\Policy\Secrets\_SC_TlntSvr\CurrVal, zmieniam '_SC_TlntSvr' na 'alamakota' i importuję. Potem uruchamiam wcześniej przygotowany kod i ... na konsoli wypisuje się hasełko, wklepane wcześniej sumiennie w oknie ustawień logowania usługi Telnet.

    Uwagi końcowe

    Podsystem LSA umożliwia zapisanie zaledwie 4096 sekretów, z czego połowa zarezerwowana jest dla systemu operacyjnego. Przechowywanie sekretów z udziałem LSA dostępne jest od Windows NT 4.0 i obecnie Microsoft nie zaleca korzystania z opisanych metod do zapisywania swoich sekretów. Począwszy od Windows 2000 dostępny jest interfejs ochrony danych DPAPI z metodami CryptProtectData i CryptUnprotectData, jednak w tym przypadku to programista musi zadbać o przechowywanie wrażliwych danych, co - jak widać - może być jednak lepszym rozwiązaniem. Trzeba również pamiętać, że większość z opisanych metod wymaga odpowiednich przywilejów, dostępnych praktycznie rzecz biorąc wyłącznie dla administratorów systemów.

    Z oczywistych względów nie udostępniam gotowego narzędzia, a jedynie opisuję technikę.
    [Edit: Postanowiłem jednak udostępnić prostą wersję programu, ktory wykonuje opisane w tekście czynności. Wszystko oczywiście wyłącznie do celów 'edukacyjnych'. W przyszłości zamierzam rozwijać to narzędzie o kolejne elementy, które postaram się sumiennie opisywać na blogu. Program wymaga zainstalowanego .NET Framework w wersji 2.0 oraz uruchamianie z konta systemowego.
    ]

    Od dawien dawna dostępne jest narzędzie, które wyłuskiwało sekrety systemowe korzystając z dll injection, jednak od pewnego czasu ta technika zastosowana do Lsass powoduje załamanie tego procesu i wymusza reset komputera.

    Sprawdziłem opisaną metodę na Windows 2000 Server, Windows XP oraz Windows Vista, wszędzie z tym samym rezultatem.

    Źródła (podaję polskie tłumaczenia, dostępne jeszcze na rynku - są tańsze od oryginałów i znośnie przetłumaczone):

    1. 'Bezpieczny kod. Tworzenie i zastosowanie' - Michael Howard i David LeBlanc. (Ech, czas kupić kolejne wydanie tej rewelacyjnej książki!)
    2. 'Microsoft Windows 2000 od środka' - David A. Solomon, Mark E. Russinovich. (Tu też już jest kolejne wydanie i naprawdę nie wiem, czemu go jeszcze nie mam w swojej biblioteczce!)

  • Bloggers Underground - 'bloggero ergo sum'!

    Tak, to już dziś! Spotkanie ostatnich sprawiedliwych w jaskini zła, a właściwie w knajpie wszelkiego dobra, czyli... spytajcie Karola gdzie :P.

    A kogóż tam licho zagna? Ano będą chłopcy stojący po czarnej stronie mocy (pijący na codzień mocną czarną cavę), będzie siedmiu wspaniałych z zina, będą też MS-ITProsi i czort wie, kto jeszcze! Sporo luda z grup offline, małżeństwo graczy w 'Go, yoshi, go!', więc miałoby tam zabraknąć mnie? Ależ skąd! No więc 'wepchłem' się na krzywy ryj i ja!

    Do zobaczenia na leżakach ;)

    PS. Tsiii... wezmę 'ukrytą kamerę', to zobaczycie jak wyglądają rycerze Jedi po kilku głębszych... prezentacjach ;)

  • .NET Matrix :)

    Zainspirowany małą pogaduchą z Pawłem opartą na cytatach z 'Matrixa', pomyślałem, że fajnie będzie rzucić parę z nich na blog - a nuż komuś jeszcze przyjdzie coś fajnego do łebka ;)

    Morpheus: .NET is a system, Neo. That system is our enemy. But when you're inside, you look around, what do you see? Businessmen, teachers, lawyers, carpenters. The very minds of the people we are trying to save. But until we do, these people are still a part of that system and that makes them our enemy. You have to understand, most of these people are not ready to be unplugged. And many of them are so inert, so hopelessly dependent on the system, that they will fight to protect it.

    Neo: Why do SQL CLR hurt?
    Morpheus: You've never used them before.

    Neo: What are you trying to tell me? That I can write .NET code?
    Morpheus: No, Neo. I'm trying to tell you that when you're ready, you won't have to.

    Morpheus: Unfortunately, no one can be told what the .NET is. You have to see it for yourself.

    Żeby nie psuć zabawy, zostawiam na razie tyle. Może ktoś dorzuci coś od siebie w komentarzach? ;)

    Cytaty na podstawie strony miłośników filmu.

  • Festiwal Nauki. Nie byłeś? Żałuj!

    Poniższe zdjęcie powinno być wystarczającą rekomendacją dla wszystkich tych, którzy do dziś uważali, że to strata czasu i pieniędzy.



    Asia zachwycona balonem na ciepłe powietrze, czyli workiem
    na śmieci, nadmuchanym suszarką do włosów i unoszącym się
    wiele metrów popod kopułę Wydziału Fizyki PW :)


    Festiwal odbył się w Warszawie między 19 a 28 września, na terenie wielu uczelni warszawskich, i nie tylko tam. Ja załapałem się z moim najstarszym dzieckiem na ostatni weekend festiwalu i zajrzeliśmy tylko na Wydział Fizyki PW.
    Z początku myślałem, że nic z tego nie będzie. Fizyka dla trzylatka? Sam od lat interesuję się naukami przyrodniczymi, ale trzylatek to prawdziwe wyzwanie!
    Uspokoiłem się jednak już po paru minutach. Toczenie koła, malowanie rąk, ‘lewitujący’ pojazd na torze eliptycznym, kula ‘z błyskawicami’, przelewanie wody z kubeczka do kuwety, latające piłeczki do ping-ponga - było wszystko to, co musiało być i znacznie więcej. Po dwóch godzinach przechodzenia po tunelach z lustrami, wycinaniu własnej lupy, graniu na flecie, etc. wróciliśmy do domu z zupełnie nowymi doświadczeniami. I nawet nie chodziło o to, żeby czegokolwiek się nauczyć, czy zapamiętać. Na to jeszcze będzie czas. Ciekawość świata, zabawa ‘naukowa’ z rówieśnikami, przyzwyczajenie się do istnienia ‘świątyń nauki’ - to było to, na czym przede wszystkim mi zależało. Pierwszy krok już został wykonany, czas robić następne.

    • Czy takie festiwale są potrzebne? Też pytanie!
    • Czy odwiedzimy w przyszłym roku kolejną edycję? Oczywiście!
    opublikowano 2 października 2008 19:41 przez mgrzeg | 2 komentarzy
    Filed under:
  • Kolejny z blogerów zine.net dołączył do grona MVP!

    Co należy zrobić, żeby zostać MVP? Należy blogować na zine.net.pl!!! ;)
    Żarty na bok, a teraz wielki szacun dla jednego z największych stachanowców grup offline – Marcina Celeja!!! Tak, Marcin po kilku latach pracy organicznej w grupie KGD oraz aktywnemu wsparciu dla grup offline w całej Polsce, nareszcie został doceniony przez korporację i dołączył do grona polskich MVP.

    GRATULUJĘ!!!

    Kim jest Marcin? To jeden z najsympatyczniejszych i najbardziej otwartych ludzi, z jakimi przyszło mi kiedykolwiek kontaktować się. Swoim rozbrajającym uśmiechem przypomina mi mojego najmłodszego synka, któremu niczego nie mogę odmówić :). Marcin potrafi jak rzadko kto zjednywać sobie ludzi, do tego jest entuzjastą, który zaraża swoją pasją wszystkich wokół. Gdy dowiedziałem się, że Marcin będzie reprezentował KGD na C2C, to byłem spokojny o sesję i jej odbiór. Marcin bawi się technologią, widać wyraźnie, że tworzenie sprawia Mu radość. I przekazuje tę radość innym.

    Marcin prowadzi(ł) swój blog na geekswithblogs. Jednak od pewnego czasu można czytać teksty Marcina również na Jego blogu na zine, gdzie zdradza tajnikich nowych technologii. Tam również możecie Go znaleźć - zawsze chętnie odpowiada na każdy komentarz.

    Czuję się wyróżniony, że tylu wybitnych ludzi zgodziło się współtworzyć zine.net. Dla mnie wszyscy jesteście MVP i wiem, że to kwestia czasu, kiedy stanie się to faktem!

    Marcinie, jeszcze raz gratuluję i życzę Ci dalszych, jeszcze większych sukcesów!



    Marcin podczas zanurzenia :)

    opublikowano 2 października 2008 18:35 przez mgrzeg | 3 komentarzy
    Filed under:
  • Zine.net.pl - krótka przerwa dzisiejszej nocy

    Kilka dni temu dostałem maila tej treści:

    ------------
    W związku z pracami konserwacyjnymi prowadzonymi przez jednego z naszych dostawców (Datacenter Energis) w nocy z soboty (2008-08-30) poczynając od godziny 23:30, na niedzielę do godziny 06:00 mogą wystąpić przerwy w działaniu serwerów HostedWindows.pl.

    Przepraszamy za ewentualne utrudnienia.
    ------------

    Jeśli zatem chcecie coś skomentować, wrzucić nowy post na blog - uwzględnijcie w swoich planach ten short break.

  • Mój harmonogram na MTS 2008

    MTS - call to action :)

    No i wybrałem. Chwila zastanowienia, konsultacje z chłopakami z grup oraz zina i padło na następujące sesje:

    Środa

    07:30 - 09:30 Rejestracja
    09:30 - 11:00 Sesja generalna (otwierająca)
    11:00 - 11:20 Przerwa kawowa
    11:20 - 12:35 Wydajne aplikacje ASP.NET w świecie Web 2.0 Tymoteusz Chmielewski
    12:35 - 13:35 Lunch
    13:35 - 14:50 The World of Expression Michael Köster
    14:50 - 15:10 Przerwa kawowa
    15:10 - 16:25 .NET bez wizardów – sposoby tworzenia i dynamicznego
    aktywowania komponentów w aplikacjach
    Bartosz Pampuch
    16:25 - 16:45 Przerwa kawowa
    16:45 - 18:00 Dynamics CRM 4.0 jako platforma dla aplikacji biznesowych Daniel Biesiada

    Czwartek

    07:30 - 09:00 Rejestracja
    09:00 - 10:15 Bezpieczeństwo serwisów WWW – praktyczne uwagi
    o implementacji zaleceń DBTI ABW w ASP.NET
    Zbigniew Łapiński
    10:15 - 10:35 Przerwa kawowa
    10:35 - 11:50 Zasadzki systemowe w Windows Server 2008 Grzegorz Tworek
    11:50 - 12:50 Lunch
    12:50 - 14:05 Programowanie .NET na platformie SQL Server Krzysztof Kozielczyk
    14:05 - 14:25 Przerwa kawowa
    14:25 - 15:40 Testy w Microsoft Visual Studio Team System 2008 Artur Jedynak
    15:40 - 16:00 Sesja generalna (zamykająca)

    Czemu tak? Część sesji i tak zobaczę w ramach spotkań grup offline, część mam nadzieję obejrzeć po konferencji (będą materiały??), więc nie ma sensu wybierać ich na mtsie. Grzesia Tworka chętnie poznam osobiście, a sesja o internalsach w2k8 już mnie rozgrzewa :)

    Jakoś wcześnie w tym roku rozchodzimy się do domków... Ale pewnie tylko po to, żeby uczesać kudełki i zabrać się na wieczorną imprezę :)

    Zatem - do zobaczenia!

    Ciekawe jak inni wybrali...

    opublikowano 19 sierpnia 2008 14:31 przez mgrzeg | 11 komentarzy
    Filed under:
  • Pokoloruj sobie skladnię w Visual Studio, czyli Babel Framework w akcji. Screencast.

    Biblijna wieża Babel w Visual Studio? No cóż, jeśli spojrzeć na Visual Studio jako kombajn, który dostarcza podstawową funkcjonalność dla wielu różnych języków, to czemu nie moglibyśmy wprowadzić się z naszym własnym i spróbować wykorzystać możliwości tegoż potwora do naszych celów?

    Kontynuując zatem temat usług językowych Visual Studio, napoczęty tekstami o MPLex i MPPG, tym razem skupimy się na tym, jak można wykorzystać oba te narzędzia do "pokolorowania sobie plików" opisanych naszym lekserem i parserem.

    Tym razem jednak, hołdując zasadzie, że jeden obraz znaczy więcej niż tysiąc słów, postanowiłem przygotować niespełna półgodzinny sceencast, w którym pokazałem krok po kroku jak przygotować projekt usługi językowej do pokolorowania składni przykładowego języka opisanego w poprzednich dwóch odcinkach. Sugeruję oglądanie w trybie HD z wyłączonym skalowaniem.

    Babel w akcji

    Z góry przepraszam za błędy, potknięcia, etc. - jest to mój pierwszy publiczny screencast :) Jeszcze nie wiem, dlaczego obciętych jest kilka pierwszych zdań, ale nie są one niezbędne do zrozumienia całości (nie ma tam trzęsienia ziemi w stylu Hitchcocka ;)). (Edit: Video jest już w całości). Czekam zatem na komentarze, bo zastanawiam się, czy ta forma prezentacji w moim wykonaniu ma sens. Jeśli tak - to pewnie jeszcze po nią sięgnę w przyszłości.

    Przy okazji chciałbym zachęcić do obejrzenia innych screencastów poświęconych Visual Studio Extensibility, dostępnych na MSDN.

  • MPPG - YACC dla Visual Studio

    Intro

    W poprzednim odcinku tej małej serii przedstawiłem w paru przykładach mplex - generator skanerów w C#, dedykowany do współpracy z Visual Studio. Dziś skupimy się na kolejnym z 'narzędzi językowych' dostępnych w SDK dla Visual Studio 2005, czyli MPPG.
    MPPG, czyli Managed Package Parser Generator, to odpowiednik unixowego YACC’a , czyli 'Yet Another Compiler Compiler' - kompilatora kompilatorów. Podstawą MPPG jest projekt GPPG, który jest wspierany przez Microsoft w ramach prac nad Ruby.NET (GPPG stanowi jego część). MPPG generuje kod w C# i współpracuje z MPLexem przy tworzeniu analizatorów składni.
    O tym, gdzie można znaleźć narzędzia językowe oraz jak przygotować sobie podstawowe środowisko pracy można przeczytać w poprzednim odcinku, więc przechodzimy od razu do przykładów.

    Przykłady

    Zacznijmy od skanera.

    A. ex4.lex. Wracamy do naszego przykładu z poprzedniego odcinka i nieco go modyfikujemy na potrzeby parsera.

    /* SEKCJA 1: DEFINICJE */
    %using System.Collections;
    %using Babel;
    %using Babel.Parser;
    %namespace Babel.Lexer
    %{
    const int LOOKUP = 0;
    int stan;
    Hashtable words = new Hashtable();
    internal void add_word(int s, string text) {
    if(!words.Contains(text))
     words.Add(text, s);
    }
    internal int lookup_word(string text) {
     if(words.Contains(text)) return (int) words[text];
     else return LOOKUP;
    }
    %}
    %%
     /* SEKCJA 2: REGULY */
    \n           { stan = LOOKUP;}
    \.          {stan = LOOKUP; return (int)Tokens.KROPKA;}
    ^rzeczownik   {stan = (int)Tokens.RZECZOWNIK;}
    ^czasownik    {stan = (int)Tokens.CZASOWNIK;}
    ^przymiotnik  {stan = (int)Tokens.PRZYMIOTNIK;}
    [a-zA-Z]+   {
      if(stan != LOOKUP) { add_word((int)stan, yytext);}
      else {
       switch(lookup_word(yytext)) {
        case (int)Tokens.RZECZOWNIK: return((int)Tokens.RZECZOWNIK);
        case (int)Tokens.CZASOWNIK: return((int)Tokens.CZASOWNIK);
        case (int)Tokens.PRZYMIOTNIK: return((int)Tokens.PRZYMIOTNIK);
        default: {return (int)Tokens.LEX_ERROR;}
       }
      }
     }
    %%
     /* SEKCJA 3: KOD UZYTKOWNIKA */

    Typ wyliczeniowy Locals z ex3.lex zastępujemy nieznanym nam jeszcze typem Tokens. Rezygnujemy z wypisywania informacji o zdefiniowanych częściach mowy, zamiast tego zwracamy liczbę określającą token powiązany z daną częścią mowy. Z sekcji kodu użytkownika, w której uprzednio mieliśmy zdefiniowany Main, nie pozostało już nic. W sekcji reguł z przyzwoitości zdefiniowaliśmy kropkę :). Reszta właściwie bez zmian.

    Czas na wprowadzenie parsera.

    1. ex41.y

    /* SEKCJA 1: DEFINICJE */
    %namespace Babel.Parser
    %partial
    %token RZECZOWNIK CZASOWNIK PRZYMIOTNIK
    %token KROPKA
    %token LEX_ERROR
    %token maxParseToken
    %%
     /* SEKCJA 2: REGULY */
    zdanie: RZECZOWNIK CZASOWNIK KROPKA {Console.WriteLine("Zdanie prawidlowe!");}
          ;
    %%
     /* SEKCJA 3: KOD UZYTKOWNIKA */
    public static void Main(string[] args) {
     Babel.Lexer.Scanner scnr = new Babel.Lexer.Scanner();
     Parser p = new Parser();
     p.scanner = scnr;
     //p.Trace = true;
     string line = Console.ReadLine();
     do {
      scnr.SetSource(line, 0);
      p.Parse();
     } while((line = Console.ReadLine()) != null);
    }

    O co w tym chodzi???

    Sprawa jest prostsza, niż się wydaje :). Po pierwsze, widać gołym okiem, że pliki mppg, podobnie jak pliki mplex, składają się z 3 części: Definicji, Reguł składniowych i Kodu użytkownika. I tak:

    • W sekcji Definicji, korzystając ze specjalnego symbolu %namespace, ustalamy przestrzeń, w jakiej znajdzie się klasa Parser wygenerowanego analizatora składniowego. Dzięki symbolowi %partial ustalamy, że klasa Parser będzie klasą częściową, dzięki czemu możemy podzielić definicję klasy pomiędzy pliki (my jednak z tego nie korzystamy). Kolejny symbol specjalny to %token, dzięki któremu definiujemy symbole, których oczekujemy od leksera. Tak zdefiniowane symbole trafią do typu wyliczeniowego Tokens, który widzieliśmy już w naszym pliku leksera.
    • W sekcji Reguł ustalamy reguły gramatyczne, dzięki którym określamy, czy dana konstrukcja jest prawidłowa składniowo, czy też nie. W naszym przypadku definiujemy regułę, która mówi, że zdanie jest prawidłowe, jeśli składa się z rzeczownika, czasownika i zakończone jest kropką. Dla przykładu, oczekujemy, że zdanie:
      Ala je.
      jest prawidłowe, o ile 'Ala' to rzeczownik, a 'je' to czasownik, natomiast spodziewamy się, że zdanie:
      Ala je obiad.
      jest nieprawidłowe, bez względu na definicję poszczególnych słów, ponieważ nasze zdanie pozwala tylko na konstrukcje składające się z dwóch słów, w ustalonej kolejności, po których jest kropka. A zatem komunikat 'Zdanie prawidłowe!' powinien pojawić się wyłącznie w przypadku zdań takich, jak to z pierwszego przykładu.
    • W sekcji kodu użytkownika dołączamy metodę Main, dzięki której możemy sprawdzić działanie naszego analizatora. Tworzymy obiekty klasy Scanner i Parser, wiążemy je ze sobą i przechodzimy w pętli do parsowania. Dla ciekawskich, którzy chcieliby zobaczyć jak odbywają się poszczególne redukcje oraz przesunięcia w toku działania parsera, wystarczy odkomentować linijkę 'p.Trace = true;'.

    Wszystko w porządku, ale jak to teraz skompilować?

    Jak już wcześniej pisałem, oba narzędzia - mplex oraz mppg wykorzystywane są przez usługi językowe Visual Studio, przez co wygenerowane klasy muszą implementować określone interfejsy oraz dziedziczyć po określonych klasach. W przypadku samego lexera wystarczyło dodać plik dummy.cs z odpowiednimi definicjami i sprawa była załatwiona. Tym razem jednak jest nieco trudniej. A zatem - krok po kroku:

    1. Lexer. Tak, jak poprzednio, uruchamiamy:

    >mplex ex4.lex

    2. Parser. W wierszu poleceń wykonujemy:

    >mppg ex41.y > ex41.cs

    mppg wyrzuca wygenerowaną zawartość na wyjście standardowe, więc musimy przekierować wyjście do pliku, żeby móc z tego później skorzystać.

    3. Z katalogu "%VS2K5SDK%\2007.02\VisualStudioIntegration\Common\Source\CSharp\" kopiujemy katalog Babel do katalogu, w którym znajdują się nasze pliki .lex i .y. Ponadto modyfikujemy nieco nasz plik dummy.cs:

    //dummy.cs
    using System;
    namespace Babel.Parser
    {
        public interface IColorScan
        {
            void SetSource(string source, int offset);
            int GetNext(ref int state, out int start, out int end);
        }
        public interface IErrorHandler
        {
            int ErrNum { get; }
            int WrnNum { get; }
            void AddError(string msg, int lin, int col, int len, int severity);
        }
    }

    4. Podczas kompilacji musimy dołączyć kilka referencji do bibliotek z katalogu "%VS2K5SDK%\2007.02\VisualStudioIntegration\Common\Assemblies\", a mianowicie:

    • Microsoft.VisualStudio.TextManager.Interop.dll;
    • Microsoft.VisualStudio.OLE.Interop.dll;
    • Microsoft.VisualStudio.Package.LanguageService.dll;
    • Microsoft.VisualStudio.Shell.dll;
    • Microsoft.VisualStudio.Shell.Interop.dll;
    • Microsoft.VisualStudio.Shell.Interop.8.0.dll;
    • Microsoft.VisualStudio.TextManager.Interop.8.0.dll.

    Mając tę wiedzę, możemy wreszcie skompilować nasz projekt (przy standardowej instalacji SDK tak to wygląda):

    >set VS2K5SDK=C:\Program Files\Visual Studio 2005 SDK
    >set REFDIR=%VS2K5SDK%\2007.02\VisualStudioIntegration\Common\Assemblies
    >csc /out:ex41.exe /r:"%REFDIR%\Microsoft.VisualStudio.TextManager.Interop.dll" /r:"%REFDIR%\Microsoft.VisualStudio.OLE.Interop.dll" /r:"%REFDIR%\Microsoft.VisualStudio.Package.LanguageService.dll" /r:"%REFDIR%\Microsoft.VisualStudio.Shell.dll" /r:"%REFDIR%\Microsoft.VisualStudio.Shell.Interop.dll" /r:"%REFDIR%\Microsoft.VisualStudio.Shell.Interop.8.0.dll" /r:"%REFDIR%\Microsoft.VisualStudio.TextManager.Interop.8.0.dll" dummy.cs babel\IScanner.cs babel\ShiftReduceParser.cs babel\State.cs babel\ParserStack.cs babel\Rule.cs ex41.cs ex4.cs

    UFF!!!

    Oczywiście, nie życzę nikomu takiej zabawy na dłuższą metę i sugeruję przygotowanie sobie czy to pliku .bat, czy też odpowiedniego pliku dla msbuild. W tym drugim przypadku można skorzystać z przygotowanych tasków MPLexCompile oraz MPPGCompile, do których jeszcze wrócimy przy omawianiu usług językowych.

    Po wykonaniu tych kroków mamy wreszcie nasz program, który wreszcie możemy uruchomić i potestować:

    >ex41.exe
    mgrzeg je.
    rzeczownik mgrzeg
    czasownik je
    mgrzeg je.
    Zdanie prawidłowe!

    Tak, "mgrzeg je." i jest to zgodne z naszą gramatyką :)

    Pobawmy się przez chwilę specjalnym symbolem 'error', dzięki któremu możemy powiedzieć, że coś jest nie do końca tak, jak powinno być. Zmodyfikujmy w tym celu sekcję reguł, pozostawiając resztę kodu niezmienną.

    2. ex42.y

    /* SEKCJA 2: REGULY */
    zdanie: RZECZOWNIK CZASOWNIK KROPKA {Console.WriteLine("Zdanie prawidlowe!");}
          | RZECZOWNIK CZASOWNIK error {Console.WriteLine("Brakuje kropki!");}
          | RZECZOWNIK error {Console.WriteLine("Brakuje czasownika!");}
          | error CZASOWNIK {Console.WriteLine("Brakuje rzeczownika!");}
          ;
    %%

    Teraz po uruchomieniu programu nasza sesja może wyglądać następująco:

    >ex42.exe
    ala je.
    rzeczownik ala
    czasownik je
    ala je.
    Zdanie prawidlowe!
    ala.
    Brakuje czasownika!
    je.
    Brakuje rzeczownika!
    ala je
    Brakuje kropki!

    A zatem potrafimy już wychodzić (a przynajmniej informować użytkownika o tym) z sytuacji błędnych, czas na nieco bardziej złożoną gramatykę. Zmodyfikujmy zatem nieco naszą definicję zdania, dodajmy części zdania. Zmieńmy zatem po raz kolejny sekcję Reguł, resztę kodu pozostawiając bez zmian.

    3. ex43.y

    zdanie: podmiot orzeczenie dopelnienie KROPKA {Console.WriteLine("Zdanie prawidlowe!");}
          | podmiot orzeczenie dopelnienie error {Console.WriteLine("Brakuje kropki");}
          | podmiot error {Console.WriteLine("Brakuje orzeczenia");}
          | error orzeczenie {Console.WriteLine("Brakuje podmiotu");}
          ;
         
    podmiot:    RZECZOWNIK
          ;
    orzeczenie: CZASOWNIK
          ;
         
    dopelnienie:  /* pusto! */
          |       PRZYMIOTNIK RZECZOWNIK
          |       PRZYMIOTNIK error {Console.WriteLine("Brakuje rzeczownika w dopelnieniu!");}
          |       RZECZOWNIK
          ;

    Teraz przykładowa sesja z programem może wyglądać następująco:

    >ex43.exe
    rzeczownik ala kota
    czasownik ma
    przymiotnik czarnego
    ala ma czarnego kota.
    Zdanie prawidlowe!
    ala ma kota.
    Zdanie prawidlowe!
    ala ma.
    Zdanie prawidlowe!
    ala kota.
    Brakuje orzeczenia
    ma czarnego kota.
    Brakuje podmiotu
    ala ma kota
    Brakuje kropki

    Teraz nasza definicja zdania zakłada, że składa się ono z podmiotu, orzeczenia, dopełnienia i zakończone jest kropką. Reszta definicji jest oczywista, może dodatkowego komentarza wymaga dopełnienie, które może być puste, lub składać się z przymiotnika i rzeczownika, lub samego rzeczownika. Wszystkie trzy przypadki zostały przez nas sprawdzone w przykładowej sesji.

    W ostatniej zabawie ze zdaniami możemy pokusić się o regułę rekurencyjną

    4. ex44.y

    zdanie        : zdanie_proste KROPKA {Console.WriteLine("Zdanie proste!");}
          |         zdanie_zlozone KROPKA {Console.WriteLine("Zdanie zlozone!");}
          ;
    zdanie_proste : podmiot orzeczenie dopelnienie
          | podmiot error {Console.WriteLine("Brakuje orzeczenia");}
          | error orzeczenie {Console.WriteLine("Brakuje podmiotu");}
          ;
    zdanie_zlozone :  zdanie_proste SPOJNIK zdanie_proste
          |           zdanie_proste error zdanie_proste {Console.WriteLine("Brak spojnika");}
          |           zdanie_zlozone SPOJNIK zdanie_proste
          ;
    ...reszta reguł,

    Zdefiniowanie dodatkowego tokena oraz pozostałą zabawę pozostawiam jako samodzielne ćwiczenie.

    Na sam koniec przykładów baaardzo prosty przykład kalkulatora (w końcu Gutek też pisał kalkulator :P), który potrafi tylko dodawać i odejmować liczby całkowite :D.

    B. ex6.lex

    /* SEKCJA 1: DEFINICJE */
    %using Babel;
    %using Babel.Parser;
    %namespace Babel.Lexer
    %%
     /* SEKCJA 2: REGULY */
    [0-9]+  { yylval.value = int.Parse(yytext); return (int)Tokens.NUMBER;}
    [ \t] ;
    \n|\r\n  return 0;
    .   return yytext[0];
    %%
     /* SEKCJA 3: KOD UZYTKOWNIKA */

    Do tego dorzucamy plik parsera.

    1. ex61.y

    /* SEKCJA 1: DEFINICJE */
    %namespace Babel.Parser
    %partial
    %union {
      public int value;
    }
    %start statement
    %token NAME NUMBER
    %token maxParseToken
    %%
     /* SEKCJA 2: REGULY */
    statement:    expression '.' {Console.WriteLine("={0}", $1.value);}
              ;
             
    expression:   expression '+' NUMBER {$$.value = $1.value + $3.value;}
              |   expression '-' NUMBER {$$.value = $1.value - $3.value;}
              |   NUMBER {$$.value = $1.value;}
              ;
             
    %%
     /* SEKCJA 3: KOD UZYTKOWNIKA */
    public static void Main(string[] args) {
     Babel.Lexer.Scanner scnr = new Babel.Lexer.Scanner();
     Parser p = new Parser();
     p.Initialize();
     p.scanner = scnr;
     //p.Trace = true;
     string line = Console.ReadLine();
     do {
      scnr.SetSource(line, 0);
      p.Parse();
     } while((line = Console.ReadLine()) != null);
    }

    Warto zwrócić uwagę na wykorzystanie symbolu specjalnego %union, który definiuje nam strukturę, obiekt której widoczny jest od strony scannera jako yylval, a od strony parsera możemy operować na nim wykorzystując symbole $$ oraz $i, gdzie i oznacza pozycję terminala w danej regule, dla przykładu:
    expression:   expression '+' NUMBER {$$.value = $1.value + $3.value;}
    $$.value - wartość pola value symbolu nieterminalnego $$;
    $1.value - wartość terminala expression z prawej strony reguły
    '+' - odpowiada $2,
    $3.value - wartość terminala NUMBER.

    Przykładowe działanie programu:

    >ex61.exe
    2+3+4-10+89.
    =88

    Ostatki

    Na koniec pare informacji o debuggowaniu. Jakkolwiek MPLex nie pomaga nam w tym za bardzo, to MPPG emituje do wygenerowanego pliku z klasą Parsera informację o numerach wierszy pliku źródłowego. Tym samym wystarczy podpiąć się z debuggerem gdziekolwiek w kodzie i możemy na bieżąco śledzić bieżące wartości pól skanera i parsera. Dla przykładu, żeby wskoczyć z debuggerem do pliku .y i sprawdzić jakie mamy bieżące wartości odpowiednich terminali w regule dodawania, wystarczy zapisać ją:

    expression:   expression '+' NUMBER {
    System.Diagnostics.Debugger.Break();
    $$.value = $1.value + $3.value;
    }

    Sugeruję umieszczanie kolejnych instrukcji w osobnych wierszach - debugger na podstawie wygenerowanego pliku ma tylko informację o numerze linii, nie wie nic o kolumnie :(.
    Oczywiście, należy też pamiętać o zmuszeniu kompilatora do wygenerowania informacji dla debuggera, czyli dodaniu opcji /debug+ w wywołaniu csc.

  • MPLEX - Lex dla Visual Studio

    Intro

    Dziś będzie trochę o zamierzchłych czasach, gdy dotnet jeszcze nie był nawet w planach, ludzie zamiast Tuwima czytali wiersze poleceń, a na świecie panowały narzędzia konsolowe. Czasy się zmieniły, niektóre ze starszych narzędzi wymarły, inne przybrały nową postać, ale część z nich pozostała i dobrze wpasowała się w nową sytuację. Jednym z takich narzędzi jest unixowy lex. Oryginalnie lex na podstawie reguł opisanych przez wyrażenia regularne tworzył kod leksera w języku C, który można było dalej wykorzystać przez narzędzia typu yacc do tworzenia bardziej skomplikowanych narzędzi - kompilatorów. O yaccu jeszcze przyjdzie coś powiedzieć, teraz jednak skupmy się na lexie. Oczywiście lekser w C jest raczej mało przydatny w środowisku .NET i najfajniej byłoby mieć narzędzie, które na podstawie dostarczonego przez nas pliku z regułami leksykalnymi wygeneruje kod w C#. Na szczęście Microsoft wspiera od czasu do czasu pewne projekty akademickie i dzięki temu w SDK do VS pojawił się projekt Managed Babel, oparty o projekty GPPG oraz GPLEX, czyli parsera i leksera na platformę .NET. Tak oto dotarliśmy do miejsca, w którym czas najwyższy przedstawić MPLex - Managed Package Lex. Do zabawy użyjemy mplex z SDK do VS2005 z lutego 2007.

    Gdzie jest mplex?

    MPLex wraz z innymi przydatnymi narzędziami z SDK pojawia się w naszym systemie po zainstalowaniu VS2005 (wersja Standard lub wyższa) wraz z SP1 oraz SDK 4.0 do VS. Znaleźć go możemy w katalogu "%VS2K5SDK%\ 2007.02\VisualStudioIntegration\Tools\Bin", gdzie %VS2K5SDK% na moim komputerze to "C:\Program Files\Visual Studio 2005 SDK". Dla wygody proponuję dorzucić w tym momencie ścieżkę to katalogu z mplexem do zmiennej systemowej PATH. W kilku następnych przykładach będziemy używali cmd, więc warto ułatwić sobie dostęp do tego narzędzia z dowolnego miejsca na dysku. Dodatkowo, w katalogu "%VS2K5SDK%\2007.02\VisualStudioIntegration\ExtraDocumentation" znajdują się 3 pdfy z dokumentacją do pakietu Babel oraz narzędzi MPLex i MPPG.

    Przy okazji - ja czasem potrzebuję mieć trochę więcej, niż tylko cmd i mplex w ścieżce, więc najwygodniej jest mi uruchomić cmd VS2k5 w danym katalogu wprost z explorera, w czym pomaga mi odpowiedni wpis w rejestrze (xp):
    ------vs2k5.reg-----
    Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\Folder\shell\VS2K5 cmd]

    [HKEY_CLASSES_ROOT\Folder\shell\VS2K5 cmd\command]
    @="cmd.exe /k \"C:\\Progra~1\\MICROS~4\\VC\\VCVARS~1.BAT x86\""
    ------vs2k5.reg-----
    gdzie "MICROS~4" to "Microsoft Visual Studio 8.0" (dir /x).
    Oczywiście podobnie można ustawić sobie cmd z explorera dla innych wersji VS.

    Przykłady

    Zacznijmy od najprostszych przykładów, bez większego wgłębiania się w składnię lexa.

    1. ex0.lex

      /* SEKCJA 1: DEFINICJE */
    %namespace LexScanner
    %%
      /* SEKCJA 2: REGULY */
    .|\n  ECHO();
    %%
      /* SEKCJA 3: KOD UZYTKOWNIKA */
     public static void Main(string [] args) {}

    Teraz przechodzimy do wiersza poleceń i w katalogu z plikiem ex0.lex wykonujemy:

    >mplex ex0.lex

    Po wykonaniu tego polecenia w katalogu zawierającym ex0.lex powinien pojawić się dodatkowo plik ex0.cs z wygenerowanym scannerem.

    Dorzućmy do tego plik dummy.cs (z dokumentacji do MPLex):

    //dummy.cs
    using System;
    namespace LexScanner
    {
      public class Tokens
      {
        public const int EOF = 0;
        public const int maxParseToken = int.MaxValue;
      }
      public abstract class ScanBase
      {
        protected int currentScOrd;
        public virtual int
        GetEolState() { return currentScOrd; }
        public virtual void
    SetEolState(int value) { currentScOrd = value; }
        public abstract void
    SetSource(string s, int o);
        public abstract int
        GetNext(ref int state, out int start, out int end);
        public abstract int yylex();
      }
      public interface IErrorHandler
      {
        int ErrNum { get; } int WrnNum { get; }
        void AddError(string msg,
    int lin, int col, int len, int severity);
      }
    }

    i będąc dalej w wierszu poleceń wykonajmy:

    >csc /out:ex0.exe ex0.cs dummy.cs

    Jeśli nie popełniliśmy po drodze żadnego błędu, to w tym momencie powinniśmy uzyskać program ex0.exe, który uruchomiony... nic nie robi :)
    Gratuluję, oto spędziłeś(aś) właśnie cały wieczór na przygotowaniu programu, który nic nie robi :). Ale nie przejmuj się. Zmodyfikujmy jednak nieco kod Main():

    2. ex1.lex

      /* SEKCJA 1: DEFINICJE */
    %namespace LexScanner
    %%
      /* SEKCJA 2: REGULY */
    .|\n  ECHO();
    %%
      /* SEKCJA 3: KOD UZYTKOWNIKA */
    public static void Main(string [] args) {
        Scanner scnr = new Scanner();
        string line = Console.ReadLine();
        int tok;
        do {
          scnr.SetSource(line, 0);
          tok = scnr.yylex();
          Console.WriteLine();
        } while((line = Console.ReadLine()) != null);
     }

    i teraz nasz wynikowy program powinien wypluwać na konsolę to, co wprowadzimy z klawiatury.

    3. ex2.lex.

    Tym razem klasyczny już przykład licznika słów, wierszy i znaków w pliku:
     /* SEKCJA 1: DEFINICJE */
    %namespace LexScanner
    %{
    static int lineTot = 0;
    static int wordTot = 0;
    static int charTot = 0;
    %}
    word [^ \t\r\n]+
    eol [\n]
    %%
     /* SEKCJA 2: REGULY */
    %{
    int lines = 0;
    int words = 0;
    int chars = 0;
    %}
    {word}  {words++; chars += yyleng;}
    {eol} {lines++; chars += yyleng;}
    . {chars++;}
    <<EOF>> {
    Console.Write("wierszy: " + lines); lineTot += lines;
    Console.Write(", slow: " + words); wordTot += words;
    Console.WriteLine(", znakow: " + chars); charTot += chars;
    }
    %%
     /* SEKCJA 3: KOD UZYTKOWNIKA */
    public static void Main(string[] argp)
    {
      for (int i = 0; i < argp.Length; i++)
      {
        string name = argp[i];
        try
        {
          int tok;
          FileStream file = new FileStream(name, FileMode.Open);
          Scanner scnr = new Scanner(file);
          Console.WriteLine("Plik: " + name);
          do
          {
            tok = scnr.yylex();
          } while (tok > Tokens.EOF);
        }
        catch (IOException ex)
        {
          Console.WriteLine(ex.Message);
        }
      }
      if (argp.Length > 1)
      {
        Console.Write("Wierszy w sumie: " + lineTot);
        Console.Write(", Slow: " + wordTot);
        Console.WriteLine(", Znakow: " + charTot);
      }
    }

    Po wygenerowaniu skanera oraz skompilowaniu programu i jego uruchomieniu otrzymujemy dla przykładu:

    >ex2.exe ex0.lex ex1.lex ex2.lex
    Plik: ex0.lex
    wierszy: 8, slow: 29, znakow: 181
    Plik: ex1.lex
    wierszy: 16, slow: 55, znakow: 417
    Plik: ex2.lex
    wierszy: 57, slow: 174, znakow: 1207
    Wierszy w sumie: 81, Slow: 258, Znakow: 1805

    Po tym przykładzie czas na chwilę refleksji.

    Plik mplex-a składa się z trzech sekcji:

    1. Definicji - tu, jak sama nazwa wskazuje :), jest miejsce na definicje, które później można wykorzystać w pozostałych sekcjach. Tu ustalamy przestrzeń, w jakiej ma znaleźć się klasa skanera (%namespace LexScanner), możemy też dodawać odpowiednie usingi (%using System.Text;);
    2. Reguł - w tym miejscu umieszczamy wzorce oraz kod C#, który jest przetwarzany w momencie wystąpienia danego wzorca;
    3. Kodu użytkownika - tu jest najlepsze miejsce na zdefiniowanie niektórych metod pomocniczych oraz głównej metody: Main.

    W sekcjach definicji oraz reguł możemy dodawać dodatkowy kod C#, który będzie umieszczony w klasie skanera w pliku wynikowym. Kod taki rozpoczynamy sekwencją "%{", a zamykamy "%}". I tak - jeśli dodatkowy kod umieścimy w sekcji Definicji, to pojawi się on w definicji klasy Scanner, natomiast jeśli umieścimy nasz kod w sekcji Reguł, to zostanie on wstawiony do metody Scan, czyli głównej metody skanera, wołanej przez metodę yylex().
    W pliku dummy.cs zdefiniowana jest klasa abstrakcyjna ScanBase, która implementowana jest przez klasę Scanner, wygenerowaną przez MPLex w wyniku przetwarzania naszego pliku .lex. Dodatkowo pojawia się również definicja interfejsu IErrorHandler, o którym powiemy więcej przy omawianiu MPPG.
    Zauważmy, że w definicjach wzorców użyte są wyrażenia regularne, które wydatnie upraszczają całą zabawę. Warto zwrócić także uwagę na specjalny symbol <<EOF>>, który określa koniec pliku (w oryginalnym lex do obsługi końca pliku wykorzystywana była funkcja yywrap).

    Na koniec stosunkowo prosty przykład, który wykorzystamy w dalszych zabawach z VS.

    4. ex3.lex

     /* SEKCJA 1: DEFINICJE */
    %using System.Collections;
    %namespace LexScanner
    %{
    public enum Locals {
    LOOKUP = 0,
    RZECZOWNIK,
    CZASOWNIK,
    PRZYMIOTNIK
    };
    Locals stan;
    Hashtable words = new Hashtable();
    internal void add_word(Locals s, string text) {
    if(!words.Contains(text))
     words.Add(text, s);
    }
    internal Locals lookup_word(string text) {
     if(words.Contains(text)) return (Locals) words[text];
     else return Locals.LOOKUP;
    }
    %}
    %%
     /* SEKCJA 2: REGULY */
    \n            {stan = Locals.LOOKUP;}
    ^rzeczownik   {stan = Locals.RZECZOWNIK;}
    ^czasownik    {stan = Locals.CZASOWNIK;}
    ^przymiotnik  {stan = Locals.PRZYMIOTNIK;}
    [a-zA-Z]+   {
      if(stan != Locals.LOOKUP) add_word(stan, yytext);
      else {
       switch(lookup_word(yytext)) {
        case Locals.RZECZOWNIK: Console.WriteLine("{0}: RZECZOWNIK!", yytext);
        break;
        case Locals.CZASOWNIK: Console.WriteLine("{0}: CZASOWNIK!", yytext);
        break;
        case Locals.PRZYMIOTNIK: Console.WriteLine("{0}: PRZYMIOTNIK!", yytext);
        break;
        default: Console.WriteLine("{0}: zdefiniuj, bo nie znam :(", yytext);
        break;
       }
      }
      }
    %%
     /* SEKCJA 3: KOD UZYTKOWNIKA */
      public static void Main(string[] args) {
        Scanner scnr = new Scanner();
        string line = Console.ReadLine();
        int tok;
        do {
          scnr.SetSource(line, 0);
          tok = scnr.yylex();
        } while((line = Console.ReadLine()) != null);
      }

    Przykładowe wykonanie programu:

    >ex3.exe
    ala ma ładnego kota
    ala: zdefiniuj, bo nie znam :(
    ma: zdefiniuj, bo nie znam :(
    adnego: zdefiniuj, bo nie znam :(
    kota: zdefiniuj, bo nie znam :(
    rzeczownik ala kota
    czasownik ma
    przymiotnik ładnego
    ala ma ładnego kota
    ala: RZECZOWNIK!
    ma: CZASOWNIK!
    adnego: PRZYMIOTNIK!
    kota: RZECZOWNIK!

    W sekcji Definicji tworzymy mały słownik, w którym będziemy przechowywali interesujące nas części mowy. Przy okazji definiujemy typ wyliczeniowy Locals, którym będziemy operować przy określaniu stanu w jakim aktualnie się znajdujemy

    W sekcji Reguł ustalamy:

    1. przy przejściu do nowego wiersza przechodzimy do stanu wyszukiwania słowa w słowniku;
    2. po pojawieniu się na początku wiersza słowa rzeczownik, czasownik, lub przymiotnik przechodzimy do odpowiadającego mu stanu;
    3. w pozostałych przypadkach, zależnie od stanu w jakim aktualnie się znajdujemy, albo dodajemy nowe słowo do słownika, albo wyszukujemy w słowniku pojawiające się słowo.

    Kod użytkownika jest bardzo podobny do tego z przykładu ex1.lex, usunięte zostało tylko niepotrzebne przechodzenie do nowego wiersza.

    Trzy słowa na zakończenie

    Jak widać, zabawa z MPLexem jest całkiem prosta, a efekty mogą być naprawdę zaskakujące. Tworzenie własnego programu do analizy leksykalnej z wykorzystaniem MPLex jest zdecydowanie prostsze niż pisanie go ręcznie, a wszystko to jest dostępne w ukochanym C# ;).
    Oczywiście, MPLex nie jest jedyną implementacją unixowego lexa generującego kod w C#, jednak jest to jedyne narzędzie wspierane przez Microsoft i dedykowane do zabawy z usługami językowymi Visual Studio. Dlatego nie ma sensu omawianie innych narzędzi tej klasy :(, chętnych zapraszam do google.com :).
    I już na samo zakończenie słowo wyjaśnienia, dlaczego skupiam się na MPLex z SDK do VS2k5, a nie do VS2k8. Otóż niestety wersja 0.60 GPLEX, która była podstawą do MPLEX z SDK do VS2k8 wprowadza błędy, które nie pozwalają nawet na przejście powyższych przykładów :(. Podobnie ma się sprawa z wersją 0.61 i dopiero w wersji 0.62 zostało to usunięte. Jak przypuszczam, SDK v.2 do VS2k8 wykorzysta najnowszą wersję GPLEX, w którym m.in. klasa Scanner może być już partial, co oczywiście pozwala na łatwiejsze zarządzanie kodem; dodatkowo nie są już wymagane dodatkowe definicje klasy bazowej skanera, jest też wiele innych przyjemnych nowości, ale szkoda czasu na dalsze dywagacje na temat narzędzia, z którego na razie nie ma jak sensownie skorzystać :(

Więcej wypowiedzi Następna strona »
W oparciu o Community Server (Personal Edition), Telligent Systems