4

On XP this code worked fine if I wanted to view XML in a TWebBrowser:

uses ComObj, MSHTML, ActiveX;

procedure DocumentFromString(ABrowser: TWebBrowser; const HTMLString: wideString);
var
  v: OleVariant;
  HTMLDocument: IHTMLDocument2;
begin
  if not Assigned(ABrowser.Document) then
  begin
    ABrowser.Navigate('about:blank');
    while ABrowser.ReadyState <> READYSTATE_COMPLETE do
    begin
      Application.ProcessMessages;
      Sleep(0);
    end;
  end;
  HTMLDocument := ABrowser.Document as IHTMLDocument2;
  v := VarArrayCreate([0, 0], varVariant);
  v[0] := HTMLString;
  HTMLDocument.Write(PSafeArray(TVarData(v).VArray));
  HTMLDocument.Close;
end;

procedure WebBrowserXML(ABrowser: TWebBrowser; const XmlString: WideString);
var
  xml, xsl: OleVariant;
  HTMLString: WideString;
begin
  xml := CreateOleObject('Msxml2.DOMDocument');
  xml.async := False;
  xml.loadXML(XmlString);
  // Assert(xml.parseError.errorCode = 0);
  xsl := CreateOleObject('Msxml2.DOMDocument');
  xsl.validateOnParse := False;
  xsl.async := False;
  xsl.load('res://msxml.dll/defaultss.xsl');
  // Assert(xsl.parseError.errorCode = 0);
  HTMLString := xml.transformNode(xsl);
  ABrowser.HandleNeeded;
  DocumentFromString(ABrowser, HTMLString);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  WebBrowserXML(WebBrowser1, '<xml><node>Hello</node></xml>');
end;

The method is as followed: the XML is transformed with XSLT (defaultss.xsl) and the result is HTML.

On Vista I get an exeption in the line xml.transformNode(xsl);:

The stylesheet does not caontain a document element. The stylesheet may be empty, or it may not be a well-formed XML document

I have tried to directly load my own copy of XSLT from file like this xsl.load('my.xsl'):

http://forums.asp.net/t/1141304.aspx?xslt+viewing+XML+like+that+of+IE

but still I get the same error that the XSLT is not valid.

How do I make this code work on Vista?


From the comments to the link I provided:

I have also found that after calling directly into res://msxml#.dll/defaultss.xsl for years, this method no longer works in Vista. I've messed with all sorts of security settings, but that does not seem to be the issue. It looks like my only option is to release my own copy of defaultss.xsl.

I can't seem to provide a valid "my own copy" of defaultss.xsl. they all fail with the same exception error. what can I do?

Vlad
  • 1,383
  • 14
  • 29
  • Did you verify that the `load()` is actually succeeding before you call `transformNode()`? Did you verify that the `res:` URL is valid? When I try to load that URL into IE11, it can't access it. – Remy Lebeau Nov 13 '14 at 22:55
  • @RemyLebeau, Yes I verified all. `xsl.load()` fails. This is exactlly the probelm: there is no valid `res://msxml.dll/defaultss.xsl`. I don't mind using my own xslt which I can embed in my exe res, as long as it is valid xslt - which I can't make work. – Vlad Nov 13 '14 at 23:15
  • [`load()`](http://msdn.microsoft.com/en-us/library/ms762722.aspx) accepts a lot of different values as input. If you can't load it by URL (BTW, the documentation shows an example that uses `res://msxml3.dll/xml/defaultss.xsl` instead) or by filename (which you are specifying by relative path, try using an absolute path instead), then try loading the actual xslt data as an `IStream` or a `PSafeArray` of bytes. – Remy Lebeau Nov 14 '14 at 00:09
  • 1
    If you want to embed the xslt as a resource in your app, just make sure you use a `res:` URL that refers to your app. See [MSDN's documentation](http://msdn.microsoft.com/en-us/library/aa767740.aspx) for that syntax. – Remy Lebeau Nov 14 '14 at 00:11
  • 1
    I just checked on Windows 7 and `msxml3.dll` does have an `XML` resource named `DEFAULTSS.XSL`, but `msxml4.dll` and `msxml6.dll` do not, and there is no `msxml.dll` file. As MSDN says, `res:` defaults to `/html/` or `/file/` if you don't specify a resource type. so `res:msxml3.dll/defaultss.xls` shouldn't work since the resource is `XML`, thus use `res:msxml2.dll/xml/defaults.xls` instead. – Remy Lebeau Nov 14 '14 at 00:15
  • 1
    so `res://msxml3.dll/defaultss.xls` shouldn't work since the resource type is `XML`, thus use `res://msxml3.dll/xml/defaults.xls` instead. – Remy Lebeau Nov 14 '14 at 00:21
  • @RemyLebeau `res://C:%5Cwindows%5Csystem32%5Cmsxml3.dll/XML/DEFAULTSS.XSL` and `res://msxml3.dll/XML/DEFAULTSS.XSL` worked ok on Vista. please make an answer. – Vlad Nov 14 '14 at 10:23
  • You don't need to specify the full system path. – Remy Lebeau Nov 14 '14 at 18:47

1 Answers1

5

The load() documentation shows an example that uses this URL:

res://msxml3.dll/xml/defaultss.xsl

If you want to embed an XSLT as a resource in your app, just make sure you use a res: URL that refers to that resource in your app. See MSDN's documentation for that syntax:

res Protocol

Syntax

res://sFile[/sType]/sID

Tokens

sFile
Percent-encoded path and file name of the module that contains the resource.

sType
Optional. String or numerical resource type. This can be either a custom resource or one of the predefined resource types that are recognized by the FindResource function. If a numerical resource type is specified, the number of the identifier must follow a # character. If this parameter is not specified, the default resource type is RT_HTML or RT_FILE.

sID
String or numerical identifier of the resource. If a numerical identifier is specified, the actual number of the identifier, not the identifier itself, must follow a # character.

I just checked on Windows 7 and msxml3.dll does have an XML resource named DEFAULTSS.XSL, but msxml4.dll and msxml6.dll do not, and there is no msxml.dll file.

As MSDN says, res: defaults to HTML or FILE if a resource type is not specified, so using res://msxml3.dll/defaultss.xls will not work since the XSLT resource type is XML instead. Thus, you need to use res://msxml3.dll/xml/defaultss.xls instead.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770