Under the quarantine working period, the problem became as follows: it is indeed necessary to minimize the number of visits to specialists' offices who provide support and consulting on application software. Frankly, users often abuse the help of specialists, not wanting to delve into the issue themselves, saying, 'they will come â help â fix it, and I will just have a smoke or drink coffee, etc.' Phone consultation while sharing access to the server is more effective if the remote screen is viewed.

Already after the 'invention' of our bicycle, reasonable information on the topic of the article emerged: eller eller . All of them imply the use of the console, even with elements of simple dialogue.
All the information presented below is intended for those who can normally endure abnormal deviations to achieve the desired result by inventing unnecessary methods.
To 'not drag the cat by the tail', I will start with the last: the bicycle works for the ordinary user using the utility , for which the author deserves thanks.
I. Console and shadow RDP.
Since using the Server Manager console with admin rights -> QuickSessionCollection -> by clicking on the session of the interested user, selecting Shadow (Shadow Copy) in the context menu for the staff instructing on the use of software, â is not an option, another 'wooden' method was considered, namely:
1. Find out the RDP session id:
query user | findstr Administratoror:
qwinsta | findstr Administrator Moreover, "| findstr Administrator" was only convenient when you know exactly what you need, or to use only the first part to see all users logged onto the server. Administrator 2. Connect to this session, provided that in the domain
![]()
Group Policies the parameter 'Establishes rules for remote control of user sessions for Remote Desktop Services' has at least the option 'Observe session with user's permission' selected ( mstsc /shadow:127):
Please note that only user logins will be in the list.I repeat that without admin rights you will get the following:
But for preliminary debugging of the program that will be discussed, I used an account with administrator rights.

Men för förhandsdebugging av programmet som kommer att diskuteras anvÀnde jag ett konto med administratörsrÀttigheter.
II. Programmet
SÄ hÀr ser uppgiften ut: att skapa ett enkelt grafiskt grÀnssnitt för att ansluta till anvÀndarens skuggsensor med hans tillstÄnd, och skicka ett meddelande till anvÀndaren. Programmeringsmiljön som valdes Àr Lazarus.
1. Vi fĂ„r en fullstĂ€ndig domĂ€nlista över anvĂ€ndare "inloggning" â "fullstĂ€ndigt namn" frĂ„n administratören, eller Ă„terigen via konsolen:
wmic useraccount get Name,FullName ingen hindrar oss frÄn att göra det sÄ hÀr:
wmic useraccount get Name,FullName > c:testusername.txtJag sÀger genast att det var i Lazarus som det fanns ett problem med att bearbeta den hÀr filen, eftersom dess standardkodning Àr UCS-2, sÄ jag var tvungen att konvertera den manuellt till vanlig UTF-8. Strukturen i filen har mÄnga tabbar, eller snarare mÄnga mellanslag, som vi beslutade att ÀndÄ bearbeta programmÀssigt; förr eller senare kommer problemlösningen med kodningen att komma, och filen kommer att uppdateras programmÀssigt.
SÄ, planen Àr att ha en mapp som Àr tillgÀnglig för programmets anvÀndare, till exempel c:test, dÀr det kommer att finnas 2 filer: den första med inloggning och fullstÀndigt namn, den andra med id_rdp och inloggning för anvÀndarna. Vi bearbetar sedan dessa data som vi kan :)
Och för att koppla det till sessionslistan flyttar vi detta (inloggning och fullstÀndigt namn) innehÄll till en array:
procedure Tf_rdp.UserF2Array;
var
F:TextFile; i:integer; f1, line1:String; fL: TStringList;
begin // f_d global sökvÀg till filerna
f1:=f_d+'user_name.txt'; // uppgiften Àr att lÀsa filens innehÄll till arrayen
fL := TStringList.Create; // vi kommer att utsÀtta strÀngen för metamorfoser med avgrÀnsare
fL.Delimiter := '|'; fL.StrictDelimiter := True;
AssignFile(F,f1);
try // Ăppna filen för lĂ€sning
reset(F); ReadLn(F,line1);
i:=0;
while not eof(F) do // LĂ€ser rader tills filen tar slut
begin
ReadLn(F,line1);
line1:= StringReplace(line1, ' ', '|',[]); // ersÀtter första tvÄ mellanrum med avgrÀnsaren |
// tar bort alla dubbla mellanrum
while pos(' ',line1)>0 do line1:= StringReplace(line1, ' ', ' ', [rfReplaceAll]);
begin
if (pos('|',line1)>0) then
begin // om avgrÀnsaren existerar lÀgger vi till den i arrayen
fL.DelimitedText :=line1; // delar upp i kolumner
if (fL[0]'') then // om kontot har ett namn
begin // lÀgger till det i arrayen
inc(i); // tar bort eventuella ensamma mellanslag i inloggningen
fam[0,i]:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]);
fam[1,i]:=fL[0];
end;end;end;end; // Klart. StÀnger filen.
CloseFile(F);
Fl.Free;
except
on E: EInOutError do ShowMessage('Fel vid bearbetning av filen. Detaljer: '+E.Message);
end;end;Jag ber om ursÀkt för "mycket kod", de följande punkterna kommer att vara mer koncisa.
2. PÄ samma sÀtt som i föregÄende punkt lÀser vi av resultatet av bearbetningen av listan i elementet StringGrid, hÀr Àr ett 'betydelsefullt' kodstycke:
2.1 Vi fÄr den aktuella listan över RDP-sessioner i en fil:
f1:=f_d+'user.txt';
cmdline:='\/c query user >'+ f1;
if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1)=0 then;
Sleep(500); \/\/ man kan vÀnta lite lÀngre medan filen skapas för lÀsning2.2 Bearbeta filen (endast betydelsefulla kodrader anges):
StringGrid1.Cells[0,i]:=fL[1]; StringGrid1.Cells[2,i]:=fL[3]; \/\/lÀgger i en loop i StringGrid1
login1:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]); \/\/tar bort mellanslag frÄn inloggningen
if (SearchArr(login1)>=0) then \/\/söker i arrayen frÄn p1. loggningen och skriver in i tabellen Namn
StringGrid1.Cells[1,i]:=fam[1,SearchArr(login1)]
else StringGrid1.Cells[1,i]:='+'; \/\/ annars skriver vi plusset:)
.... \/\/beroende pÄ anvÀndarens val sorterar och formaterar vi efter data
if (b_id.Checked=true) then SortGrid(0) else SortGrid(1);
StringGrid1.AutoSizeColumn(0);StringGrid1.AutoSizeColumn(1); StringGrid1.AutoSizeColumn(2); 3. SjÀlva anslutningen vid klick pÄ raden med anvÀndaren och numret pÄ hans session:
id:=(StringGrid1.Row);\/\/ fÄr radnumret IntToStr(StringGrid1.Row)
ids:=StringGrid1.Cells[2,id]; \/\/hÀmtar rdp-identifieraren
cmdline:='\/c mstsc \/shadow:'+ ids; \/\/och vi ansluter....
if (b_rdp.Checked=True) then if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1) =0 then; 4. Flera dekoreringar Àr gjorda, sÄsom sortering vid klick pÄ radioknappen och meddelanden till anvÀndaren, eller till alla anvÀndare.

â Den fullstĂ€ndiga kĂ€llkoden kan ses
III. AnvĂ€ndning av AdminLink â vad jag sĂ„g:
AdminLink genererar verkligen en genvÀg som refererar till platsen för verktyget admilaunch.exe, och en personlig kopia av körverktyget AdmiRun.Exe som finns i anvÀndarens mapp, till exempel vasya, typ C:UsersvasyaWINDOWS. Allt som allt Àr det inte sÄ illa: med rÀttigheterna till genvÀgsfilen och annat, kan man leka lite för att stilla sin administrativa samvete.
KĂ€lla: habr.com
