4

I have some working code using Delphi's TXMLDocument class, and using the TransformNode method to perform XSLT translation.

But, I need to enable XSLT Javascript functions (<msxml:script> tags) and - after much googling - this means I need to set the AllowXsltScript property of the IXMLDOMDocument2 to true.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms760290(v=vs.85).aspx

I've achieved this successfully - but only by modifying the source of the Delphi Library function CreateDOMDocument in msxmldom.pas.

function CreateDOMDocument: IXMLDOMDocument;
var doc :IXMLDOMDocument2;
begin

  doc := TryObjectCreate([CLASS_DOMDocument60, CLASS_DOMDocument40, CLASS_DOMDocument30,
    CLASS_DOMDocument26, msxml.CLASS_DOMDocument]) as IXMLDOMDocument2;
  if not Assigned(doc) then
    raise DOMException.Create(SMSDOMNotInstalled);
  doc.setProperty('AllowXsltScript', true);  // Allow XSLT scripts!!
  Result := doc;
end;

Obviously this is far from satisfactory - so how can I access IXMLDOMDocument2 objects without modifying library code??

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • Can't you just call `CoCreateInstance` yourself and make an `IXMLDOMDocument2`? – David Heffernan Jun 17 '13 at 12:06
  • 1
    @DavidHeffernan - the problem is I've got a bunch of existing code based around TXMLDocument that I don't want to mess with. If I was doing this from a clean sheet I probably wouldn't be using MSXML anyway! – Roddy Jun 17 '13 at 12:11

2 Answers2

4

You can override the create function via the MSXMLDOMDocumentCreate variable:

unit Unit27;

interface

uses
  xmldoc, xmlintf, msxml, msxmldom, Forms, SysUtils, 
  ActiveX, ComObj, XmlDom, XmlConst,
  Windows, Messages, Classes, Controls, StdCtrls;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

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 CreateDOMDocument2: IXMLDOMDocument;

var
  Doc2 : IXMLDOMDocument2;

begin
  Doc2 := TryObjectCreate([CLASS_DOMDocument60, CLASS_DOMDocument40, CLASS_DOMDocument30,
    CLASS_DOMDocument26, msxml.CLASS_DOMDocument]) as IXMLDOMDocument2;
  if not Assigned(Doc2) then
    raise DOMException.Create(SMSDOMNotInstalled);
  Doc2.setProperty('AllowXsltScript', true);
  Result := Doc2;
end;


procedure TForm1.FormCreate(Sender: TObject);

var
 Doc : IXMLDocument;

begin
 Doc := TXMLDocument.Create(nil);
 Doc.LoadFromFile('c:\temp\test.xml');
end;


initialization
 MSXMLDOMDocumentCreate := CreateDOMDocument2;
end.
whosrdaddy
  • 11,720
  • 4
  • 50
  • 99
  • @Roddy: Changed the code sample, this should work (tested on delphi XE) – whosrdaddy Jun 17 '13 at 11:09
  • Much better ;-) I simplified it a bit like this using the existing `CreateDomdDocument` function: `function CreateDOMDocument2: IXMLDOMDocument; begin result := CreateDomDocument; (result as IXMLDOMDocument2).setProperty('AllowXsltScript', true); end;` – Roddy Jun 17 '13 at 11:27
  • I suspect this is the best we can get - but one issue is that this enables scripts for *all* IXMLDocuments, regardless. – Roddy Jun 17 '13 at 11:29
3

Note that in XE3 and above, MSXMLDOMDocumentCreate is deprecated in favor of subclassing TMSXMLDOMDocumentFactory and overriding it's CreateDOMDocument function. For future reference, here's an example for XE3 and XE4:

interface

type
  TMSXMLDOMDocument2Factory = class(TMSXMLDOMDocumentFactory)
  public
    class function CreateDOMDocument: IXMLDOMDocument; override;
  end;

implementation

{ TMSXMLDOMDocument2Factory }

class function TMSXMLDOMDocument2Factory.CreateDOMDocument: IXMLDOMDocument;
begin
  Result := inherited;
  if not Assigned(Result) then
    raise DOMException.Create(SMSDOMNotInstalled);
  AddDOMProperty('AllowXsltScript', True);
  SetDOMProperties(Result as IXMLDOMDocument2);
end;

initialization
  MSXMLDOMDocumentFactory := TMSXMLDOMDocument2Factory;

end.
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • 1
    Well, it seems for XE3/4 you woudln't need to subclass anyway - you'd just call `Xml.Win.msxmldom.MSXMLDOMDocumentFactory.AddDOMProperty('AllowXsltScript', True);` See the emb. wiki page : http://docwiki.embarcadero.com/Libraries/XE4/en/Xml.Win.msxmldom.TMSXMLDOMDocumentFactory.AddDOMProperty – Roddy Jun 17 '13 at 18:20
  • @Roddy: I was referring specifically (as I said in the text above the code) to `MSXMLDOMDocumentCreate` being deprecated now, and showing the replacement for it. Since my code also uses `AddDOMProperty`, it should be pretty clear I knew it was there. ;-) The question asked about how to access `IXMLDOMDocument2` via `TXMLDocument`, and that's the portion I was addressing, not the specific setting of a second level DOM property. – Ken White Jun 17 '13 at 18:24
  • Ken, Thanks: yes, I appreciate that: I accept that my question may have been a little misleading. as my research had driven me towards IXMLDOMDocument2 as the *only* way of setting second-level DOM properties. With XE3/4 that's no longer the case. (At first glance at your code I assumed AddDOMProperty must be protected, and thus requiring the subclassing) – Roddy Jun 17 '13 at 18:43