Ver Mensaje Individual
  #7  
Antiguo 05-01-2014
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Reputación: 29
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Cita:
Empezado por Al González Ver Mensaje
¿Qué usar entonces?

a) parámetro(s) adicional(es) en el constructor
[...]
d) una variable matriz global que pueda ser modificada por el programador
Terminé aplicando esas dos opciones. Ahora explicaré a grandes rasgos los nuevos elementos involucrados en esta mejora. Agradeceré que externen la valoración que hagan de ellos, pues eso sin duda ayudará en muchos aspectos.

Primero los átomos agregados al núcleo de la biblioteca (unidad GHFRTL.pas), empezando por las nuevas constantes:
Código Delphi [-]
  Const
    { Characters }
    ghchDotDecimals = ghdgDecimals + ['.'];
ghchDotDecimals es una constante conjunto que agrupa a los 10 dígitos decimales y el punto. Es común encontrar cadenas o subcadenas formadas por una combinación de estos 11 caracteres, como es el caso de las versiones de un producto (14.8.01), visto también en el sufijo de un ProgID (ADODB.Command.2.8, Msxml2.DOMDocument.6.0). En el presente caso es utilizada por la función ghRightDigitsDots que aparece más abajo.
Código Delphi [-]
    { Empty PWideChar }
    ghEmptyPWideChr :PWideChar = '';
ghEmptyPWideChr es una constante PWideChar que indica una cadena vacía. Resulta útil para darse en lugar de Nil cuando alguna rutina espera una cadena de caracteres WideChar terminada en nulo, aunque esté vacía pero que no sea un puntero en blanco. En el presente caso es usada por la función ghPWideChr que aparece más abajo.
Código Delphi [-]
    { Class IDs }

    // MSXML.  NOTE: Versions 2.6 and earlier are obsolete.

    // MSXML2.DOMDocument.3.0
    ghciMSXMLDoc30 :TGUID = '{F5078F32-C551-11D3-89B9-0000F81FE221}';

    // MSXML2.DOMDocument.4.0
    ghciMSXMLDoc40 :TGUID = '{88D969C0-F192-11D4-A65F-0040963251E5}';

    // MSXML2.DOMDocument.5.0
    ghciMSXMLDoc50 :TGUID = '{88D969E5-F192-11D4-A65F-0040963251E5}';

    // MSXML2.DOMDocument.6.0
    ghciMSXMLDoc60 :TGUID = '{88D96A05-F192-11D4-A65F-0040963251E5}';
Las constantes ghciMSXMLDocNN contienen los identificadores de clases COM (CLSIDs) que Microsoft asignó al objeto DOMDocument de MSXML 3.0, 4.0, 5.0 y 6.0. Observen que estos identificadores son GUIDs (del tipo TGUID en Delphi) que se expresan como si fueran cadenas de 38 caracteres, pero el compilador realmente los toma como valores de 16 bytes.
Código Delphi [-]
    { MSXML Document Class IDs }
    ghMSXMLDocClassIDs :Array [0..3] Of TGUID = (
      // MSXML2.DOMDocument.3.0
      '{F5078F32-C551-11D3-89B9-0000F81FE221}',

      // MSXML2.DOMDocument.4.0
      '{88D969C0-F192-11D4-A65F-0040963251E5}',

      // MSXML2.DOMDocument.5.0
      '{88D969E5-F192-11D4-A65F-0040963251E5}',

      // MSXML2.DOMDocument.6.0
      '{88D96A05-F192-11D4-A65F-0040963251E5}');
ghMSXMLDocClassIDs es una matriz (array) unidimensional que contiene esos mismos identificadores de clases. Noten que debí poner nuevamente los cuatro valores GUIDs, toda vez que el compilador de Delphi no admite referencias a constantes tipificadas dentro de la definición de otras constantes.
Código Delphi [-]
    { MSXML Schema Cache Class IDs }
    ghMSXMLSchemaCacheClassIDs :Array [0..3] Of TGUID = (
      // MSXML2.XMLSchemaCache.3.0
      '{F5078F34-C551-11D3-89B9-0000F81FE221}',

      // MSXML2.XMLSchemaCache.4.0
      '{88D969C2-F192-11D4-A65F-0040963251E5}',

      // MSXML2.XMLSchemaCache.5.0
      '{88D969E7-F192-11D4-A65F-0040963251E5}',

      // MSXML2.XMLSchemaCache.6.0
      '{88D96A07-F192-11D4-A65F-0040963251E5}');
ghMSXMLSchemaCacheClassIDs es una matriz que lleva relación con la anterior, pero esta contiene los identificadores de clases para el objeto XMLSchemaCache, el cual se utiliza en MSXML para validar documentos contra esquemas XSD. Es importante anotar que la mencionada API no admite combinar objetos DOMDocument y XMLSchemaCache de diferentes versiones, y que TghXMLDoc previene eso mismo en su método AddSchema mostrado más abajo.
Código Delphi [-]
    { MSXML Versions }
    ghMSXMLVersions :Array [0..3] Of String = ('3.0', '4.0', '5.0', '6.0');
ghMSXMLVersions es una matriz relacionada con las anteriores dos, pero en lugar de GUIDs almacena los cuatro números de versión como simples cadenas de caracteres. La finalidad de esta matriz es permitir conocer la versión de cualquiera de los GUIDs anteriores sin tener que consultar el registro de Windows.

Ahora pasemos a las funciones átomo, también agregadas a GHFRTL.pas:
Código Delphi [-]
  { Equals? }
  Function ghEquals (Const Value1, Value2 :TGUID) :Boolean; Overload;
  Var
    GUID1 :Array [0..3] Of Integer Absolute Value1;
    GUID2 :Array [0..3] Of Integer Absolute Value2;
  Begin
    Result := (GUID1 [0] = GUID2 [0]) And (GUID1 [1] = GUID2 [1]) And
      (GUID1 [2] = GUID2 [2]) And (GUID1 [3] = GUID2 [3]);
  End;
En Delphi 7 todavía no era posible comparar dos valores TGUID usando el operador "=", y por muchos años la recomendación fue usar la función IsEqualGUID importada de OLE32.dll. Pero me surgió la pregunta: ¿qué tan eficiente será hacer una simple comparación de 16 bytes con IsEqualGUID? Por otra parte, encontré que las versiones recientes de Delphi sí admiten la comparación de TGUIDs con el operador "=". Cuando eso ocurre, el compilador inserta una llamada al método interno TGUID.Equal:
Código Delphi [-]
class operator TGUID.Equal(const Left, Right: TGUID): Boolean;
var
  a, b: PIntegerArray;
begin
  a := PIntegerArray(@Left);
  b := PIntegerArray(@Right);
  Result := (a^[0] = b^[0]) and (a^[1] = b^[1]) and (a^[2] = b^[2]) and (a^[3] = b^[3]);
end;
El cual es de cuatro a cinco veces más rápido que IsEqualGUID. Así que buscando algo similar para Delphi 7 creé esa función ghEquals, y para mi sorpresa resultó ligeramente más eficiente que el método TGUID.Equal (en Delphi 7 genera un poco menos de código máquina y en XE2 conviene añadirle la directiva InLine).
Código Delphi [-]
  { Position of GUID }
  Function ghPosGUID (Const Values :Array Of TGUID; Const ID :TGUID;
    Const StartPos :Integer = 0) :Integer;
  Begin
    For Result := StartPos To High (Values) Do
      If ghEquals (Values [Result], ID) Then
        Exit;

    Result := -1;
  End;
En GHF hay varias funciones para buscar un elemento dentro de una matriz unidimensional. Es el caso de ghPosGUID, la cual nos dice en qué posición (a partir de 0) de una matriz de GUIDs se encuentra un GUID en particular. Si devuelve -1 significa que ID no se encuentra en la matriz Values. Noten que ghPosGUID llama a ghEquals.
Código Delphi [-]
  { Valid GUIDs? }
  Function ghValidGUIDs (Const Values, IDs :Array Of TGUID) :Boolean;
  Var
    I :Integer;
  Begin
    For I := 0 To High (Values) Do
      If ghPosGUID (IDs, Values [i]) = -1 Then
      Begin
        Result := False;
        Exit;
      End;

    Result := True;
  End;
ghValidGUIDs llama a ghPosGUID con cada uno de los elementos de una matriz de GUIDs (Values) para verificar que todos ellos se encuentren incluidos en otra matriz de GUIDs (IDs). Devuelve True si se cumple la validación, o False si alguno de los elementos de Values no está en IDs.
Código Delphi [-]
  { Check MSXML Document Class IDs }
  Procedure ghCheckMSXMLDocClassIDs (Const Values :Array Of TGUID);
  Begin
    If Not ghValidGUIDs (Values, ghMSXMLDocClassIDs) Then
      ghRaise ('Invalid XML document class IDs.');
  End;
ghCheckMSXMLDocClassIDs usa la función ghValidGUIDs para verificar que todos los GUIDs dados (parámetro Values) sean identificadores de clases MSXML DOMDocument (la constante matriz ghMSXMLDocClassIDs mostrada con anterioridad). En caso de que alguno de los elementos de la matriz Values no se encuentre en la matriz ghMSXMLDocClassIDs, eleva una excepción con el procedimiento ghRaise.

Espero no se hayan cansado de leer, falta un poco más.

Código Delphi [-]
  { Create COM Object }
  Function ghCreateCOMObj (Const ClassID, IntfID :TGUID; Out Intf)
    :Integer; Overload;
  Begin
    Result := CoCreateInstance (ClassID, Nil, ClsCtx_InProc_Server Or
      ClsCtx_Local_Server, IntfID, Intf);
  End;
ghCreateCOMObj se encarga de crear una instancia de objeto COM. Es una simple envoltura de la función estándar CoCreateInstance que nos ahorra escribir el segundo y tercer parámetro de ésta última, los cuales por lo general son "Nil" y "ClsCtx_InProc_Server Or ClsCtx_Local_Server".
Código Delphi [-]
  { Set GUID }
  Function ghSetGUID (Const Ref :Pointer; Const Value :TGUID) :Boolean;
    Overload;
  Begin
    Result := Ref <> Nil;

    If Result Then
      PGUID (Ref)^ := Value;
  End;
ghSetGUID es una de esas funciones que los puristas odian por ser una especie de "If envuelto", pero que con el advenimiento de la compilación in-line se vuelven importantes para la simplificación del código fuente de las rutinas llamadoras. Recibe un puntero a TGUID (Ref) y un TGUID (Value), y si la referencia es válida le asigna éste.
Código Delphi [-]
  { COM Object }
  Function ghCOMObj (Const ClassIDs :Array Of TGUID; Const IntfID :TGUID;
    Const UsedClassID :PGUID = Nil) :IUnknown; Overload;
  Var
    Error, I :Integer;
  Begin
    Error := E_InvalidArg;  // If ClassIDs is empty

    For I := 0 To High (ClassIDs) Do
    Begin
      Error := ghCreateCOMObj (ClassIDs [i], IntfID, Result);

      If Error = S_OK Then
      Begin
        ghSetGUID (UsedClassID, ClassIDs [i]);
        Exit;
      End;
    End;

    OLEError (Error);
  End;
Haciendo uso de ghCreateCOMObj y ghSetGUID, ghCOMObj recibe una lista (matriz ClassIDs) de identificadores de clases COM e intenta crear una instancia de objeto con el primero de esos identificadores. Si no lo consigue (seguramente por no encontrarse instalada esa CLSID), entonces intenta con el segundo, y así sucesivamente, hasta lograr crear el objeto COM, el cual es regresado como resultado en forma de interfaz. Si el parámetro UsedClassID no es una referencia Nil, pone en ella el valor del GUID que tuvo éxito, es decir, la CLSID con la que finalmente se consiguió crear la instancia COM. Si no han perdido detalle de lo tratado en este hilo, lograrán adivinar que esta función es la clave para permitirle a TghXMLDoc trabajar con cualquiera de las versiones de MSXML que se tengan instaladas.
Código Delphi [-]
  { COM Dispatch }
  Function ghCOMDispatch (Const ClassIDs :Array Of TGUID;
    Const UsedClassID :PGUID = Nil) :IDispatch; Overload;
  Begin
    Result := IDispatch (ghCOMObj (ClassIDs, IDispatch, UsedClassID));
  End;
ghCOMDispatch es una envoltura de la función ghCOMObj, cuya finalidad es indicarle a CoCreateInstance que deseamos una interfaz IDispatch y obtener ese IDispatch como resultado en lugar de una interfaz genérica IUnknown. Esto es importante para lograr acceso práctico a todas las propiedades y métodos de instancias COM mediante variables OLEVariant.

Hagamos una pausa para comer...
Responder Con Cita