Archivo

Archivo del autor

II – «Registros» de una DLL de previsualización (PreviewHandler)

miércoles, 22 de mayo de 2024 Sin comentarios
Share Button

INTRODUCCIÓN

Continuando el tema de «Crear una Previsualización de ficheros en Windows», vamos a seguir completando esta serie, haciendo referencia al tema del registro que nos quedó pendiente en la anterior entrada.
Os adjunto los links de todas las entradas de esta serie, que iré completando a medida que avance:

  1. Crear una Previsualización de ficheros en Windows
  2. «Registros» de una DLL de previsualización
  3. Generando previsualización funcional (PreviewHandler)

 

Hablo de «registros» en el título de esta entrada, porque vamos a abordar por un lado, los cambios en el «registro de Windows» que debemos realizar para que nuestra DLL quede «inscrita» en el sistema (con esto le decimos al sistema, que cuando quiera visualizar un determinado fichero de extensión AAA, debe cargar nuestra DLL para mostrar ese contenido). Y en segundo lugar, veremos cómo registrar nuestra DLL en Windows a través de regsrv32.

.

CLAVES DE REGISTRO

Cuando registramos nuestra DLL (regsvr32) necesitamos modificar algunos valores en el registro de Windows para indicar al sistema que tiene una DLL disponible para utilizar cuando se le solicite una vista previa de un archivo con extensión AAA.
Para ello tenemos disponible esta documentación que explica los diferentes cambios a crear/borrar cuando realizamos el register/unregister.

https://learn.microsoft.com/en-us/windows/win32/shell/how-to-register-a-preview-handler
https://learn.microsoft.com/es-es/windows/win32/shell/how-to-register-a-preview-handler

Lo primero que debemos tener en cuenta, es que si nuestro controlador de Vista previa (de 32 bits) se está registrando en un sistema de 64 bits, tal y como se describe aquí, debe usar un IdApp diferente;

En caso de un sistema de 32 bits usaremos este:

AppId = {6d2b5079-2f0b-48dd-ab7f-97cec514d30b}

Y en el caso de un sistema de 64 bits usaremos este otro:

AppId = {534A1E02-D58F-44f0-B58B-36CBED287C7C}

Extensión del archivo

Lo primero es registrar la extensión del archivo que queremos visualizar. Para ello usamos las siguientes claves:

   Equipo\HKEY_CLASSES_ROOT\.aaa (Extensión)
   Equipo\HKEY_CLASSES_ROOT\aaaFile (ProgID asociado a la extensión)

Donde aaa es la extensión que queremos registrar.

Controlador de Vista previa

Una vez registrada la extensión, hay que registrar la DLL que queremos asociar a esa extensión para visualizarla.
Para ello utilizaremos la subclave dentro de CLSID con los datos del controlador. El detalle de los valores los podéis ver aquí.

   Equipo\HKEY_CLASSES_ROOT\CLSID
   (Equipo\HKEY_CLASSES_ROOT\WOW6432Node\CLSID)

 

Lista de controladores

Finalmente hay que añadir el controlador a la lista de controladores instalados en el sistema. Es un valor redundante, pero es una optimización que el sistema usa para enumerar todos los controladores de vista previa registrados con fines de visualización.

   Equipo\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PreviewHandlers

.

REGISTRO DE LA DLL

Una vez compilada la  DLL, debemos registrarla en Windows utilizando la herramienta regsvr32.

Para registrar la DLL podemos ejecutar este comando desde el directorio de la DLL (permisos de Administrador):

regsvr32 Nombre_de_la_DLL.dll

Para desregistrarla utilizamos el mismo comando, pero con el parámetro /u:

regsvr32 /u Nombre_de_la_DLL.dll

.

PROBANDO EL PROYECTO

Os adjunto más abajo un ZIP con todos los ficheros del proyecto. Está todo lo necesario para compilarlo, registrar una extensión y probar que la DLL está funcionando.

Recordar que, por ahora, nuestra DLL sólo muestra un formulario vacío (botón y memo), no muestra ninguna previsualización, pero este esqueleto está pensado para comprobar que la estructura funciona correctamente. Más adelante añadiremos código funcional.

En la unit uConstantes.pas tenéis unas mínimas constantes necesarias. En concreto está la única que debéis cambiar si queréis que vuestro proyecto funcione con un tipo de fichero diferente.

  // NOTA: sólo cambiando esta constante podemos registrar otra extensión diferente
  const
    EXTENSION_FICHERO_SIN_PUNTO = 'ccc';

La unit uRegister.pas tiene todo lo necesario para añadir y eliminar del registro la claves y valores que hemos comentado antes.

Tenemos el uPreviewForm donde se define el formulario básico que se mostrará en la previsualización.

Y la unit uPreviewHandler.pas que realmente es la importante por ahora que posee toda la «chicha» del visualizador. Está bastante comentada (y la vimos en detalle en la primera entrada de esta serie), así que si algo no ha quedado claro podéis revisarla y revisar los comentarios que incluye.

Os adjunto los siguientes ficheros:

En este proyecto he usado la extensión de fichero CCC. De forma que podéis probarla con este tipo de ficheros.

El proyecto incluye los ficheros .BAT para registrar/desregistrar la DLL. Basta que estén en el mismo directorio y lo ejecutéis con permisos de Administrador.

Primer video mostrando el registro y funcionamiento de nuestra DLL

Categories: Código, DLL, Ejemplos, Windows Tags: , , ,

I – Creando una DLL de previsualización de ficheros (Preview Handler)

miércoles, 22 de mayo de 2024 Sin comentarios
Share Button

La idea de esta serie de entradas es ver todos los pasos necesarios para generar desde cero una DLL de Previsualización para Windows, de un determinado tipo de ficheros.

Antes de continuar os adjunto los links de las entradas de esta serie, que iré ampliando a medida que las vaya publicando:

  1. Crear una Previsualización de ficheros en Windows
  2. «Registros» de una DLL de previsualización
  3. Generando previsualización funcional

¿A qué me refiero con eso de «PreviewHandler»?

Si abrimos el Explorador de Windows, para algunos tipos de ficheros que existen en nuestro equipo, el sistema operativo dispone de vista una previa desde el propio explorador. Nos permite ver el contenido del fichero de forma rápida, sin necesidad de abrir el programa asociado.
Al seleccionar una imagen o un fichero .INI en el explorador de Windows, si tenemos la previsualización activada, veremos directamente una «vista previa» de ese archivo sin necesidad de abrir otro programa.

Si por el contrario, el fichero no tiene vista previa definida, veremos un mensaje de Windows indicándolo. Algo similar a esto (varía dependiendo de la versión):

.

INTRODUCCION

Lo que vamos a ver aquí es cómo crear desde cero, un «Controlador de Vista previa» para un determinado tipo de fichero, en Delphi; Entender lo básico que necesitamos implementar para tener un vista previa funcional. Para ello tenemos que crear y registrar una DLL, que el propio sistema pueda utilizar para previsualizar ese determinado tipo de fichero.

Aquí podéis consultar la documentación de Microsoft al respecto:
https://learn.microsoft.com/en-us/windows/win32/shell/preview-handlers
https://learn.microsoft.com/es-es/windows/win32/shell/preview-handlers

La documentación nos describe el proceso que el sistema sigue para mostrar el fichero, de esta manera:
«Windows llama a los controladores de vista previa cuando se selecciona un elemento para mostrar una vista previa ligera, enriquecida y de solo lectura (1) del contenido del archivo en el panel de lectura de la vista. Esto se hace sin iniciar la aplicación asociada del archivo. (2)»

  1. Nos dice los pasos a seguir para que se muestre el archivo.
  2. Es importante porque nos recuerda que la aplicación asociada no se inicia (la vista previa puede -y debería- ser más ligera y rápida que el programa asociado a ese tipo de fichero).

Si seguimos con la documentación, llegamos también a la interfaz que debemos implementar en nuestra DLL:   IPreviewHandler
La documentación nos lleva ahora aquí.

Los métodos que debemos implementar de esa interfaz son los siguientes:

  • DoPreview: Permite cargar datos desde el origen especificado en una llamada de método Initialize anterior y para comenzar la representación en la ventana del controlador de vista previa. Este método será el que utilicemos realmente para mostrar el archivo que queremos.
  • QueryFocus: Permite devolver el HWND desde una llamada a la función GetFocus.
  • SetFocus: Permite establecer el foco en sí mismo.
  • SetRect: Permite cambiar el área dentro del hwnd primario en el que se dibuja.
  • SetWindow: Establece la ventana primaria de la ventana de previsualización, así como el área dentro del elemento primario que se va a usar para la ventana del preview.
  • TranslateAccelerator: Permite controlar una pulsación de tecla pasada desde la cola de mensajes del proceso en el que se ejecuta el controlador de vista previa.
  • Unload: Permite dejar de representar una vista previa y liberar todos los recursos asignados en función del elemento pasado durante la inicialización.

A la hora de inicializar el controlador tenemos que implementar una de estas 2 interfaces que se muestran a continuación, una que trabaja a partir del path del fichero y la otra a partir del stream con los datos:

type 
  TBaseShellExt = class (TComObject, IPreviewHandler, 
                         IInitializeWithStream, IInitializeWithFile)

Las dos con el método Initialize y la primera (Stream) como preferida según la documentación. Si creamos una definición de clase, como la anterior, habrá que usar sólo una de ellas.

.

UNIT DE PREVISUALIZACIÓN

Con todo lo visto anteriormente vamos a crear un primer esqueleto para un proyecto (DLL) con lo imprescindible para crear una  previsualización.
Este proyecto va a constar de 3 units/formularios:

  • Implementación del Preview: La unit que implementa la interfaz comentada anteriormente. Es la que realmente realiza todo el trabajo de comunicarse con el sistema.
  • Formulario a incrustar: Formulario con los componentes necesarios para mostrar la previsualización que queremos crear. Por ejemplo, si quisiéramos mostrar imágenes, pues debería ser un formulario con un componente TImage o un componente con Canvas donde pudiéramos dibujar. Si es una previsualización de texto, pues debería incluir algún componente para mostrar texto (TMemo, TRichEdit o algún otro similar). En este ejemplo, por ahora, nuestro formulario sólo tiene un botón y un memo, pero ya nos sirve para ver si la previsualización está cargándose correctamente y no tiene errores.
  • Unit de registro: Unit que incluye lo que debemos realizar en el registro de Windows (siguiente entrada de la serie).

Os dejo el código de la unit que implementa el controlador, con lo visto hasta ahora. Es una primera versión que irá sufriendo cambios y evoluciones, pero es el esqueleto básico para que funcione es este. Creo que es bastante fácil de entender e incluye comentarios a lo largo del código.

{
Unit que implementa las interfaces necesarias (sobre la clase TBaseShellExt) para el previsualizador.
@author Germán Estévez
@cat Unit
}
unit uPreviewHandler;
 
interface
 
uses
  Windows, Messages, ActiveX, Classes, ComObj, ComServ, ShlObj, Registry,
  PropSys, Types, SysUtils, Math,
  VCL.ExtCtrls, VCL.StdCtrls, VCL.Controls,
  uPreviewForm;
 
type
  TBaseShellExt = class (TComObject, IPreviewHandler, IInitializeWithStream, IInitializeWithFile)
  strict private
    function IInitializeWithStream.Initialize = IInitializeWithStream_Init;
    function IInitializeWithFile.Initialize = IInitializeWithFile_Init;
  private
    // Permite cargar datos desde el origen especificado en una llamada de método Initialize anterior 
	// y para comenzar la representación en la ventana del controlador de vista previa
    function DoPreview: HRESULT; stdcall;
    // Permite devolver el HWND desde una llamada a la función GetFocus.
    function QueryFocus(var phwnd: HWND): HRESULT; stdcall;
    // Permite establecer el foco en sí mismo.
    function SetFocus: HRESULT; stdcall;
    // Permite cambiar el área dentro del hwnd primario en el que se dibuja.
    function SetRect(var prc: TRect): HRESULT; stdcall;
    // Establece la ventana primaria de la ventana del previsualizador, 
    // así como el área dentro del elemento primario que se va a usar para la 
    // ventana del previsualizador.
    function SetWindow(hwnd: HWND; var prc: TRect): HRESULT; stdcall;
    // Permite controlar una pulsación de tecla pasada desde la bomba de mensajes 
    // del proceso en el que se ejecuta el controlador de vista previa
    function TranslateAccelerator(var pmsg: tagMSG): HRESULT; stdcall;
    // Permite dejar de representar una vista previa y liberar todos los recursos 
    // asignados en función del elemento pasado durante la inicialización
    function Unload: HRESULT; stdcall;
 
    // Inicilización para stream
    function IInitializeWithStream_Init(const pstream: IStream; grfMode: DWORD): HRESULT; stdcall;
    // Inicialización para ficheros
    function IInitializeWithFile_Init(pszFilePath: LPCWSTR; grfMode: DWORD): HRESULT; stdcall;
  private
    FFilePath: string;
    FParent: HWND;     // Handle de la ventana de previsualizacion
    FBounds: TRect;    // Define el area de la previsualizacion
  protected
    FStream:IStream;
    FormPreview: TFormPreview;     // formuario de previsualizacion
    // Procedimiento que realmente realiza la previsualizacion
    procedure InternalDoPreview;
    // testear la ventana de previsualizacion
    procedure TestPreviewWindows;
  public
    destructor Destroy; override;
  end;
 
var
  res: HRESULT;
 
implementation
 
uses
  uConstantes, uUtils, Vcl.AxCtrls,
  VCL.Graphics, VCL.Forms;
 
{ TBaseShellExt }
 
procedure TBaseShellExt.TestPreviewWindows;
begin
  // Si no está asignado el formulario, lo creamos...
  if not Assigned(FormPreview) then begin
    FormPreview := TFormPreview.Create(nil);
    FormPreview.ParentWindow := FParent;
  end;
 
  // Caracteristicas del form de preview
  FormPreview.BorderStyle := bsNone;
  FormPreview.Align := TAlign.alClient;
  FormPreview.WindowState := TWindowState.wsMaximized;
  FormPreview.Show;
  // forzar el repintado
  FormPreview.Invalidate;
  // Forzar la posicion
  SetWindowPos(FormPreview.Handle, 0, fBounds.left, fBounds.top,  RectWidth(fBounds), 
			   RectHeight(fBounds), SWP_NOZORDER or SWP_NOACTIVATE);
end;
 
procedure TBaseShellExt.InternalDoPreview;
begin
  // Chequear la ventana
  TestPreviewWindows;
  // Cargar la preview
  //...
end;
 
function TBaseShellExt.DoPreview: HRESULT;
begin
  Result := S_OK;
  // si no tengo asignado parent (de la ventana) salgo..
  if FParent = 0 then begin
    Exit;
  end;
 
  // Realizo el trabajo de previsualizacion
  TestPreviewWindows;
end;
 
destructor TBaseShellExt.Destroy;
begin
  inherited;
  if Assigned(FormPreview) then
    FreeAndNil(FormPreview);
end;
 
function TBaseShellExt.QueryFocus(var phwnd: HWND): HRESULT;
begin
  phwnd := GetFocus;    // devuelvo el foco
  result := S_OK;
end;
 
function TBaseShellExt.SetFocus: HRESULT;
begin
  Result := S_OK;
end;
 
function TBaseShellExt.SetRect(var prc: TRect): HRESULT;
begin
  FBounds := prc;      // me quedo con la nueva posicion y repinto
  InternalDoPreview;
  Result := S_OK;
end;
 
function TBaseShellExt.SetWindow(hwnd: HWND; var prc: TRect): HRESULT;
begin
  if (hwnd <> 0) then
    FParent := hwnd;
  if (@prc <> nil) then
    FBounds := prc;
  InternalDoPreview;            // forzar redibujadio
  Result := S_OK;
end;
 
function TBaseShellExt.TranslateAccelerator(var pmsg: tagMSG): HRESULT;
begin
  Result := S_FALSE;     // Por ahora así; False cuando no se controla nada.
end;
 
function TBaseShellExt.Unload: HRESULT;
begin
  // liberar lo creado
  FreeAndNil(FormPreview);
  FStream := nil;
  FParent := 0;
  Result := S_OK;
end;
 
function TBaseShellExt.IInitializeWithFile_Init(pszFilePath: LPCWSTR; grfMode: DWORD): HRESULT;
begin
  // inicializaciones
  FormPreview := nil;
  FFilePath := string.Empty;
 
  // Apuntamos el path del fichero
  FFilePath := pszFilePath;
  Result := S_OK;
end;
 
function TBaseShellExt.IInitializeWithStream_Init(const pstream: IStream; grfMode: DWORD): HRESULT;
begin
  // inicializaciones
  FormPreview := nil;
 
  // Apuntamos al stream
  FStream := pstream;
  Result := S_OK;
end;
 
 
initialization
  res := OleInitialize(nil);
 
  // Permite registrar nuestro "Preview" con el ID, nombre, descripción y extensión del archivo
  TComObjectFactory.Create(ComServer, TBaseShellExt, IID_EXT_ShellHandler, sExtFile, 
				sAppDescription, ciMultiInstance, tmApartment);
 
finalization
  if res = S_OK then
    OleUninitialize();
 
end.

Como veis, nuestra clase para el controlador se llama TBaseShellExt; No es casualidad. Nos ha servido bien como esqueleto y como primera aproximación, pero la idea es que más adelante ampliemos nuestro visualizador y hagamos uso de la herencia. Así que más adelante tendremos clases que deriven de esta y redefinan algunos de sus métodos (incluso que esta clase llegue a ser abstracta). En la próxima entrada continuaremos con el resto de piezas del proyecto.

Nuestra clase implementa los dos interfaces, IInitializeWithStream, IInitializeWithFile. Como hemos dicho antes, debería implementar sólo uno de ellos. En este código he añadido los dos para poder mostrar los métodos, pero si se hace así sólo tendrá efecto el IInitializeWithStream.

En la siguiente entrada veremos todo los referente al Registro de la DLL y a las claves que se deben crear para que funcione.

Como siempre los comentarios, sugerencias y demás son bienvenidos.

Delphi 12 Athens; Tratamiento de aplicaciones MDI

jueves, 4 de abril de 2024 Sin comentarios
Share Button

Interfaz MDI

Aunque seguro que no es una de las características más utilizadas hoy en día en los programas nuevos desarrollados en Delphi, es probable que tengamos programas antiguos que funcionan con esta interfaz de usuario. Yo mismo tengo alguno que todavía la usa.

Hasta ahora migrar esos programas a versiones nuevas podía ser un problema debido a que Microsoft ha declarado MDI como obsoleta y el funcionamiento no acaba de de ser el correcto. Problemas con el repintado, los estilos y con High DPI entre otros.

Para la versión 12 de Delphi se ha intentado solventar estos problemas implementando la solución desde el propio Delphi y no dejando que lo solvente Windows. El resultado no sólo es, que ahora ya podemos recompilar nuestras aplicaciones antiguas con la versión 12 y volver a tener lo mismo que teníamos antes, sino que con esta modificación, el resultado puede ser «mejor» todavía que el que teníamos en la versión antigua.
¿Porqué esto último?
Porque además de implementar las características del MDI para que funcionen correctamente, se ha añadido un nuevo componente, que si lo utilizamos (no es obligatorio), las mejorará sobre todo a nivel visual y de usabilidad.

Leer más…

El día de los Desarrolladores Delphi y C++ Builder 2024 en Madrid (Fotos)

lunes, 18 de marzo de 2024 Sin comentarios
Share Button

(Tened paciencia que como hay fotos tarda en cargar…)
Lo prometido es deuda, así que os adjunto algunas fotos del encuentro del pasado Jueves 14 de Marzo en Madrid.
Desde aquí mi felicitación a Danysoft por la organización del evento.

Las fotos no son de mucha calidad (que estaba pendiente de otras cosas  ;-D  ), pero espero que sirvan para que os hagáis una idea.

Leer más…

El Día de los Desarrolladores Delphi y C++ Builder 2024 (Madrid)

lunes, 11 de marzo de 2024 2 comentarios
Share Button

Para los que echabais en falta los eventos presenciales, pues hay buenas noticias.
El próximo día 14 en Madrid Danysoft ha organizado un evento completo para desarrolladores Delphi y C++ Builder.
La información completa la tenéis aquí.
Y el acceso al registro aquí.


Nos vemos allí.

Un saludo.

Categories: Delphi, Eventos Tags: , , ,

Librería GLibWMi v.3.2 actualizada para Delphi 12

jueves, 7 de marzo de 2024 Sin comentarios
Share Button

Actualización 18/03/2024
A día de hoy ya está disponible para descarga a través de GetIt para las últimas versiones de Delphi (inluídda la 12).

__________________________________________________________________

Ya hace unos meses que está disponible Athens, la versión 12 de RAD Studio.
Anunciar que ya está disponible para descarga desde Github, la versión 3.2 de la librería GlibWMI.
Debido a problemas técnicos el repositorio ha cambiado; el nuevo es este:
https://github.com/germanestevez/GLibWMI

Y el antiguo quedará obsoleto y estancado en la versión 3.0.

Tenéis la información completa en la sección de componentes del blog.
Espero que en breve esté disponible a través de GetIt, como las versiones anteriores (hasta ahora la 3.1).
Cualquier duda o comentario estoy a vuestra disposición.

Un saludo y hasta la próxima!

JSON Data Binding Wizard (Delphi 12)

martes, 19 de diciembre de 2023 2 comentarios
Share Button

Hace poco que ya está disponible la última versión de RAD Studio.

La versión 12 Athens trae bastantes novedades; Podéis ver la lista completa y explicada en la propia web de embarcadero:

En esta entrada me voy a centrar en el nuevo asistente «JSON Data Binding Wizard».
Anteriormente en otras entradas ya he realizado ejemplos para trabajar con ficheros JSON. A continuación os adjunto algunos links de entradas donde por diferentes necesidades he trabajado con archivos de este tipo:

Es junto al XML el formato más utilizado para intercambio de datos en la web y mayoritariamennte usado cuando descargamos información desde servidores REST mediante API, como se hace en las entradas anteriores.
Lo habitual en versiones antiguas de Delphi, es utilizar una librería externa ya que Delphi no la trae integrada (lkJSON, SuperObject,…) y en las versiones nuevas de Delphi ya se puede utilizar la que trae el propio Delphi (System.Json, REST.Json).

Lo que he necesitado hacer en esos ejemplos, es leer la estructura de datos y navegar por esa estructura jerárquica del JSON (similar al XML) e ir saltando por diferentes nodos hasta encontrar la información que necesitamos. Si el archivo es muy grande y la estructura compleja con muchos niveles, esta navegación (y su implementación puede ser más o menos compleja). Para escribir debemos completar los diferentes nodos de la estructura para finalmente generar el JSON.

Leer más…

Categories: Código, Delphi, JSON, OOP Tags: , ,

RAD Studio Live 2023 (Integración Continua)

miércoles, 15 de marzo de 2023 Sin comentarios
Share Button

El pasado 28 de Febrero se celebró el RAD Studio Live 2023 en Español.

Ya comenté sobre él y las diferentes charlas en la anterior entrada.

Por si no pudisteis asistir o por si queréis ver de nuevo alguno de los vídeos, ya se han publicado en el canal de Youtube de Embarcadero Latinoamérica.

La referente a «Integración continua con Delphi», que presenté yo mismo, también la podéis ver (formato original) en mi canal de youtube.

Igualmente aquí tenéis los enlaces individuales a los vídeos de la 4 charlas:

Espero que disfrutéis de todas ellas.

RAD Studio Live 2023 en Español (28 de Febrero)

jueves, 23 de febrero de 2023 Sin comentarios
Share Button

Pues ya está programado el RAD Studio Live en Español para este año 2023.

Será el día 28 de febrero y las sesiones desde las 17:00 hasta las 21:00 hora española.

Las sesiones y los ponentes son las siguientes:

La información en línea y el acceso al registro lo tenéis aquí.

Categories: Delphi Tags: , ,

Felices Fiestas!

viernes, 23 de diciembre de 2022 3 comentarios
Share Button

Un año más, aprovecho para desearos una Felies Fiestas a todos los que visitáis el blog.

Categories: Delphi Tags: