O problema durante o período de quarentena da empresa era o seguinte: é realmente necessário minimizar o número de visitas a gabinetes por especialistas que prestam serviços de manutenção e aconselham sobre software de aplicativos e, francamente, os usuários frequentemente abusam da ajuda de especialistas que não desejam se aprofundar na pergunta, dizendo "eles virão - eles ajudarão, eles farão isso; e enquanto fumo / tomo café, etc. " A consulta por telefone ao compartilhar um servidor é mais eficaz ao exibir uma tela remota.
Já após a "invenção" de nossa bicicleta, surgiram informações sãs sobre o tópico do artigo: RDS Shadow - conexão de sombra às sessões de usuário RDP no Windows Server 2012 R2 ou modo Shadow de um usuário não privilegiado no servidor Windows ou Delegamos o gerenciamento de sessões RDP . Todos eles implicam o uso de um console, mesmo com elementos de um simples diálogo.Todas as informações abaixo destinam-se a quem normalmente tolera perversões anormais para obter o resultado desejado, inventando métodos desnecessários.Para não "puxar o gato pela cauda", começarei com o último: a bicicleta funciona para o usuário comum usando o utilitário AdmiLink , pelo qual agradeço ao autor.I. Console e sombra RDP.Como o uso do console do Gerenciador do Servidor com direitos de administrador -> QuickSessionCollection -> clicando na sessão do usuário de seu interesse, selecionar Shadow no menu de contexto para a equipe que instrui no trabalho com o software não é uma opção, outro método "de madeira" foi considerado, a saber:1. aprendemos a id de sessão RDP:query user | findstr Administrator
ou:qwinsta | findstr Administrator
E o " | findstr Administrator " era conveniente apenas quando você sabe o que precisa do administrador ou usa apenas a primeira parte para ver todos os logons no servidor.
2. Estamos conectados a esta sessão, desde que nas políticas de grupo de domínio a opção "Defina as regras de controle remoto para sessões de usuário dos Serviços de Área de Trabalho Remota" seja selecionada pelo menos "Monitorar a sessão com permissão do usuário" ( mais ):mstsc /shadow:127
Observe que a lista conterá apenas logins de usuários.Repito que, sem direitos de administrador, você receberá o seguinte:
Mas, para depuração preliminar do programa que será discutido, usei uma conta com direitos de administrador.II ProgramaPortanto, a declaração do problema: criar um tipo de interface gráfica simples para conectar-se ao sensor de sombra do usuário com sua permissão, enviando uma mensagem ao usuário. O ambiente de programação é selecionado pelo Lazarus.1. Nós obtemos a lista de domínio completa dos usuários "login" - "nome completo" do administrador ou novamente através do console:wmic useraccount get Name,FullName
ninguém proíbe nem isso:wmic useraccount get Name,FullName > c:\test\username.txt
Devo dizer imediatamente que foi o Lazarus quem teve o problema de processar este arquivo, já que sua codificação padrão é UCS-2, então eu apenas tive que convertê-lo manualmente para UTF-8 regular. Existem muitas guias na estrutura do arquivo, ou melhor, muitos espaços que foram decididos a serem programados, mais cedo ou mais tarde o problema de codificação será resolvido e o arquivo será atualizado programaticamente.Portanto, na ideia da pasta, acessível aos usuários do programa, por exemplo c: \ test, na qual haverá 2 arquivos: o primeiro com login e nome completo, o segundo com id_rdp e usuários de login. Além disso, processamos esses dados como podemos :).Enquanto isso, para associar à lista de sessões, transferimos esse conteúdo (login e nome completo) para uma matriz:procedure Tf_rdp.UserF2Array;
var
F:TextFile; i:integer; f1, line1:String; fL: TStringList;
begin
f1:=f_d+'user_name.txt';
fL := TStringList.Create;
fL.Delimiter := '|'; fL.StrictDelimiter := True;
AssignFile(F,f1);
try
reset(F); ReadLn(F,line1);
i:=0;
while not eof(F) do
begin
ReadLn(F,line1);
line1:= StringReplace(line1, ' ', '|',[]);
while pos(' ',line1)>0 do line1:= StringReplace(line1, ' ', ' ', [rfReplaceAll]);
begin
if (pos('|',line1)>0) then
begin
fL.DelimitedText :=line1;
if (fL[0]<>'') then
begin
inc(i);
fam[0,i]:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]);
fam[1,i]:=fL[0];
end;end;end;end;
CloseFile(F);
Fl.Free;
except
on E: EInOutError do ShowMessage(' . : '+E.Message);
end;end;
Peço desculpas pelo "lote de código", os seguintes pontos serão mais concisos.2. Da mesma forma, usando o método do parágrafo anterior, lemos o resultado do processamento da lista em um elemento StringGrid, enquanto darei um trecho de código "significativo":2.1 Obtenha a lista atual de sessões RDP em um arquivo:f1:=f_d+'user.txt';
cmdline:='/c query user >'+ f1;
if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1)=0 then;
Sleep(500);
2.2 Processamos o arquivo (apenas linhas de código significativas são indicadas):StringGrid1.Cells[0,i]:=fL[1]; StringGrid1.Cells[2,i]:=fL[3];
login1:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]);
if (SearchArr(login1)>=0) then
StringGrid1.Cells[1,i]:=fam[1,SearchArr(login1)]
else StringGrid1.Cells[1,i]:='+';
....
if (b_id.Checked=true) then SortGrid(0) else SortGrid(1);
StringGrid1.AutoSizeColumn(0);StringGrid1.AutoSizeColumn(1); StringGrid1.AutoSizeColumn(2);
3. A conexão em si quando você clica na linha com o usuário e seu número de sessão: id:=(StringGrid1.Row);
ids:=StringGrid1.Cells[2,id];
cmdline:='/c mstsc /shadow:'+ ids;
if (b_rdp.Checked=True) then if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1) =0 then;
4. Mais algumas decorações foram feitas, como ordenar clicando no botão de opção e mensagens para o usuário ou todos os usuários.
→ O código fonte completo pode ser visto aquiIII. Aplicativo AdminLink - o que vi: oAdminLink realmente gera um atalho que se refere à localização do utilitário admilaunch.exe e uma cópia pessoal do utilitário de inicialização AdmiRun.Exe localizado na pasta do usuário, por exemplo, vasya , como C: \ Users \ vasya \ WINDOWS \ . Em geral, nem tudo é tão ruim: você pode brincar com os direitos de acesso ao arquivo de atalho e outros, para limpar sua própria consciência de administrador.