11 de setembro de 2009

Obtendo nível de sinal do Wifi usando WMI com Delphi

O WMI - Windows Management Instrumentation - é um serviço do Windows que permite o gerenciamento centralizado de informações e operações do Sistema. Isso se dá através da publicação de uma série de interfaces padronizadas com as quais é possível obter informações sobre o hardware do computador, sobre o Registry, status dos Serviços e uma infinidade de outros recursos do Windows. Fiz uso da infraestrutura do WMI quando mostrei aqui no blog como montar um script para manusear Serviços do Windows. Naquela ocasião, usei VBScript; neste post, usarei o Delphi para acessar o WMI, montando uma função que pesquisa o nível (força) do sinal WiFi que está chegando na NIC.

O funcionamento das interfaces do WMI se parecem com o acesso a um banco de dados, onde você pode submeter queries nas tabelas que detem as informações que se quer recuperar sobre o Sistema Operacional. Uma lista das "tabelas" (na verdade são classes) existentes pode ser encontrada no site para desenvolvedores da Microsoft.

A Microsoft disponibiliza o acesso ao WMI através de COM de modo que é possível realizar tarefas de gerenciamento até mesmo com scripts. Para usá-lo no Delphi ou C++ Builder, é preciso importar a type library correspondente cujo nome é Microsoft WMI Scripting V1.2 Library - o número da versão pode variar, dependendo do que você tem instalado em seu computador.

O primeiro passo é criar uma instância de classe que implemente a interface ISWbemLocator. Essa interface é responsável pela conexão com o serviço WMI, como no exemplo abaixo.
var Locator : ISWbemLocator;
Services: ISWbemServices;
begin
Locator := CoSWbemLocator.Create;
Services := Locator.ConnectServer('.', 'root\wmi','', '', '','', 0, Nil);

A função ConnectServer permite também a conexão com o WMI de outro computador, bastando informar qual é esse computador e as credenciais de acesso (usuário e senha). No exemplo, estou conectando ao WMI do meu próprio computador, representado pelo nome '.'. O valor 'root\wmi' aponta o namespace onde está a informação que eu quero; consulte a documentação para mais detalhes sobre os namespaces. O que essa função retorna é a interface ISWbemServices, responsável por providenciar acesso às tarefas disponíveis no WMI.

No caso tratado por este post, a tarefa é apenas a execução de uma query para recuperar o nível do sinal WiFi percebido pelo computador. O nome da tabela (ou classe) que tem essa informação é a MSNdis_80211_ReceivedSignalStrength e por isso vou montar o SELECT nela:
var ObjSet: ISWbemObjectSet;
begin
ObjSet := Services.ExecQuery(
'SELECT * FROM MSNdis_80211_ReceivedSignalStrength',
'WQL', wbemFlagReturnImmediately , nil);

A função ExecQuery retorna uma espécie de record set, isto é, uma coleção de registros que atendem a query especificada. No caso aqui, será retornado um valor para cada placa de rede encontrada. Esse record set implementa a interface IEnumVariant para percorrer todos os "registros" que forem retornados pela query. Assim, posso obter o valor da propriedade Ndis80211ReceivedSignalStrength de cada um deles.
var SObject: ISWbemObject;
SProp: ISWbemProperty;
Enum: IEnumVariant;
Value: Cardinal;
ObjRet: OleVariant;
begin
Enum := ObjSet._NewEnum As IEnumVariant;
while (Enum.Next(1, objRet, Value) = S_OK) do
begin
SObject := IUnknown(objRet) As ISWBemObject;
SProp := SObject.Properties_.Item('Ndis80211ReceivedSignalStrength', 0);
if not VarIsNull(SProp.Get_Value) then
Result := String (SProp.Get_Value) + #13#10;
end;

A propriedade Ndis80211ReceivedSignalStrength é tratada como se fosse um campo numa tabela no banco de dados e é ela que tem de fato o nível do sinal. Note, no entanto, que o valor retornado por ela não é fixo. Cada vez que a função for executada, o valor reportado será o nível do sinal percebido naquele momento. O número retornado é expresso em DBMs e pode assumir valores entre -50 (sinal mais forte) e -100 (sinal mais fraco).

No trecho de código acima, o cast String (SProp.Get_Value) só funciona porque o valor retornado por essa propriedade é conversível para String. Outras propriedades podem ter tipos diferentes e exigirão casts diferentes. Isso inclui até objetos, caso em que a conversão terá que ser feita de outra maneira - talvez uma interface específica.

O acesso ao WMI utilizando a versão .NET do Delphi é ligeiramente diferente. Nesta plataforma, o ObjSet não é compatível com a interface IEnumVariant e sim com a IEnumerator. Portanto, todo o laço onde é feita a navegação pelos registros tem que ser revisto para poder funcionar no .NET.

Esse tipo de abordagem com o WMI não funciona no Windows Vista. Veja este artigo, publicado no site Technet da Microsoft.

Mais Informações
Documentação do WMI

4 comentários :

Rodrigo disse...

Gostaria de agradecer ao Luís que me ajudou muito, sem ele não desenvolveríamos esse código. Graças a ele poderei dar continuidade ao meu trabalho de conclusão da faculdade.

Muito sucesso na vida e no blog. Sempre que eu puder, estarei aqui ajudando ou dando uma força.

Muito obrigado por tudo...

att,

Rodrigo de Souza Kuck

Anônimo disse...

Material realmente interessante.

Pesquisei pela documentação WMI e verifiquei uma vasta listagem de objetos, métodos e propriedades realmente uteis, porém, nem todos os métodos e propriedades estão disponíveis ou foram implementadas ainda.

Pesquiso por uma forma de obter o sinal de rede independente do hardware utilizado e infelizmente o exemplo dado pelo Luíz Gustavo não solucionou o meu problema...

Tentei utilizar o objeto Win32_NetworkAdapter mas as propriedades que retornam velocidade (Speed), e velocidade máxima (MaxSpeed), que também seriam interessantes em meu trabalho, não retornaram valor algum, e a referencia da documentação diz que estas propriedades não foram implementadas para a plataforma Win2003Server.

Procuro, outra forma de capturar o nível de sinal da rede entre outras propríedades.

Anônimo disse...

Olá,
Sabe como faço para alterar essa função para poder trazer o serial id do sistema operacional?
No dos fica assim: wmic os get serialnumber

Luís Gustavo Fabbro disse...

A classe que vc está procurando provavelmente é a Win32_OperatingSystem. Ela está sob o root CIMV2 e a documentação dela no MSDN está no endereço http://msdn.microsoft.com/en-us/library/aa394239%28v=vs.85%29.aspx.

[]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.