ÍNDICE de ARTÍCULOS


Aquí tienes numerosos artículos de Delphi, espero que te gusten y sean de tu agrado.  Selecciona el articulo que quieres leer.

  1. Ficheros INI.
  2. Revisión en profundidad para mostrar y esconder ventanas.
  3. Componentes en tiempo de diseño.
  4. Cursores animados.
  5. Programando el planificador de tareas.
  6. Soluciones simples con el planificador de tareas.
  7. Bitmaps transparentes en Delphi.
  8. Vista preliminar de la impresora.
  9. Acción inmediata.
  10. Triggers y Vistas.
  11. ¿Qué es un Alias en Delphi?
  12. ¿Tabla o Consulta?
  13. Ventanas de Formas irregulares.
  14. Marcas en Windows.
  15. Aplicaciones con ayuda HTML compilada.
  16. La huella de los programadores.
  17. EXE generado con Delphi, ¿Demasiado grande?
  18. Asociación de Ficheros a las aplicaciones que tu creas.
  19. Aplicacxiones de consola.
  20. ActiveX Script en windows 98, vuelven los comandos.
  21. ActiveX Scripting, guiones de ejecucion.

Ficheros INI

 

Introducción

Voy a contar como Delphi gestiona los ficheros ini. Por fichero ini se entiende todo fichero que tiene extensión ini, está en formato Ascii, y tiene una estructura interna dividida en secciones. Un claro ejemplo es el famoso Win.ini, que lo podéis encontrar en el directorio Windows. Se puede abrir con el Bloc de Notas de Windows, aunque Windows, incluye un programa visor para ver los ficheros del sistema, que se llama Sysedit, si ejecutáis este programa podréis ver el contenido de los ficheros de sistema, entre ellos los de extensión ini.

Los ficheros ini, normalmente, tienen una estructura. Están divididos en bloques o secciones, y dentro de cada sección hay valores que se usan en la configuración del programa que gestiona ese fichero o esa sección. Digo esa sección porque el fichero Win.ini almacena datos sobre Windows, y algunos programas añaden secciones a él. Aquí hay que hacer una aclaración ya que esto solo ocurre en Windows 3.x, y Windows 95 tiene este archivo aunque esta en desuso (por compatibilidad).

Los nombres de las secciones están indicados entre corchetes, e inmediatamente van los variables que haya puesto el programa con sus valores. Este es un ejemplo de una entrada de mi fichero Win.ini:


[Ports]
LPT1:=
LPT2:=
LPT3:=
COM1:=9600,n,8,1,x
COM2:=9600,n,8,1,x
COM3:=9600,n,8,1,x
COM4:=9600,n,8,1,x
FILE:=


Como podéis ver es todo Ascii, así que alguien puede pensar que con lo que sabe de ficheros, se puede sentar delante del ordenador y hacerse unas rutinas para leerlos, a lo cual yo respondo que tu mismo, pero que Delphi ya tiene un sistema para leerlos, y muy bueno.

Delphi tiene una unidad donde tiene todos los procedimientos y funciones definidos. Así que lo único que tienes que hacer para poder empezar a trabajar con estos ficheros es añadir la palabra IniFiles a la cláusula uses de tu unidad. Esta es la lista de funciones y procedimientos para leer y escribir en estos ficheros.

Create(Filename)

Para acceder a un fichero

Free

Cierra el fichero

ReadSecctionValues(Seccion,TString)

Para leer todas la variables

ReadSections(TString)

Lee las secciones

ReadSection(Seccion,TString)

Lee una Seccion entera

ReadString(Seccion,Variable, Defecto)

Lee una variable tipo String

ReadInteger(Seccion,Variable,Defecto)

Lee una variable tipo integer

ReadBool(Seccion,Variable,Defecto)

Lee una variable tipo boleano

WriteString(Seccion,Variable,Valor)

Escribe un valor en una Variable.

WriteInteger(Seccion,Variable,Valor)

Escribe un valor tipo Integer

WriteBool(Seccion,Variable,Valor)

Escribe un valor boleano

Quizás ante tanto nombre os asustéis, pero en cuanto explique un par de detalles el resto será coser un cantar. Para acceder a un fichero ini, lo primero es indicar que fichero es, y eso se hace cuando creamos el objeto. He dicho objeto, y lo he soltado así de refilón, resulta que como Delphi es un lenguaje orientado al objeto, y el sistema de lectura/escritura de ficheros ini, es una clase pues hay que inicializarlo. Seguro que hay alguno de esta pensando que se ha metido en un lió, pero Delphi no nos abandona en los momentos difíciles, y el sistema es muy fácil. Un procedimiento genérico (cuando digo genérico es que se puede poner dentro de cualquier procedimiento) seria como sigue:


Var
MiFicheroIni : TiniFile;
Begin
MiFicheroIni := TIniFile.Create ('pepe.ini');
MiFicheroIni.Free;


En este mini programa lo que he hecho crear una variable tipo IniFile, la cual inicializo en la primera línea indicando que el fichero con el cual vamos a trabajar se llama Pepe.ini, después podría hacer lo que me plazca, y al final libero la variable que he creado, para que no ocupe memoria. Atención porque sino se indica el nombre del fichero con la ruta completa se asume que este está en el directorio Windows, y NO donde se esta ejecutando tu programa. Si quieres acceder al fichero dentro de otro directorio, debes ponerlo así:


MificheroIni := TiniFile.Create ('c:\mi directorio\pepe.ini');


Os comento que los ejemplo que voy a poner trabajarán en el directorio donde se ejecuta la aplicación. Antes de que se me pase, cuando el fichero no se encuentra este es creado.

Vamos hacer un ejemplo donde se usen las principales funciones. Para lo cual construye un formulario como este, con dos botones, dos campos edit, un CheckBox, y un par de etiquetas. Como os dije, el fichero se creará en el directorio donde se ejecuta este programa, así que para ello debemos saber donde se ejecuta, la manera que yo he usado es declarar un variable en la sección privada de la unidad, la cual contendrá el directorio y el nombre del fichero, y estos datos le serán asignados en el momento de la creación del formulario, así que este procedimiento será como sigue:


procedure TForm1.FormCreate(Sender: TObject);
begin
Fichero := ExtractFileDir (ParamStr(0))+'\Fichero.ini';
end;


El truco para saber el directorio donde está ejecutandose el programa, está en ParamStr, el cual contiene en su elemento 0 (es un array), el nombre con la ruta completa del programa. Con la función ExtractFileDir obtengo solo la ruta.

El LeerDatos, que se llama Button1, tiene las siguientes líneas de código:

procedure TForm1.Button1Click(Sender: TObject);
Var
MiFichero : TIniFile;
Edad : Integer;
begin
MiFichero := TiniFile.Create (Fichero);
Edit1.Text := MiFichero.ReadString ('Usuario','Nombre','Desconocido');
Edad := MiFichero.ReadInteger ('Usuario','Edad',99);
CheckBox1.Checked := MiFichero.ReadBool ('Usuario','Español',True);
MiFichero.Free;
Edit2.Text := IntToStr (Edad);
end;

Las funciones de lectura de datos devuelven los valores en el formato que los leen. Así cuando usamos ReadString la variable que recoge los datos debe ser un String, cae de cajón, pero por si las moscas. Además tienen tres parametros, el primero es el nombre de la sección, el segundo el nombre de la variable, y el tercero el valor por defecto, en el caso que no exista tal variable, sección, o incluso si el fichero no existiese y fuera creado en el momento de su apertura. Como la edad he decido almacenarla como un valor numérico (integer), para poderla mostrarla en el campo Edit2 he de convertirla a texto, que es lo que hace la última línea.

El procedimiento de escritura es similar:


procedure TForm1.Button2Click(Sender: TObject);
Var
MiFichero : TIniFile;
Edad : Integer;
begin
Edad := StrToInt (Edit2.Text);
MiFichero := TiniFile.Create (Fichero);
MiFichero.WriteString ('Usuario','Nombre',Edit1.Text);
MiFichero.WriteInteger ('Usuario','Edad',Edad);
MiFichero.WriteBool ('Usuario','Español',CheckBox1.Checked);
MiFichero.Free;
end;


Fijaros que el procedimiento es casi indentico al anterior, pero esta vez se usan las ordenes de escritura. Aqui ocurre lo mismo, si el fichero, la seccion, o la variable no existen son creadas. Como regla general se puede decir que si al leer o escribir algo no existe, este es creado.

He dejado atrás funciones que son: ReadSectionValues, ReaSections y ReadSection, las cuales tienen como parametros variables de tipo TStrings, las cuales más que ser una variable es un objeto, el cual no esta de más que vallas tratando porque es muy potente. Básicamente es una lista de cadenas, algo así como un Array o Matriz, pero se pueden hacer muchas cosas con el, añadir elementos, borrarlos, acceder por el indice, ordenarlos, etc. Y lo mejor es que los componentes que usan esten objeto lo hacen practicamente todo ellos. Un componente que usa este objeto son los TListBox, es el séptimo componente empezando por la derecha en la paleta Standar.


Estas funciones leen partes de un fichero ini. Así ReadSectionValues, lee los valores de las variables contenidas en una seccion. ReadSections, lee el nombre de las secciones de un fichero ini, y ReadSection lee el nombre de las variables de la sección. He hecho un pequeño ejemplo que lee de nuestro fichero ini.

He puesto en un formulario tres botones que invocan a estos tres procedimientos y un TListBox, el cual su propiedad items, es del tipo TStrings. Esta es la imagen del formulario que preparé y los tres procedimientos.


procedure TForm1.Button1Click(Sender: TObject);
Var
MiFichero : TiniFile;
begin
MiFichero := TIniFile.Create (Fichero);
MiFichero.ReadSection ('Usuario',ListBox1.Items);
MiFichero.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
Var
MiFichero : TiniFile;
begin

MiFichero := TIniFile.Create (Fichero);
MiFichero.ReadSections (ListBox1.Items);
MiFichero.Free;

end;

procedure TForm1.Button3Click(Sender: TObject);
Var
MiFichero : TiniFile;
begin
MiFichero := TIniFile.Create (Fichero);
MiFichero.ReadSectionValues ('Usuario',ListBox1.Items);
MiFichero.Free;

end;

Bueno pues los ficheros ini no tienen más secretos, solo comentar que yo personalmente prefiero trabajar sobre un fichero ini propio que tocar en el win.ini. El motivo es que así el fichero ini lo creo en el directorio donde esta el programa ejecutable, con lo que obtengo una ventaja, y es que es que si el usuario borra el programa, borrando el directorio donde está el programa ya borra el fichero ini, y no se queda en el directorio Windows y si hubiera añadido entradas al fichero win.ini este no quedaria engordado inultilmente.       

ir al índice


ActiveX Scripting - Guiones de ejecución
Introducción

Las aplicaciones que implementan capacidades de lenguajes macro o lenguajes interpretados en tiempo de ejecución, suministran una funcionalidad extraordinaria a los usuarios. Un ejemplo muy conocido es el editor de documentos Microsoft Word.

Este artículo muestra una forma de incorporar lenguajes interpretados en tiempo de ejecución a las aplicaciones, mediante la tecnología ActiveX Script de Microsoft, utilizando como plataforma de construcción a Delphi versión 3 o posterior.

ActiveX Script es una especificación de interfaces de objetos COM (Common Object Model), que posibilitan a los desarrolladores, incorporar en sus aplicaciones los motores de ejecución de lenguajes interpretados de una manera fácil.

Por desarrolladores en este trabajo, entenderemos las empresas o personas cuyo producto es un programa o paquete especifico, por ejemplo un editor de texto, un sistema financiero contable, un navegador de Internet, etc, para distinguirlo de aquel que se especializa en desarrollar motores o maquinas interpretación de guiones de ejecución, que llamaremos proveedor.

La definición del lenguaje del guión de ejecución (el script en si mismo), la sintaxis, el formato de almacenamiento, el modelo de ejecución y otras temas relacionadas, son responsabilidad de los proveedores de motores ActiveX Script. El hospedar o soportar un motor de interprete de guiones de ejecución, es responsabilidad del desarrollador de la aplicacion.

El guión de ejecución (el script) en general es un texto, escrito con las reglas del lenguaje que soporta el motor en el cual se ejecuta.

Los pasos básicos

Las componentes de ActiveX Script caen en dos categorías: Los hospederos (HOST) de guiones de ejecución y los motores de interpretación (SCRIPT ENGINE).

Los hospederos son las aplicaciones que crean los guiones y llaman al motor de interpretación adecuado para ejecutar el guión. Un ejemplo de aplicación hospedera es el Microsoft Internet Explorer, tan utilizado para navegar por las páginas WEB.

Los motores de interpretación se pueden desarrollar para cualquier lenguaje y ambiente de ejecución, por ejemplo el Microsoft Visual Basic Scripting Edition (VBScript), utilizado en el Microsoft Internet Explorer.

Los pasos involucrados en la secuencia básica para la ejecución de un guión es la siguiente (algunos elementos utilizados de momento, serán explicados mas adelante):

  • Por algún medio se crea un texto en el lenguaje soportado en el motor de interpretación que es el guión de ejecución.
  • El hospedero crea una instancia del motor de interpretación y la inicializa. Generalmente esto se realiza haciendo una llamada a la función CoCreateInstance a la que se le especifica el identificador del motor de interpretación. La función devuelve un puntero a la interfaz IActiveScript soportada por el motor. A esta interfaz se le pregunta para obtener un puntero a la interfaz IActiveScriptParse, utilizado el método QueryInterface. Posteriormente, se llama a la funcion InitNew de la interfaz IActiveScriptParse obtenida, para inicializar el motor de interpretación.
  • El hospedero suministra al motor de interpretación, que objetos tiene disponibles para la ejecución de los guiones, llamando a la funcion AddNamedItem de la interfaz IActiveScript.
  • El hospedero alimenta el guión de ejecución al motor de interpretación. El hospedero que mantiene el guión como un texto llama a la funcion ParseScriptText de la interfaz IActiveScriptParse.
  • El hospedero instruye al motor de interpretación que ejecute el guión. Esto se realiza llamando a la función SetScriptState de la interfaz IActiveScript.
  • El motor de interpretación obtiene información de los objetos del hospedero a través de la función GetItemInfo de la interfaz IActiveScriptSite implementada por el hospedero.
  • Antes de ejecutar el guión, el motor de interpretación conecta los eventos de todos los objetos relevantes a través de la interfaz IConnectionPoint si es soportada.
  • Finalmente el motor ejecuta los métodos de los objetos referenciados en el guión llamando la función Invoke y notifica al hospedero de los eventos que ocurren.
El hospedero

La aplicación (hospedero) que soporte guiones de ejecución ActiveX Script tiene que contener al menos una instancia de un objeto que exponga la interfaz IActiveScriptSite. Este objeto, es el punto de interacción del hospedero con el motor de interpretación. Usualmente este objeto es el contenedor de todos los otros objetos que son visibles para el guión que ejecuta el hospedero. La figura 1 muestra el código pascal de definición de dicha interfaz.

A continuación veamos una breve descripción de cada uno de los miembros de la interfaz IActiveScriptSite

GetLCID Esta función permite al hospedero indicar las constantes de localización para la interacción del motor de interpretación y el usuario de la aplicación.
GetItemInfo Esta función es llamada por el motor de interpretación para buscar los objetos nominalizados dentro de la aplicación.
GetDocVersionString Esta función es llamada por el motor de interpretación para obtener una el número de versión textualizado del documento vigente. Esta cadena puede ser usada para validar que cualquier estado congelado en el motor de ejecución pueda ser salvado consistentemente en el documento vigente.
OnScriptTerminate Esta función es llamada cuando el motor de interpretación términa. En muchos motores, esta función no es llamada y es posible utilizar el guión interpretado para generar eventos en la aplicación hospedera
OnStateChange Esta función es llamada cuando el motor de intérprete cambia de estado tanto explicitamente mediante la función SetScriptState o implícitamente por los eventos que se generan en el guión de ejecución.
OnScriptError Esta función es llamada cuando durante el análisis sintáctico del guión o durante la ejecución del mismo, se encuentra un error. El motor de interpretación suministra una implementación de la interfaz IActiveScriptError que describe el error en tiempo de ejecución en términos de una estructura EXCEPINFO, en adición a la indicación de la localización del error en el texto original del guión.
OnEnterScript Esta función es llamada por el motor de ejecución para indicar el comienzo de una unidad de trabajo.
OnLeaveScript Esta función es llamada por el motor de ejecución para indicar la terminacion de una unidad de trabajo.
 
Type
IActiveScriptSite = Interface( IUnknown )
['{DB01A1E3-A42B-11CF-8F20-00805F2CD064}']
function GetLCID( out Lcid: TLCID ): HRESULT; stdcall;
function GetItemInfo( const pstrName: POleStr;
dwReturnMask: DWORD;
out ppiunkItem: IUnknown;
out Info: ITypeInfo): HRESULT; stdcall;
function GetDocVersionString( out Version: TBSTR): HRESULT; stdcall;
function OnScriptTerminate( const pvarResult: OleVariant;
const pexcepinfo: TExcepInfo): HRESULT; stdcall;
function OnStateChange( ScriptState: TScriptState ): HRESULT; stdcall;
function OnScriptError( const pscripterror: IActiveScriptError ): HRESULT; stdcall;
function OnEnterScript: HRESULT; stdcall;
function OnLeaveScript: HRESULT; stdcall;

end;

figura 1. Definición de la interfaz IActiveScriptSite

El motor de interpretación

El motor de interpretación ActiveX Script, tiene que implementar la interfaz IActiveScript. Cuando el hospedero crea una instancia del motor de interpretación, llama a la función SetScriptSite de esta interfaz, para establecer el puente de interacción hospedero-motor de interpretación.

A continuación veamos una breve descripción de cada uno de los miembros de la interfaz IActiveScript

SetScriptSite Informa al motor de lenguaje del objeto establecido en el hospedero que expone la interfaz IActiveScriptSite
GetScriptSite Puede ser llamada para obtener el hospedero del motor de interpretación
SetScriptState Pone el motor de interpretación en un estado dado
GetScriptState Obtiene el estado del motor de interpretación
Close Causa que el motor de interpretación abandone cualquier guión cargado, pierda el estado en que se encuentra, librere los objetos internos y punteros a las interfaces de otros objetos y entre en el estado closed
AddNamedItem Adiciona un nombre en el nivel primario del espacio nominal del motor de interpretación
AddTypedLib Adiciona una librería de tipo en el espacio nominal del motor de interpretación
GetScriptDispath Obtiene un puntero a la interfaz IDispatch para los métodos y propiedades asociadas con el guión que se está ejecutando
GetCurrentScriptThread Obtiene el identificador definido del motor de interpretación para el hilo de ejecución (thread) vigente
GetScriptThreadID Obtiene el identificador de hilo de ejecución (thread) asociado en el motor de interpretación asociado con el hilo de ejecución dado en Microsoft WIN32
GetScriptThreadState Obtiene el estado vigente en el hilo de ejecución (thread) del guión
InterrupScriptThread Interrumpe la ejecución del guión que se esta ejecutando
Clone Sintetiza una copia gemela del motor de interpretación (excepto cualquier estado de ejecución), retornando una nueva instancia del motor de interpretación cargado sin asociación al hospedero en el hilo de ejecución vigente (thread)

El motor de interpretación permite controlar la funcionalidad básica de la ejecucion del guion; pero si soporta guiones textuales y evaluación de expresiones textuales compatibles con el lenguaje, entonces tiene que implementar la interfaz IActiveScriptParse.

La interfaz IActiveScriptParse constituye el analizador sintáctico del lenguaje interpretado y el ejecutor de las instrucciones del guión del motor de interpretación. El puntero a esta interfaz se obtiene llamando el metodo QueryInterface de IActiveScript. Los miembros fundamentales de la misma son:

InitNew Inicializa el motor de interpretacion
AddScriptlet Adiciona un fragmento de codigo a un guion
ParseScriptText Efectúa el analisis sintactico de un guion, adicionando las declaraciones dentro del espacion nominal del motor y evaluando apropiadamente el codigo

La función ParseScriptText ejecuta el analisis sintáctico de acuerdo a las banderas de control que se establezcan en su llamada. Esta función tiene la siguiente sinopsis:

HRESULT ParseScriptText (
  LPCOLESTR pstrCode, // Buffer con el texto del guion
  LPCOLESTR pstrItemName, // Nombre de item que suministra el contexto
  IUnknown* punkContext, // Contexto de depuracion
  LPCOLESTR pstrEndDelimiter, // Delimitador de fin del guion
  DWORD dwFlags, // Banderas de control
  VARIANT* pvarResult, // Retorno de los resultados de la ejecucion
  EXCEPINFO* pexcepinfo); // Buffer para retornar los errores

El parámetro dwFlags, determina el procesamiento y puede ser cualquiera o una combinacion de las siguientes constantes:

SCRIPTTEXT_ISEXPRESSION Si la distinción entre una expresion computacional y una sentencia es importante pero sintacticamente ambigua en el lenguaje, esta bandera especifica si el texto debe ser interpretado como una expresion o como una lista de sentencias. Implicitamente se asume que son sentencias
SCRIPTTEXT_ISPERSISTENT Indica que el codigo adicionado durante la llamada a esta funcion debe ser salvado en el motor de interpretacion o si éste debe retornar a su condicion en el estado de inicializado
SCRIPTTEXT_ISVISIBLE Indica si el texto del guion debe ser visible (y por tanto, accesable por nombre) como un metodo global del espacio nominal del guion

El parámetro pexcepinfo apunta a una estructura para recibir la informacion de excepcion, que llenada si la funcion ParseScriptText retorna el valor DISP_E_EXCEPTION.

Aplicacion de ejemplo

Se puede construir una aplicación rudimentaria que actúe de hospedero de los motores de interpretación. El código que acompaña este artículo, tienen las fuentes completas del ejemplo que explicamos a continuación. Para crear el hospedero, creemos una nueva aplicacion en Delphi. Incorporemos un componente Memo1 del tipo TMemo, para la edición de los guiones. Adicionemos un componente TMenu con al menos un elemento TMenuItem para ejecutar el guión editado asociado al siguiente codigo:

procedure TForm1.mnEjecutar1Click(Sender: TObject);
var
    ActiveScriptParse : IActiveScriptParse;
    ActiveScript : IActiveScript;
    ExcepInfo : TExcepInfo;
    AppScriptSite : TScriptSiteWindow;
    EngineID : TCLSID;
begin
    // En esta aplicacion, se utilizara el motor de interpretacion de Microsoft
    // para Visual Basic (VBScript).
    EngineID := ClSID_VBScript;
    if Memo1.Lines.Text = '' then Exit;
    FResult := UnAssigned;
    // Crear el objeto que implementa la interfase IActiveScriptSite del hospedero
    AppScriptSite := TScriptSiteWindow.Create;
    AppScriptSite.WindowHandle := Application.Handle;
    // Inicializar las variables que sostendran las interfases que suministrara
    // el motor de interpretacion. El moldeo a Pointer es requerido, para
    // evitar la manipulacion automatica de Delphi del contador de referencias
    // de la interfase IUnknown.
    Pointer(ActiveScript) := nil;
    Pointer(ActiveScriptParse) := nil;
    // Crear una instancia del motor de interpretacion y obtener el puntero a su
    // interfase IActiveScript.
    // Si hay error en la creacion de la instancia del objeto, OleCheck genera una
    // excepcion y la instancia AppScriptSite creada anteriormente será liberada
    // automaticamente al salir del marco.
    OleCheck( CoCreateInstance(
        EngineID, // Indentificador del motor de interpretacion.
        nil,
        ClsCtx_InProc_Server, // El motor tiene que estar implementado
        // en una biblioteca de enlace dinamico
        IID_IActiveScript, // Identificador de la interfase implementada
        // en el objeto que se quiere obtener
        ActiveScript) // Puntero para recibir el retorno la
        // interfase solicitada.
        );
    // Obtener el puntero a la interface IActiveScriptParse del motor de
    // interpretacion.
    ActiveScript.QueryInterface(IID_IActiveScriptParse, ActiveScriptParse);
    // Registrar con el motor de interpretacion, el hospedero del mismo.
    OleCheck( ActiveScript.SetScriptSite( AppScriptSite ));
    try
        // Comenzar la ejecucion de un nuevo guion en el motor de interpretacion
        if SUCCEEDED( ActiveScriptParse.InitNew ) then
        begin
        // Para crear los objetos COM globales del hospedero que pueden ser
        // accesados por el motor de interpretacion se indica en comentario
        // el codigo a ejecutar.
        // Los objetos globales se adicionan en un listado
        // El primer caso es un objeto COM no registrado que es parte de la
        // aplicacion. El segundo adiciona un objeto registrado en la PC donde se
        // corre esta aplicacion. El primer parametro de procedimiento AddGlobal,
        // es el nombre con que el objeto va a ser referenciado en los guiones
        // de ejecucion.
        // AppScriptSite.AddGlobal('AppObj', TObjetoDeAplicacion.Create);
        // AppScriptSite.AddGlobal('PCO', CreateOleObject('PCOBJDLL.PCObjeto') );
        // Adicionados los objetos globales accesables desde el guion, cuando el
        // motor de interpretacion necesita obtener informacion de cualquier objeto
        // llamará a la funcion GetItemInfo de la interfase IActiveScriptSite.
        // Adicionemos pues los seudonimos de los objetos, al espacio global de
        // nombres de hospedero, donde hara sus busquedas el motor de interpretacion.
        { ActiveScript.AddNamedItem('AppObj',
        SCRIPTITEM_ISVISIBLE
        or SCRIPTITEM_GLOBALMEMBERS //Permitir al guion usar los miembros del
        //objeto sin referenciar directamente el objeto
        or SCRIPTITEM_ISPERSISTENT //Permitir que el objeto sea salvado
        //directamente por el motor de interpretacion
        );
        ActiveScript.AddNamedItem('PCO', SCRIPTITEM_ISVISIBLE or
        SCRIPTITEM_GLOBALMEMBERS or
        SCRIPTITEM_ISPERSISTENT
        );
        }
    // Activar el motor de interpretacion
    OleCheck( ActiveScript.SetScriptState(SCRIPTSTATE_CONNECTED) );
    // Analizar y ejecutar el guion
    OleCheck( ActiveScriptParse.ParseScriptText(
        POleStr(WideString(Memo1.Lines.Text)), // Texto del guion de ejecucion
        nil, // Nombre del elemento que da contexto al guion a ejecutar
        nil, // Contexto de depuracion (debugger)
        nil, // Delimitador del fin del guion de ejecucion
        0, // Contexto fuente del Cookie
        0, // Linea de comienzo de guion
        SCRIPTTEXT_ISPERSISTENT or SCRIPTTEXT_ISVISIBLE, // Banderas
        FResult, // Buffer para retornar el resultado de la ejecucion del guion
        ExcepInfo // Buffer para recibir la informacion de error
        ));
    end;
    except // Si algo fue mal, mostrar el mensaje de error
        On e:Exception do
            ShowMessage( e.Message );
    end;
    // Finalmente es interesante notar que
    // El compilador de Delphi (version 3 o posterior), genera automaticamente el
    // codigo para liberar (llamada a procedimiento _Release del objeto) los
    // objetos creados que implementan la interfaz IUnknown cuando estos se van
    // fuera de su marco validez.
end;

El código ha sido abundantemente comentado para permitir identificar claramente los pasos explicados para la implementación de la tecnología ActiveX Script en una aplicación.

ir al índice

Microsoft(r) Windows(r) Scripting Host es un hospedero de guiones de ejecución independiente del lenguaje para motores de interpretación ActiveX™ en plataformas Windows de 32 bits.

Las plataformas de Microsoft Windows(r) 98, Windows NT(r) Workstation versión 5.0 y Windows NT Server versión 5.0 soportan este hospedero de guiones de ejecución. La propia compañía ya ha incluído los motores Microsoft(r) Visual Basic(r) Scripting Edition (VBScript) y Microsoft(r) JScript™. Otras compañías de proporcionarán otros motores ActiveX™ para lenguajes como Perl, TCL, REXX, Python y otros.

Este hospedero presenta baja ocupación de memoria durante la ejecución directa de los guiones, las secuencias de comandos no necesitan estar incrustadas en un documento HTML y es ideal para implementar comandos no interactivos que realicen tareas administrativas y de inicio de sesiones.

Windows Scripting Host utiliza la extensión del archivo de comandos para determinar qué motor de secuencias de comandos debe utilizar. Como resultado, quien escribe secuencias de comandos no necesita obtener el ID de motor de secuencias de comandos. El hospedero mantiene por sí solo, una asignación de extensiones de secuencias de comandos con ID de los motores y utiliza el modelo de asociación de Windows para iniciar el motor apropiado para la secuencia de comandos dada.

Windows Scripting Host expone nueve objetos globales de los cuales sólo tres están disponibles para los guiones de ejecución. Estos son WScript, WshArguments y WshShell. Las propiedades y metodos de los mismos quedan fuera del alcance de este articulo. El lector interesado, puede encontrar la documentacion de referencia en la ayuda del Personal Web Server de Windows 98.

En Windows 98 puede correrse desde la consola un guión de ejecución con el utilitario CSCRIPT.EXE. En la carpeta SAMPLES\WHS dentro del directorio de instalación de Windows 98, encontrará el archivo SHOWVAR.VBS, que es un guión de ejecucion en VBScript. Revise su contenido con el utilitario NOTEPAD.EXE. Para correrlo, haga doble CLICK sobre el mismo o ejecute desde la opcion EJECUTAR del menú de inicio la siguiente linea de comando,

CSCRIPT SHOWVAR.VBS

ir al índice


Aplicaciones de Consola

El ambiente de Delphi, está concebido fundamentalmente para desarrollar aplicaciones que soportan el ambiente gráfico de Windows de forma rápida, sin embargo, existe un conjunto de aplicaciones que no requieren de una interfaz gráfica que Delphi construye muy eficientemente también. En este artículo, vamos a examinar las aplicaciones de tipo consola, que son programas que se caracterizan por ser rápidos y el tamaño del ejecutable es pequeño.

Las generalidades

Las aplicaciones de consola, son programas escritos sin hacer uso de una interfaz gráfica de usuario.

En Windows, las aplicaciones de consola se ejecutan en una ventana especial basada en texto, conocida como la ventana de consola, a través de la cual se realiza la interacción con la aplicación. Los dispositivos estandares de entrada y salida de texto, están automáticamente asociados a esta ventana de consola.

Delphi no crea una nueva forma cuando crea una aplicación de tipo consola. Los controles visuales de la VCL no son generalmente empleados en una aplicacion de tipo consola, sin embargo, pueden ser usados.

El progama más simple de una aplicación de tipo consola que usted puede compilar en Delphi y correr desde la línea de comando es el siguiente:

Program Consola

{$APPTYPE CONSOLE}

Begin
  Write( 'Hola a todos' );
End.

La primera línea, declara el nombre del programa, que tiene que ser igual nombre del fichero en que se guarda la aplicación con extensión PAS. La directiva {$APPTYPE CONSOLE} instruye al compilador que construya una aplicación del tipo consola. Esta directiva, tiene sólo sentido para un programa y no debe se usada en una librería, package o unit. Debemos resaltar, que las aplicaciones de consola, son programas de 32 bits dentro del ambiente de Delphi.

El contenido de lo que hay que ejecutar dentro del programa, se enmarca entre el begin y end. En este ejemplo, el programa simplemente escribe una cadena al dispositivo estandard de salida (ie. el monitor de la computadora).

El procedimiento Write, está definido en la unidad SYSTEM y es implícitamente importada en todos los programas. En Delphi 4, el ejecutable de este programa compilado tendrá un tamaño de fichero de 15.5 Kb. Compárelo contra el tamaño del programa más simple que puede construirse, usando una interfaz gráfica.

Las aplicaciones de consola, pueden dividirse en varios ficheros. En este caso, utilizaremos la claúsula Uses para indicar los ficheros que se incluyen en la aplicación. También, podemos incorporar recursos, en particular, es útil adicionar recursos de versión. Esto se realiza adicionando la directiva {$R *.RES}.

Una forma alternativa y equivalente de indicar, dentro del ambiente de Delphi, que estamos compilando una aplicación de tipo consola, es con la opción Generate Console Application del LINKER. Personalmente preferimos, hacerlo explícitamente con la directiva antes señalada.

Las particularidades

En la unidad SYSTEM, está declarada una variable lógica, IsConsole, que puede ser usada para examinar en tiempo de ejecución, si el programa que está corriendo es una consola o tiene interfaz gráfica y tomar las acciones correspondientes.

El tamaño pequeño de las aplicaciones consola, está vinculado a no usar la unidad Forms. Si en el ejemplo inicial, adicionaramos la línea,

Uses Forms;

el tamaño del ejecutable generado, cambiaría a 271 Kb. O sea, la aplicación incrementa el tamaño del ejecutable en más de 17 veces. El único argumento, por el cual desearíamos adicionar la unit Forms a una aplicación de consola, sería para proporcionar acceso a la variable Application, que es una instancia del objeto TApplication. Así que, no incluya la unit Forms en sus aplicaciones de tipo consola.

Para acceder a la cola del comando, cuando se invoca una aplicación, podemos hacer uso de las funciones ParamCount y ParamStr.

La ParamCount, retorna el número de parámetros pasados a la aplicación en la cola del comando. Por su parte, ParamStr(n) retorna la n-ésima cadena de la cola del comando. ParamStr(0) siempre retorna el nombre de la aplicación con el camino completo desde la carpeta en que reside. Si el valor de n que se suministra es mayor que el valor retornado por ParamCount, entonces tendremos una cadena vacía.

Adicionalmente, la unidad System, suministra un conjunto de variables públicas muy útiles, que son inicializadas cuando se carga la misma y el conjunto de funciones básicas requeridas en cualquier aplicación: manipulación de cadenas, acceso a ficheros, operaciones con variants y manejo de memoria. Recomendamos que eche un vistazo a esta unidad.

Algo avanzado

Vamos a ver ahora, a través de un ejemplo sensillo, cómo una aplicación con interfaz grafica de windows interactúa con una aplicación de consola.

La aplicacion con interfaz gráfica MiGUI

Esta aplicación muestra en su interfaz un botón para lanzar la aplicación de consola, una caja de edición para establecer los parámetros del comando de consola que se va a invocar y un control MEMO para mostrar el resultado.

La aplicacion de consola MiCon1

La aplicación de consola es muy sencilla. Simplemente realiza la suma de dos números suministrados en la cola de comando y escribe el resultado.

El esquema funcional de interacción de ambas aplicaciones es el siguiente:

  • La aplicación de Windows, MiGUI, lanza la aplicación de consola MiCon1, pasándole por la cola de comando los parámetros funcionales (dos números que se van a sumar).
  • La aplicación de consola, MiCon1, ejecuta su trabajo y escribe al dispositivo de salida estandard, el resultado.
  • La aplicación MiGUI, captura la salida de la aplicación de consola y la muestra en su interfaz.

Asociado al botón de lanzar de la aplicación MiGUI se tiene el siguiente código, que ha sido abundantemente comentado para facilitar su comprensión:

procedure TForm2.Button1Click(Sender: TObject);
const
  BufSize = $4000;
var
  si : TStartupInfo;
  pi : TProcessInformation;
  ec, attr, BytesRead : DWORD;
  WriteHandle, ReadHandle : THandle;
  ConsoleOut : string;

      procedure TerminarConsola;
      begin
        Button1.Enabled := True;
        CloseHandle( ReadHandle ); // Cerrar los handles del pipe
        CloseHandle( WriteHandle );
      end;
begin
  // Creamos un pipe anonimo cuyo extremo de escritura vamos a
  // utilizar como dispositivo de salida del proceso de consola
  // que se va a lanzar. El buffer del pipe tiene que ser suficientemente
  // grande como contener la salida del proceso de consola.
  if not CreatePipe(ReadHandle, WriteHandle, nil, BufSize) then Exit;

  // Inicializar la estructura de informacion de arranque del proceso
  FillChar(si, SizeOf( TStartupInfo ), 0);
  si.cb := SizeOf( TStartupInfo );

  // Establecemos los dispositivos de salida y error de proceso de consola
  si.hStdOutput  := WriteHandle;
  si.hStdError   := WriteHandle;

  // Establecer la prioridad del hilo de ejecucion de la aplicacion de consola
  // Variantes: IDLE_PRIORITY_CLASS, HIGH_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS
  attr := DETACHED_PROCESS or NORMAL_PRIORITY_CLASS;

  // Indicar que se atienda los valores pasados en el miembro wShowWindow y
  // los handles suministrados en la estructura
  si.dwFlags     := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  // Indicar la forma en que se muestra la aplicacion lanzada
  // Variantes: SW_SHOWNORMAL, SW_MAXIMIZE, SW_MINIMIZE
  si.wShowWindow := SW_HIDE;

  // La funcion CreateProcess, crea un nuevo proceso donde ejecuta el fichero
  // ejecutable especificado.
  ConsoleOut := Edit1.Text; // Ejecutable a lanzar
  try
    if not CreateProcess(nil,	// lpApplicationName (nil para consolas)
             PChar(ConsoleOut),  // Ejecutable de consola y cola de comando
             nil,		// LPSECURITY_ATTRIBUTES
             nil,		// LPSECURITY_ATTRIBUTES
             FALSE,	// bInheritHandles
             attr,         // dwCreationFlags
             nil,	   	// lpEnvironment
             nil,		// lpCurrentDirectory
             si,		// lpStartupInfo
             pi )		// lpProcessInformation

    then // Si la aplicacion de consola no es lanzada, retornar
    begin
      TerminarConsola;
      Exit;
    end;
  except
    TerminarConsola;
    Exit;
  end;

  // Evitar recurrencia al lanzar la aplicacion de consola
  Button1.Enabled := False;

  // Ciclo para esperar que la aplicacion de consola termine su trabajo
  Repeat
    if not GetExitCodeProcess(pi.hProcess, ec) then
    begin
      TerminarConsola;
      Exit;
    end;
    Application.ProcessMessages;
  Until (ec <> STILL_ACTIVE);

  try
    // Leer del pipe, la informacion escrita por la aplicacion de consola
    SetLength(ConsoleOut, BufSize);
    ReadFile( ReadHandle, ConsoleOut[1], BufSize, BytesRead, nil );
    SetLength(ConsoleOut, BytesRead);

    // Copiar al memo, el texto retornado por la aplicacion de consola
    Memo1.Lines.Text := ConsoleOut;
  finally
    TerminarConsola;
  end;
end;

El ejemplo es muy sencillo; pero permite imaginar su alcance. En primer lugar, la aplicación que sostiene la interfaz gráfica y la aplicación de consola, se ejecutan automáticamente en dos hilos de ejecución diferentes. En un ambiente multitarea y multihilo como Win32, se pueden establecer prioridades diferentes para cada aplicación. La aplicación de consola se ejecuta sin ser mostrada. Si ésta cae en un ciclo de ejecución prolongado, desde la aplicación con interfaz puede cancelarse la misma o inclusive ejecutar otras cosas.

Construya la aplicación MiGUI (las fuentes están construidas con Delphi 4) suministrada con este artículo y ejecute a través de ella, el siguiente ejemplo de aplicación de consola, que es parte del sistema operativo:

mem /c

Interesante, ¿ Verdad ?

   ir al índice


Asociación de ficheros a las aplicaciones que tu creas


En el Explorador de Windows, cuando pinchamos con el botón derecho del ratón sobre un fichero con extensión BMP, automáticamente emerge un menú, en una de cuyas opciones encontraremos Abrir. Si seleccionamos esta opción, se abre la aplicación Paint mostrando la imagen contenida en el fichero para ser editada. Esta funcionalidad, se logra con la asociación de tipos de ficheros a las aplicaciones que son capaces de cargarlos para ejecutar algún trabajo con él.

La asociación de ficheros a una aplicación es conocida desde Windows 3.1, donde bastaba adicionar en la sección Extensions del fichero del sistema WIN.INI, la siguiente línea:

BMP=C:\WINDOWS\PAINT.EXE   ^.BMP

Pero, en la plataforma Win32, el soporte de WIN.INI solamente es mantenido por compatibilidad con las versiones anteriores. En Windows 95, Windows 98, Windows 2000 y Windows NT, el registro de Windows es el repositorio donde se almacena la información de asociación de ficheros con una aplicación, ahora extendida con un modificador llamado verbo (verb), que califica la acción a ejecutar sobre el mismo. 

El verbo indica la acción que la aplicación asociada debe realizar sobre el documento contenido en un fichero. Existen tres verbos clásicos: Open, utilizado para abrir y visualizar el documento, Edit utilizado para abrir el documento en modo de edición y Print, utilizada para mandar el documento a la impresora predeterminada en el sistema.

El procedimiento de asociar un tipo de fichero a una aplicación se realiza en dos pasos:

  1. Inscribir el tipo de documento utilizando la extensión del fichero que lo contiene en el Registro de Windows, con la clase de una aplicación, que implicitamente lo va a manipular.
  2. Registrar la clase de la aplicación que es capaz de abrir un documento y los verbos para el Shell de Windows, con los cuales es capaz de actuar sobre el documento.

En lo adelante, supondremos que las operaciones con el registro de Windows, se realizan bajo al llave HKEY_CLASSES_ROOT

Para realizar el primer punto, debemos añadir una llave con el nombre de la extensión del fichero (incluyendo el punto precedente de la extensión), bajo la cual estableceremos una llave con el nombre default a la que asociaremos como valor el nombre de la clase de la aplicación. Usualmente, se utiliza como nombre de clase de una aplicación, el nombre de la misma, seguida de un punto y la palabra Document. Así, en el editor del registro de Windows, se vería:

HKEY_CLASSES_ROOT\ 
.ext\ 
Default = "MiApp.Document"

Para registrar la clase de la aplicación, hay que crear una llave con MiApp.Document. Bajo esta llave, se especifican las acciones que se ejecutan desde el Shell de Windows, donde cada acción visible es una clave cuyo valor es la cadena del comando para ejecutar dicha acción. En el registro de Windows, debe quedar algo como esto,

HKEY_CLASSES_ROOT\
MiApp.Document\
Shell\
Open\Command\Default="MiAppPath\MiApp.exe %1"
Edit\Command\Default="MiAppPath\MiApp.exe %1"
Print\Command\Default="MiAppPath\MiApp.exe /p %1"

En el ejemplo a continuación se muestra el código completo de una unit, que contiene el procedimiento RegisterAssociation, que le permite asociar un tipo de fichero a su aplicación, que adicionalmente incorpora el icono principal de la aplicación a las metáforas gráficas que son mostradas en el Explorador. Simplemente, incorpore la llamada a este procedimiento con cuanto tipo de fichero usted quiera asociar a su aplicación dentro del evento de CreateForm de la forma principal de su aplicación.

Unit Associat;

{******************************************************************************}
Interface

Uses Windows, Forms, Registry, SysUtils, Dialogs;

  procedure RegisterAssociation(const ext, Desc: string);

{******************************************************************************}
Implementation

Const
  OPEN_CMD = '\Shell\Open\Command';
  EDIT_CMD = '\Shell\Edit\Command';
  ICON_OBJ = '\DefaultIcon';

{-------------------------------------------------------------------------------
 Registra la asociacion de ficheros de la aplicacion en WINDOWS.
 ej: Si los ficheros asociados a la aplicacion desde donde se llama esta
 funcion son de extension TIF, entonces se registran estos ficheros mediante
 la siguiente llamada

     RegisterAssociation( '.TIF', 'Archivo de imagen TIF' );

 Importante: No olvidar el punto de las extensiones.
-------------------------------------------------------------------------------}

procedure RegisterAssociation(const ext, Desc: string);
var
  Obj, pth : string;
  lng : Integer;
begin
  with TRegistry.Create do
  try
    RootKey := HKEY_CLASSES_ROOT;

    with Application do
    begin
      lng := Length(ExtractFileName( ExeName )) - Length(ExtractFileExt( ExeName ));
      Obj := Copy(ExtractFileName( ExeName ), 0, lng) + '.Document';
    end;

    pth := ext;
    if ext[1] <> '\' then
    begin
      if ext[1] <> '.' then
        pth := '\.' + ext
      else
        pth := '\' + ext;
    end
    else if ext[2] <> '.' then
      pth := '\.' + Copy(ext, 2, Length(ext) - 1)
    else
      pth := '\' + ext;

    if not KeyExists(pth) then
    begin
      if OpenKey(pth, True) then
        WriteString('', Obj);
      CloseKey;
    end;

    if not KeyExists(Obj) then
    begin
      if OpenKey(Obj, True) then
      begin
        WriteString('', Desc);
        CloseKey;
      end;
      if OpenKey(Obj + OPEN_CMD, True) then
      begin
        WriteString('', Application.ExeName + ' %1');
        CloseKey;
      end;
      if OpenKey(Obj + ICON_OBJ, True) then
      begin
        WriteString('', Application.ExeName + ',1');
        CloseKey;
      end;
    end;
  finally
    Free;
  end;
end;

End.

ir al índice


EXE generado con Delphi ¿demasiado GRANDE ?

Muchas veces puede suceder que a varios nos parezca demasiado grande el .EXE generado por Delphi, y realmente lo es. ¿Por qué ? ¿Cómo evitarlo ? Estas son algunas de las cosas que debemos tener en cuenta al desarrollar una aplicacion hecha en Delphi y queremos que funcionen con el máximo de optimización y eficiencia posibles.

Desde los antiguos programas de MS-DOS generados en Turbo Pascal ó C, a muchos de nosotros nos gustaban usar las famosas librerías TVision ó Turbo Power para darle belleza y colorido a nuestras aplicaciones. Esto por supuesto tenía el inconveniente de mientras más unidades (units) usábamos, mas grande se nos hacia el .EXE resultante.

Con la llegada de Windows al mercado, la programacion tradicional de MS-DOs quedó obsoleta y entró un nuevo modo de programar el cual abstraía mucho más a los usuarios de sus aplicaciones en general, se trataba del Sistema de manipulación de Mesjaes de Windows, en el que Windows es el que manda un mensaje a la aplicacion, la aplicación espera por ese mensaje y si lo conoce y tiene un manipulador de este mensaje, sencillamente le da respuesta. Con esta gran ventaja surgió adjunto el desarrollo de la Programación visual Orientado a Objetos que ya venía desde el Ms-DOS y surgieron las librerias de Windows llamadas API. Pero la API no era POO y tenía muchas dificultades al expresar cosas evidentes y muy sencillas, además del tiempo que el usuario gastaba en realizar una sencilla aplicación. Surgieron las MFC (Microsoft Fundation Class) que no son más que las mismas funciones de la API creadas por Microsoft pero ahora con POO y de Borland la VCL (Visual Component Library).

Por supuesto que a partir de ese momento todos los productos de Microsoft soportaban la API que era la librería que ya venía implicita en Windows. Por eso si se desarrolla una aplicacion con un producto de Microsoft, el EXE generado es relativamente más pequeño que si se hace con un producto Borland con la VCL, porque ya las DLLs de MFC están en Windows, mientras que la VCL no y hay que distribuirlas.

Como todos conocemos, Dephi es un compilador de código nativo, o sea, no nesesita ninguna DLL acompañada del .EXE para ejecutarse en una PC sacada de la caja.

Cuando instalamos Delphi, la primera vez que lo corremos el introduce dentro del .EXE las DLLs según las vaya usando. Usted toma ese .EXE y se va para cualquier lugar, lo corre y listo. Pero esto tiene el inconveniente que según se usen mas BPLs (Borland Package Library - Las DLLs de Borland) asi será mas grande el EXE resultante. La solución está en ir al menú Projects/Options/Packages, y poner el Check Box Build whot runtime Packages en TRUE. Esto hará que dentro del .EXE no se metan las BPLs, solo el código nesesario de nuestra aplicación. Los BPLs se distribuyen mediante una utilidad llamada Install shield que viene con Delphi y donde se le dice que BPL usará. Para saber las PBLs que usa nuestro proyecto basta con ir al menú Project/Information y allí veremos todas las BPLs que hay que poner en el Install Shield. Esto es una muy buena opción ya que si Ud. desarrolla varias aplicaciones en una misma PC, todas las aplicaciones usan una misma copia de las BPLs, ahorrando memoria, tiempo de instalación de Install Shield, porque el no sobreescribe la copia del BPL a menos que Ud diga lo contrario. De esta forma es más efectivo y beneficioso porque Build Whit runtime pachkages, como lo trae Delphi por defecto mete los BPLs dentro de nuestro EXE, cosa que no nos satisface.

Pero hay algo que quizás puede pasar inadvertido para algunos usuarios, sobre todo principiantes (a mi me pasó). Como todos sabemos, los componentes de la paleta de compoenetes de Delphi se agrupan en Páginas, c/u de estas páginas está asociada a un BPL en específico, por Ej: Standard - VCL40.BPL, adicional - VCLX40.BPL, Data Access y DataControls VCLDB40 y VCLDBX40.BPL respectivamente, QReport a QRP40.BPL y asi sucesivamente. Que quiere decir esto ?? Sencillo. Si usted usa un componente en Delphi, el le adciona a la clausula uses de la forma la unit a que pertenece el componente, por tanto si agrega un TTable agregará al uses DBTables si agrega un Navigator agregará un DB y usted estrá usando el VCLDB40 y VCLDBX40.BPL en su programa, porque el compilador se guia en as BPLs que uses por la unit en la cláusula uses. Pero cuando eliminamos un componete de nuestra forma, Delphi no le quita la unit de la cláusula uses, y entonces, aunque no tengamos una sola Tabla ó navigator en nuestra forma, si sus respectivas units están reflejadas en la forma, entonces el seguirá pidiendole las BPLs asociadas a estos compoenetes. Por tanto, es recomendable, cuando borremos algún componente si estamos seguros de que no usamos más esa unit para nada (Por eso Dephi no la borra del uses cuando borras un componente) que la ELIMINEMOS de la cláusula uses de nuestra forma. De cualquier manera, existe un componente en la Forma y usted borra su unit, el vuelve a agregarla al compilar. (Cada vez que compila chequea los componentes y las units de c/u de ellos).

Concluyendo:

Este es una de las posibles soluciones, hay para tratar de hacer nuestras aplicaciones eficientes y optimas, minimizando el tiempo de carga ejecucion y usando los recursos del sistema de forma más racional, ahorrando memoria (recuerden que al ejecutar un .EXE todo está en memoria), etc. También debemos optimizar internamente nuestro código y usar las facilidades que nos da el lenguaje Object - Pascal internamente y la POO.

Suerte...

ir al índice


La huella de los programadores

(Nota: Los textos e ideas originales de esta exposición no son míos. En cada caso, se cita la fuente de donde extraje la información, que por curiosa, me pareció interesante hacerselas llegar)

Cuando se ejecuta un gran proyecto de software en un equipo, los programadores, gustan de dejar en el paquete alguna huella que revele su participación. Después de mostrar algunos ejemplos, vamos a relatar una técnica para lograr esto en nuestros proyectos en Delphi.

En el sitio www.ethek.com, podemos encontrar un reseña debida a Jesús Manuel Rodríguez, que nos explica la forma de ver los créditos del equipo de Windows 98.  

  1. Menú de Inicio -> Panel de control -> Fecha y hora, o también hacer doble click sobre la hora en la barra de tareas.
  2. Seleccionar temporalmente la zona horaria GMT +01:00 Bruselas, Conpenhague, Madrid, París, Vilnius
  3. En la imagen del mapa mundi que está mostrada, poner el cursor en el punto 1, pinchar el ratón y  manteniendo oprimida la tecla Ctrl, arrastrarlo hasta el punto 2.
  4. Soltar la tecla Ctrl .
  5. Con el cursor sobre el punto 2, pinchar el ratón y  manteniendo oprimida la tecla Ctrl, arrastrarlo hasta el punto 3.
  6. Soltar la tecla Ctrl .

 

Entonces, se abre la ventana que mientras carga otros recursos, muestra unos números en azul y luego los nombres y fotos del equipo de programación de Windows 98.

No es fácil lograr que se muestren los créditos del equipo de programación de Windows 98. Tendrá que intertarlo varias veces. Yo generalmente lo consigo a la tercera oportunidad. Bueno, lo que se muestra es una página WEB, que se abre temporalmente en el siguiente camino:

    Directorio de Windows\Application Data\Microsoft\Welcome

En el directorio SYSTEM, se encuentra una librería, MEMBG.DLL, que contiene los recursos mostrados en la página WEB, dinámicamente construida.

Otra forma alternativa de ver los nombres de los participantes en el proyecto Windows 98 es la siguiente.

  1. Menú de Inicio -> Panel de control ->Pantalla, o también hacer click con el botón derecho sobre la pantall y seleccionar Propiedades
  2. Seleccionar la pestaña "Protector de Pantalla".
  3. Seleccionar el protector de pantalla "Texto 3D" y en activar la configuración. Escribir el texto "Volcano", aceptar y después hacer una vista previa.
  4. Los nombres de las personas participantes en el equipo, irán alternandose uno a uno.

Veamos ahora una propuesta de hacer algo como esto. Bajo el título "Implementing an Easter Egg", por colaboracion de Michael Burton, en el número 38 (Abril de 1999) de la páginas de UNDU, se suministra un código que al ocurrir una combinación de teclas poco usuales, se activa un diálogo que puede servir para mostrar los créditos de los participantes en la construcción de un proyecto en Delphi.

Aquí reproducimos el código suministrado en el artículo (traducción mediante).

1. En la forma en que se desee activar los créditos del equipo, establezca la propiedad KeyPreview en True. Esto permite a la forma tener la entrada de teclado antes que el control activo en la misma. 

2. Crear dos variables privadas en la forma:

  eeCount: integer;
  sEgg: string;

La variable eeCount, cuenta los golpes de tecla para procesar la activación de los creditos de equipo. La variable sEgg contiene la secuencia de teclas entradas.

3. Crear dos constantes en la forma:

const
  EE_CONTROL: TShiftState = [ssCtrl, ssAlt];
  EASTER_EGG = 'RIMROCK';

EE_CONTROL contiene las teclas de control que deben estar oprimidas cuando el usuario quiere activar los créditos del equipo de programación. Pueden ser cualquier combinacion de ssCtrl, ssShift y ssALt, pero se debe usar cualquier combinacion que no interfiera con los controles en la forma.

4. En el evento OnCreate de la forma, hacer la siguiente inicialización:

eeCount := 1;
sEgg    := EASTER_EGG;

5. En el evento OnKeyDown de la forma implemente el siguiente código:

procedure TForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  {are the proper control keys down?}
  if Shift = EE_CONTROL then begin
    {was the proper key pressed?}
    if Key = Ord(sEGG[eeCount]) then begin
      {was this the last keystroke in the sequence?}
      if eeCount = Length(sEGG) then begin
        {Easter egg activation code goes here, e.g.,}
         ShowMessage('This is an easter egg');
          eeCount := 1; {failure - reset the count}
      end else begin
        Inc(eeCount); {success - increment the count}
      end;
    end else begin

      eeCount := 1; {failure - reset the count}
    end;
  end;
end;

Puede reemplazar la llamada a ShowMessage con lo que usted desee. Compile y pruebe.

Los programadores de Delphi, también han dejado sus huellas en los programas. Por ejemplo, en Delphi 1, si usted muestra la ventana de créditos (About box) y sosteniendo oprimidas las teclas Shift y Alt, teclea la palabra TEAM, veremos entonces los créditos de los programadores.

¿ Estará implementada esta curiosidad en las versiones posteriores, digamos en Delphi 3 o 4 ? Si usted lo conoce, hágamelo saber y muchas gracias por anticipado. 

ir al índice


Aplicaciones con ayuda HTML compilada.

Parte 1

Microsoft ha incorporado en su plataforma Windows, un nuevo tipo de ayuda, más en correspondencia con los desarrollos de Internet y nuevas formas de presentación y búsqueda de la información, que es la ayuda HTML compilada (HTML Help compiled). 

Hay 2 razones por la que los desarrolladores tenemos que prestar atención a la ayuda HTML compilada.

  1. Es parte integrante de las nuevas versiones de Windows.
  2. Suministra mayor funcionalidad y es más fácil de construir y mantener que la ayuda basada en ficheros HLP.

Para ver los ficheros de ayuda HTML compilada, se requiere tener instalado los siguientes componentes:

hh.exe Visor de ficheros de ayuda compilada, que se instala en el directorio de WINDOWS.
hhCtrl.ocx Motor de ejecución de la ayuda compilada, que se instala en el directorio SYSTEM.
itircl.dll Librería utilizada en la visualización de la ayuda compilada instalada en el directorio SYSTEM.
itss.dll Librería utilizada en la visualización de la ayuda compilada instalada en el directorio SYSTEM.

Si ha instalado Microsoft Internet Explorer 4 o superior, automáticamente estos componentes se habrán instalados. En Windows 98 y NT 5, ya se suministran como parte del sistema operativo.

En esta parte, queremos mostrar como se accede a los ficheros de ayuda compilada, desde una aplicación de Delphi. Primeramente abordaremos, de una forma muy general, el API de la ayuda compilada, explicaremos la forma en que se especifica una URL en un fichero de ayuda compilada y mediante ejemplos, mostraremos como desde un programa de Delphi se accede a la tabla de contenido, el índice, las búsquedas, y obtener ayuda sensible al contexto. Usted puede apoyarse, durante la lectura de este artículo, descargando la fuentes del programa de ejemplo suministrado.

Para construir la ayuda compilada, debemos usar el paquete MS HTML Help Workshop, que puede ser obtenido gratuitamente, descargandolo en Internet desde el sitio de Microsoft.

El API del HTML Help

El API de HTML Help, permite a los programas de Windows crear ventanas de ayuda y mostrar los tópicos de la ayuda, con un control completo sobre el tipo, estilo y posición de la ventana de ayuda.

También, la ventana de ayuda, puede emerger en un programa Windows sin uso de la tecnología OLE. Además, está implementado la ayuda sensible al contexto, búsqueda por palabras claves, interacción con los programas de Windows y control del panel de navegación en el visor de ayuda.

El API de HTMP Help tiene una sola función que muestra la ventana de la ayuda. A través de esta función usted puede especificar que tópico mostrar en la ventana de la ayuda, si es mostrado en una ventana emergente (popup), si el tópico es accesado a través de su ID, una palabra clave, un salto URL, etc.

HWND HtmlHelp(HWND hwndCaller, LPCSTR pszFile, UINT uCommand, DWORD dwData) ;

   
hwndCaller
Especifica el handle de la ventana que llama la función. Esta ventana es la propietaria de la ventana de ayuda que se crea. Cuando la ventana de ayuda de cierra, el foco es retornado a la ventana propietaria. Adicionalmente, la funcion HtmlHelp le envía cualquier mensaje de notificación desde la ventana de ayuda, si este elemento está habilitado.
pszFile
Depende del valor de uCommand y especifica el camino del fichero de ayuda compilada o un tópico dentro del fichero. Si el comando especificado, no requiere de un fichero, este valor es ignorado y puede ser puesto en 0.
uCommand
Especifica el comando a cumplimentar.
dwData
Especifica cualquier dato que sea requerido basado en el valor de uCommand.

Dependiendo del valor de uCommand especificado, esta funcion retorna:

  • El handle a la ventana de ayuda.
  • 0 (NULL). En algunos casos, esto indica fallo y en otros indica que la ventana de ayuda no ha sido creada.

Un listado de los comandos disponibles (parámetro uCommand de la función HtmlHelp) por categorías, es el siguiente:

Categoría Comando
Tipo de Ventana HH_CLOSE_ALL 
HH_GET_WIN_HANDLE 
HH_GET_WIN_TYPE 
HH_SET_WIN_TYPE 
Ayuda sensible al contexto HH_DISPLAY_TEXT_POPUP 
HH_DISPLAY_TOPIC 
HH_HELP_CONTEXT 
HH_TP_HELP_CONTEXTMENU 
HH_TP_HELP_WM_HELP 
Búsqueda por palabras claves HH_ALINK_LOOKUP 
HH_KEYWORD_LOOKUP 
Panel de Navegación HH_DISPLAY_INDEX 
HH_DISPLAY_SEARCH 
HH_DISPLAY_TOC 
Mensajes de error HH_GET_LAST_ERROR 
Sincronizar con el contenido HH_SYNC
Hilo de ejecución simple HH_INITIALIZE 
HH_PRETRANSLATEMESSAGE 
HH_UNINITIALIZE

El comando más importante es HH_DISPLAY_TOPIC, que permite abrir un fichero de ayuda compilada (CHM) en una ventana de ayuda y mostrar el tópico especificado dentro de este fichero. 

Para acceder al API de la ayuda compilada, hemos traducido al PASCAL el fichero HtmlHelp.h suministrado con el paquete de Microsoft. Usted encontrará este fichero con el nombre HHelp.pas en el archivo compactado que acompaña este artículo. En cada unidad en que haga acceso a la ayuda compilada, deberá incluir el mismo en la claúsula uses.

Las URL de ayuda compilada

En un fichero de ayuda HTML compilada, una URL especifica un fichero o un tópico y opcionalmente el tipo de ventana para mostrar la parte referenciada por la URL.

Para especificar una URL a un fichero de ayuda compilada, se hace como sigue:

          FicheroAyuda.chm[>nombre de Window] 

donde FicheroAyuda.chm es el nombre del fichero de la ayuda HTML compilada y nombre de Window, es el nombre de la ventana de ayuda donde se desea que aparezcan los tópicos.

Para especificar un tópico dentro de un fichero de ayuda compilada, se escribe:

          FicheroAyuda.chm::Topico.htm[>nombre de Window] 

donde Topico.htm, es el nombre del fichero HTML que se quiere mostrar en la ventana de la ayuda.

Si la ayuda compilada ha sido construida, preservando el camino de acceso a los ficheros HTML, entonces una URL a un fichero con estas características, se escribe:

          FicheroAyuda.chm::/Camino/Topico.htm[>nombre de Window] 

donde Camino, es la cadena de carpetas anidadas por la que se llega al fichero.

También es usual, anteceder estas URL con la partícula "ms-its:". Esta partícula, identifica un protocolo, registrado en Windows, similar a cuando en un navegador de internet escribimos "http://".

Así que en general, desde una página HTML, al referirnos a otra página HTML almacenada en un fichero de ayuda compilada, debemos escribir:

          ms-it:FicheroAyuda.chm::/Camino/Topico.htm[>nombre de Window] 

El visor de ayuda HTML compilada

La forma más inmediata y sencilla de ver un fichero de ayuda HTML compilada, es hacer doble CLICK sobre el nombre del mismo (extensión CHM) en el Explorador de Windows.

Desde nuestras aplicaciones, podemos ejecutar la siguiente línea de código, para obtener el mismo resultado:

{ Cuando se instala el visualizador de ficheros de ayuda HTML compilada, los ficheros con  extension CHM, se asocian a la utilidad HH.EXE, que los muestra. La funcion ShellExecute, retorna el handle a la aplicación asociada que abre el fichero de ayuda compilada, Esto puede ser utilizado para realizar algún procesamiento adicional al hecho de visualizar la ayuda }

  ShellExecute(Handle,
               'open',
               PChar(Application.HelpFile), 
               nil,
               nil,
               SW_SHOWNORMAL);

El visor de la ayuda HTML compilada, es una ventana de tres paneles, como usted puede ver mientras lee los artículos del Taller de Delphi. 

Cada panel del visor, puede ser manipulado a través de la estructura HH_WINTYPE y el API de la ayuda HTML compilada; pero esto no presenta interés en esta exposición. 

Tabla de contenido e índice de la ayuda HTML compilada

En lo adelante, supondremos que en la propiedad HelpFile de nuestra aplicación, hemos establecido el nombre del fichero de ayuda HTML compilada.

Para obtener la tabla de contenido de la ayuda, ejecutamos el siguiente comando:

  {Muestra el visor de ayuda con la pestaña de Contenido activada, mostrando la página principal}
  HtmlHelp(Handle,
           PChar(Application.HelpFile),
           HH_DISPLAY_TOC,
           0);

De la misma manera, para obtener el índice de la ayuda, ejecutamos el siguiente comando:

  var
    ss : string;

  {Muestra el visor de ayuda con la pestaña de Indice activada}
  ss := 'Pagina 1';
  HtmlHelp(Handle,
           PChar(Application.HelpFile),
           HH_DISPLAY_INDEX,
           DWORD(PChar(ss)));

Búsquedas por palabras

Para comenzar una búsqueda podemos usar el siguiente procedimiento:

{Muestra el visor de ayuda con la pestaña de Buscar activada y realiza una busqueda por proximidad de la cadena suministrada}
procedure HHelpBuscar(ss: string);
var
  q : HH_FTS_QUERY;
begin
  q.cbStruct        := SizeOf(HH_FTS_QUERY);
  q.fUniCodeStrings := FALSE;
  q.pszSearchQuery  := PChar(ss);
  q.iProximity      := HH_FTS_DEFAULT_PROXIMITY;
  q.fStemmedSearch  := FALSE;
  q.fTitleOnly      := FALSE;
  q.fExecute        := FALSE;
  q.pszWindow       := nil;
  HtmlHelp(Handle,
           PChar(Application.HelpFile),
           HH_DISPLAY_SEARCH,
           DWORD(@q));
end;

Si en la estructura HH_FTS_QUERY, establecemos el miembro fExecute en True, entonces al invocar la función HtmlHelp, se realiza la búsqueda solicitada. Si el miembro fTitleOnly se establece en True, la búsqueda se realiza solamente sobre los títulos de los tópicos de ayuda.

Obteniendo ayuda sobre un tópico

En la ayuda HTML compilada, un tópico se identifica con una página HTML. Si esto es conocido, se puede mostrar un tópico, suministrando al siguiente procedimiento, el nombre de la página donde está contenido. 
 
{Procedimiento para activar la ayuda contenida en una página determinada.}
procedure MostrarTopico(Topico : string);
begin
  HtmlHelp(Handle,
           PChar(Application.HelpFile + '::/' + Topico),
           HH_DISPLAY_TOPIC,
           0);
end;
 
En Delphi, cada componente descendiente de TWinControl, tiene la propiedad HelpContext, que puede contener cualquier valor entero. Nuestro objetivo es mostrar la página que es referenciada por el valor de la propiedad HelpContext. Básicamente, un tópico de la ayuda se invoca, a modo de ejemplo, de la siguiente manera:
 
  { Al invocar la ayuda contextual, es requisito que el fichero de ayuda HTML compilada, haya sido construido llenando en la seccion MAP la asociacion de las cadenas contextuales con las páginas de la ayuda. En el fichero de cabecera tienen estar referidas igualmente las cadenas contextuales con los valores numericos de la ayuda contextual que usamos en el campo HelpContext de los controles.} 
  HtmlHelp(Handle,
           PChar(Application.HelpFile),
           HH_HELP_CONTEXT,
           HelpContext);
 
Para que esto funcione correctamente, tenemos que seguir algunos pasos adicionales durante la construcción de la ayuda HTML compilada:
  1. Construir el fichero asociación de las constantes simbólicas de la ayuda contextual y los valores numéricos de las mismas (fichero de inclusión). 
  2. Adicionar en la sección MAP del proyecto de ayuda compilada, el nombre del fichero de inclusión.
  3. Adicionar en la sección ALIAS del proyecto de ayuda compilada, la correspondencia entre las constantes simbólicas de la ayuda contextual y las páginas HTML contenedoras del texto del tópico de contexto. 
El fichero de inclusión, se construye incorporando para cada valor de contexto una línea, como se muestra en el siguiente ejemplo:
 
// Constantes para los tópicos de ayuda contextual
#define IDH_MAIN        10000
#define IDH_PAGINA1  11000
#define IDH_PAGINA2  12000
#define IDH_POPUP      20000
 
Se acostumbra a llamar a este fichero de inclusión con el nombre CTXHelp.h y a que las constantes simbólicas comiencen con la partícula IDH_.

Para adicionar en la sección MAP, el nombre de fichero de inclusión, abrir con un editor de texto, ie. NOTEPAD.EXE, buscar la sección MAP y si no existe crear y poner el nombre del fichero. Esto quedará como se muestra a continuación:

[MAP]
#include CTXHelp.h

El nombre del fichero de inclusión debe estar contenido, además, en el listado de ficheros de la ayuda HTML compilada, que se encuentra en la sección FILES del proyecto de ayuda.

Finalmente, la sección ALIAS debe ser constuída o de existir ya, adicionar una línea para cada constante simbólica asociada a una página de ayuda contextual, como se muestra en el siguiente ejemplo:

[ALIAS]
IDH_PAGINA1=Pagina1.htm
IDH_PAGINA2=Pagina2.htm

Dedicaremos otro artículo a la construcción de la ayuda HTML compilada y abundaremos más sobre en contenido del proyecto de ayuda. Por el momento, aceptemos esto como necesario para lograr el funcionamiento de la ayuda HTML en nuestros programas.