Mostrando postagens com marcador Windows. Mostrar todas as postagens
Mostrando postagens com marcador Windows. Mostrar todas as postagens

19 de março de 2014

Configurando sites do IIS com Delphi e WMI - Part I

Dois dos produtos no portfólio da ABC71 são soluções para internet e que, portanto, necessitam de configurações num servidor de aplicações web antes de ficarem disponíveis para os usuários. Configurar o IIS (Internet Information Services) manualmente em si nem é tão complexo mas, dada a quantidade de detalhes envolvidos, acaba se tornando um processo bastante sujeito a falhas.

Minimizamos as possibidades de erro deste processo criando um instalador em Delphi para realizar a parte mais braçal. Ele se baseia nos recursos da tecnologia WMI (Windows Management Instrumentation) para o IIS, o que permite criar sites e pools de aplicação, gerenciar as propriedades de ambos, adicionar tipos de arquivos reconhecidos pela aplicação (MIMEs), aplicar permissões de acesso, iniciar, interromper e reciclar sites, etc.

As classes WMI para acessar esses recursos não são instalados por padrão com o IIS mas podemos garantir que eles estejam presentes com o DISM, programa do Windows que serve, entre outras coisas, para gerenciar quais recursos do sistema operacional estarão habilitados. A linha de comando a seguir habilita as ferramentas para gerenciar o ISS via WMI; ela deve ser executada no servidor do IIS com o usuário Administrador para ter efeito.
dism.exe /Online /Enable-Feature /FeatureName:IIS-WebServerManagementTools /FeatureName:IIS-ManagementScriptingTools

O comando acima pode ser extendido para incluir a instalação do IIS e ativar os recursos que sejam necessários para o funcionamento do seu site, tais como extensões e filtros ISAPI, aplicações ASP ou CGI, etc.

Falei em outra ocasião aqui no blog sobre o WMI, tecnologia da Microsoft para administração centralizada de diversos aspectos de um computador. No post Obtendo nível de sinal do Wifi usando WMI com Delphi eu importei a interface COM do WMI, obtendo acesso às funções desse mecanismo. Neste post, usarei um recurso do Delphi para tornar o acesso mais prático: late binding; ou seja, acessarei diretamente os nomes das funções e propriedades de uma classe WMI, deixando para a linguagem resolvê-los em tempo de execução.

Antes de partir para qualquer tipo de ação, precisamos obter em nosso programa uma instância do WMI. O WMI é organizado hierarquicamente, tendo uma raiz (o namespace) para cada grupo de aspectos gerenciáveis do computador, representados por classes. Dado um nome de computador e um namespace, podemos obter a respectiva instância do WMI através da função abaixo:
function GetWMIObject(wmiHost, wmiRoot: string): IDispatch;
var chEaten: Integer;
BindCtx: IBindCtx;
Moniker: IMoniker;
objectName : String;
begin
{ Monta o nome do objeto WMI desejado }
objectName := Format('winmgmts:\\%s\%s',[wmiHost,wmiRoot]);
OleCheck(CreateBindCtx(0, bindCtx));

{ Obtém um "moniker" para o objeto indicado pelo nome montado a partir do host e do root }
OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));

{ Recupera o IDispatch para facilitar o uso do objeto no programa }
OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;

O resultado deste código é a instância do WMI criada como uma interface genérica IDispatch. Para ter acesso aos recursos do IIS no computador local, chamamos esta função passando o valor '.' como Host e 'root\WebAdministration' como raiz.
var FWmiObj : IDispatch;
begin
FWmiObj := GetWMIObject('.', 'root\WebAdministration');
{ ... }

De posse da instância do WMI, agora podemos iniciar a administração de sites propriamente dita. No caso do ERP Pronto da ABC71, nós criamos um Application Pool específico para isolar a execução de nossa aplicação, protegendo-a de problemas que eventualmente surjam em outros sites no mesmo servidor. Usando o WMI, podemos perguntar ao IIS se o pool já está criado e, então, criá-lo se for necessário:
var FObj, FEnum, FItem, FAppPool: OLEVariant;
lEnum: IEnumVariant;
FNome: String;
qtde: LongWord;
begin
FObj := FWmiObj;
FNome := 'ERPProntoServApp';
FENum := FObj.ExecQuery('SELECT * FROM ApplicationPool WHERE Name="' + FNome + '"', 'WQL', 0)._NewEnum;

{ Recupera instância do IEnumerateVariant para poder navegar pelos registros encontrados. }
lEnum := IUnknown(FEnum) As IEnumVariant;

{ Navega para o 1o registro do conjunto. Se achou, FItem contem o item encontrado. }
if (lEnum.Next(1, FItem, qtde) <> 0) then begin
{ Não achou, cria o pool aqui }
FAppPool := FObj.Get('ApplicationPool');
FAppPool.Create(FNome, false);

{ Localiza o pool recém criado para poder configurá-lo }
FENum := FObj.ExecQuery('SELECT * FROM ApplicationPool WHERE Name="' + FNome + '"', 'WQL', 0)._NewEnum;

lEnum := IUnknown(FEnum) As IEnumVariant;
if (lEnum.Next(1, FItem, qtde) <> 0) then
Raise Exception.Create ('Não foi possível criar pool ' + FNome);
end;

{ Outras configurações para o Pool }
FItem.AutoStart := true;
FItem.Enable32BitAppOnWin64 := true;
FItem.ManagedPipelineMode := 0; {integrado }
FItem.ManagedRuntimeVersion := ''; { sem .NET / código gerenciado }
FItem.RapidFailProtection := false;

{ Efetiva as alterações, gravando-as no IIS }
FItem.Put_();
{ ... }

Para forçar o Delphi a usar late binding, declaro uma variável OLEVariant e lanço nela o objeto WMI instanciado. Com isso, posso inquerir a classe ApplicationPool sem me preocupar em declarar a função ExecQuery do WMI. O resultado da chamada a essa função é um variant contendo um IEnumVariant, interface que permite percorrer a lista de registros encontrados. Como este resultado não implementa a interface IDispatch, o Delphi não consegue descobrir seus métodos e propriedades automaticamente, razão pela qual a conversão explícita é obrigatória neste caso. Já para os itens extraídos do enumerado, podemos contar com o late binding normalmente, conforme demonstra o código.

Se o ApplicationPool com o nome proposto não for encontrado pela consulta, o programa cria o pool. Isto é feito obtendo uma instância estática da classe de pools e invocando o método Create dela. Após isto, o pool já está criado e pode ser posicionado normalmente através de query.

Nossa aplicação é construída em C++ 32 bits, isto é, sem código gerenciado da plataforma .NET. Assim, o final do código é reservado para garantir que o pool execute corretamente neste ambiente, ligando as configurações apropriadas e gravando-as no IIS.

Num próximo post, mostro como criar a aplicação no IIS e associá-la ao pool criado aqui.

29 de maio de 2013

Verificando a versão de bibliotecas e programas em Delphi

Um dos aspectos mais complicados de se gerenciar na entrega (deploy) de uma nova release de um programa é garantir a compatibilidade entre as bibliotecas DLL e outros executáveis que compõem a solução. Isso é particularmente verdade se esses arquivos também podem ser entregues de forma independente e/ou em pastas separadas, como em geral ocorre com os pacotes BPLs que compõem soluções em Delphi ou C++ Builder.

A ABC71 adota uma estratégia interessante para minimizar esse tipo de problema. Quando construímos aplicações Windows, sejam programas ou bibliotecas, podemos adicionar ao arquivo gerado informações extras úteis: os recursos. As informações incluídas como recursos num executável podem ser facilmente recuperadas via programação, do mesmo modo que o Windows Explorer faz quando apresenta a guia Versão nas propriedades de um arquivo.

O ERP comercializado pela ABC71 é composto por dezenas de bibliotecas e pacotes independentes, contendo as diversas funcionalidades do sistema. A ideia é incluir um número de versão em todos os executáveis, através de recursos do Windows. Como cada arquivo é carregado dinamicamente conforme a necessidade, podemos utilizar o momento da carga para verificar se a versão do arquivo é compatível com o programa e, em caso negativo, notificar o usuário para que ele atualize os arquivos. Esse mecanismo pode ser aplicado também a bibliotecas de terceiros, como o ADO ou pacotes de componentes visuais.

Para implementar uma verificação nesses moldes em suas próprias bibliotecas, o primeiro passo é adicionar a elas um arquivo de recursos do Windows com informações relevantes sobre a versão atual. O quadro a seguir mostra um trecho do arquivo RC que adicionamos a nossos projetos:
#define DATARELEASE "28/05/2013\0"
#define VERSAO "26.9.5.6\0"
#define VERSAO_DB "9.5\0"

#ifdef WCOMP1
#define DESCRICAO "Componentes auxiliares\0"
#define NOME_INTERNO "WComp1\0"
#else
#define DESCRICAO "ERP Omega\0"
#define NOME_INTERNO "Omega\0"
#endif

VS_VERSION_INFO VERSIONINFO
FILEVERSION 26,9,5,6
PRODUCTVERSION 26,9,5,6
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x8L
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments",DESCRICAO
VALUE "CompanyName","ABC71 Soluções em Informática\0"
VALUE "InternalName", NOME_INTERNO
VALUE "LegalCopyright", "2013 \xA9 ABC71 Soluções em Informática\0"
VALUE "ProductName", "ERP OMEGA\0"
VALUE "ProductVersion", VERSAO
VALUE "FileDescription", "ERP OMEGA\0"
VALUE "FileVersion", VERSAO
VALUE "VersaoBaseDados", VERSAO_DB
VALUE "DataRelease", DATARELEASE
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
O exemplo usa compilação condicional para diferenciar os diversos projetos, permitindo reaproveitar o mesmo arquivo em todos eles. Isso elimina a necessidade de alterar cada um deles quando a versão (ou outra informação) muda; basta recompilá-los com a versão nova do arquivo de recursos. Note que a sintaxe do arquivo lembra mais a do C/C++ do que a do Pascal/Delphi, incluindo os IFDEF e o terminador nulo ao fim de cada texto fixo.

Uma vez incluídas essas informações em cada executável, pacote e biblioteca, o passo seguinte é extrai-las para comparar com as existentes no programa que está carregando o pacote/biblioteca. A API do Windows possui uma série de funções para essa tarefa, agrupadas sob o nome de Version Information Functions. O quadro abaixo mostra uma função simples usando essa API; ela é capaz de recuperar qualquer uma das informações contidas no arquivo de recursos.
function GetStringValue (AHandle: HMODULE; ATexto: String) : String;
var lIgnore, lTamInfo: DWORD;
lBuffer: LPVOID;
lInfo: PChar;
lPath, lTrans: String;
lModulo: array[0..MAX_PATH] of Char;
begin
{ Obtem o nome do executável, DLL ou pacote cujo handle foi informado no parâmetro }
GetModuleFileName (AHandle, lModulo, sizeof(lModulo));

{ Calcula o tamanho que deve ter o buffer para recuperar as informações de versão }
lTamInfo := GetFileVersionInfoSize(lModulo, lIgnore);
GetMem (lBuffer, lTamInfo);

{ Recupera as informações no buffer alocado }
if GetFileVersionInfo(lModulo, lIgnore, lTamInfo, lBuffer)
then begin
{ Lingua 0409 com code page 04B0. No arquivo de recurso desse exemplo existe apenas textos nessa lingua.}
lTrans := '040904B0';

{ Monta o caminho onde está o texto procurado no buffer }
lPath := '\StringFileInfo\' + lTrans + '\' + ATexto;

{ Recupera o valor do texto solicitado }
if VerQueryValue (lBuffer, pChar(lPath), Pointer (lInfo), lTamInfo)
then
Result := lInfo
else
Result := '';
end;
FreeMem(lBuffer);
end;
Pelo código no quadro, observamos três etapas distintas para obter o valor desejado. Primeiro, a função GetFileVersionInfoSize nos reporta o tamanho do bloco de informações de versão contido no módulo (executável, pacote ou DLL). Com essa informação, podemos alocar a memória necessária para ler o bloco todo.

Na segunda estapa, a função GetFileVersionInfo extrai o bloco de informações e o coloca na memória que alocamos anteriormente. Finalmente, o programa busca o texto solicitado montando um caminho e chamando a função VerQueryValue. Um arquivo de recursos pode ser organizado com blocos de textos em línguas específicas (português, inglês, etc.) de modo que o conteúdo apropriado pode ser exibido de acordo com as preferências do usuário; por questão de simplicidade, esse exemplo trata apenas uma língua, motivo pelo qual foi possível manter a busca fixa, sem me preocupar em levantar as línguas incluídas.

Com essa função, podemos obter, por exemplo, a data de release do módulo usando o seguinte código:
var data: String;
begin
{ Obtem a data de release contida no programa atual }
data := GetStringValue (0, 'DataRelease');
Para implementar a verificação de compatibilidade de versão, podemos incluir código similar ao anterior na área de initialization numa unit de um pacote:
var dataP, dataE : String;

initialization

dataE := GetStringValue (0, 'DataRelease');
dataP := GetStringValue (HInstance, 'DataRelease');

if (dataE <> dataP) then begin
ShowMessage ('Este pacote é incompatível com o executável.'#13#10 +
' Data do Pacote : ' + dataP + #13#10 +
' Data do Executável : ' + dataE );
ExitProcess(-1);
end;
end.
Com isso, quando nosso programa carregar esse pacote, o código acima obterá o campo DataRelease tanto do executável quanto do próprio pacote e, se não forem compatíveis, o programa é abortado. O exemplo é bastante simples mas a solução pode ser usada para contemplar informações mais complexas, como um número mínimo de versão ou release. Essa técnica também é aplicável às DLLs.

Há um artigo neste endereço do site delphiDabbler dando explicações mais detalhadas sobre o funcionamento das APIs de versão do Windows. Ele também constrói uma classe para encapsular a leitura dessas informações, facilitando bastante o processo de extrair aquelas que interessam para a validação de compatibilidade.

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.

30 de dezembro de 2011

Preparando a execução de um programa em desktop remoto

A adoção maciça da internet e os benefícios associados a ela têm levado as empresas a optar cada vez com mais frequência por programas que possam ser executados nessa infraestrutura. O principal benefício aqui está relacionado aos custos já que os desktops não precisam ser tão potentes - basta que tenham acesso à internet para executarem esses programas remotos - e a manutenção dos equipamentos passa ser centralizada.

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.

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.

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.

15 de setembro de 2011

Funcionamento do Certificate Store do Windows

Tenho recebido com frequência dúvidas a respeito do acesso a certificados digitais importados no Certificate Store do Windows. Pela forma com que as dúvidas têm sido colocadas, percebi que faltou nos posts sobre esse assunto dar uma ideia global do que é o Certificate Store e como ele funciona.

Como disse no post sobre acesso ao Certificate Store do Windows com C#, o Certificate Store é um repositório centralizado onde são armazenados os certificados digitais disponíveis em um computador. É possível acessar esse repositório visualmente, acessando o console de gerenciamento pela opção Executar do menu Iniciar do Windows. Na caixa de edição que se abre, digite o comando abaixo:
mmc \windows\system32\certmgr.msc
Normalmente, a tela que aparece é similar à que está reproduzida abaixo:
Certificados Digitais
Com essa aplicação, é possível gerenciar os certificados instalados no computador, fazendo a importando de novos certificados e eliminando aqueles que não são mais desejados. Observe que o painel à esquerda mostra uma organização hierárquica, com uma raiz e diversas pastas embaixo dela. O nó raiz nesse exemplo apresenta o nome Certificates - Current User. Isso significa que todas as pastas sob ele e os certificados listados no painel à direita estão disponíveis apenas para o usuário que atualmente está logado no Windows.

As pastas servem para agrupar os certificados por semelhança na finalidade de uso. Na imagem, a pasta Personal está selecionada, permitindo-nos visualizar à direita os certificados catalogados para uso pessoal do usuário Windows atual. Outro exemplo de pasta é a que contém certificados identificando as Autoridades Certificadoras (CA), que são as entidades responsáveis pela emissão dos certificados.

Do ponto de vista do programador que vai acessar o Store usando a classe X509Store (ou o CAPICOM), essas duas informações - nó raiz e pasta - são imprescindíveis. O construtor da X509Store aceita como parâmetros o Store Name e o Store Location, sendo que o name corresponde à "pasta" onde está o certificado e location é o nó raiz ao qual a pasta está atrelada. A pasta personal é mapeada com o nome "My" para essa função.

Agora, imagine que você construiu um serviço que acessa o Certificate Store. No Windows, cada serviço é executado por um usuário específico que é configurado na guia Log On das propriedades do serviço. Com isso, ele poderá entrar em execução mesmo que ninguém esteja logado no Windows pois o login será feito automaticamente para o usuário que foi associado ao serviço. Portanto, se você importou um certificado para seu usuário mas está executando o serviço com um usuário diferente, esse outro usuário não enxergará o seu certificado.

Isso vale também para aplicações ISAPI - sites construídos em ASP ou ASP.NET se encaixam nessa categoria - uma vez que elas rodam como parte do serviço do IIS (Internet Information Services). O IIS é atrelado normalmente a um usuário padrão do Windows chamado Local System, significando que as credencias desse usuário é que serão utilizadas para acessar os recursos do sistema, tais como o registry e a hierarquia de certificados.

Para resolver esse impasse, podemos importar o certificado num Store Location acessível por todos os usuários. Como a aplicação disponível carrega apenas originalmente o Location "Current User", teremos que adicionar na mão um plugin para o Location com a característica que precisamos. Esse Location é o "Local Computer".

Para isso, execute o MMC.exe sem parâmetros no Iniciar -> Executar do Windows. No menu File da aplicação, clique na opção Add/Remove snap-in e então o botão "Add" para ativar a tela de adição dos plugins. Na lista de plugins que aparece, selecione certificates e novamente clique em "Add". A imagem abaixo mostra a tela de adição do plugin de gerenciamento de certificados com a opção "Computer Account" (o computer local) selecionada.
Add snap-in

Após a adição, o console mostrará uma hierarquia similar à da imagem no início deste post, com a exceção de que o nó raiz será "Local Computer". Agora podemos importar certificados na pasta "Personal" e torná-los disponíveis para os programas, independentemente das credenciais do usuário que está executando esse programa ou serviço.

Certificados importados na pasta "Personal" do "Local Computer" podem, então, ser acessados abrindo-se o Certificate Store com os parâmetros mostrados abaixo:
X509Store lStore = new X509Store (StoreName.My, StoreLocation.LocalComputer);
/* ... */


2 de junho de 2011

Microsoft mostra recursos da próxima versão do Windows

Nem bem acabou de lançar o Windows 7, a Microsoft já estava planejando a nova versão. A empresa havia feito antes algumas revelações sobre como seria o Windows 8 mas em primeiro de junho chegaram a mostrar coisas mais concretas a esse respeito. Ao que parece, a proposta é aproximar o Windows Phone daquele voltado para desktops, de modo que a interação com o usuário seria idêntica em um e outro.

A aproximação ainda teria como objetivo preparar uma versão otimizada para tablets, disponibilizando a mesma usabilidade nas três plataformas. Apesar da pressa pra não perder o bonde, essa versão específica para tablets deverá ser entregue, na melhor das hipóteses, no começo de 2012. Mas, segundo o Citigroup, a Microsoft não está atrasada para a festa dos tablets pois há uma base muito grande de programas feitos para seu sistema operacional. Como ela está trabalhando para garantir que esses programas rodem também nos tablets, muitos dos que os usuários já estão acostumados a utilizar em seus pcs estarão automaticamente disponíveis nos dispositivos com o novo Windows.

Mesmo que seja um sistema operacional muito bem resolvido e abarrotado de recursos interesses - como parece ser o Windows Phone 7 - , somente o tempo dirá se eles estão ou não atrasados. Se demorarem muito, os usuários que hoje são fiéis a programas Windows já podem ter encontrado substitutos a altura nos sistemas de seus tablets.

O texto que vai abaixo é uma reprodução da matéria publicada pelo IDG Now! a respeito da apresentação dos recursos previstos para o Windows 8. A reportagem original pode se acessada aqui.
O Windows 8 está a caminho, e será bem diferente do Windows que conhecemos hoje. Esta foi a mensagem que a Microsoft passou na noite desta quarta-feira (01/06) durante a conferência anual D: All Things Digital, fealizada próxima a Los Angeles, EUA.

Na conferência a Microsoft apresentou uma versão preliminar do Windows 8, com uma interface radicalmente modificada. Em vez do tradicional desktop com janelas, barra de tarefas e Menu Iniciar, a interface se parece muito com a usada no Windows Phone 7, completa com "Tiles" otimizadas para uso com o toque.

A Microsoft também postou um vídeo que mostra alguns dos novos recursos. A empresa diz que a interface foi projetada não apenas para laptops e desktops, mas para tablets também. A nova interface com Tiles, "quadrados" coloridos que exibem informações e servem de atalho para aplicativos, substitui o tradicional menu iniciar e os ícones no desktop, de acordo com a empresa.

O Windows 8 irá rodar aplicativos Windows já existentes e terá suporte aos periféricos já existentes. No vídeo a Microsoft mostra aplicativos tradicionais, como o Office, rodando em uma interface desktop similar à do Windows 7, e é possível trocar entre a nova interface e o modo desktop ao toque de um botão.

O sistema também terá uma versão do navegador Internet Explorer 10 otimizada para toque, muito similar à versão do Internet Explorer utilizada no Windows Phone 7. Para facilitar o uso do sistema em Tablets a Microsoft também incluiu um teclado virtual completo, com um modo "separado" que agrupa as teclas nas laterais da tela para facilitar a digitação com os dedões.

Aplicativos desenvolvidos especialmente para o Windows 8 também serão diferentes, e usarão "o poder do HTML5, acessando recursos nativos do Windows através de JavaScript e HTML", diz a empresa. Ainda não está claro se isso significa que aplicativos para o Windows 8 serão exclusivamente baseados em HTML5, JavaScript e outras tecnologias originadas na web ou se haverá a possibilidade de usar ferramentas de desenvolvimento mais tradicionais já oferecidas pela Microsoft e outras empresas.

O que é interessante no Windows 8 é que ele é outro passo rumo à transformação dos computadores pessoais em aparelhos mais parecidos com tablets. A Apple também está deixando o Mac OS X mais similar ao iPad ao incorporar vários recursos do iOS ao Mac OS X 10.7 "Lion", embora o Windows pareça ir um passo adiante na fusão entre os tablets e os PCs.

A própria Microsoft confirma o escopo das mudanças, dizendo que o Windows 8 é "uma recriação do Windows, do processador à interface. Um PC com o Windows 8 é na verdade uma nova categoria de aparelho, que pode ir de telas pequenas sensíveis ao toque até os grandes monitores de um desktop, com ou sem um teclado e mouse".

Provavelmente haverá resistência a estas mudanças e teremos que esperar para ver como tudo funciona na prática. Mas o rumo está traçado.


3 de setembro de 2010

Criando Serviços que permitam várias instalações num mesmo computador

Como parte de sua solução de ERP Omega, a ABC71 entrega a seus Clientes um Serviço do Windows para execução de processos de forma agendada. Isto é, o Cliente dita a frequência e o horário mais apropriado e o Serviço, que pode ser instalado em outro computador, se encarrega da execução na data e hora indicada.

A maioria de nossos Clientes precisa gerenciar apenas um banco de dados, mesmo quando há mais de uma planta envolvida já que cada uma controla seu próprio banco. Essa característica permitiu ao Serviço descrito no parágrafo anterior atender muito bem a necessidade de agendamento desses Clientes. A situação mudou quando em alguns Clientes se fez necessário monitorar de forma centralizada as execuções em mais de um banco simultaneamente, cada um deles representando uma filial distinta do mesmo Cliente.

Um solução óbvia é fazer com que o mesmo Serviço do Windows seja instalado mais de uma vez e, então, cada instalação cuida de um banco de dados diferente.

Implementar essa solução num programa feito em C++ Builder ou Delphi é relativamente simples, exigindo duas alterações na forma de se registrar o serviço. Uma vez que o Windows não aceita a existência simultânea de serviços com um mesmo nome interno, a primeira modificação é fazer com que cada instalação seja feita com um nome diferente. A segunda modificação no programa diz respeito a como informar ao Serviço qual é o banco de dados que ele deve usar. A resposta curta para isso é passar a informação como um parâmetro na linha de comando registrada para o Serviço.

A linha de comando nada mais é que o nome do programa (com o caminho e a extensão EXE) seguido de valores separados por espaços em branco. Tais valores são repassados para o programa quando ele é executado, de forma que é possível lê-los e utilizá-los conforme a necessidade. A linha de comando de um serviço pode ser alterada após sua instalação através da função ChangeServiceConfig da API do Windows. Então, nós a chamaremos no evento AfterInstall do TService:
void __fastcall TSchedService::ServiceAfterInstall(TService *Sender)
{
SC_HANDLE mh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (mh != NULL)
{
{ Abre o serviço com o Nome calculado para distinguir instâncias que apontem para bancos de dados diferentes. }
SC_HANDLE sh = OpenService(mh, Name.c_str(), SC_MANAGER_ALL_ACCESS);
if (sh != NULL)
{
{ Incluir como parâmetro de execução do serviço o nome da configuração que deve ser usada, conforme definido pelo Configurador do Scheduler }
AnsiString lParam = "USE_ALIAS=\"" + _CfgName + "\"";
AnsiString lPath = System::ParamStr (0) + " " + lParam;

ChangeServiceConfig(sh, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE, lPath.c_str(), NULL, NULL,
NULL, NULL, NULL, NULL);

CloseServiceHandle (sh);
}
CloseServiceHandle (mh);
}
};

A expressão System::ParamStr (0) dá acesso ao caminho completo do executável para o Serviço, isto é, do programa que está atualmente em execução. Veja que foi criado um par de valores no formato NOME=VALOR, que é acrescentado ao executável. Durante a execução do serviço eu consigo obter essa informação e extrair o valor. Segue o código para realizar essa extração em C++ Builder - no Delphi, varia apenas a sintaxe:
AnsiString __fastcall TSchedService::ExtractCfgName (void)
{
bool lOk = false;
AnsiString lNomeParam ("USE_ALIAS="), ret;
int i = 1, lCompr = lNomeParam.Length();

{ Percorre os parâmetros da linha de comando para encontra o que nos interessa }
while ((i <= System::ParamCount()) && (lOk == false))
{
ret = System::ParamStr (i);

if (ret.SubString (1, lCompr) == lNomeParam)
{
lOk = true;
{ Extrai apenas o valor após o sinal de igual }
ret = ret.SubString (lCompr+1, ret.Length ());
}
i ++;
}

if (! lOk)
ret = "";
return (ret);
};

No meu caso, o valor extraído representa uma chave do Registry onde gravei com antecedência toda a configuração para acessar o banco de dados correto mas poderia representar um nome de arquivo ou uma tag num XML, por exemplo. Em última análise, eu poderia ter passado todas as informações necessárias para a conexão com essa técnica mas isso tornaria mais complicado dar manutenção no programa, exigindo atenção especial à passagem dos parâmetros e ao limite de tamanho da linha de comando (normalmente, 256 caracteres).

Serviços do Windows têm um nome interno usado pelo sistema operacional como identificador único e um nome externo usado como uma descrição a ser apresentada ao usuário. Tanto o C++ Builder quanto o Delphi usam como nome interno do Serviço o valor da propriedade Name do componente TService. Por isso, é preciso modificar esse valor antes do programa ter a oportunidade de registrar o Serviço ou realizar qualquer outra operação que dependa do nome. Um bom lugar é o próprio construtor do TService.

Uma implicação importante ao se usar esse método é que alterar o nome de um componente nos força a respeitar as regras de nomeação do ambiente. Ou seja, o nome não pode ser iniciado por um número, não pode apresentar espaços em branco nem caracteres que não existam na língua inglesa, como caracteres acentuados (agudo, circunflexo, til, trema, crase) ou o cê cidilha. Podemos usar o próprio nome da configuração obtido anteriormente para diferenciar cada instância instalada. Para que isso funcione, teremos que preparar o nome obtido para garantir sua validade, substituindo os caracateres que não forem permitidos. Veja um exemplo:
char __fastcall TWConfigSched::PrepChar (char AChar)
{
char lCh;
switch (Byte (AChar) )
{
case 192: case 193: case 194: case 195:
case 196: case 197:
lCh = 'A';break;
case 199:
lCh = 'C';break;
case 200: case 201: case 202: case 203:
lCh = 'E';break;
case 204: case 205: case 206: case 207:
lCh = 'I';break;
case 210: case 211: case 212: case 213:
case 214: case 216:
lCh = 'O';break;
case 217: case 218: case 219: case 220:
lCh = 'U';break;
}

{ Outros caracteres inválidos são substituídos pelo underscore }
if ( ! ( (lCh >= '0' && lCh <= '9') ||
(lCh >= 'a' && lCh <= 'z') ||
(lCh >= 'A' && lCh <= 'Z') ) )
{
lCh = '_';
}

return (lCh);
};

Como esta função faz a troca de um único caracter, é preciso percorrer todo o nome do Serviço e trocá-los um a um para compor o novo nome. O conjunto de substituições reproduzido acima não está completo - falta tratar caracteres minúsculos e aqueles usados por línguas como o espanhol e o alemão (N com til, por exemplo). No entanto, as letras que não se encaixarem na substituição direta são trocadas por um underscore.

Mais Informações API de Serviços do Windows, Criação de Serviços (parte 1 e parte 2)

18 de junho de 2010

Registrando automaticamente uma fonte de dados para acesso ODBC

Muitas aplicações que trabalham com banco de dados ainda fazem o acesso através de uma fonte de dados ODBC. Isso normalmente exige que se faça uma configuração através das Ferramentas Administrativas do Painel de Controle do Windows em cada estação que fará uso da aplicação em questão.

A ABC71 usa o ADO com OleDB na maioria de suas aplicações para acessar banco de dados. Os poucos módulos que necessitam ter uma configuração ODBC externa são os relacionados à Qualidade (Metrologia e Inspeção da Qualidade), que já estão sendo revistos e também deverão adotar o ADO em breve.

Enquanto isso não acontece, incluimos no projeto desses módulos código para criar automaticamente as configurações, de modo que o usuário não precisa se preocupar com essa criação. Basicamente, configurar uma fonte de dados ODBC é criar algumas entradas no registry do Windows - a localização das chaves variará, dependendo se você quer disponibilizar a fonte só para o usuário atual do Windows ou para todos. Qualquer que seja o caso, a hierarquia de chaves é \\SOFTWARE\\ODBC\\ODBC.INI. Esta chave é onde são armazenados os drivers ODBC instalados no computador. Ai também fica a chave "ODBC Data Sources", que, como diz o nome, é onde o Windows armazena as fontes de dados para conexões ODBC. Veja abaixo um exemplo em C++ Builder para acessar essa chave. Para usar em Delphi, basta adaptar a sintaxe para Pascal.
void TWRegConfig::ConfigODBC (AnsiString ServerName)
{
AnsiString metrologia ("mt305w");
AnsiString sqlServer ("SQL Server");
AnsiString _OdbcIni = "\\SOFTWARE\\ODBC\\ODBC.INI\\";

TRegistry *_Reg = new TRegistry ();
_Reg->RootKey = HKEY_LOCAL_MACHINE;

_Reg->OpenKey (_OdbcIni + "ODBC Data Sources", true);
/* ... */
No código acima, é criada uma instância da classe TRegistry que encapsula o acesso ao registry do Windows. A raiz para o acesso (RootKey) no exemplo é o HKEY_LOCAL_MACHINE, indicando que acessaremos o local de armazenamento das fonte de dados disponíveis para todos os usuários do computador. Use HKEY_CURRENT_USER como RootKey para acessar apenas as fontes do usuário atual do Windows.

O comando OpenKey abre a chave especificada, preparando-a para manutenções (leitura e gravação). O segundo parâmetro da função OpenKey indica se a chave deve ser criada caso ainda não exista. Agora, podemos criar as entradas que configuram a fonte de dados ODBC:
/* ... */
if ( _Reg->KeyExists(_OdbcIni + metrologia) == false)
{
/* cria o nome da fonte de dados, indicando o tipo de banco de dados que será acessado */
_Reg->WriteString (metrologia, sqlServer);

/* efetivamente faz a configuração da fonte de dados */
_Reg->OpenKey (_OdbcIni + metrologia, true);
_Reg->WriteString ("driver", "C:\\WINDOWS\\System32\\SQLSRV32.dll");
_Reg->WriteString ("Description", metrologia);
_Reg->WriteString ("Server", ServerName);
_Reg->WriteString ("LastUser", metrologia);
}
delete _Reg;
Pelo exemplo acima, nota-se que há duas informações distintas a serem gravadas. A primeira é o nome da fonte de dados, isto é, o nome pelo qual referenciaremos a fonte de dados quando formos conectar via ODBC. Veja que é gravado o nome da fonte (Metrologia) e o tipo de banco de dados que desejamos (Sql Server). Os valores válidos para tipo de banco depende do que você tem instalado no computador. Este valor tem que corresponder a uma das entradas existentes na chave ODBCINST.INI.

A segunda informação é uma chave do Registry que deve ser criada como sub-chave do ODBC.INI com o mesmo nome da fonte de dados - no exemplo, este nome é Metrologia. A configuração propriamente dita é feita criando-se entradas dentro dessa nova sub-chave. Algumas informações são comuns a todos os tipos de banco de dados, como é o caso de driver e Description; outras, são específicas de cada tipo de banco. No exemplo aparecem as entradas necessárias para configurar um banco MS SQL Server.

A informação menos óbvia do exemplo é o driver. Essa entrada deve indicar o caminho da biblioteca do próprio fabricante; biblioteca esta que fará, em última análise, todo o acesso ao banco de dados. O valor correto pode ser facilmente encontrado dentro da chave ODBCINST.INI. Como dentro dela há uma chave para cada driver instalado, basta procurar o nome do driver desejado (SQL Server, por exemplo) e copiar o valor da entrada cujo nome é "Driver".

Uma forma de descobrir quais entradas devem ser criadas é criando uma fonte de dados manualmente através do painel de controle do Windows e depois analisar o resultado na hierarquia do ODBC.INI dentro do registry.

31 de maio de 2010

Fazendo aplicações ASP tradicionais funcionarem em servidores 64 Bits

Já tem algum tempo que os sistemas operacionais de 64 bits da Microsoft estão no mercado - Windows Vista, Seven, Windows Server - mas parece que agora é que as empresas estão de fato atualizando seus servidores e adotando em massa essa tecnologia. Pelo menos entre os Clientes da ABC71 essa afirmação vem se mostrando verdadeira.

Embora aplicações 32 bits funcionem normalmente nos sistemas de 64 bits (usando automaticamente a emulação do ambiente 32 bits), sempre aparece alguma situação problemática. É o caso da biblioteca que a ABC71 desenvolveu para que seus Clientes possam construir sites de e-Vendas integrados com o ERP Omega, alimentando diretamente suas tabelas com pedidos de venda on-line de uma maneira que respeita as regras de negócio vigentes no ERP. Essa biblioteca disponibiliza suas funções através de COM mas foi construída para uso com o ASP tradicional (não com o ASP.NET), sendo portanto uma tecnologia 32 bits.

Quando um Cliente nosso trocou seu servidor por um com sistema de 64 bits, o site dele simplesmente parou de funcionar. A razão é que as hospedagem do site utiliza o IIS 7 (Internet Information Services), versão de 64 bits e não é possível fazer um software de 64 bits carregar e executar um de 32 bits diretamente. Para fazer isso, teríamos que construir alguma ponte que permitisse tal acesso.

Algumas soluções foram sugeridas. A de usar uma máquina virtual com algum Windows de 32 bits exclusivamente para hospedar o site foi descartada pois a intenção da troca de servidores era incrementar a performance do site, objetivo que ficaria restringido.

Compilamos, então, a biblioteca de modo que ela passasse a usar .NET 64 bits. Após registrá-la corretamente como um COM usando a ferramenta RegAsm 64 bits (no meu computador, essa versão está na pasta C:\Windows\Microsoft.NET\Framework64\v2.0.50727) passamos a receber nas páginas ASP uma mensagem indicando que o tipo 'InternalField' não pode ser convertido para o tipo 'Short' toda vez que tentávamos passar um valor como parâmetro em chamadas a funções da biblioteca COM. Parece que há alguma coisa no ASP normal executando em IIS7 que o impede de trabalhar com um COM gerado em .NET 64 bits.

Quando pesquisei a respeito na internet, me deparei com um tutorial passo a passo mostrando como configurar o IIS 7 para rodar aplicações ASP que utilizam banco de dados. O tutorial é mais completo do que eu precisava - muitas das configurações eu já havia feito - mas me apresentou um parâmetro que eu desconhecia e que resolveu o problema de incompatibilidade. O parâmetro é o Habilitar aplicativos de 32 Bits, como mostra a imagem abaixo:
Configuração do IIS 7
O valor desse parâmetro é FALSE por padrão, significando que o site só executará aplicações de 64 bits. Para mudar-lhe o valor, acesse o Gerenciador do IIS 7, clique em Pool de Aplicativos e selecione o site que deseja configurar. Depois, escolha a ação Configurações Avançadas e habilite aplicativos 32 bits no site selecionando TRUE como valor da propriedade. Apenas o site em questão será afetado.

Com essa configuração ativa, o IIS passou a entender todo o site como sendo uma aplicação 32 bits de forma que mesmo a biblioteca original pode ser carregada sem problemas. Assim, foi possível continuar usando todo o site feito em ASP normal e também a biblioteca COM sem modificações - apenas reconfigurando o servidor.

24 de maio de 2010

Transportando arquivos muito grandes

Nas versões do Windows que ainda possuem suporte hoje em dia – XP, Vista, Seven e suas variações – os discos rígidos (HDs) são quase sempre formatados usando a estrutura de pastas chamada NTFS (NT File System, ou seja, sistema de arquivos do Windows NT). No entanto, muitos dos pen drives e HDs externos ainda vêm formatados usando FAT32, sistema de arquivos usado pelas versões anteriores do Windows (95 e 98) mas ainda reconhecidos nas versões mais recentes.

Ambos os formatos servem ao mesmo propósito, que é organizar o acesso aos arquivos que existem num disco. O que os difere é como é feita essa organização e os recursos adicionais proporcionados por cada formato. O NTFS é mais robusto, tendo sido projetado para, entre outras coisas, comportar informações de segurança mais refinadas, permitindo que sejam inseridas configurações de acesso no nível de pasta e até de arquivo, de modo que apenas usuários cujas credenciais atendam tais configurações é que podem acessar uma pasta ou arquivo. O FAT32, por ser mais antigo, é mais simples e não permite essa distinção.

Outra diferença crucial entre os dois formatos é o tamanho máximo que um arquivo pode alcançar. Enquanto no FAT32 o tamanho máximo não deve passar de 4 GB (gigabytes), a implementação atual do NTFS aceita arquivos com até 16 TB (terabytes).

Por essa razão, se você possui um arquivo de imagem de um DVD (4.5 GB) , um backup de um banco de dados de um Cliente ou outro arquivo qualquer cujo tamanho ultrapasse 4 GB, será um problema copiá-lo de um sistema com NTFS para um pen drive formatado com FAT32. Embora seja permitido que se formate um pen drive ou HD externo com NTFS, nem sempre isso é desejado.

Há uma ferramenta bastante simples e eficaz para resolver este tipo de impasse. Trata-se do HJ-Split, uma aplicação gratuita que não necessita instalação e que se propõe a fracionar um arquivo qualquer, criando arquivos menores que depois podem ser reagrupados para formar o arquivo original novamente.

A interface inicial da aplicação apresenta 4 funções representadas por botões: Split (fracionar), Join (juntar), Compare (comparar dois arquivos) e Checksum (calcular identificação para validar um arquivo). A reprodução abaixo é a tela apresentada quando se solicita a função Split para quebrar um arquivo em arquivos menores:
HJ-Split
Para usar esta função, basta informar o arquivo original, o tamanho máximo que cada fragmento gerado deverá ter e pressionar o botão Start. Na mesma pasta onde está o arquivo original serão gerados tantos fragmentos de arquivos quantos forem necessários, respeitando-se o tamanho máximo estipulado para cada parte. Em cada fragmento será acrescentado um número sequencial (começando com 001) indicando a posição do fragmento dentro do arquivo original. Esses arquivos menores agora podem ser copiados para o pen drive sem problemas.

Quando precisar do arquivo completo novamente, use a função Join do HJ-Split. Nessa função, o botão Input File permite que se localize o primeiro fragmento do conjunto que foi gerado com a função Split – aquele arquivo com o 001 acrescentado ao nome. É importante que todos os fragmentos estejam na mesma pasta para que o HJ-Split os encontre. Informe também a pasta de destino, isto é, o local onde será recriado o arquivo original completo.

No endereço http://www.freebyte.com/hjsplit há links para download de versões para diversos Sistemas Operacionais (Linux, Mac, Windows, etc.).

21 de janeiro de 2010

Explorando informações sobre programas em execução

Quem trabalha com desenvolvimento de software certamente já se deparou com a situação de enviar para um Cliente o programa compilado mas esquecer de enviar uma ou mais das bibliotecas dinâmicas (DLLs). Tendo este tipo de problema em mente, passei a buscar algum meio de fazer um levantamento das DLLs necessárias para a execução de um programa.

Como resultado desta busca, acabei me deparando com uma boa surpresa: uma ferramenta chamada Process Explorer. Esta ferramenta era uma das desenvolvidas pela empresa SysInternals mas atualmente pode ser encontrada diretamente no Technet - o site técnico da Microsoft. Digo que foi uma grata surpresa porque esta ferramenta faz muito mais coisas do eu estava procurando, com funcionalidades que realmente podem ser muito úteis para desenvolvedores e profissionais voltados para suporte.

Ao entrar no Process Explorer, ele imediatamente varre os processos que estão em execução em seu computador e os lista num painel na metade superior da interface gráfica, incluindo informações como o nome do fabricante e uma descrição do programa conforme registrado no próprio executável do processo.
Tela do Process Explorer

Clicar com o botão direito do mouse na barra onde estão os nomes das colunas permite selecionar a exibição de outros dados. As colunas disponíveis incluem informações sobre o consumo de memória pela aplicação (virtual, paginada, working set, etc.) dados do handle do processo e dados sobre sua performance (uso da cpu, etc.) e disponibiliza até mesmo qual a conta de usuário sendo usada para a execução.

Clicar com o botão direito do mouse sobre um dos processos dá acesso a outras ações, como modificar a prioridade de execução, suspender a execução temporariamente ou até mesmo interrompê-lo definitivamente. A opção "Propriedades" carrega uma janela onde todas as informações disponíveis sobre o processo são exibidas de forma centralizada (threads, segurança, linha de comando, conexões TCP/IP ativas e outras informações), além de monitorar em tempo real a execução do processo, mostrando gráficos de performance e variação do uso da memória. Ou seja, o Process Explorer é uma espécie de Task Manager turbinado.

Finalmente, selecionar um dos programas da lista faz com que o Process Explorer faça o levantamento de todas as bibliotecas dinâmicas (DLL) que estão carregadas pelo processo no momento. A lista de biblitoecas e os dados coletados sobre elas são então apresentados num outro painel, na parte de baixo da interface gráfica. Como esse painel permite exibir a versão de cada DLL, fica fácil também verificar se as versões carregadas são as corretas e não alguma outra que pode causar erros durante a execução. Ou seja, ajuda a detectar ocorrências de DLL Hell. Para desenvolvedores, esse painel pode ainda mostrar informações sobre o uso de memória por cada uma das bibliotecas e o nome da pasta a partir de onde ela foi carregada.

Um outro recurso interessante é o que permite mostrar todos os arquivos que estão abertos por um processo. Esse recurso pode ser acessado através da opção "Handles", encontrada no menu View e Lower Pane View, sendo que a lista de arquivos substitui a visão de DLLs no painel inferior.

Há ainda um ferramenta de busca no Process Explorer que permite encontrar rapidamente nomes de DLLs, pastas ou arquivos que estiverem em uso em qualquer um dos processos ativos.

O donwload do Process Explorer é gratuito e pode ser feito a partir do endereço http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx.

26 de outubro de 2009

Descobrindo se seu computador comporta o Windows 7

Sempre que sai uma nova versão de um Sistema Operacional - notadamente o Windows - temos uma certa resistência em fazer a troca. As preocupações vão desde o básico "será que o novo sistema terá um boa performance no meu computador ?" até as mais profundas, envolvendo a compatibilidade dos softwares que você mais usa.

Ciente disso, a Microsoft preparou um programa para diminuir os riscos, ao menos no que diz respeito à performance geral do computador e à compatibilidade de periféricos como impressoras e placas de vídeo. O programa chama-se Windows 7 Upgrade Advisor e pode ser executado em computadores com qualquer das versões do Windows que ainda têm suporte, a saber: o próprio Windows 7, o Windows Vista e o Windows XP com Service Pack 2. O programa, que é gratuíto, está disponíel no site da Microsoft através do link http://go.microsoft.com/fwlink/?LinkId=161223. Também pode ser encontrado em outros endereços em sites de download (Info Online, PC World, entre outros).
Tela do Upgrade Advisor

Após o download e a instalação, o programa poderá ser executado para determinar a compatibilidade com o Windows 7. Ele procurará em todo o seu computador por problemas potenciais com hardware, dispositivos existentes e programas que você tenha instalado. Caso encontre algum problema, ele dará recomendações sobre o que deve ser feito antes de tentar partir para a migração para o Windows 7.

Por causa da forma com que o Upgrade Advisor trabalha, você deve ligar os periféricos que você normalmente usa de modo que o programa possa acessá-los e determinar sua compatibilidade com o novo Windows. Isso inclui impressoras, scanners, discos externos, equipamentos que usem a USB (neste caso, o equipamento - como câmeras fotográficas - deve estar conectado na USB e deve estar ligado), etc.

A Microsoft não recomenda que o programa seja executado em ambiente virtualizado (usando o Virtual PC ou Remote Desktop) pois ele poderá deixar de detectar alguns recursos.

Você encontrará no site PC Saudável um tutorial básico para a execução do Advisor.