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!