Problem w okresie pracy przedsiębiorstwa w czasie kwarantanny był następujący: rzeczywiście należy zminimalizować liczbę wizyt w biurach specjalistów zajmujących się obsługą i doradztwem w zakresie oprogramowania aplikacyjnego, a szczerze mówiąc, użytkownicy często nadużywają pomocy specjalistów, nie chcąc zagłębiać się w sam problem, twierdząc „przyjdą - pomogą - zrobią, a ja w tym czasie zapalę papierosa / wypiję kawę itp.”. Konsultacja telefoniczna przy wspólnym dostępie do serwera jest bardziej efektywna, gdy jesteśmy w stanie przeglądać zdalny ekran.

Już po „wynalezieniu” naszego roweru pojawiły się sensowne informacje na temat artykułu: lub lub . Wszystkie one zakładają korzystanie z konsoli, nawet z elementami prostego dialogu.
Wszystkie zamieszczone poniżej informacje są przeznaczone dla tych, którzy normalnie znoszą nienormalne wynaturzenia w celu osiągnięcia pożądanego rezultatu, wynajdując niepotrzebne sposoby.
Aby „nie ciągnąć kota za ogon”, zacznę od ostatniego: rower działa u zwykłego użytkownika za pomocą narzędzia , za co autorowi należy się podziękowanie.
I. Konsola i shadow RDP.
Ponieważ korzystanie z administratorskimi prawami w konsoli Menedżera Serwera -> QuickSessionCollection -> klikając na sesję interesującego użytkownika, wybierając w menu kontekstowym Shadow (Kopia cieniowa) dla personelu, instruującego w pracy z oprogramowaniem, — nie było to możliwe, rozważono inny „drewniany” sposób, a mianowicie:
1. Sprawdzamy RDP id sesji:
query user | findstr Administratorlub:
qwinsta | findstr Administrator Przy czym "| findstr Administrator" było wygodne tylko wtedy, gdy wiesz, że akurat Administrator potrzebujesz, ewentualnie używać tylko pierwszej części do przeglądania wszystkich zalogowanych na serwerze.
![]()
2. Łączymy się z tą sesją, pod warunkiem, że w politykach grupowych domeny wskaźnik „Ustala zasady zdalnego zarządzania sesjami użytkowników w usługach zdalnych pulpitu” został wybrany ze wskaźnikiem przynajmniej „Obserwacja sesji za zgodą użytkownika” ( mstsc /shadow:127):
Proszę zwrócić uwagę, że na liście będą tylko loginy użytkowników.Powtarzam, że bez uprawnień administratora otrzymasz następujący rezultat:
Ale do wstępnego debugowania programu, o którym mowa, korzystałem z konta z prawami administratora.

Jednak do wstępnego debugowania programu, o którym mowa, użyłem konta z uprawnieniami administratora.
II. Program
Zadanie: stworzenie prostego interfejsu graficznego do połączenia z użytkownikiem przy jego zgodzie oraz wysyłania wiadomości do użytkownika. Wybrano środowisko programistyczne Lazarus.
1. Uzyskujemy pełną listę domen użytkowników „login” – „pełne imię” od administratora lub ponownie za pomocą konsoli:
wmic useraccount get Name,FullName Nikt nie zabrania tak zrobić:
wmic useraccount get Name,FullName > c:testusername.txtZaraz powiem, że Lazarus miał problem z przetwarzaniem tego pliku, ponieważ domyślnie jego kodowanie to UCS-2, więc musiałem po prostu ręcznie przekształcić na zwykły UTF-8. W strukturze pliku jest dużo tabulacji, a właściwie wiele spacji, które postanowiliśmy przetworzyć programowo; prędzej czy później problem z kodowaniem zostanie rozwiązany, a plik będzie aktualizowany programowo.
Zatem w koncepcji jest folder dostępny dla użytkowników programu, na przykład c:test, w którym będą 2 pliki: pierwszy z loginem i pełnym imieniem, drugi z id_rdp i loginami użytkowników. Następnie przetwarzamy te dane jak potrafimy :).
Na razie, aby skojarzyć z listą sesji, przenosimy te (login i pełne imię) zawartości do tablicy:
procedure Tf_rdp.UserF2Array;
var
F: TextFile; i: integer; f1, line1: String; fL: TStringList;
begin // f_d globalna ścieżka do lokalizacji plików
f1 := f_d + 'user_name.txt'; // zadanie polega na odczytaniu zawartości pliku do tablicy
fL := TStringList.Create; // poddamy linię metamorfozom z separatorami
fL.Delimiter := '|'; fL.StrictDelimiter := True;
AssignFile(F, f1);
try // Otwórz plik do odczytu
reset(F); ReadLn(F, line1);
i := 0;
while not eof(F) do // Odczytuje wiersze, dopóki nie skończy się plik
begin
ReadLn(F, line1);
line1 := StringReplace(line1, ' ', '|', []); // zamienia pierwszy napotkany 2 spacje na separator |
// usuwamy wszystkie podwójne spacje
while pos(' ', line1) > 0 do line1 := StringReplace(line1, ' ', ' ', [rfReplaceAll]);
begin
if (pos('|', line1) > 0) then
begin // jeśli separator istnieje, zapisujemy go w tablicy
fL.DelimitedText := line1; // dzielimy na kolumny
if (fL[0] '') then // jeśli konto ma nazwę
begin // wpisujemy je do tablicy
inc(i); // pozbywamy się możliwych pojedynczych spacji w loginie
fam[0, i] := StringReplace(fL[1], ' ', '', [rfReplaceall, rfIgnoreCase]);
fam[1, i] := fL[0];
end;end;end;end; // Gotowe. Zamykamy plik.
CloseFile(F);
Fl.Free;
except
on E: EInOutError do ShowMessage('Błąd przetwarzania pliku. Szczegóły: ' + E.Message);
end;end;Przepraszam za „dużo kodu”, kolejne punkty będą bardziej zwięzłe.
2. W podobny sposób, jak w poprzednim punkcie, odczytujemy wynik przetwarzania listy do elementu StringGrid, przytaczając „istotny” fragment kodu:
2.1 Otrzymujemy aktualną listę sesji RDP w pliku:
f1:=f_d+'user.txt';
cmdline:='\/c query user >'+ f1;
if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1)=0 then;
Sleep(500); \/\/ można poczekać dłużej, aż plik zostanie utworzony do odczytu2.2 Przetwarzamy plik (podane tylko istotne linie kodu):
StringGrid1.Cells[0,i]:=fL[1]; StringGrid1.Cells[2,i]:=fL[3]; \/\/ wrzucamy w pętli do StringGrid1
login1:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]); \/\/ usuwamy spacje z logina
if (SearchArr(login1)>=0) then \/\/ szukamy w tablicy logina z p1. i zapisujemy w tabeli FIO
StringGrid1.Cells[1,i]:=fam[1,SearchArr(login1)]
else StringGrid1.Cells[1,i]:='+'; \/\/ albo zapisujemy plusa:)
.... \/\/ w zależności od wyboru użytkownika sortujemy i formatujemy dane
if (b_id.Checked=true) then SortGrid(0) else SortGrid(1);
StringGrid1.AutoSizeColumn(0);StringGrid1.AutoSizeColumn(1); StringGrid1.AutoSizeColumn(2); 3. Bezpośrednie połączenie po kliknięciu na wiersz z użytkownikiem i numerem jego sesji:
id:=(StringGrid1.Row);\/\/poznajemy numer wiersza IntToStr(StringGrid1.Row)
ids:=StringGrid1.Cells[2,id]; \/\/ uzyskujemy identyfikator rdp
cmdline:='\/c mstsc \/shadow:'+ ids; \/\/ i łączymy się....
if (b_rdp.Checked=True) then if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1) =0 then; 4. Dodano jeszcze kilka ulepszeń, takich jak sortowanie po kliknięciu na radiobutton, oraz wiadomości do użytkownika lub do wszystkich użytkowników.

→ Pełny kod źródłowy można zobaczyć
III. Zastosowanie AdminLink — co zauważyłem:
AdminLink rzeczywiście generuje skrót, który odnosi się do lokalizacji narzędzia admilaunch.exe, oraz osobistej kopii narzędzia uruchamiającego AdmiRun.Exe , która znajduje się w folderze użytkownika, na przykład vasya, w stylu C:UsersvasyaWINDOWS. W sumie nie jest tak źle: z prawami dostępu do pliku skrótu i innymi, można się pobawić, aby oczyścić sumienie.
Źródło: habr.com
