Por outro lado, nem todos os programas em uso pelas empresas operam nativamente na internet. Seja por que são programas legados ou por que o fabricamente ainda não os preparou para o ambiente web ou simplesmente porque há muitos programas e não houve tempo hábil para uma conversão em massa. Por isso, programas como o RDS (Remote Desktop Services) da Microsoft e as soluções da Citrix para virtualização de desktops têm sido bastante adotados.
Essas soluções permitem que um usuário execute remotamente os programas que necessita, através de um desktop virtual acessível via rede local ou internet. Com isso, o hardware mais robusto fica centralizado num servidor e pode ser compartilhado por diversos usuários simultâneos.
Nesse ambiente, diversos usuários podem estar executando sua aplicação no mesmo computador remoto, compartilhando recursos como a pasta de trabalho, memória mapeada, disco, etc. Desse modo, as diferentes execuções simultâneas do programa podem conflitar entre si. Decisões baseadas no nome do computador, seu endereço IP ou outra informação de hardware também podem causar problemas já que todos os usuários remotos lerão os mesmos valores. A ABC71, por exemplo, usa o nome do computador onde nosso ERP está em execução como parte do controle de licenças.
Em Windows, normalmente as soluções de desktop virtual se baseiam nos serviços de Remote Desktop (antes chamado de Terminal Services), razão pela qual podemos usar a Remote Desktop Services API para preparar nossas aplicações para que rodem nesse ambiente. Essa API permite resgatar e modificar informações sobre um usuário específico, além de executar tarefas relativas à administração do serviço.
Para começar, temos que determinar se o programa está executando num desktop convencional ou num virtual. Isso pode ser conseguido com uma chamada à função GetSystemMetrics. Saber que estou numa sessão remota me permite, por exemplo, ajustar uma pasta de trabalho coerente para cada execução distinta do meu programa.
function TForm1.IsRemoteSession : boolean;
var res : integer;
begin
res := GetSystemMetrics (SM_REMOTESESSION);
Result := (res <> 0);
end;
A função GetSystemMetrics é um curinga da API do Windows; com ela é possível recuperar uma porção de parâmetros do ambiente operacional. Chamada com SM_REMOTESESSION, ela retorna um valor diferente de zero se o programa estiver num desktop virtual.var res : integer;
begin
res := GetSystemMetrics (SM_REMOTESESSION);
Result := (res <> 0);
end;
Muitas informações valiosas sobre a sessão remota em andamento podem ser obtidas através da função WTSQuerySessionInformation. No exemplo abaixo, eu a utilizo para recuperar o nome do computador que está acessando o desktop remoto:
procedure TForm1.GetSessionInfo;
var lSessionId: DWORD;
lBuffer : PChar;
lBytesReturned : DWORD;
lStationName : String;
begin
lSessionId := 0;
{ Descobre a identificação da sessão do usuário com base na identificação do programa no Windows }
if not ProcessIdToSessionId (GetCurrentProcessId (), DWORD(@lSessionId)) then
raise Exception.Create ('Não foi possível obter Remote SessoinId');
lBuffer := Nil;
lBytesReturned := 0;
lStationName := '';
{ Obtem nome da máquina Client }
if (WTSQuerySessionInformation (WTS_CURRENT_SERVER_HANDLE,
lSessionId,
WTSClientName,
lBuffer,
lBytesReturned))
then
lStationName := String(lBuffer)
else
raise Exception.Create ('Não foi possível obter o nome da estação');
{ Libera a memória alocada automaticamente }
WTSFreeMemory (lBuffer);
{ ... }
end;
Vamos por partes. Para recuperar as informações de uma sessão remota, primeiro temos que identificar essa sessão. A função ProcessIdToSessionId da API do Windows nos fornece isso, mapeando o Process ID de nosso programa para a correspondente identificação da sessão remota onde ele está executando.var lSessionId: DWORD;
lBuffer : PChar;
lBytesReturned : DWORD;
lStationName : String;
begin
lSessionId := 0;
{ Descobre a identificação da sessão do usuário com base na identificação do programa no Windows }
if not ProcessIdToSessionId (GetCurrentProcessId (), DWORD(@lSessionId)) then
raise Exception.Create ('Não foi possível obter Remote SessoinId');
lBuffer := Nil;
lBytesReturned := 0;
lStationName := '';
{ Obtem nome da máquina Client }
if (WTSQuerySessionInformation (WTS_CURRENT_SERVER_HANDLE,
lSessionId,
WTSClientName,
lBuffer,
lBytesReturned))
then
lStationName := String(lBuffer)
else
raise Exception.Create ('Não foi possível obter o nome da estação');
{ Libera a memória alocada automaticamente }
WTSFreeMemory (lBuffer);
{ ... }
end;
O passo seguinte é chamar a função WTSQuerySessionInformation para levantar as informações desejadas. Essa função recebe 5 parâmetros. O primeiro é um handle para o servidor da sessão remota. No exemplo, passei a constante WTS_CURRENT_SERVER_HANDLE para indicar que quero informações sobre o servidor atual. Poderia ter usado WTSOpenServer para abrir outro servidor remoto.
O segundo parâmetro é a identificação da sessão que obtivemos no primeiro passo. O parâmetro seguinte é o que determina qual informação será recuperada. Os valores permitidos são os listados no enumerado WTS_INFO_CLASS. No exemplo, usei WTSClientName para obter o nome do computador do usuário que diparou o acesso remoto; a lista de informações recuperáveis inclui o endereço IP desse mesmo computador, a pasta de trabalho remota (no servidor), o nome do usuário e informações sobre o uso da sessão e sobre a Client, dentre outras.
Os dois últimos parâmetros são, respectivamente, um ponteiro para a área que receberá a informação solicitada e a quantidade de bytes que essa informação está ocupando. Como ambos são calculados pela função WTSQuerySessionInformation, não é preciso alocar previamente a memória para eles. No entanto, é nossa responsabilidade liberar a memória alocada usando a função WTSFreeMemory, como mostra o passo final do código do exemplo.
Se tivesse usado GetComputerName, eu obteria o nome do computador remoto, isto é, o servidor onde o programa está efetivamente sendo executado. A questão é que o mesmo nome seria retornado para qualquer usuário que acesse um desktop remoto nesse servidor ...
A lista de funções da Remote Desktop Services API inclui ainda formas de administrar o RDS tais como iniciar e encerrar sessões, inventariar as sessões ativas, modificar configurações de uma sessão, etc.