msxml3: Fix transformation result output for stream and BSTR cases.

This commit is contained in:
Nikolay Sivov 2014-04-14 09:00:51 +04:00 committed by Alexandre Julliard
parent 7367c68cc9
commit 52c6c6a1d1
5 changed files with 270 additions and 66 deletions

View File

@ -35,6 +35,7 @@
# ifdef HAVE_LIBXSLT_TRANSFORM_H # ifdef HAVE_LIBXSLT_TRANSFORM_H
# include <libxslt/transform.h> # include <libxslt/transform.h>
# endif # endif
# include <libxslt/imports.h>
# include <libxslt/xsltutils.h> # include <libxslt/xsltutils.h>
# include <libxslt/variables.h> # include <libxslt/variables.h>
# include <libxslt/xsltInternals.h> # include <libxslt/xsltInternals.h>
@ -171,6 +172,7 @@ DECL_FUNCPTR(xsltCleanupGlobals);
DECL_FUNCPTR(xsltFreeStylesheet); DECL_FUNCPTR(xsltFreeStylesheet);
DECL_FUNCPTR(xsltFreeTransformContext); DECL_FUNCPTR(xsltFreeTransformContext);
DECL_FUNCPTR(xsltNewTransformContext); DECL_FUNCPTR(xsltNewTransformContext);
DECL_FUNCPTR(xsltNextImport);
DECL_FUNCPTR(xsltParseStylesheetDoc); DECL_FUNCPTR(xsltParseStylesheetDoc);
DECL_FUNCPTR(xsltQuoteUserParams); DECL_FUNCPTR(xsltQuoteUserParams);
DECL_FUNCPTR(xsltSaveResultTo); DECL_FUNCPTR(xsltSaveResultTo);
@ -196,6 +198,7 @@ static void init_libxslt(void)
LOAD_FUNCPTR(xsltFreeStylesheet, 1); LOAD_FUNCPTR(xsltFreeStylesheet, 1);
LOAD_FUNCPTR(xsltFreeTransformContext, 1); LOAD_FUNCPTR(xsltFreeTransformContext, 1);
LOAD_FUNCPTR(xsltNewTransformContext, 1); LOAD_FUNCPTR(xsltNewTransformContext, 1);
LOAD_FUNCPTR(xsltNextImport, 1);
LOAD_FUNCPTR(xsltParseStylesheetDoc, 1); LOAD_FUNCPTR(xsltParseStylesheetDoc, 1);
LOAD_FUNCPTR(xsltQuoteUserParams, 1); LOAD_FUNCPTR(xsltQuoteUserParams, 1);
LOAD_FUNCPTR(xsltSaveResultTo, 1); LOAD_FUNCPTR(xsltSaveResultTo, 1);

View File

@ -367,7 +367,7 @@ extern HRESULT node_get_text(const xmlnode*,BSTR*) DECLSPEC_HIDDEN;
extern HRESULT node_select_nodes(const xmlnode*,BSTR,IXMLDOMNodeList**) DECLSPEC_HIDDEN; extern HRESULT node_select_nodes(const xmlnode*,BSTR,IXMLDOMNodeList**) DECLSPEC_HIDDEN;
extern HRESULT node_select_singlenode(const xmlnode*,BSTR,IXMLDOMNode**) DECLSPEC_HIDDEN; extern HRESULT node_select_singlenode(const xmlnode*,BSTR,IXMLDOMNode**) DECLSPEC_HIDDEN;
extern HRESULT node_transform_node(const xmlnode*,IXMLDOMNode*,BSTR*) DECLSPEC_HIDDEN; extern HRESULT node_transform_node(const xmlnode*,IXMLDOMNode*,BSTR*) DECLSPEC_HIDDEN;
extern HRESULT node_transform_node_params(const xmlnode*,IXMLDOMNode*,BSTR*,const struct xslprocessor_params*) DECLSPEC_HIDDEN; extern HRESULT node_transform_node_params(const xmlnode*,IXMLDOMNode*,BSTR*,IStream*,const struct xslprocessor_params*) DECLSPEC_HIDDEN;
extern HRESULT node_create_supporterrorinfo(const tid_t*,void**) DECLSPEC_HIDDEN; extern HRESULT node_create_supporterrorinfo(const tid_t*,void**) DECLSPEC_HIDDEN;
extern HRESULT get_domdoc_from_xmldoc(xmlDocPtr xmldoc, IXMLDOMDocument3 **document) DECLSPEC_HIDDEN; extern HRESULT get_domdoc_from_xmldoc(xmlDocPtr xmldoc, IXMLDOMDocument3 **document) DECLSPEC_HIDDEN;

View File

@ -35,6 +35,7 @@
# ifdef HAVE_LIBXSLT_TRANSFORM_H # ifdef HAVE_LIBXSLT_TRANSFORM_H
# include <libxslt/transform.h> # include <libxslt/transform.h>
# endif # endif
# include <libxslt/imports.h>
# include <libxslt/variables.h> # include <libxslt/variables.h>
# include <libxslt/xsltutils.h> # include <libxslt/xsltutils.h>
# include <libxslt/xsltInternals.h> # include <libxslt/xsltInternals.h>
@ -65,6 +66,7 @@ MAKE_FUNCPTR(xsltCleanupGlobals);
MAKE_FUNCPTR(xsltFreeStylesheet); MAKE_FUNCPTR(xsltFreeStylesheet);
MAKE_FUNCPTR(xsltFreeTransformContext); MAKE_FUNCPTR(xsltFreeTransformContext);
MAKE_FUNCPTR(xsltNewTransformContext); MAKE_FUNCPTR(xsltNewTransformContext);
MAKE_FUNCPTR(xsltNextImport);
MAKE_FUNCPTR(xsltParseStylesheetDoc); MAKE_FUNCPTR(xsltParseStylesheetDoc);
MAKE_FUNCPTR(xsltQuoteUserParams); MAKE_FUNCPTR(xsltQuoteUserParams);
MAKE_FUNCPTR(xsltSaveResultTo); MAKE_FUNCPTR(xsltSaveResultTo);
@ -905,6 +907,8 @@ HRESULT node_get_xml(xmlnode *This, BOOL ensure_eol, BSTR *ret)
return *ret ? S_OK : E_OUTOFMEMORY; return *ret ? S_OK : E_OUTOFMEMORY;
} }
#ifdef SONAME_LIBXSLT
/* duplicates xmlBufferWriteQuotedString() logic */ /* duplicates xmlBufferWriteQuotedString() logic */
static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string) static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string)
{ {
@ -914,7 +918,7 @@ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string
{ {
if (xmlStrchr(string, '\'')) if (xmlStrchr(string, '\''))
{ {
xmlOutputBufferWrite(buf, 1, "\""); xmlOutputBufferWrite(buf, 1, "\"");
base = cur = string; base = cur = string;
while (*cur) while (*cur)
@ -932,13 +936,13 @@ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string
} }
if (base != cur) if (base != cur)
xmlOutputBufferWrite(buf, cur-base, (const char*)base); xmlOutputBufferWrite(buf, cur-base, (const char*)base);
xmlOutputBufferWrite(buf, 1, "\""); xmlOutputBufferWrite(buf, 1, "\"");
} }
else else
{ {
xmlOutputBufferWrite(buf, 1, "\'"); xmlOutputBufferWrite(buf, 1, "\'");
xmlOutputBufferWriteString(buf, (const char*)string); xmlOutputBufferWriteString(buf, (const char*)string);
xmlOutputBufferWrite(buf, 1, "\'"); xmlOutputBufferWrite(buf, 1, "\'");
} }
} }
else else
@ -949,6 +953,112 @@ static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string
} }
} }
static int XMLCALL transform_to_stream_write(void *context, const char *buffer, int len)
{
DWORD written;
HRESULT hr = IStream_Write((IStream*)context, buffer, len, &written);
return hr == S_OK ? written : -1;
}
/* Output for method "text" */
static void transform_write_text(xmlDocPtr result, xsltStylesheetPtr style, xmlOutputBufferPtr output)
{
xmlNodePtr cur = result->children;
while (cur)
{
if (cur->type == XML_TEXT_NODE)
xmlOutputBufferWriteString(output, (const char*)cur->content);
/* skip to next node */
if (cur->children)
{
if ((cur->children->type != XML_ENTITY_DECL) &&
(cur->children->type != XML_ENTITY_REF_NODE) &&
(cur->children->type != XML_ENTITY_NODE))
{
cur = cur->children;
continue;
}
}
if (cur->next) {
cur = cur->next;
continue;
}
do
{
cur = cur->parent;
if (cur == NULL)
break;
if (cur == (xmlNodePtr) style->doc) {
cur = NULL;
break;
}
if (cur->next) {
cur = cur->next;
break;
}
} while (cur);
}
}
#undef XSLT_GET_IMPORT_PTR
#define XSLT_GET_IMPORT_PTR(res, style, name) { \
xsltStylesheetPtr st = style; \
res = NULL; \
while (st != NULL) { \
if (st->name != NULL) { res = st->name; break; } \
st = pxsltNextImport(st); \
}}
#undef XSLT_GET_IMPORT_INT
#define XSLT_GET_IMPORT_INT(res, style, name) { \
xsltStylesheetPtr st = style; \
res = -1; \
while (st != NULL) { \
if (st->name != -1) { res = st->name; break; } \
st = pxsltNextImport(st); \
}}
static void transform_write_xmldecl(xmlDocPtr result, xsltStylesheetPtr style, BOOL omit_encoding, xmlOutputBufferPtr output)
{
int omit_xmldecl, standalone;
XSLT_GET_IMPORT_INT(omit_xmldecl, style, omitXmlDeclaration);
if (omit_xmldecl == 1) return;
XSLT_GET_IMPORT_INT(standalone, style, standalone);
xmlOutputBufferWriteString(output, "<?xml version=");
if (result->version)
{
xmlOutputBufferWriteString(output, "\"");
xmlOutputBufferWriteString(output, (const char *)result->version);
xmlOutputBufferWriteString(output, "\"");
}
else
xmlOutputBufferWriteString(output, "\"1.0\"");
if (!omit_encoding)
{
const xmlChar *encoding;
/* default encoding is UTF-16 */
XSLT_GET_IMPORT_PTR(encoding, style, encoding);
xmlOutputBufferWriteString(output, " encoding=");
xmlOutputBufferWriteString(output, "\"");
xmlOutputBufferWriteString(output, encoding ? (const char *)encoding : "UTF-16");
xmlOutputBufferWriteString(output, "\"");
}
/* standalone attribute */
if (standalone != -1)
xmlOutputBufferWriteString(output, standalone == 0 ? " standalone=\"no\"" : " standalone=\"yes\"");
xmlOutputBufferWriteString(output, "?>");
}
static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc)
{ {
xmlDtdPtr cur = doc->intSubset; xmlDtdPtr cur = doc->intSubset;
@ -963,7 +1073,7 @@ static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc)
{ {
xmlOutputBufferWriteString(buf, " "); xmlOutputBufferWriteString(buf, " ");
xml_write_quotedstring(buf, cur->SystemID); xml_write_quotedstring(buf, cur->SystemID);
} }
} }
else if (cur->SystemID) else if (cur->SystemID)
{ {
@ -973,7 +1083,8 @@ static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc)
xmlOutputBufferWriteString(buf, ">\n"); xmlOutputBufferWriteString(buf, ">\n");
} }
static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc) /* Duplicates htmlDocContentDumpFormatOutput() the way we need it - doesn't add trailing newline. */
static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc, const char *encoding, int format)
{ {
xmlElementType type; xmlElementType type;
@ -982,34 +1093,155 @@ static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc)
doc->type = XML_HTML_DOCUMENT_NODE; doc->type = XML_HTML_DOCUMENT_NODE;
if (doc->intSubset) if (doc->intSubset)
htmldtd_dumpcontent(buf, doc); htmldtd_dumpcontent(buf, doc);
if (doc->children) if (doc->children) {
{
xmlNodePtr cur = doc->children; xmlNodePtr cur = doc->children;
while (cur) {
while (cur) htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
{
htmlNodeDumpFormatOutput(buf, doc, cur, NULL, 1);
cur = cur->next; cur = cur->next;
} }
} }
doc->type = type; doc->type = type;
} }
static const xmlChar *get_output_buffer_content(xmlOutputBufferPtr output) static inline BOOL transform_is_empty_resultdoc(xmlDocPtr result)
{ {
#ifdef LIBXML2_NEW_BUFFER return !result->children || ((result->children->type == XML_DTD_NODE) && !result->children->next);
return xmlOutputBufferGetContent(output);
#else
return xmlBufferContent(output->buffer);
#endif
} }
static inline BOOL transform_is_valid_method(xsltStylesheetPtr style)
{
return !style->methodURI || !(style->method && xmlStrEqual(style->method, (const xmlChar *)"xhtml"));
}
/* Helper to write transformation result to specified output buffer. */
static HRESULT node_transform_write(xsltStylesheetPtr style, xmlDocPtr result, BOOL omit_encoding, const char *encoding, xmlOutputBufferPtr output)
{
const xmlChar *method;
int indent;
if (!transform_is_valid_method(style))
{
ERR("unknown output method\n");
return E_FAIL;
}
XSLT_GET_IMPORT_PTR(method, style, method)
XSLT_GET_IMPORT_INT(indent, style, indent);
if (!method && (result->type == XML_HTML_DOCUMENT_NODE))
method = (const xmlChar *) "html";
if (method && xmlStrEqual(method, (const xmlChar *)"html"))
{
htmlSetMetaEncoding(result, (const xmlChar *)encoding);
if (indent == -1)
indent = 1;
htmldoc_dumpcontent(output, result, (const char*)encoding, indent);
}
else if (method && xmlStrEqual(method, (const xmlChar *)"xhtml"))
{
htmlSetMetaEncoding(result, (const xmlChar *) encoding);
htmlDocContentDumpOutput(output, result, encoding);
}
else if (method && xmlStrEqual(method, (const xmlChar *)"text"))
transform_write_text(result, style, output);
else
{
transform_write_xmldecl(result, style, omit_encoding, output);
if (result->children)
{
xmlNodePtr child = result->children;
while (child)
{
xmlNodeDumpOutput(output, result, child, 0, indent == 1, encoding);
if (indent && ((child->type == XML_DTD_NODE) || ((child->type == XML_COMMENT_NODE) && child->next)))
xmlOutputBufferWriteString(output, "\r\n");
child = child->next;
}
}
}
xmlOutputBufferFlush(output);
return S_OK;
}
/* For BSTR output is always UTF-16, without 'encoding' attribute */
static HRESULT node_transform_write_to_bstr(xsltStylesheetPtr style, xmlDocPtr result, BSTR *str)
{
HRESULT hr = S_OK;
if (transform_is_empty_resultdoc(result))
*str = SysAllocStringLen(NULL, 0);
else
{
xmlOutputBufferPtr output = xmlAllocOutputBuffer(xmlFindCharEncodingHandler("UTF-16"));
const xmlChar *content;
size_t len;
*str = NULL;
if (!output)
return E_OUTOFMEMORY;
hr = node_transform_write(style, result, TRUE, "UTF-16", output);
#ifdef LIBXML2_NEW_BUFFER
content = xmlBufContent(output->conv);
len = xmlBufUse(output->conv);
#else
content = xmlBufferContent(output->conv);
len = xmlBufferLength(output->conv);
#endif
/* UTF-16 encoder places UTF-16 bom, we don't need it for BSTR */
content += sizeof(WCHAR);
*str = SysAllocStringLen((WCHAR*)content, len/sizeof(WCHAR) - 1);
xmlOutputBufferClose(output);
}
return *str ? hr : E_OUTOFMEMORY;
}
static HRESULT node_transform_write_to_stream(xsltStylesheetPtr style, xmlDocPtr result, IStream *stream)
{
static const xmlChar *utf16 = (const xmlChar*)"UTF-16";
xmlOutputBufferPtr output;
const xmlChar *encoding;
HRESULT hr;
if (transform_is_empty_resultdoc(result))
{
WARN("empty result document\n");
return S_OK;
}
if (style->methodURI && (!style->method || !xmlStrEqual(style->method, (const xmlChar *) "xhtml")))
{
ERR("unknown output method\n");
return E_FAIL;
}
/* default encoding is UTF-16 */
XSLT_GET_IMPORT_PTR(encoding, style, encoding);
if (!encoding)
encoding = utf16;
output = xmlOutputBufferCreateIO(transform_to_stream_write, NULL, stream, xmlFindCharEncodingHandler((const char*)encoding));
if (!output)
return E_OUTOFMEMORY;
hr = node_transform_write(style, result, FALSE, (const char*)encoding, output);
xmlOutputBufferClose(output);
return hr;
}
#endif
HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, BSTR *p, HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, BSTR *p,
const struct xslprocessor_params *params) IStream *stream, const struct xslprocessor_params *params)
{ {
#ifdef SONAME_LIBXSLT #ifdef SONAME_LIBXSLT
xsltStylesheetPtr xsltSS; xsltStylesheetPtr xsltSS;
HRESULT hr = S_OK;
xmlnode *sheet; xmlnode *sheet;
if (!libxslt_handle) return E_NOTIMPL; if (!libxslt_handle) return E_NOTIMPL;
@ -1058,21 +1290,12 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet,
else else
result = pxsltApplyStylesheet(xsltSS, This->node->doc, NULL); result = pxsltApplyStylesheet(xsltSS, This->node->doc, NULL);
if(result) if (result)
{ {
const xmlChar *content; if (stream)
hr = node_transform_write_to_stream(xsltSS, result, stream);
xmlOutputBufferPtr output = xmlAllocOutputBuffer(NULL); else
if (output) hr = node_transform_write_to_bstr(xsltSS, result, p);
{
if(result->type == XML_HTML_DOCUMENT_NODE)
htmldoc_dumpcontent(output, result->doc);
else
pxsltSaveResultTo(output, result->doc, xsltSS);
content = get_output_buffer_content(output);
*p = bstr_from_xmlChar(content);
xmlOutputBufferClose(output);
}
xmlFreeDoc(result); xmlFreeDoc(result);
} }
/* libxslt "helpfully" frees the XML document the stylesheet was /* libxslt "helpfully" frees the XML document the stylesheet was
@ -1083,7 +1306,7 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet,
if(!*p) *p = SysAllocStringLen(NULL, 0); if(!*p) *p = SysAllocStringLen(NULL, 0);
return S_OK; return hr;
#else #else
FIXME("libxslt headers were not found at compile time\n"); FIXME("libxslt headers were not found at compile time\n");
return E_NOTIMPL; return E_NOTIMPL;
@ -1092,7 +1315,7 @@ HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet,
HRESULT node_transform_node(const xmlnode *node, IXMLDOMNode *stylesheet, BSTR *p) HRESULT node_transform_node(const xmlnode *node, IXMLDOMNode *stylesheet, BSTR *p)
{ {
return node_transform_node_params(node, stylesheet, p, NULL); return node_transform_node_params(node, stylesheet, p, NULL, NULL);
} }
HRESULT node_select_nodes(const xmlnode *This, BSTR query, IXMLDOMNodeList **nodes) HRESULT node_select_nodes(const xmlnode *This, BSTR query, IXMLDOMNodeList **nodes)

View File

@ -477,7 +477,7 @@ static HRESULT WINAPI xslprocessor_put_output(
WARN("failed to get IStream from output, 0x%08x\n", hr); WARN("failed to get IStream from output, 0x%08x\n", hr);
break; break;
default: default:
FIXME("output type %d not handed\n", V_VT(&output)); FIXME("output type %d not handled\n", V_VT(&output));
hr = E_FAIL; hr = E_FAIL;
} }
@ -530,22 +530,8 @@ static HRESULT WINAPI xslprocessor_transform(
if (!ret) return E_INVALIDARG; if (!ret) return E_INVALIDARG;
SysFreeString(This->outstr); SysFreeString(This->outstr);
hr = node_transform_node_params(get_node_obj(This->input), This->stylesheet->node, &This->outstr, This->output, &This->params);
hr = node_transform_node_params(get_node_obj(This->input), This->stylesheet->node, &This->outstr, &This->params); *ret = hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE;
if (hr == S_OK)
{
if (This->output)
{
ULONG len = 0;
/* output to stream */
hr = IStream_Write(This->output, This->outstr, SysStringByteLen(This->outstr), &len);
*ret = len == SysStringByteLen(This->outstr) ? VARIANT_TRUE : VARIANT_FALSE;
}
}
else
*ret = VARIANT_FALSE;
return hr; return hr;
#else #else
FIXME("libxml2 is required but wasn't present at compile time\n"); FIXME("libxml2 is required but wasn't present at compile time\n");

View File

@ -6121,7 +6121,7 @@ static void test_testTransforms(void)
ok(hr == S_OK, "ret %08x\n", hr ); ok(hr == S_OK, "ret %08x\n", hr );
if(hr == S_OK) if(hr == S_OK)
{ {
ok( compareIgnoreReturns( bOut, _bstr_(szTransformOutput)), "Stylesheet output not correct\n"); ok( compareIgnoreReturns( bOut, _bstr_(szTransformOutput)), "got output %s\n", wine_dbgstr_w(bOut));
SysFreeString(bOut); SysFreeString(bOut);
} }
@ -8471,7 +8471,7 @@ todo_wine {
hr = IXSLProcessor_get_output(processor, &v); hr = IXSLProcessor_get_output(processor, &v);
ok(hr == S_OK, "got 0x%08x\n", hr); ok(hr == S_OK, "got 0x%08x\n", hr);
ok(V_VT(&v) == VT_BSTR, "got type %d\n", V_VT(&v)); ok(V_VT(&v) == VT_BSTR, "got type %d\n", V_VT(&v));
ok(lstrcmpW(V_BSTR(&v), _bstr_("")) == 0, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); ok(*V_BSTR(&v) == 0, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
IXMLDOMDocument_Release(doc2); IXMLDOMDocument_Release(doc2);
VariantClear(&v); VariantClear(&v);
@ -11695,12 +11695,6 @@ static const char omitxmldecl_doc[] =
" <item name=\"item2\"/>" " <item name=\"item2\"/>"
"</a>"; "</a>";
static const char omitxmldecl_result[] =
"<node>item1</node><node>item2</node>";
static const char omitxmldecl_result2[] =
"<node>item1</node><node>item2</node>\n";
static void test_xsltext(void) static void test_xsltext(void)
{ {
IXMLDOMDocument *doc, *doc2; IXMLDOMDocument *doc, *doc2;
@ -11730,9 +11724,7 @@ static void test_xsltext(void)
hr = IXMLDOMDocument_transformNode(doc2, (IXMLDOMNode*)doc, &ret); hr = IXMLDOMDocument_transformNode(doc2, (IXMLDOMNode*)doc, &ret);
ok(hr == S_OK, "got 0x%08x\n", hr); ok(hr == S_OK, "got 0x%08x\n", hr);
/* Old enough libxslt places extra '\n' at the end of the output. */ ok(!lstrcmpW(ret, _bstr_("<node>item1</node><node>item2</node>")), "transform result %s\n", wine_dbgstr_w(ret));
ok(!lstrcmpW(ret, _bstr_(omitxmldecl_result)) ||
!lstrcmpW(ret, _bstr_(omitxmldecl_result2)), "transform result %s\n", wine_dbgstr_w(ret));
SysFreeString(ret); SysFreeString(ret);
IXMLDOMDocument_Release(doc2); IXMLDOMDocument_Release(doc2);