• This is a usual time of the year for summaries so let’s keep the tradition alive and write one. Here’s my 2017 achievements split between months. January Blogging for 18 days straight – nothing near gutek’s achievement (whole year!) but still a nice streak Microsoft MVP title (thx Konrad Kokosa for pointing this one ;)) February […]

  • “Advent of Code is a series of small programming puzzles for a variety of skill levels.” Each day was a fun an interesting coding challenge. I’ve decided to practice and code this in python to learn the skill. Some of them might not be the best python scripting as I was short on time in […]

  • Some time ago I’ve attended a .net developer days 2017 conference. I was quite busy since (traveling, teaching .net, working) that only now I got some time to share some thoughts about it. As a bonus I’m including a short interview I did during the event. I need to state here, just to be clear, […]

  • We developers don’t like documentation. We don’t like to read it, and we even more we don’t like to write it. But sometimes it is worth to read it. Like when you find out that by using datetime in SQL DB you got a milliseconds precision but only if it ends on 0,3,7 (link). That […]

  • In the previous post we’ve removed some of the technical debt that could be found in our NetDeveloperPoland Website application. In this one we will remove it even more. We can even maybe reach a B? Let’s see where we’ll end up at the end of this part.

Postanowiłem napisać kilka postów, które odkrywają “nieznane” funkcje Visual Studio. Dziś jeden z nich.
Czasem widzę, że największą furorę robią posty opisujące rzeczy (dla mnie) oczywiste. Okazuje się, jednak, że to co jest znane mnie, niekoniecznie jest wiedzą dość dobrze rozpowszechnioną. Zaczynamy zatem.

Analiza dumpów

Jeszcze do wersji 2008 ta funkcjonalność zarezerwowana była tylko dla świetnego narzędzia jakimi jest WinDbg. Jednakże od wersji 2010 VisualStudio potrafi odczytać i przeanalizować pliki *.dmp czyli zrzuty pamięci.
dump
Za pomocą polecenia Open możemy wczytać taki plik i wyświetlić jego zawartość. Co więcej VisualStudio w ładny sposób potrafi pokazać nam, gdzie nasz program znajdował się w momencie wykonania zrzutu pamięci. Wystarczy, po załadowaniu pliku dumpa, wybrać opcję ‘Debug with managed’ (bądź native – w zależności od potrzeb) a następnie skorzystać z opcji Parallel Stacks. Dzięki temu naszym oczom ukaże się ładny stan aplikacji w momencie zrzutu.
stacks
Teraz już bez problemu będziemy mogli znaleźć wątki naszej aplikacji i zobaczyć, że tak na prawdę dwa z nich próbują wstawiać elementy do tego samego słownika powodując wyjątek.
threads

Wcześniej, aby osiągnąć te rezultaty trzeba było analizować zrzut pamięci w WinDbg, co dla niektórych stanowiło barierę przed tego typu analizami. Co prawda WinDbg wspiera nas poleceniami typu !analyze –v –hang ale mimo to niewiele osób to robiło w jakikolwiek sposób.
analyze
Dodatkowo po załadowaniu SOS (.loadby sos clr) a potem po !DumpStack –short możemy zauważyć, że wątki 32 oraz 34 siedzą w tej samej metodzie a mianowicie Dictionary.Insert wywołanej w tym samym miejscu.
dict_insert
No więc mamy to samo co udostępnia teraz VisualStudio. Miłego analizowania zrzutów.

Jakiś czas temu Tomek Wiśniewski na swoim blogu opisał (Debugger Canvas – nowe spojrzenie na ”odrobaczanie”) ciekawy dodatek do Visual Studio a mianowicie Debugger Canvas.
Ponieważ lubię wszelkie rzeczy związane z “ odrobaczaniem” postanowiłem się trochę przyjrzeć mu bliżej. W tym wpisie chciałbym pokazać trochę dodatkowych “smaczków”, które są udostępnione za pomocą Debugger Canvas.

dc
Tak więc co dodatkowego posiada Debugger Canvas prócz tego co opisał Tomek? Na każdym z “bąbli” (wolę to określenie od chmurek) w prawym górnym rogu widoczna jest mała niebieska ikonka (to samo można uzyskać za pomocą opcji Locals Bubble). Po jej przyciśnięciu mamy dostęp do locals’ów czyli zmiennych widocznych w bieżącej ramce.
locals
Ale to nie wszystko, klikając ikonę aparatu możemy zrobić “snapshot” bieżących wartości i w kolejnym przebiegu programu możemy je porównywać z aktualnymi.
locals_cmp
Dzięki temu w prosty sposób możemy zaobserwować jakie zmiany zachodzą w programie i dlaczego w poprzednim przebiegu nasz program zachował się inaczej niż obecnie.
W domyślnym (i jedynym :)) swoim widoku, Debugger Canvas, wyświetla ciało jednej metody bądź właściwości. Z jednej strony ogranicza nasze pole widzenia tylko do tego co aktualnie istotne (wykonywany kod) z drugiej strony czasem pojawia się potrzeba zobaczenia czegoś więcej. Możemy wtedy skorzystać z małej czarnej strzałki w nagłówku każdego z bąbli.
other_stuff
Z listy, która się rozwinie możemy otworzyć w dodatkowych “bąblach” pozostałe fragmenty kodu dostępne na danym obiekcie.
Możemy również podkreślić całą ścieżkę wykonania programu klikając na strzałki pomiędzy bąblami. Ścieżka wykonania będzie oznaczona się wtedy na czerwono.
path
Dodatkowo pod prawym przyciskiem myszy mamy możliwość utworzenia notatki, czy też wyszukania fragmentu kodu.
stickynoteFindCode
W opcjach jak na razie jest niewiele ustawień. Mamy możliwość włączenia edycji kodu w “bąblach”, ale w chwili obecnej jest to opcja eksperymentalna i może spowodować, iż nasze Visual Studio przestanie działać.
options
Na koniec cały efekt naszej pracy, o czym wspominał Tomek, możemy zapisać do XPS’a i podesłać koledze aby zapoznał się z naszymi obserwacjami.
Podsumowując – Debug Canvas to interesujący dodatek. Przedstawienie kodu w postaci grafu przejścia może pomóc w łatwiejszym zrozumieniu co dzieje się z naszym programem i szybszym wyśledzeniu błędu.

Poniżej linki do moich sesji na portalu VirtualStudy z cyklu kurs C#. Przeznaczone główne dla osób początkujących, choć z sesji o reflection i atrybutach być może i ktoś z większą wiedzą także skorzysta :).
16. Czym są wyrażenia regularneprezentacja, przykłady.
18. Odczyt danych z i do plikuprezentacja, przykłady.
19. Poznajmy atrybuty i mechanizm refleksjiprezentacja, przykłady
20. Dostęp do danych to podstawowa funkcjonalność programów, zobaczmy, jak to się robiprezentacja, przykłady
24. Wzorce projektoweprezentacja, przykłady
Oczywiście dla zainteresowanych polecam pozostałe filmy z kursu C# jak też inne sesje z portalu VirtualStudy.pl

Ostatnio, przez ponad pół roku, wraz z Wojtkiem Poniatowskim, Mirkiem Pragłowskim, Tomkiem Wiśniewskim prowadziliśmy kurs C# na portalu VirtualStudy. Kurs przeznaczony był dla osób początkujących ale dzięki temu miałem możliwość zobaczenia jakie elementy .NET i C# sprawiają najwięcej trudności osobom, które dopiero zaczynają swoją przygodę z tą technologią. Jeśli masz choć trochę doświadczenia z .NET to zapewne nie znajdziesz tu zbyt wielu przydatnych informacji. Tak czy inaczej zapraszam do czytania:

jawny typ vs. var vs dynamic

Po ilości pytań odnośnie tego zagadnienia wnioskuję, że jest to numer jeden “problemów” z jakim muszą sobie poradzić osoby dopiero zaczynające w .NET. Podawać typ? Użyć var? A tak w ogóle czym to się różni od tego dynamic? Zacznijmy zatem od początku. Dawno dawno temu w czasach .NET 2.0 i wcześniejszych aby zadeklarować i utworzyć zmienną należało posłużyć się następującą konstrukcją

Importer importer = new Importer();

Czyli nazwa typu, potem nazwa zmiennej i przypisanie wartości. Widać, że jest to dość spora duplikacja. Skoro po prawej stronie po słowie kluczowym new mamy już nazwę typu po co podawać ją jeszcze z lewej strony? Wychodząc z tego założenia twórcy .NET w jego wersji 3.5 dołożyli słowo kluczowe var. Dzięki temu od frameworka w tej wersji możemy napisać

var importer = new Importer();

i ma to takie samo znaczenie jak powyższy zapis. Słowo kluczowe var mówi kompilatorowi – jestem zbyt leniwy, aby podać jawnie typ, ale ty drogi kompilatorze domyśl się jaki powinien on być i wpisz go za mnie zanim przeprowadzisz kompilację. Dzięki temu nasz program jest nadal silnie typowany.
Niestety dzięki temu można tworzyć kod, który czyta się dość ciężko a mianowicie taki:

var user = GetLoggedUser();

Czemu jest on taki zły? Dlatego, że bez sprawdzenia w nagłówku metody GetLoggerUser, lub bez najechania kursorem myszy na słówko var nie zobaczymy, jakiego typu jest user. Skoro var ma takie minusy czemu został w ogóle wprowadzony do języka? Bez niego ciężko byłoby działać z użyciem LINQu. W prostych przypadkach moglibyśmy typ podawać jawnie, ale pewnie niewielu osobom będzie się chciało domyślać się jaki będzie typ wyrażenia

Enumerable.Range(0, 1000).Where(i => i%5 == 0 || i%3 == 0).GroupBy(x => x%3)

Dzięki var, możemy to wyrażenie przypisać do zmiennej, którą zadeklarujemy z tym słowem kluczowym i kompilator sam domyśli się typu. My nie tracimy czasu na określanie typu (który byłby dość długi) a nadal mamy pełne silne typowanie.
Drugim przypadkiem, także w LINQu, są typy anonimowe. Bez var, nie moglibyśmy napisać

var anonimowy = new {Title = “Anonimowi”, Age = 99};

Skoro wiemy już jaka jest różnica pomiędzy jawnym podaniem typu a var to czymże jest dynamic? To słowo kluczowe jest nowością w .NET 4 i oznacza, że nie wiemy jaki jest typ zmiennej. Nie wiemy jakie udostępnia ona metody i właściwości. Dopiero w runtime może się okazać czy dana metoda istnieje na tym obiekcie czy nie. Dzięki dynamic możemy wywołać dowolną metodę i kompilator zbuduje nasz program bez przeszkód. Dopiero jednak w runtime okaże się czy dana operacja powiedzie się i czy podana metoda istnieje na obiekcie. Zobaczmy na przykładzie, czym różni się dynamic od var i jawnego typu.
Zdefiniujmy sobie 3 zmienne

string zmienna = “jawny typ”;
var zmiennaVar = “var”;
dynamic zmiennaDynamic = “dynamic”;

Zobaczmy na co pozwoli nam kompilator. W przypadku zmienna oraz zmiennaVar mamy dostęp tylko i wyłącznie do metod zdefiniowanych na typie string (+ wszystkie extension metody). Jeśli chodzi o zmiennaDynamic

zmiennaDynamic.EndsWith(“dynamic”);
zmiennaDynamic.JakasFajnaMetodaNieistniejąca();

Możemy podać cokolwiek. To czy zadziała dowiemy się dopiero podczas uruchomienia. Po cóż takiego? Pomaga to w przypadku używania np. obiektów zdefiniowanych za pomocą komponentów COM.

using vs using(IDisposable)

Kolejna rzecz, która na początku wprawia w lekkie zakłopotanie szczególnie, gdy ktoś mówi, iż dane wywołanie należy umieścić w klauzuli using. Od razu pojawiają się pytania, zaraz zaraz czy using nie stosujemy aby dodać przestrzeń nazw do naszej aplikacji? Używamy, jednak using ma jeszcze drugie użycie, które wiąże się z typem IDisposable. Pomińmy zatem using w kontekście dodawania przestrzeni nazw.

using (FileStream stream = new FileStream(“plik.txt”,FileMode.Open))
{
}

Taka konstrukcja jest równoważna następującej.

FileStream stream = null;
try
{
    stream = new FileStream(“plik.txt”, FileMode.Open);
    //…
}
finally
{
    if (stream != null)
        stream.Close();
}

Widzimy, że w rozwiniętej formie przypisanie do zmiennej następuje w bloku try, a zamknięcie otwartego pliku w bloku finally. natomiast pierwsza forma jest dużo wygodniejsza i bardziej czytelna. Czy musimy tak robić z typami IDisposable? Prosta odpowiedź – nie. Bardziej złożona – nie, ale…ale musimy pamiętać, że możemy mieć wycieki pamięci. Ale jak to? Przecież w .NET jest GarbageCollector, który zwalnia za nas pamięć. Mając w pamięci to zdanie przechodzimy do ostatniego tematu.

Wycieki pamięci w .NET

Tak, tak. Mimo, że w .NET mamy GC, który dba o zwalnianie pamięci to wycieki są możliwe. Jak? GC dba tylko o zwalnianie zasobów zarządzanych (managed, choć też nie zawsze – o tym może kiedy indziej). Z tymi unmanaged sobie nie poradzi, gdyż nie będzie wiedział kiedy mogą być one zwolnione. Jakie to są zasoby unmanaged? Pliki, połączenia do bazy danych, pędzle (Brush) itp.. I po to jest zaimplementowany interface IDisposable, abyśmy mogli w przystępny sposób, za pomocą wywołania Dispose powiedzieć GC kiedy już danego zasobu nie potrzebujemy i kiedy go można zwolnić.
Trzeba uważać na ten interface, gdyż wiele początkujących osób łapie się na tym, że gdzieś kiedyś usłyszało, że w .NET wycieków pamięci być nie może i tkwią w takim przeświadczeniu dość długo.
Tyle jeśli chodzi o zauważone przeze mnie pytania od osób, które (z reguły) zaczynają przygodę z .NET. A jakie były wasze spostrzeżenia gdy zaczynaliście przygodę z .NET?
Jakby ktoś chciał obejrzeć filmy z sesji z kursu C# to są one dostępne na stronie Spis publikacji.

Pod ostatnim wpisem “Jak udostępnić folder w .NET” pojawił się komentarz użytkownika zine, który wspomniał, iż folder można udostępnić także przez NetShare API.
NetShare API definiuje kilka metod dzięki którym możemy operować na udziałach. Są to: NetShareAdd, NetShareDel, NetShareEnum. W powyższym linku są jeszcze opisane inne metody z API, ale w tym wpisie skupimy się na tych

[DllImport(“Netapi32.dll”)]
private static extern uint NetShareAdd(
    [MarshalAs(UnmanagedType.LPWStr)] string strServer,
    Int32 dwLevel,
    ref SHARE_INFO_502 buf,
    out uint parm_err
);
[DllImport(“netapi32.dll”, SetLastError = true)]
static extern uint NetShareDel(
            [MarshalAs(UnmanagedType.LPWStr)] string strServer,
            [MarshalAs(UnmanagedType.LPWStr)] string strNetName,
            Int32 reserved //must be 0
);
[DllImport(“Netapi32.dll”, CharSet = CharSet.Unicode)]
 private static extern int NetShareEnum(
    [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
      int level,           
      ref IntPtr shares,
      uint prefmaxlen,
      ref int entriesread,
      ref int totalentries,
      ref int resume_handle
);

Mamy tu 3 podstawowe funkcje. Dodanie udziału, jego usunięcie oraz wylistowanie dostępnych. Zacznijmy od metody NetShareDel. Bierze ona jako pierwszy parametr nazwę serwera na którym chcemy usunąć udział oraz nazwę tego udziału. Trzeci parametr nie jest używany i zawsze wynosi 0. Proste. Wywołanie?

uint result = NetShareDel(“”, “dokumenty”, 0);

Banalnie proste. Jako serwer w tym wypadku podajemy pusty string. Chcemy skasować udział lokalny + nazwa udziału i 0. Udział skasowany.
Na drugi ogień idzie metoda Add. Tu już mamy trochę więcej do zdefiniowania. Pojawia się struktura SHARE_INFO_502. Tak na prawdę metoda bierze parametr LPBYTE buf, który może wskazywać na jedną z trzech struktur. Dodatkowo mogą to być SHARE_INFO_2 lub SHARE_INFO_503. Parametr dwLevel określa z jaką strukturą mamy do czynienia. Dla naszych celów użyjemy 502. Jak ona wygląda?

[StructLayout(LayoutKind.Sequential)]
private struct SHARE_INFO_502
{
    [MarshalAs(UnmanagedType.LPWStr)]
    public string shi502_netname;
    public SHARE_TYPE shi502_type;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string shi502_remark;
    public Int32 shi502_permissions;
    public Int32 shi502_max_uses;
    public Int32 shi502_current_uses;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string shi502_path;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string shi502_passwd;
    public Int32 shi502_reserved;
    public IntPtr shi502_security_descriptor;
}

Standardowo posiada ona ustawiony atrybut StructLayout aby określić rozmieszczenie pól w pamięci. Z ciekawych pól mamy: shi502_netname – określający nazwę pod jakim ma być widoczny udział, shi502_path – ścieżka do obiektu, który będzie widziany jako udział, shi502_remark – opis, shi502_type – typ udziału, gdyż możemy udostępniać więcej niż tylko foldery. Dostępne opcje to:

private enum SHARE_TYPE : uint
{
    STYPE_DISKTREE = 0,
    STYPE_PRINTQ = 1,
    STYPE_DEVICE = 2,
    STYPE_IPC = 3,
    STYPE_SPECIAL = 0x80000000,
}

Ale nas w tym wypadku interesuje STYPE_DISKTREE. Po wypełnieniu takiej struktury przekazujemy ją do polecenia i jeśli wynik będzie równy zero to udało nam się udostępnić folder/zasób. inne zwracane wyniki to:

private enum NetError : uint
{
    NERR_Success = 0,
    NERR_BASE = 2100,
    NERR_UnknownDevDir = (NERR_BASE + 16),
    NERR_DuplicateShare = (NERR_BASE + 18),
    NERR_BufTooSmall = (NERR_BASE + 23),
}

Wywołanie metody? Prosto…

NetError result = NetShareAdd(string.Empty, 502, ref info, out param);
if (result != NetError.NERR_Success)
{
    Console.WriteLine(“Błąd…”);
    return;
}

Podajemy jako nazwę serwera pusty string jak poprzednio, gdyż chcemy udostępnić na udział na naszym lokalnej maszynie. Następnie jako typ 502 czyli będziemy przekazywać strukturę SHARE_INFO_502. Jak wygląda wypełniona struktura?

var info = new SHARE_INFO_502
                          {
                              shi502_netname = “dokumenty”,
                              shi502_path = @”C:\Users\pawlos\Documents”,
                              shi502_passwd = null,
                              shi502_type = SHARE_TYPE.STYPE_DISKTREE,
                              shi502_remark = “opis”,
                              shi502_max_uses = -1,
                              shi502_permissions = 0,
                              shi502_current_uses = 0,
                              shi502_security_descriptor = IntPtr.Zero
                          };

Uzupełniamy wszystkie niezbędne dane: nazwę, ścieżkę, typu udziału, opis. Hasła nie ustawiamy. Gotowe. Na koniec pozostaje enumeracja udostępnionych zasobów.

[DllImport(“Netapi32.dll”, CharSet = CharSet.Unicode)]
 private static extern NetError NetShareEnum(
    [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
      int level,           
      ref IntPtr shares,
      uint prefmaxlen,
      ref int entriesread,
      ref int totalentries,
      ref int resume_handle
);

Ponownie musimy podać nazwę serwera, typ obiektów które chcemy aby zostały zwrócone, adres miejsca gdzie mają zostać zwrócone nasze dane. Pozostałe dane informacyjne tj. ile maksymalnie danych może zostać odczytanych, ile obiektów udało się odczytać, ilość wszystkich dostępnych wpisów. Ostatnim parametrem jest uchwyt, który przydaje się w przypadku następujących odczytów, aby funkcja wiedział gdzie ostatni odczyt się zakończył.

NetShareEnum(string.Empty, 1, ref shares, Int32.MaxValue, ref howMany, ref totalItems, ref handle);
IntPtr currentPtr = shares;
for (int i = 0; i < howMany; i++)
{
   SHARE_INFO_1 shi1 = (SHARE_INFO_1)Marshal.PtrToStructure(currentPtr, typeof(SHARE_INFO_1));
   shareInfo.Add(shi1);
   currentPtr = new IntPtr(currentPtr.ToInt32() + Marshal.SizeOf(typeof(SHARE_INFO_1)));
}
NetApiBufferFree(shares);

Do wywołania metody Enum przekazujemy nazwę serwera (ponownie pusty string), typ danych jakie mają zostać zwrócone (jednocześnie definiując jaki będzie typ obiektów) oraz adres gdzie mają być zaalokowane dane. Następnie podajemy jak dużo danych chcemy aby zostało zwrócone (u nas max ile się da). 3 ostatnie parametry zgodnie z tym co opisane jest wyżej. Zobaczmy jeszcze jak wygląda struktura SHARE_INFO_1.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHARE_INFO_1
{
    public string shi1_netname;
    public uint shi1_type;
    public string shi1_remark;
    public SHARE_INFO_1(string sharename, uint sharetype, string remark)
    {
        this.shi1_netname = sharename;
        this.shi1_type = sharetype;
        this.shi1_remark = remark;
    }
}

Zawiera ona podstawowe elementy. Nazwę, typ oraz opis udziału.
Gdy funkcja zwróci nam wyniki (powinniśmy sprawdzić zwracany rezultat) możemy przejść do odczytu danych. Za pomocą Marshaler’a zamieniamy wskaźnik na strukturę, którą to dodajemy do listy. Następnie przechodzimy na adres kolejnej struktury i powtarzamy tyle razy ile zostało zwróconych obiektów. Następnie zwalniamy zaalokowane zasoby i nie pozostaje nam nic innego jak wypisać znalezione zasoby.
shares
Można to sobie wszystko jeszcze ładnie opakować w Extension Methods i gotowe. Miłego kodowania!

Pytanie zadane w tytule tego posta, to drugie z trudniejszych pytań jakie pojawiło się podczas mojej sesji o plikach na portalu VirtualStudy.
Przeszukałem trochę sieć i okazało się, że folder można w dość prosty sposób udostępnić za pomocą WMI – Windows management Instrumentation – które w .NET reprezentowane jest przez przestrzeń nazw System.Management.
Aby udostępnić folder wystarczy przekazać odpowiednie parametry do klasy Win32_Share i wywołać metodę Create.

public static void Share(string path, string shareName, string description)
{
    var managementClass = new ManagementClass(“Win32_Share”);
    ManagementBaseObject parameters = managementClass.GetMethodParameters(“Create”);
    parameters[“Description”] = description;
    parameters[“Name”] = shareName;
    parameters[“Path”] = path;
    parameters[“Type”] = 0;
    ManagementBaseObject outs = managementClass.InvokeMethod(“Create”, parameters, null);
    if (outs == null || ((uint)(outs.Properties[“ReturnValue”].Value) != 0))
    {
        throw new IOException(“Unable to share directory.”);
    }
}

Co tutaj się dzieje? Na początku pobieramy klasę Win32_Share, a następnie tworzymy obiekt reprezentujący parametry, które będę przekazywane do polecenia Create. Wypełniamy je odpowiednimi danymi (nie są to wszystkie parametry, które możemy przekazać/ustawić – pełna lista tu). Następnie wołamy tę metodę przekazując obiekt parametrów. Na koniec sprawdzamy czy operacja się udała (zwróciła 0; pełna lista błędów w linku poprzednim). Jeśli wszystko się powiodło, nasz folder zostanie udostępniony pod nazwą, którą przekazaliśmy w parametrze shareName. Aby taki udostępniony folder usunąć wystarczy wykonać polecenie Delete, na konkretnym udostępnionym folderze.

public static void Unshare(string shareName)
{
    var managementObject = new ManagementObject(string.Format(“Win32_Share.Name='{0}'”, shareName));
    try
    {
        managementObject.InvokeMethod(“Delete”, null, null);
    }
    catch (ManagementException ex)
    {
        throw new IOException(“Unable to unshare folder”, ex);
    }
}

Wyszukujemy nasz udostępniony folder na podstawie nazwy pod jaką jest udostępniony i wywołujemy na nim metodę Delete. Proste.
Możemy dodatkowo umieścić wywołanie takich metod jako Extension Method na klasie DirectoryInfo.

public static void Share(this DirectoryInfo dir, string shareName, string description)
{
    ShareFolder.Share(dir.FullName, shareName, description);
}
public static void Unshare(this DirectoryInfo dir, string shareName)
{
    ShareFolder.Unshare(shareName);

Użycie tego wszystkiego jest jeszcze prostsze.

DirectoryInfo directoryInfo = new DirectoryInfo(@”C:\Users\pawlos\Documents\”);
directoryInfo.Share(“Dokumenty”, “Udostępnione dokumenty”);
directoryInfo.Unshare(“Dokumenty”);

I gotowe. Miłego kodowania.

Dziś temat, który pojawił się podczas mojej ostatniej sesji na portalu VirtualStudy o plikach (niedługo powinna być dostępna pod tym adresem). Pojawiło się pytanie od słuchaczy, czy za pomocą ogólnodostępnych klas w .NET można dobrać się do Alternate Data Stream. Pytanie to zadałem jako zagadkę dla dociekliwych w zamian za konto VIP. Jako, że konkurs już został rozwiązany można przedstawić to dla szerszego grona.

Czym są Alternate Data Stream?

Jest to mechanizm systemu plików NTFS pozwalający tworzyć dodatkowe strumienie danych związane z danym plikiem (stąd nazwa :)). Jak tego użyć?
ads_cmd
Co tu się dzieje? Na początku wrzucamy treść do pliku plik.txt i wypisujemy na konsolę jego zawartość. Następnie sprawdzamy poleceniem dir aby się upewnić czy udało się zapisać. Następnie wprowadzamy nową zawartość a jako nazwy pliku używamy plik.txt:ads.txt. W ten sposób określamy, że będzie to Alternate Data Stream. Następnie sprawdzamy rozmiar pliku i wypisujemy ADS na ekran. Widzimy, że mimo wprowadzenia tam dodatkowych danych rozmiar nie uległ zmianie!

A jak w .NET?

Niestety użycie w ADS w .NET nie jest takie proste. Niestety nie wystarczy użyć klasy File jak poniżej

File.WriteAllText(“plik.txt:ukryta.txt”, “ukryta zawartość pliku”);

Taki kod da nam wyjątek z informacją, że dany format nie jest wspierany w .NET. A jak z FileStream?

using (FileStream stream = new FileStream(“plik.txt:ukryta.txt”, FileMode.Open))
{
    using (StreamWriter writer = new StreamWriter(stream))
    {
        writer.Write(“ukryta zawartośc pliku”);
    }
}

Podobnie jak poprzednio poinformuje, że nie jest on wspierany! Co zatem nam pozostaje? P/Invoke! Za pomocą strony pinvoke.net importujemy definicje metod CreateFile, WriteFile oraz ReadFile.

[DllImport(“kernel32.dll”)]
static extern bool WriteFile(IntPtr hFile, byte[] lpBuffer,
  uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten,
  [In] ref NativeOverlapped lpOverlapped);
[DllImport(“Kernel32.dll”, SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr CreateFile(
    string fileName,
    [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
    [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
    int securityAttributes,
    [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
    int flags,
    IntPtr template);
[DllImport(“kernel32.dll”, SetLastError = true)]
static extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead,
  out uint lpNumberOfBytesRead, [In] ref NativeOverlapped lpOverlapped);

Mając te definicje wystarczy nam z nich tylko skorzystać!

File.WriteAllText(“plik.txt”, “normalna zawartość pliku”);
IntPtr file = CreateFile(“plik.txt:ukryta.txt”, FileAccess.ReadWrite, FileShare.ReadWrite, 0, FileMode.OpenOrCreate, 0, IntPtr.Zero);
byte[] bytes = Encoding.ASCII.GetBytes(“ukryta zawartość pliku”);
uint howMany;
var overlapped = new NativeOverlapped();
WriteFile(file, bytes, (uint)bytes.Length, out howMany, ref overlapped);
byte[] read = new byte[21];
ReadFile(file, read, 20, out howMany, ref overlapped);
Console.WriteLine(Encoding.ASCII.GetString(read));

I voila! Miłego używania Alternate Data Stream!
PS. Zapraszam na swoją kolejną sesję na portalu VirtualStudy podczas, której opowiem o atrybutach i reflection.

Dziś powiemy sobie na temat (chyba) najbardziej zaawansowanego narzędzia służącego do debuggowania naszego kodu na produkcji. WinDbg, bo o nim będzie mowa, to część pakietu Debugging Tools for Windows.
Do celów przykładowych posłużymy się aplikacją z poprzedniego wpisu o mDbg.

WinDbg

Tak jak już było wspomniane wyżej WinDbg to część większego pakietu Debugging Tools for Windows i jest dostępny w dwóch wersjach 32- i 64-bitowej. Obowiązkowe narzędzie dla kogoś kto chce na poważnie zajmować się debuggowaniem. Zatem jak zacząć? Po uruchomieniu WinDbg nie sprawia dobrego wrażenia – surowy interface nie zachęca do pracy z nim.
windbg
Pierwsze od czego powinniśmy zacząć pracę z tym narzędziem to skonfigurowanie symboli. Aby to zrobić należy wybrać opcję File| Symbol File Path i wpisać: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols. Uruchamiamy naszą aplikację pod kontrolą WinDbg. Różne są ustawienia aplikacji. Ja otwieram standardowo następujące okna: disassembly, calls, command, registers.
Aby móc debuggować aplikacji napisane w .NET musimy załadować specjalny dodatek sos (Son of Strike). Rozszerza on możliwości WinDbg o polecenia właściwe dla .NET (np. przeglądanie zarządzanego stosu itp.). Robimy to poleceniem .load <ścieżka do katalogu z .NET frameworkiem>\sos.dll lub też za pomocą .loadby sos np. .loadby sos clr.  Ja pozwoliłem aplikacji uruchomić się, aby załadowane zostały wszystkie dll’ki. Następnie CTRL-Pause Break przenosi nas do WinDbg a za pomocą ostatniego polecenia załadujemy rozszerzenie sos.
Mając załadowanie rozszerzenie sos, możemy przystąpić do działania. Wszystkie polecenia będące rozszerzeniem wykonujemy poprzedzając je znakiem ! (wykrzyknika) np. !EEheap – zwróci zawartość zarządzanego stosu, !clrstack wypisze zarządzany stos.
Jak znaleźć przyczynę błędu? Zobaczmy gdzie znajduje się nasza aplikacja obecnie. Wprowadzamy polecenie !dumpstack –EE i nie otrzymujemy nic interesującego. No tak, mamy wiele wątków w aplikacji i ten w którym aktualnie jesteśmy zatrzymani niekoniecznie jest wątkiem .NET. Szybkie podpatrzenie co zwróci nam polecenie !threads i już wiemy, że musimy przełączyć się na wątek 0 lub 2. ~0s i jesteśmy w pierwszym z nich. Ponownie !dumpstack i bingo. Widzimy tam wywołania takie jak ReadLine. To jest to – nasza aplikacja czeka przecież na naszą reakcję wprowadzenie danych z klawiatury.
dumpstack
Teraz wystarczy zrzucić IL’a wykonując polecenie !DumpIL 001537f0 (wartość z 4 kolumny ostatniej linii). I widzimy kod odpowiedzialny za obliczenia.
il
Co w nim się dzieje? IL_0000 mamy wywołanie ReadLine, na który się właśnie zatrzymaliśmy. Potem mamy pobranie zawartości wartości ze stosu i przechowanie jej w zmiennej lokalnej pod indeksem 0, potem wrzucenie tej samej zmiennej na stos i wywołanie IsNullOrWhiteSpace. Jeśli wprowadzono pusty ciąg znaków kończymy przetwarzanie. w przeciwnym przypadku wrzucamy na stos tę samą zmienną oraz liczbę 515 i wołamy <Int32.Parse. Zobaczmy na dostępne definicje metody Int32.Parse. Mam ona cztery przeciążenia w tym 2, które biorą dwa parametry (dwa wrzucenia na stos). Wrzucamy na stos prostą wartość więc wywołanie z IFormatprovider odpada. Zobaczmy co kryje się w NumberStyles. Jest to enum, tak więc jego wartość będzie konwertowana na liczbę. Musimy jeszcze dowiedzieć się jakiemu elementowi odpowiada wartość 515. Krótki rzut oka na IlSpy’a i wszystko jasne!
ilspy
WinDbg to potężne narzędzie a w tym wpisie nie wymieniłem nawet 5% jego możliwości. Zachęcam do dokładniejszego przyjrzenia się mu np. na stronie windbg.info jest tam całkiem obszerny pdf – WinDbg. From A to Z!

Wielkimi krokami trwają przygotowania do 3. edycji Geeks On Tour. Tym razem Piotr Włodek z firmy Infusion będzie opowiadał o MEF czyli Managed Extensibility Framework. Podczas pierwszej sesji zostaną omówione podstawy a w trakcie drugiej zagłębimy się głębiej w to co MEF ma do zaoferowania.

3rd-Geeks-on-Tour-Bede-tam-200x250px

Przyłącz się do nas biorąc udział w sesji w jednym z miast. Więcej informacji na stronie: http://geeksontour.pl.

Poniżej fragment kodu znaleziony w jednym z projektów. Anty-przykład. tak nie należy pisać:

Opinion opinion = new Opinion();

opinion.GetOpinion(Info.pCode);

if (opinion != null && opinion.OpininsList.Count > 0)

{

    return true;

}

Sprawdzanie czy obiekt nie jest równy null – już po wykonaniu na nim operacji. Przydatne! Pisownia oryginalna, ale trochę pozmieniałem nazwy obiektów, aby za dużo nie wyjawiać :).

A Wy, jakie złe kawałki kodu widzieliście ostatnio?