Qué tal, ecfisa. Me agrada que te hayas sumado a esta
pesquisa.
Después de estudiar un poco más el problema, veo que conviene olvidarnos de CreateOLEObject y los ProgIDs. Verás, cuando ejecutamos la función CreateOLEObject de Delphi, estamos llamando indirectamente la función CLSIDFromProgID de la API de Windows, que como ya has visto sirve para convertir un ProgID en su respectivo GUID de clase COM (
CLSID). Según la
referencia técnica ésta función busca en el registro de Windows ("
Looks up a CLSID in the registry, given a ProgID"), aunque seguramente es una búsqueda más eficiente que la que podríamos hacer con las funciones y clases que normalmente se usan para leer y escribir entradas del registro.
ProgIDToClassID es una envoltura Delphi de la función CLSIDFromProgID, y CreateOLEObject la usa para obtener el CLSID con el cual llamar a CoCreateInstance, la función de la API de Windows para crear una instancia de objeto COM:
Código Delphi
[-]function CreateOleObject(const ClassName: string): IDispatch;
var
ClassID: TCLSID;
begin
ClassID := ProgIDToClassID(ClassName);
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IDispatch, Result));
end;
Ahora, si examinamos cómo trabaja predeterminadamente el componente nativo TXMLDocument (del cual no quise derivar a TghXMLDoc por razones de diseño), podemos ver que éste llama a CoCreateInstance sin pasar por CreateOLEObject o ProgIDToClassID, y además hace algo como lo que aquí pretendemos: intentar la creación del objeto con diferentes versiones de MSXML:
Código Delphi
[-]
const
...
CLASS_DOMDocument: TGUID = '{F6D90F11-9C73-11D3-B32E-00C04F990BB4}';
CLASS_DOMDocument26: TGUID = '{F5078F1B-C551-11D3-89B9-0000F81FE221}';
CLASS_DOMDocument30: TGUID = '{F5078F32-C551-11D3-89B9-0000F81FE221}';
CLASS_DOMDocument40: TGUID = '{88D969C0-F192-11D4-A65F-0040963251E5}';
CLASS_DOMDocument60: TGUID = '{88D96A05-F192-11D4-A65F-0040963251E5}'; ...
function TryObjectCreate(const GuidList: array of TGuid): IUnknown;
var
I: Integer;
Status: HResult;
begin
Status := S_OK;
for I := Low(GuidList) to High(GuidList) do
begin
Status := CoCreateInstance(GuidList[i], nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, IDispatch, Result);
if Status = S_OK then Exit;
end;
OleCheck(Status);
end;
function CreateDOMDocument: IXMLDOMDocument;
begin
Result := TryObjectCreate([CLASS_DOMDocument60, CLASS_DOMDocument40, CLASS_DOMDocument30,
CLASS_DOMDocument26, Winapi.msxml.CLASS_DOMDocument]) as IXMLDOMDocument;
if not Assigned(Result) then
raise DOMException.Create(SMSDOMNotInstalled);
end;
El secreto es que Delphi declara esas constantes TGUID, que son en sí las CLSIDs que Microsoft asignó a las distintas versiones del objeto
DOMDocument de MSXML. Las CLSIDs son fijas y por tanto no necesitamos obtenerlas buscando ProgIDs en el registro de Windows, sólo conocer cuál es su valor GUID y usar éste para llamar a CoCreateInstance. Aun así podríamos buscar cada GUID en el registro antes de crear el objeto COM, pero esto no tendría mayor beneficio que intentarlo directamente, más o menos como esas funciones CreateDOMDocument y TryObjectCreate.
Esto comienza a allanar el camino. Podríamos decir que no vale la pena consultar el registro para convertir ProgIDs a CLSIDs que ya conocemos, y que TghXMLDoc debería llamar a CoCreateInstance intentando con diferentes CLSIDs. Pero todavía hay que resolver la cuestión de
cuál es la mejor estrategia considerando, por ejemplo, cómo permitirle al programador indicar qué versión de MSXML debe utilizar nuestra clase o en qué orden de preferencia.
En las constantes que Delphi declara no hay una de nombre
CLASS_DOMDocument50, y eso es porque la versión 5.0 de MSXML sólo puede instalarse con Microsoft Office, es decir, no es formalmente estándar en Windows. Sin embargo podría ser utilizada también en equipos que tengan ese paquete instalado. Entre sus características exclusivas, destaca por ejemplo la capacidad de hacer firma digital sobre los archivos XML, algo que por alguna razón Microsoft
retiró en la versión 6.
Habiendo hecho este breve análisis, propongo que TghXMLDoc intente crear el objeto COM usando, predeterminadamente, sólo las CLSIDs CLASS_DOMDocument60, CLASS_DOMDocument40 y CLASS_DOMDocument30, en ese orden de preferencia. En esta lista predeterminada no tendría sentido contemplar versiones anteriores a la 3.0 por estar ya obsoletas. Pero sí tendrá sentido que un programador pueda indicar una versión en particular de modo expreso, cambiar el orden de preferencia o establecer su propia lista de CLSIDs.
¿Qué usar entonces?
a) parámetro(s) adicional(es) en el constructor
b) método virtual "GetCOMInstance" llamado por el constructor
c) método función virtual "COMCLSIDs" que regrese una matriz (
array) de TGUIDs
d) una variable matriz global que pueda ser modificada por el programador
e) esa matriz pero como campo o propiedad de clase (no soportada en Delphi 7)
f) una combinación de los anteriores
g) otro mecanismo
Agradezco de antemano las ideas que puedan seguir aportando.
Un saludo.