Archivo

Archivo para la categoría ‘Delphi’

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.

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:

Librería GLibWMi actualizada para Delphi 11

jueves, 16 de septiembre de 2021 Sin comentarios
Share Button

Hace pocos día recibimos la noticia de la actualización de Delphi a la versión 11. Aquí podéis revisar algunos links, si todavía no lo habéis hecho.

Web oficial de embarcadero
https://www.embarcadero.com/es/products/delphi
Puedes consultas todas las novedades en la wiki:
https://docwiki.embarcadero.com/RADStudio/Alexandria/en/What’s_New

A continuación te adjunto unos post en algunos blogs.
Post de Marco Cantú:
https://blogs.embarcadero.com/announcing-the-availability-of-rad-studio-11-alexandria/
Post de dave Nottage:
https://delphiworlds.com/2021/09/taking-it-to-11/
Post de Holger Flick:
https://flixengineering.com/archives/1588
Blog de TMS Software:
https://www.tmssoftware.com/site/blog.asp?post=834
Blog de LandGraf:
https://landgraf.dev/en/high-dpi-support-in-delphi-11-and-why-does-it-matter/
Blog de DelphiDabbler:
Algunas características del próximo Delphi 11
Blog de Darian Miler:
https://www.ideasawakened.com/post/new-links-and-resource-page-for-rad-studio-11-alexandria

Es sólo una muestra, si buscáis encontraréis más.

Si trabajais con la versión 11 de Delphi, ya está disponible la versión 3.1 de la librería GLibWMI, que funciona con esta (y anteriores). Espero que en breve esté disponible para descarga desde GetIt!

Un saludo y hasta la próxima.

La Versión Community (CE) de RAD Studio se actualiza a la 10.4.2

martes, 20 de julio de 2021 4 comentarios
Share Button

Buenos días a todos y enhorabuena a los que en estas fechas estáis ya de vacaciones.

Hoy ya puedo hacer púbica la que yo considero una gran noticia para la comunidad Delphi. Ya hace tiempo que algunos la conocemos, pero había que esperar a que llegara el día de poder hacerla pública.

La versión Community (CE) de RAD Studio se actualiza a la 10.4.2.

La noticia y el artículo completo de Jim McKeeth podéis leerlo aquí

Personalmente soy uno de los que durante muchos años ha demandado una versión como esta. De los que se quejaba a embarcadero porque las trials duraban poco y de los que escribió mail pidiendo que la «versión Starter» (predecesora de la Community) tuviera más funcionalidades. Así que para mi es una muy buena noticia.

Leer más…

Object Pascal Handbook by Marco Cantu

viernes, 11 de diciembre de 2020 2 comentarios
Share Button

Está disponible para descarga el manual «Object Pascal Handbook» de Marco Cantú, actualizado a la versión Delphi 10.4 Sydney.

Se puede descargar desde la web de embarcadero de forma grauíta.
https://lp.embarcadero.com/Object-Pascal-Handbook-2021

Os dejo una imagen de la tabla de cotenido:

El código fuente de todos los ejemplos está disponible en GitHub.

Categories: books, Código, Delphi, Embarcadero Tags: ,