29 junio 2007

Listar los programas instalados en Windows

Windows almacena la lista de programas instalados (Agregar/Quitar programas) en la clave de registro:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\

En esa clave hay tantas subclaves como programas instalados. Pero lo que nos interesa a nosotros no es el nombre de la clave del programa instalado sino el nombre del programa que muestra Windows en Agregar/Quitar programas. Para ello entramos en cada clave y leemos el valor DisplayName.

Lo primero añadimos la unidad:

uses
Windows, Messages, ..., Registry;

Y aquí tenemos un procedimiento al cual le pasamos un ListBox y nos lo rellena con la lista de programas instalados en Windows:

procedure ListarAplicaciones( Lista: TListBox );
const
INSTALADOS = '\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall';
var
Registro: TRegistry;
Lista1 : TStringList;
Lista2 : TStringList;
j, n : integer;
begin
Registro := TRegistry.Create;
Lista1 := TStringList.Create;
Lista2 := TStringList.Create;

// Guardamos todas las claves en la lista 1
with Registro do
begin
RootKey := HKEY_LOCAL_MACHINE;
OpenKey( INSTALADOS, False );
GetKeyNames( Lista1 );
end;

// Recorremos la lista 1 y leemos el nombre del programa instalado
for j := 0 to Lista1.Count-1 do
begin
Registro.OpenKey( INSTALADOS + '\' + Lista1.Strings[j], False );
Registro.GetValueNames( Lista2 );

// Mostramos el programa instalado sólo si tiene DisplayName
n := Lista2.IndexOf( 'DisplayName' );
if ( n <> -1 ) and ( Lista2.IndexOf('UninstallString') <> -1 ) then
Lista.Items.Add( ( Registro.ReadString( Lista2.Strings[n] ) ) );
end;

Lista.Sorted := True; // Ordenamos la lista alfabéticamente
Lista1.Free;
Lista2.Free;
Registro.CloseKey;
Registro.Destroy;
end;

Con esto se podría hacer un programa que eliminara de Agregar/Quitar programas aquellas claves de programas mal desinstalados.

Pruebas realizadas en Delphi 7.

28 junio 2007

Ejecutar un programa y esperar a que termine

Uno de los problemas habituales con los que se enfrenta un programador es que su cliente le pida algo que o bien no sabe como programarlo o no dispone del componente o librería necesaria para llevar tu tarea a cabo.

Un ejemplo puede ser realizar una copia de seguridad en formatos ZIP, RAR, 7Z, etc., convertir de un formato de video o sonido a otro e incluso llamar a comandos del sistema para realizar procesos criticos en un servidor. Entonces sólo se nos ocurre llamar a un programa externo que realice la tarea por nosostros (y que soporte parámetros).

Sé lo que estáis pensando (la función WinExec), pero en este caso no me vale ya que el programa tiene que esperar a que termine de ejecutarse antes de pasar al siguiente proceso.

Aquí os muestro un procedimiento que ejecuta un programa y se queda esperando a que termine:

function EjecutarYEsperar( sPrograma: String; Visibilidad: Integer ): Integer;
var
sAplicacion: array[0..512] of char;
DirectorioActual: array[0..255] of char;
DirectorioTrabajo: String;
InformacionInicial: TStartupInfo;
InformacionProceso: TProcessInformation;
iResultado, iCodigoSalida: DWord;
begin
StrPCopy( sAplicacion, sPrograma );
GetDir( 0, DirectorioTrabajo );
StrPCopy( DirectorioActual, DirectorioTrabajo );
FillChar( InformacionInicial, Sizeof( InformacionInicial ), #0 );
InformacionInicial.cb := Sizeof( InformacionInicial );

InformacionInicial.dwFlags := STARTF_USESHOWWINDOW;
InformacionInicial.wShowWindow := Visibilidad;
CreateProcess( nil, sAplicacion, nil, nil, False,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
nil, nil, InformacionInicial, InformacionProceso );

// Espera hasta que termina la ejecución
repeat
iCodigoSalida := WaitForSingleObject( InformacionProceso.hProcess, 1000 );
Application.ProcessMessages;
until ( iCodigoSalida <> WAIT_TIMEOUT );

GetExitCodeProcess( InformacionProceso.hProcess, iResultado );
MessageBeep( 0 );
CloseHandle( InformacionProceso.hProcess );
Result := iResultado;
end;

El parámetro iVisibilidad puede ser:

SW_SHOWNORMAL -> Lo normal
SW_SHOWMINIMIZED -> Minimizado (ventanas MS-DOS o ventanas no modales)
SW_HIDE -> Oculto (ventanas MS-DOS o ventanas no modales)

La función devuelve un cero si la ejecución terminó correctamente.

Por ejemplo para ejecutar la calculadora de Windows y esperar a que termine:

procedure EjecutarCalculadora;
begin
if EjecutarYEsperar( 'C:\Windows\System32\Calc.exe', SW_SHOWNORMAL ) = 0 then
ShowMessage( 'Ejecución terminada con éxito.' )
else
ShowMessage( 'Ejecución no terminada correctamente.' );
end;

Pruebas realizadas en Delphi 7.

27 junio 2007

Obtener modos de video

A la hora de realizar un programa hay que tener muy presente la resolución de la pantalla y el área de trabajo donde se pueden colocar las ventanas.

Para ello tenemos el objeto TScreen que nos devuelve no sólo la resolución actual de video sino que además nos da el tamaño del escritorio y el área de trabajo del mismo (si esta fija la barra de tareas hay que respetar su espacio y procurar que nuestra ventana no se superponga a la misma).

Si utilizamos las propiedades Position o WindowsState del formulario no hay que preocuparse por esto, pero si hemos creado nuestra propia piel y pasamos de las ventanas normales de Windows hay que andarse con ojo y no dejar que el usuario se crea que ha desaparecido la barra de tareas.

El siguiente procedimiento vuelca la información de la pantalla actual en un objeto Memo llamado PANTALLA:

procedure TFInformacion.InfoPantalla;
begin
PANTALLA.Lines.Clear;
PANTALLA.Lines.Add( Format( 'Resolución: %dx%d ', [Screen.Width, Screen.Height] ) );
PANTALLA.Lines.Add( Format( 'Escritorio: x: %d y: %d Ancho: %d Alto: %d',
[Screen.DesktopLeft, Screen.DesktopTop, Screen.DesktopWidth, Screen.DesktopHeight] ) );
PANTALLA.Lines.Add( Format( 'Area de trabajo: x: %d y: %d Ancho: %d Alto: %d',
[Screen.WorkAreaLeft, Screen.WorkAreaTop, Screen.WorkAreaWidth, Screen.WorkAreaHeight] ) );
end;

Otra información interesante sería saber que resoluciones posibles tiene nuestra tarjeta de video. Vamos a mostrar todos los modos de video posible en un ListBox llamado MODOS:

procedure TFInformacion.InfoModosVideo;
var i: Integer;
ModoVideo: TDevMode;
begin
i := 0;
MODOS.Clear;
while EnumDisplaySettings( nil, i, ModoVideo ) do
begin
with ModoVideo do
MODOS.Items.Add(Format( '%dx%d %d Colores', [dmPelsWidth, dmPelsHeight, Int64(1) shl dmBitsperPel] ) );

Inc( i );
end;
end;

Esta es la típica información que suelen mostrar los programas tipo TuneUp.

Pruebas realizadas en Delphi 7.

26 junio 2007

Formularios transparentes

Si estais hartos de que vuestros formularios tengan el mismo aspecto soso de siempre podeis crear nuevos temas y skins tales como los reproductores BSPlayer, WinDVD, PowerDVD, etc.

Para ello tenemos aquí un procedimiento que toma como parámetro un formulario y que hará que todo su fondo sea transparente. Si queremos que algo no sea transparente lo ponemos dentro de un bitmap, shape o panel.

procedure TransparentarFormulario( Form: TForm );
var
Region, RegionTemp: HRGN;
i: Integer;
Rect: TRect;
begin
Region := 0;

for i := 0 to Form.ControlCount - 1 do
begin
Rect := Form.Controls[i].BoundsRect;
OffsetRect( Rect, Form.ClientOrigin.x - Form.Left, Form.ClientOrigin.y - Form.Top );
RegionTemp := CreateRectRgnIndirect( Rect );

if Region = 0 then
Region := RegionTemp
else
begin
CombineRgn( Region, Region, RegionTemp, RGN_OR );
DeleteObject( RegionTemp );
end;
end;

RegionTemp := CreateRectRgn( 0, 0, Form.Width,
GetSystemMetrics( SM_CYCAPTION )+
GetSystemMetrics( SM_CYSIZEFRAME )+
GetSystemMetrics( SM_CYMENU ) * Ord( Form.Menu <> nil ) );

CombineRgn( Region, Region, RegionTemp, RGN_OR );
DeleteObject( RegionTemp );
SetWindowRgn( Form.Handle, Region, True );
end;

Quitando los margenes de la ventana con BorderStyle = bsNone ya tenemos un formulario completamente transparente donde sólo hay que insertarle los objetos que van a ser visibles.

Pruebas realizadas en Delphi 7.

25 junio 2007

Utilizar una fuente TTF sin instalarla

Uno de los primeros inconvenientes al distribuir nuestras aplicaciones es ver que en otros Windows aparecen nuestras etiquetas y campos desplazados debido a que la fuente que utilizan no es la que tenemos nosotros en nuestro equipo.

O bien utilizamos fuentes estandar tales como Tahoma, Arial, etc. o podemos utilizar el siguiente truco que consiste en añadir la fuente utilizada al lado de nuestro ejecutable y cargarla al arrancar nuestra aplicación.

El procedimiento para cargar una fuente es:

procedure CargarFuente( sFuente: String );
begin
AddFontResource( PChar( ExtractFilePath( Application.ExeName ) + sFuente ) );
SendMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, 0 );
end;

Y en el procedimiento OnCreate de nuestro formulario cargamos la fuente:

procedure TFPrincipal.FormCreate( Sender: TObject );
begin
CargarFuente( 'Diner.ttf' );
Etiqueta.Font.Name := 'Diner';
end;

Donde se supone que el archivo Diner.ttf está al lado de nuestro ejecutable.

Antes de cerrar nuestra aplicación debemos liberar de memoria la fuente utilizada con el procedimiento:

procedure EliminarFuente( sFuente: String );
begin
RemoveFontResource( PChar( ExtractFilePath( Application.ExeName ) + sFuente ) );
SendMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, 0 );
end;

Este prodecimiento sería llamado en el evento OnDestroy del formulario:

procedure TFPrincipal.FormDestroy( Sender: TObject );
begin
EliminarFuente( 'Diner.ttf' );
end;

Es recomendable hacer esto una sola vez en el formulario principal de la aplicación y no en cada formulario del programa, a menos que tengamos un formulario que utiliza exclusivamente una fuente en concreto.

Pruebas realizadas en Delphi 7.

Publicidad