3 de abril de 2012

Preparando aplicações Delphi para requerer incremento no Nível de Execução

Desde o Windows XP, a Microsoft vem implementando medidas que dificultam o acesso não autorizado a certos recursos do sistema, como o Registry, por exemplo. O intuito é incrementar a segurança do sistema operacional, evitando seu comprometimento ou até mesmo o roubo de informações. Esse esforço foi mais notado no Windows Vista, quando foi introduzido o UAC (User Account Control) para solicitar ao usuário permissão para acessar os recursos.

Com essa mudança, programas que antes liam tranquilamente o registro do Windows deixaram de funcionar. Dependendo de como o programa foi implementado, a exceção levantada pela falta de privilégio de acesso ao recurso pode até mesmo derrubar a aplicação com mensagens de erro pouco amistosas. O quadro abaixo traz um exemplo de código em Delphi que executa sem problemas no XP mas que não funcionará no Vista e no Win7 - a menos que o usuário comande a execução como Administrador:
procedure TForm1.BitBtn1Click(Sender: TObject);
var lReg: TRegistry;
begin
lReg := TRegistry.Create();

try
lReg.RootKey := HKEY_LOCAL_MACHINE;
lReg.OpenKey('Software\empresa', true);
lReg.WriteString('TipoImpressora', '0');
Application.MessageBox('Opção gravada com sucesso', 'Aviso', MB_OK);
except
on Erro: Exception do
Application.ShowException(Erro);
end;

lReg.Free;
end;
Felizmente, há um meio de informar ao sistema operacional que uma operação dessa natureza vai ocorrer e que, portanto, o usuário necessitará obrigatoriamente de privilégios de administrador para executar o programa. Esse processo é chamado de Requisição de Elevação do Nível de Execução e é feito através de um arquivo de manifesto que deve ser linkado junto com a aplicação.

O manifesto é um arquivo XML com estrutura bem definida, como a mostrada no quadro abaixo:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
name="ABC71.TesteManifest"
processorArchitecture="x86"
version="1.0.0.0"
type="win32"/>
<v3:trustInfo xmlns:v3="urn:schemas-microsoft-com:asm.v3">
<v3:security>
<v3:requestedPrivileges>
<v3:requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</v3:requestedPrivileges>
</v3:security>
</v3:trustInfo>
<description>TesteManifest</description>
</assembly>
O manifesto é composto de duas partes obrigatórias. O assembly é o nó raiz do XML, usado para aninhar as demais tags com as configurações em si. A tag interna assemblyIdentity identifica nossa aplicação, armazenando um nome único para ela e informando sua versão e a arquitetura para a qual ela foi desenhada. No exemplo, vemos que a nossa aplicação é Win32 e roda em processadores x86.

As outras tags são opcionais mas o nosso exemplo inclui também a tag v3:trustInfo, que é onde registramos a requisição do incremento de nível de execução. Tal requisição é feita no parâmetro level da tag requestedExecutionLevel, seguindo a estrutura mostrada acima. Quando informamos o valor requireAdministrator em level estamos dizendo ao Windows que o programa só pode executar se tiver privilégios de administrador. Então, antes de executá-lo, o Windows apresentará a tela para que o usuário forneça as credenciais do administrador - nome e senha - e só prosseguirá se elas estiverem corretas.

Para que essa configuração tenha efeito, precisamos criar um arquivo de recursos que aponta o manifesto e então, linká-lo ao programa Delphi. Um arquivo de recursos é um repositório onde são indicadas informações a serem agregadas a um programa, permitindo adicionar desde ícones e cursores até blocos de texto e dados binários para uso da aplicação. O arquivo de recurso pode ter extensão RC (quando é somente um texto) ou RES (resultado da compilação do RC). Supondo que o arquivo de manifesto se chame manifesto.manifest, um arquivo RC pode ser montado assim:
#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID 24 manifesto.manifest
O número 24 nesse arquivo indica ao Windows que o recurso em questão é um arquivo de manifesto. O arquivo RC deve ser adicionado ao projeto do Delphi e o arquivo de manifesto deve estar disponível na mesma pasta. Lembre-se que o Delphi cria automaticamente um arquivo RES com o mesmo nome do projeto; então, escolha um nome diferente do projeto para o RC extra.

Essa requisição de elevação de nível é especialmente útil em programas para configuração ou instalação de sistemas, situações que geralmente exigem privilégios mais altos para acesso a recursos protegidos, mais sensíveis. Na verdade, arquivos de manifesto são mais complexos do que o que foi apresentado aqui, servindo também para indicar se uma aplicação utilizará temas do Windows, se essa aplicação tem dependências de bibliotecas externas, entre outras coisas. O MSDN documenta o conteúdo permitido para esses arquivos neste link.

Versões mais recentes do Delphi permitem utilizar temas na criação de aplicações e, por isso, elas embutem automaticamente um manifesto em cada projeto. Assim, para conseguir inserir a requisição de incremento do nível de execução nestas versões, é preciso modificar a configuração do projeto, pedindo que se considere um arquivo de manifesto externo ao invés daquele que vem por padrão. Na página de configuração da aplicação - a mesma onde se muda o ícone do seu programa, selecione a opção "Use Custom Manifest" na caixa Runtime Themes. Depois, informe o caminho do seu arquivo de manifesto na caixa Custom Manifest, logo abaixo.

15 comentários :

Thiago disse...

Segui exatamente a recomendação e não houve mudanças na execução de quais projetos, sejam, em andamento ou somente de teste.

Não sei se tem a ver, mas o delphi eu abro sempre como administrador.

Após efetuar as manipulações acima e executar o compile, ela não muda para aquele ícone diferenciado.

Estou utilizando Delphi Xe2 no Windows 7 x64.

Você testou esse método?

Luís Gustavo Fabbro disse...

Thiago

O exemplo que aparece no post é extraído de um programa real que utilizamos na ABC71 e que está funcionando como esperado. Ele foi feito em XE2 mas ainda é Win32.

Pelo seu comentário, vc está executando o programa a partir do ambiente do Delphi. Não me lembro com certeza mas acho que, por questões de depuração, o Delphi já passa as credenciais corretas para o programa executar.

O ícone diferenciado não aparece quando vc olha pelo Windows Explorer o executável gerado ? Executando por fora do Delphi com um usuário comum as credenciais não são solicitadas ? Pode ser que o manifesto não tenha sido linkado corretamente. Vc pode verificar se o seu manifesto foi linkado ao executável fazendo a extração dos recursos embutidos nele com programas como o ResourcesExtract.

[]s

Thiago disse...

Luís, obrigado pela disponibilidade na resposta.
Eu realmente achei estranho o ocorrido, visto que além da sua técnica exposta, também verifiquei outras, mas aparentemente, no ambiente em que simulei e nos de testes, não tive muito sucesso.

Mediante ao fato, começei a alguns questionamentos em questões de Update e afins. Analisei e tomei a decisão de na instalação da minha app já se faz a indentificação de escrita na pasta referida, mesmo ela estando em ProgramFiles. Assim sendo, não terei maiores problemas futuros relacionados a atualização de arquivos, scripts e afins.

Desde já agradeço a sua atenção.

Ribamar Santos disse...

Posso usar essa API em meu sistema deskTop e comercializa-lo sem pagar nada? É free.?

Luís Gustavo Fabbro disse...

Ribamar

Esses recursos são parte integrante do Windows. Uma vez que uma versão do sistema opreacional foi adquirida, não é preciso pagar para usar a API em seu programa.

[]s

Elizangelo Macedo disse...

não funcionou, testei no windows 7

Luís Gustavo Fabbro disse...

Elizangelo

O que não funcionou? Quais os sintomas?

[]s

Marciano disse...

Vlw amigo funcionou certinho no server 2008.

Marciano disse...

Vlw amigo funcionou certinho no Server 2008R2.

Fábio Naspoolini disse...

Funcionou, para quem não funcionou deve ser porque não adicionou o arquivo RC ao project (Project -> Add to project). Dê build all depois pra garantir, se der erro feche e abra o projeto

Rafael disse...

Thiago existe a possibilidade de não pedir as credenciais? para um usuário comum. na minha rede tenho controlador de Domínio então nas maquinas que iram rodar minha aplicação são usuário sem privilégios administrador. e quero rodar minha aplicação rode.. pois a mesma so roda se eu coloca o usuario como administrador local.. se eu remover não roda.. e gostariamos de nao ter que deixar todos os usuarios como administrador local. Grato

Rafael disse...

Thiago Bom dia,

Existe a possibilidade de rodar a aplicação em Delphi 7 sem que precise colocar as credenciais de administrador.. ou Deixar o usuario como administrador para poder rodar?

Álison Bissoli Dias disse...

Redondo no windows 8 e windows 7, ambos 64

alanvcb disse...

Parece meio fora do escopo desse artigo, mas gostaria de saber te temos como pedir permissão de adm para apenas uma porção de código. No meu exemplo prático:
Estou criando um aplicativo atualizador do meu sistema. Para todas as estações, é tranquilo: Copia exes para pasta do meu sistema, executa os sqls e lindo.
Já para o servidor, preciso copiar o executável pra dentro da pasta do apache, que normalmente fica dentro do Arquivos de Programas. Nesse caso, acredito ser necessário pedir permissão de adm, mas queria fazer-lo somente no trecho de código que copia o exe do servidor.
Há algum código do tipo?

Luís Gustavo Fabbro disse...

Alan

Você pode usar uma combinação das funções LogonUser e ImpersonateLoggedOnUser, ambas parte da API do Windows. A primeira cria um token associado ao login de um usuário local e a segunda usa esse token para trocar as credenciais de segurança da thread atual para as credenciais indicadas na primeira função.

A partir desse momento, seu programa passa a executar com as novas credenciais; se forem de um administrador ou de outro usuário com permissão de gravação na pasta, a cópia pode ser feita sem problemas.

[]s

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.