[VB.NET] Pobieranie informacji o nazwach kont użytkownika

Chciałbym napisać program działający podobnie jak kontrola rodzicielska, czyli użytkownik ustawia możliwości pracy,

a następnie z rozwijanej listy w której znajdują się nazwy kont wybiera do którego mają być przypisane.

Będą mi potrzebne dwie rzeczy do tego:

  1. Odczytywanie nazw wszystkich kont na komputerze (opcjonalnie żeby pokazywał czy ma hasło i administratora)

  2. Odczytywanie nazwy aktualnie działającego konta

Jak to wykonać?

W Visual Basicu za dużo nie zrobisz, a jak zrobisz, to może nie być pięknie działające.

Przykładowy program (c#) odczytujące dane o koncie:

using System;

using System.Management;
using System.Linq;

namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
ManagementObjectSearcher usersSearcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_UserAccount");
ManagementObjectCollection users = usersSearcher.Get();

var localUsers = users.Cast<ManagementObject>().Where(
u => (bool)u["LocalAccount"] == true &&
(bool)u["Disabled"] == false &&
(bool)u["Lockout"] == false &&
int.Parse(u["SIDType"].ToString()) == 1 &&
u["Name"].ToString() != "HomeGroupUser$");

foreach (ManagementObject user in users)
{
Console.WriteLine("Account Type: " + user["AccountType"].ToString());
Console.WriteLine("Caption: " + user["Caption"].ToString());
Console.WriteLine("Description: " + user["Description"].ToString());
Console.WriteLine("Disabled: " + user["Disabled"].ToString());
Console.WriteLine("Domain: " + user["Domain"].ToString());
Console.WriteLine("Full Name: " + user["FullName"].ToString());
Console.WriteLine("Local Account: " + user["LocalAccount"].ToString());
Console.WriteLine("Lockout: " + user["Lockout"].ToString());
Console.WriteLine("Name: " + user["Name"].ToString());
Console.WriteLine("Password Changeable: " + user["PasswordChangeable"].ToString());
Console.WriteLine("Password Expires: " + user["PasswordExpires"].ToString());
Console.WriteLine("Password Required: " + user["PasswordRequired"].ToString());
Console.WriteLine("SID: " + user["SID"].ToString());
Console.WriteLine("SID Type: " + user["SIDType"].ToString());
Console.WriteLine("Status: " + user["Status"].ToString());
}

Console.ReadKey();
}
}
}

W VB.Net też można uzyskać sporo informacji :mrgreen:

Imports System.DirectoryServices
'Dodaj referencję System.DirectoryServices
Public Class Form1

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
       'Lista kont z uprawnieniami administratorskimi
        Dim MachineName As String = "wpisz nazwę komputera" 'proponują localhost, ale u mnie na Win8 nie działa
        Dim Admins As New DirectoryEntry("WinNT://" & MachineName & "/Administratorzy")
        Dim Members As Object = Admins.Invoke("Members", Nothing)
        For Each Member As Object In CType(Members, IEnumerable)
            Dim CurrentMember As New DirectoryEntry(Member)
            MsgBox(CurrentMember.Name, , "Grupa administratorzy")
        Next


       'Lista kont lokalnych
        Dim WinNt As New DirectoryServices.DirectoryEntry("WinNT://" & MachineName)
        For Each User As DirectoryServices.DirectoryEntry In WinNt.Children
            If User.SchemaClassName = "User" Then
                MsgBox(User.Name, , "Lista użytkowników")
            End If
        Next



       'Kto jest teraz zalogowany
        MsgBox(Environment.UserName, , "Aktualnie zalogowany")
    End Sub
End Class

Wypróbowałem ten sposób z kontami lokalnymi ale w oknie oprócz nazw kont które mam założone na komputerze

powyskakiwały inne i nie mówię tutaj o Administrator czy Gość bo wiem że są ukryte ale takie:

HomeGroupUser$

Administratorzy

Czytelnicy dzienników zdarzeń

Goście

IIS_IUSRS itp.

Jak je odfiltrować?

O widzisz. Na ósemce nie pokazywał takich kont, ale na siódemce już pokazuje.

 

Dorzuciłem takie coś co można spróbować ogarnąć.

W kodzie do kont lokalnych zamień message boxa na:

MsgBox(User.Name & " " & User.Properties.Count, , "Lista użytkowników")

Zauważyłem, że przy kontach Administratora, Gościa i zwykłego usera User.Properities.Count zmienia się na 24, a przy HomeGroupUser$ na 23. Może taki filtr wystarczy. Ale gdy użytkownik należy do grupy zaawansowanych to też wpada do 23 :frowning:

Dim MachineName As String = "localhost"
        Dim WinNt As New DirectoryServices.DirectoryEntry("WinNT://" & MachineName)
        For Each User As DirectoryServices.DirectoryEntry In WinNt.Children
            If User.SchemaClassName = "User" And User.Properties.Count = 24 Then
                MsgBox(User.Name, , "Użytkownicy")
            End If
        Next

Teraz działa poprawnie, ale zauważyłem drugi błąd. I w tym pierwszym sposobie i w tym po przeróbce program zawiesza się po zakończeniu wyświetlania użytkowników tak jakby cały czas tkwił w pętli For Each. Jakieś pomysły?

Trzeba w takim razie podejść do tego z innej strony. Sprawdzimy czy folder danego użytkownika istnieje na dysku :slight_smile:

Dim sciezka As String
        'ścieżka do profilu aktualnego użytkownika (np.c:\users\ktos, albo c:\documents and settings\ktos)
        sciezka = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
        For Each User As DirectoryServices.DirectoryEntry In WinNt.Children
            If User.SchemaClassName = "User" Then
                'sprawdzam czy katalog i równocześnie konto usera jest na dysku
                If Directory.Exists(Mid(sciezka, 1, InStrRev(sciezka, "\")) & User.Name) = True Then
                    MsgBox(User.Name, , "Lista uzytkowników")
                End If
            End If
        Next

Dorzuć jeszcze Imports System.IO na początek kodu :slight_smile:

No to tak…

Pomysł z porównaniem listy użytkowników z folderami jest świetny, ponieważ nie pokazuje mi już Administratora i Gościa.

Niestety to nie rozwiązało problemu bo program dalej się zapętlał, ale znalazłem sposób żeby pomóc mu wyjść z pętli.

Sposób polega na tym że pętla z funkcji ostuser() szuka w nazwach folderu nazwy ostatniego profilu, a następnie program

używa jej do porównania z aktualnie pobranym profilem i jeżeli są identyczne opuszcza pętle (Exit For).

Chyba jedyny problem jaki może wyskoczyć to jak ktoś doda folder w użytkownikach np. na literę Z.

 

I tutaj daje kod:

Imports System.DirectoryServices
Imports System.IO
Public Class Form1
    Dim sciezka As String = System.Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)

    Private Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Click
        Dim local As String = "localhost"
        Dim licznik As Byte = 0
        Dim WinNt As New DirectoryServices.DirectoryEntry("WinNT://" & local)
        Dim ost As String = ostuser()
        For Each User As DirectoryServices.DirectoryEntry In WinNt.Children
            licznik += 1
            If User.SchemaClassName = "User" Then
                If Directory.Exists(Mid(sciezka, 1, InStrRev(sciezka, "\")) & User.Name) = True Then
                    MsgBox(User.Name)
                    If ost = User.Name Then Exit For
                End If
            End If
        Next
    End Sub

    Function ostuser()
        Dim wynik As String = ""
        sciezka = Mid(sciezka, 1, InStrRev(sciezka, "\"))
        For Each i As String In Directory.GetDirectories(sciezka)
            wynik = New DirectoryInfo(i).Name
        Next
        Return wynik
    End Function
End Class

Ciekawi mnie czemu Ci się zapętla. Może mamy różne wersje frameworka (4.0 u mnie ) albo samego vb.net (2010 u mnie) ? :slight_smile:

Aha, a tam gdzie sprawdza się przynależność do grupy administratorów też się zapętla?

 

Edit.

 

Wersje frameworka nie grają roli, Environment.SpecialFolder.UserProfile jest od wersji 4.

Uruchomiłem ten kod w Windowsie 8 i zauważyłem kolejny błąd w tym moim sposobie.

Na tym komputerze jest tylko jedno konto, a ostatnim folderem w Users jest Publiczne więc program

nie wychodzi z pętli (dowodzi to też że w Windowsie 8 też program nie wyjdzie sam z pętli).

Pomyślałem żeby rozbudować funkcję ostuser o pomijanie w zapisie folderów które nie są folderami użytkowników (łącznie z ukrytymi).

U mnie są to:

Windows 7-Default, Publiczny

Windows 8-Default, Default.migrated, Publiczne

U Ciebie są takie same czy jeszcze jakieś występują?

 

KOMPILATOR: Visual Studio 2010

FRAMEWORK: Windows 7-wersja 4.5; Windows 8-wersja 4.0

Na Windows 8 normalnie idzie, na Windows 7 też, na XP też dobrze. Foldery mam takie, ale nie mogą być brane pod uwagę, bo nie ma takich użytkowników. A Twoja funkcja uniemożliwi tym bardziej zakończenie wyliczania.

 

Spróbuj tego

Imports System.DirectoryServices.AccountManagement 'plus referencja do tego
Imports System.IO

       Dim ctx As PrincipalContext = New PrincipalContext(ContextType.Machine)
        Dim up As UserPrincipal = New UserPrincipal(ctx)
        Dim ps As PrincipalSearcher = New PrincipalSearcher(up)
        Dim sciezka As String = System.Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
        Try
            Dim results As PrincipalSearchResult(Of Principal) = ps.FindAll
            For Each cr As Principal In results
                If Directory.Exists(Mid(sciezka, 1, InStrRev(sciezka, "\")) cr.Name) = True Then
                    MsgBox(cr.Name, , "Użytkownicy lokalni")
                End If
            Next
            Me.Show()
            ps.Dispose()
            up.Dispose()
            ctx.Dispose()
        Catch ex As Exception
            MsgBox(ex.ToString)
            End
        End Try