* Sync up to trunk HEAD (r62975).
[reactos.git] / dll / win32 / msxml3 / stylesheet.c
1 /*
2 * XSLTemplate/XSLProcessor support
3 *
4 * Copyright 2011 Nikolay Sivov for CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "precomp.h"
22
23 typedef struct
24 {
25 DispatchEx dispex;
26 IXSLTemplate IXSLTemplate_iface;
27 LONG ref;
28
29 IXMLDOMNode *node;
30 } xsltemplate;
31
32 typedef struct
33 {
34 DispatchEx dispex;
35 IXSLProcessor IXSLProcessor_iface;
36 LONG ref;
37
38 xsltemplate *stylesheet;
39 IXMLDOMNode *input;
40
41 IStream *output;
42 BSTR outstr;
43
44 struct xslprocessor_params params;
45 } xslprocessor;
46
47 static HRESULT XSLProcessor_create(xsltemplate*, IXSLProcessor**);
48
49 static inline xsltemplate *impl_from_IXSLTemplate( IXSLTemplate *iface )
50 {
51 return CONTAINING_RECORD(iface, xsltemplate, IXSLTemplate_iface);
52 }
53
54 static inline xslprocessor *impl_from_IXSLProcessor( IXSLProcessor *iface )
55 {
56 return CONTAINING_RECORD(iface, xslprocessor, IXSLProcessor_iface);
57 }
58
59 static void xslprocessor_par_free(struct xslprocessor_params *params, struct xslprocessor_par *par)
60 {
61 params->count--;
62 list_remove(&par->entry);
63 SysFreeString(par->name);
64 SysFreeString(par->value);
65 heap_free(par);
66 }
67
68 static void xsltemplate_set_node( xsltemplate *This, IXMLDOMNode *node )
69 {
70 if (This->node) IXMLDOMNode_Release(This->node);
71 This->node = node;
72 if (node) IXMLDOMNode_AddRef(node);
73 }
74
75 static HRESULT WINAPI xsltemplate_QueryInterface(
76 IXSLTemplate *iface,
77 REFIID riid,
78 void** ppvObject )
79 {
80 xsltemplate *This = impl_from_IXSLTemplate( iface );
81 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
82
83 if ( IsEqualGUID( riid, &IID_IXSLTemplate ) ||
84 IsEqualGUID( riid, &IID_IDispatch ) ||
85 IsEqualGUID( riid, &IID_IUnknown ) )
86 {
87 *ppvObject = iface;
88 }
89 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
90 {
91 return *ppvObject ? S_OK : E_NOINTERFACE;
92 }
93 else
94 {
95 FIXME("Unsupported interface %s\n", debugstr_guid(riid));
96 *ppvObject = NULL;
97 return E_NOINTERFACE;
98 }
99
100 IUnknown_AddRef((IUnknown*)*ppvObject);
101 return S_OK;
102 }
103
104 static ULONG WINAPI xsltemplate_AddRef( IXSLTemplate *iface )
105 {
106 xsltemplate *This = impl_from_IXSLTemplate( iface );
107 ULONG ref = InterlockedIncrement( &This->ref );
108 TRACE("(%p)->(%d)\n", This, ref);
109 return ref;
110 }
111
112 static ULONG WINAPI xsltemplate_Release( IXSLTemplate *iface )
113 {
114 xsltemplate *This = impl_from_IXSLTemplate( iface );
115 ULONG ref = InterlockedDecrement( &This->ref );
116
117 TRACE("(%p)->(%d)\n", This, ref);
118 if ( ref == 0 )
119 {
120 if (This->node) IXMLDOMNode_Release( This->node );
121 heap_free( This );
122 }
123
124 return ref;
125 }
126
127 static HRESULT WINAPI xsltemplate_GetTypeInfoCount( IXSLTemplate *iface, UINT* pctinfo )
128 {
129 xsltemplate *This = impl_from_IXSLTemplate( iface );
130 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
131 }
132
133 static HRESULT WINAPI xsltemplate_GetTypeInfo(
134 IXSLTemplate *iface,
135 UINT iTInfo, LCID lcid,
136 ITypeInfo** ppTInfo )
137 {
138 xsltemplate *This = impl_from_IXSLTemplate( iface );
139 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
140 iTInfo, lcid, ppTInfo);
141 }
142
143 static HRESULT WINAPI xsltemplate_GetIDsOfNames(
144 IXSLTemplate *iface,
145 REFIID riid, LPOLESTR* rgszNames,
146 UINT cNames, LCID lcid, DISPID* rgDispId )
147 {
148 xsltemplate *This = impl_from_IXSLTemplate( iface );
149 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
150 riid, rgszNames, cNames, lcid, rgDispId);
151 }
152
153 static HRESULT WINAPI xsltemplate_Invoke(
154 IXSLTemplate *iface,
155 DISPID dispIdMember, REFIID riid, LCID lcid,
156 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
157 EXCEPINFO* pExcepInfo, UINT* puArgErr )
158 {
159 xsltemplate *This = impl_from_IXSLTemplate( iface );
160 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
161 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
162 }
163
164 static HRESULT WINAPI xsltemplate_putref_stylesheet( IXSLTemplate *iface,
165 IXMLDOMNode *node)
166 {
167 xsltemplate *This = impl_from_IXSLTemplate( iface );
168
169 TRACE("(%p)->(%p)\n", This, node);
170
171 if (!node)
172 {
173 xsltemplate_set_node(This, NULL);
174 return S_OK;
175 }
176
177 /* FIXME: test for document type */
178 xsltemplate_set_node(This, node);
179
180 return S_OK;
181 }
182
183 static HRESULT WINAPI xsltemplate_get_stylesheet( IXSLTemplate *iface,
184 IXMLDOMNode **node)
185 {
186 xsltemplate *This = impl_from_IXSLTemplate( iface );
187
188 FIXME("(%p)->(%p): stub\n", This, node);
189 return E_NOTIMPL;
190 }
191
192 static HRESULT WINAPI xsltemplate_createProcessor( IXSLTemplate *iface,
193 IXSLProcessor **processor)
194 {
195 xsltemplate *This = impl_from_IXSLTemplate( iface );
196
197 TRACE("(%p)->(%p)\n", This, processor);
198
199 if (!processor) return E_INVALIDARG;
200
201 return XSLProcessor_create(This, processor);
202 }
203
204 static const struct IXSLTemplateVtbl XSLTemplateVtbl =
205 {
206 xsltemplate_QueryInterface,
207 xsltemplate_AddRef,
208 xsltemplate_Release,
209 xsltemplate_GetTypeInfoCount,
210 xsltemplate_GetTypeInfo,
211 xsltemplate_GetIDsOfNames,
212 xsltemplate_Invoke,
213 xsltemplate_putref_stylesheet,
214 xsltemplate_get_stylesheet,
215 xsltemplate_createProcessor
216 };
217
218 static const tid_t xsltemplate_iface_tids[] = {
219 IXSLTemplate_tid,
220 0
221 };
222
223 static dispex_static_data_t xsltemplate_dispex = {
224 NULL,
225 IXSLTemplate_tid,
226 NULL,
227 xsltemplate_iface_tids
228 };
229
230 HRESULT XSLTemplate_create(void **ppObj)
231 {
232 xsltemplate *This;
233
234 TRACE("(%p)\n", ppObj);
235
236 This = heap_alloc( sizeof (*This) );
237 if(!This)
238 return E_OUTOFMEMORY;
239
240 This->IXSLTemplate_iface.lpVtbl = &XSLTemplateVtbl;
241 This->ref = 1;
242 This->node = NULL;
243 init_dispex(&This->dispex, (IUnknown*)&This->IXSLTemplate_iface, &xsltemplate_dispex);
244
245 *ppObj = &This->IXSLTemplate_iface;
246
247 TRACE("returning iface %p\n", *ppObj);
248
249 return S_OK;
250 }
251
252 /*** IXSLProcessor ***/
253 static HRESULT WINAPI xslprocessor_QueryInterface(
254 IXSLProcessor *iface,
255 REFIID riid,
256 void** ppvObject )
257 {
258 xslprocessor *This = impl_from_IXSLProcessor( iface );
259 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
260
261 if ( IsEqualGUID( riid, &IID_IXSLProcessor ) ||
262 IsEqualGUID( riid, &IID_IDispatch ) ||
263 IsEqualGUID( riid, &IID_IUnknown ) )
264 {
265 *ppvObject = iface;
266 }
267 else if (dispex_query_interface(&This->dispex, riid, ppvObject))
268 {
269 return *ppvObject ? S_OK : E_NOINTERFACE;
270 }
271 else
272 {
273 FIXME("Unsupported interface %s\n", debugstr_guid(riid));
274 *ppvObject = NULL;
275 return E_NOINTERFACE;
276 }
277
278 IUnknown_AddRef((IUnknown*)*ppvObject);
279 return S_OK;
280 }
281
282 static ULONG WINAPI xslprocessor_AddRef( IXSLProcessor *iface )
283 {
284 xslprocessor *This = impl_from_IXSLProcessor( iface );
285 ULONG ref = InterlockedIncrement( &This->ref );
286 TRACE("(%p)->(%d)\n", This, ref);
287 return ref;
288 }
289
290 static ULONG WINAPI xslprocessor_Release( IXSLProcessor *iface )
291 {
292 xslprocessor *This = impl_from_IXSLProcessor( iface );
293 ULONG ref = InterlockedDecrement( &This->ref );
294
295 TRACE("(%p)->(%d)\n", This, ref);
296 if ( ref == 0 )
297 {
298 struct xslprocessor_par *par, *par2;
299
300 if (This->input) IXMLDOMNode_Release(This->input);
301 if (This->output) IStream_Release(This->output);
302 SysFreeString(This->outstr);
303
304 LIST_FOR_EACH_ENTRY_SAFE(par, par2, &This->params.list, struct xslprocessor_par, entry)
305 xslprocessor_par_free(&This->params, par);
306
307 IXSLTemplate_Release(&This->stylesheet->IXSLTemplate_iface);
308 heap_free( This );
309 }
310
311 return ref;
312 }
313
314 static HRESULT WINAPI xslprocessor_GetTypeInfoCount( IXSLProcessor *iface, UINT* pctinfo )
315 {
316 xslprocessor *This = impl_from_IXSLProcessor( iface );
317 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
318 }
319
320 static HRESULT WINAPI xslprocessor_GetTypeInfo(
321 IXSLProcessor *iface,
322 UINT iTInfo, LCID lcid,
323 ITypeInfo** ppTInfo )
324 {
325 xslprocessor *This = impl_from_IXSLProcessor( iface );
326 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
327 iTInfo, lcid, ppTInfo);
328 }
329
330 static HRESULT WINAPI xslprocessor_GetIDsOfNames(
331 IXSLProcessor *iface,
332 REFIID riid, LPOLESTR* rgszNames,
333 UINT cNames, LCID lcid, DISPID* rgDispId )
334 {
335 xslprocessor *This = impl_from_IXSLProcessor( iface );
336 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
337 riid, rgszNames, cNames, lcid, rgDispId);
338 }
339
340 static HRESULT WINAPI xslprocessor_Invoke(
341 IXSLProcessor *iface,
342 DISPID dispIdMember, REFIID riid, LCID lcid,
343 WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
344 EXCEPINFO* pExcepInfo, UINT* puArgErr )
345 {
346 xslprocessor *This = impl_from_IXSLProcessor( iface );
347 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
348 dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
349 }
350
351 static HRESULT WINAPI xslprocessor_put_input( IXSLProcessor *iface, VARIANT input )
352 {
353 xslprocessor *This = impl_from_IXSLProcessor( iface );
354 IXMLDOMNode *input_node;
355 HRESULT hr;
356
357 TRACE("(%p)->(%s)\n", This, debugstr_variant(&input));
358
359 /* try IXMLDOMNode directly first */
360 if (V_VT(&input) == VT_UNKNOWN)
361 hr = IUnknown_QueryInterface(V_UNKNOWN(&input), &IID_IXMLDOMNode, (void**)&input_node);
362 else if (V_VT(&input) == VT_DISPATCH)
363 hr = IDispatch_QueryInterface(V_DISPATCH(&input), &IID_IXMLDOMNode, (void**)&input_node);
364 else
365 {
366 IXMLDOMDocument *doc;
367
368 hr = DOMDocument_create(MSXML_DEFAULT, (void**)&doc);
369 if (hr == S_OK)
370 {
371 VARIANT_BOOL b;
372
373 hr = IXMLDOMDocument_load(doc, input, &b);
374 if (hr == S_OK)
375 hr = IXMLDOMDocument_QueryInterface(doc, &IID_IXMLDOMNode, (void**)&input_node);
376 IXMLDOMDocument_Release(doc);
377 }
378 }
379
380 if (hr == S_OK)
381 {
382 if (This->input) IXMLDOMNode_Release(This->input);
383 This->input = input_node;
384 }
385
386 return hr;
387 }
388
389 static HRESULT WINAPI xslprocessor_get_input( IXSLProcessor *iface, VARIANT *input )
390 {
391 xslprocessor *This = impl_from_IXSLProcessor( iface );
392
393 FIXME("(%p)->(%p): stub\n", This, input);
394 return E_NOTIMPL;
395 }
396
397 static HRESULT WINAPI xslprocessor_get_ownerTemplate(
398 IXSLProcessor *iface,
399 IXSLTemplate **template)
400 {
401 xslprocessor *This = impl_from_IXSLProcessor( iface );
402
403 FIXME("(%p)->(%p): stub\n", This, template);
404 return E_NOTIMPL;
405 }
406
407 static HRESULT WINAPI xslprocessor_setStartMode(
408 IXSLProcessor *iface,
409 BSTR p,
410 BSTR uri)
411 {
412 xslprocessor *This = impl_from_IXSLProcessor( iface );
413
414 FIXME("(%p)->(%s %s): stub\n", This, debugstr_w(p), debugstr_w(uri));
415 return E_NOTIMPL;
416 }
417
418 static HRESULT WINAPI xslprocessor_get_startMode(
419 IXSLProcessor *iface,
420 BSTR *p)
421 {
422 xslprocessor *This = impl_from_IXSLProcessor( iface );
423
424 FIXME("(%p)->(%p): stub\n", This, p);
425 return E_NOTIMPL;
426 }
427
428 static HRESULT WINAPI xslprocessor_get_startModeURI(
429 IXSLProcessor *iface,
430 BSTR *uri)
431 {
432 xslprocessor *This = impl_from_IXSLProcessor( iface );
433
434 FIXME("(%p)->(%p): stub\n", This, uri);
435 return E_NOTIMPL;
436 }
437
438 static HRESULT WINAPI xslprocessor_put_output(
439 IXSLProcessor *iface,
440 VARIANT output)
441 {
442 xslprocessor *This = impl_from_IXSLProcessor( iface );
443 IStream *stream;
444 HRESULT hr;
445
446 TRACE("(%p)->(%s)\n", This, debugstr_variant(&output));
447
448 switch (V_VT(&output))
449 {
450 case VT_EMPTY:
451 stream = NULL;
452 hr = S_OK;
453 break;
454 case VT_UNKNOWN:
455 hr = IUnknown_QueryInterface(V_UNKNOWN(&output), &IID_IStream, (void**)&stream);
456 if (FAILED(hr))
457 WARN("failed to get IStream from output, 0x%08x\n", hr);
458 break;
459 default:
460 FIXME("output type %d not handled\n", V_VT(&output));
461 hr = E_FAIL;
462 }
463
464 if (hr == S_OK)
465 {
466 if (This->output) IStream_Release(This->output);
467 This->output = stream;
468 }
469
470 return hr;
471 }
472
473 static HRESULT WINAPI xslprocessor_get_output(
474 IXSLProcessor *iface,
475 VARIANT *output)
476 {
477 xslprocessor *This = impl_from_IXSLProcessor( iface );
478
479 TRACE("(%p)->(%p)\n", This, output);
480
481 if (!output) return E_INVALIDARG;
482
483 if (This->output)
484 {
485 V_VT(output) = VT_UNKNOWN;
486 V_UNKNOWN(output) = (IUnknown*)This->output;
487 IStream_AddRef(This->output);
488 }
489 else if (This->outstr)
490 {
491 V_VT(output) = VT_BSTR;
492 V_BSTR(output) = SysAllocString(This->outstr);
493 }
494 else
495 V_VT(output) = VT_EMPTY;
496
497 return S_OK;
498 }
499
500 static HRESULT WINAPI xslprocessor_transform(
501 IXSLProcessor *iface,
502 VARIANT_BOOL *ret)
503 {
504 #ifdef HAVE_LIBXML2
505 xslprocessor *This = impl_from_IXSLProcessor( iface );
506 HRESULT hr;
507
508 TRACE("(%p)->(%p)\n", This, ret);
509
510 if (!ret) return E_INVALIDARG;
511
512 SysFreeString(This->outstr);
513 hr = node_transform_node_params(get_node_obj(This->input), This->stylesheet->node, &This->outstr, This->output, &This->params);
514 *ret = hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE;
515 return hr;
516 #else
517 FIXME("libxml2 is required but wasn't present at compile time\n");
518 return E_NOTIMPL;
519 #endif
520 }
521
522 static HRESULT WINAPI xslprocessor_reset( IXSLProcessor *iface )
523 {
524 xslprocessor *This = impl_from_IXSLProcessor( iface );
525
526 FIXME("(%p): stub\n", This);
527 return E_NOTIMPL;
528 }
529
530 static HRESULT WINAPI xslprocessor_get_readyState(
531 IXSLProcessor *iface,
532 LONG *state)
533 {
534 xslprocessor *This = impl_from_IXSLProcessor( iface );
535
536 FIXME("(%p)->(%p): stub\n", This, state);
537 return E_NOTIMPL;
538 }
539
540 static HRESULT xslprocessor_set_parvalue(const VARIANT *var, struct xslprocessor_par *par)
541 {
542 HRESULT hr = S_OK;
543
544 switch (V_VT(var))
545 {
546 case VT_BSTR:
547 {
548 par->value = SysAllocString(V_BSTR(var));
549 if (!par->value) hr = E_OUTOFMEMORY;
550 break;
551 }
552 default:
553 FIXME("value type %d not handled\n", V_VT(var));
554 hr = E_NOTIMPL;
555 }
556
557 return hr;
558 }
559
560 static HRESULT WINAPI xslprocessor_addParameter(
561 IXSLProcessor *iface,
562 BSTR p,
563 VARIANT var,
564 BSTR uri)
565 {
566 xslprocessor *This = impl_from_IXSLProcessor( iface );
567 struct xslprocessor_par *cur, *par = NULL;
568 HRESULT hr;
569
570 TRACE("(%p)->(%s %s %s)\n", This, debugstr_w(p), debugstr_variant(&var),
571 debugstr_w(uri));
572
573 if (uri && *uri)
574 FIXME("namespace uri is not supported\n");
575
576 /* search for existing parameter first */
577 LIST_FOR_EACH_ENTRY(cur, &This->params.list, struct xslprocessor_par, entry)
578 {
579 if (!strcmpW(cur->name, p))
580 {
581 par = cur;
582 break;
583 }
584 }
585
586 /* override with new value or add new parameter */
587 if (par)
588 {
589 if (V_VT(&var) == VT_NULL || V_VT(&var) == VT_EMPTY)
590 {
591 /* remove parameter */
592 xslprocessor_par_free(&This->params, par);
593 return S_OK;
594 }
595 SysFreeString(par->value);
596 par->value = NULL;
597 }
598 else
599 {
600 /* new parameter */
601 par = heap_alloc(sizeof(struct xslprocessor_par));
602 if (!par) return E_OUTOFMEMORY;
603
604 par->name = SysAllocString(p);
605 if (!par->name)
606 {
607 heap_free(par);
608 return E_OUTOFMEMORY;
609 }
610 list_add_tail(&This->params.list, &par->entry);
611 This->params.count++;
612 }
613
614 hr = xslprocessor_set_parvalue(&var, par);
615 if (FAILED(hr))
616 xslprocessor_par_free(&This->params, par);
617
618 return hr;
619 }
620
621 static HRESULT WINAPI xslprocessor_addObject(
622 IXSLProcessor *iface,
623 IDispatch *obj,
624 BSTR uri)
625 {
626 xslprocessor *This = impl_from_IXSLProcessor( iface );
627
628 FIXME("(%p)->(%p %s): stub\n", This, obj, debugstr_w(uri));
629 return E_NOTIMPL;
630 }
631
632 static HRESULT WINAPI xslprocessor_get_stylesheet(
633 IXSLProcessor *iface,
634 IXMLDOMNode **node)
635 {
636 xslprocessor *This = impl_from_IXSLProcessor( iface );
637
638 FIXME("(%p)->(%p): stub\n", This, node);
639 return E_NOTIMPL;
640 }
641
642 static const struct IXSLProcessorVtbl XSLProcessorVtbl =
643 {
644 xslprocessor_QueryInterface,
645 xslprocessor_AddRef,
646 xslprocessor_Release,
647 xslprocessor_GetTypeInfoCount,
648 xslprocessor_GetTypeInfo,
649 xslprocessor_GetIDsOfNames,
650 xslprocessor_Invoke,
651 xslprocessor_put_input,
652 xslprocessor_get_input,
653 xslprocessor_get_ownerTemplate,
654 xslprocessor_setStartMode,
655 xslprocessor_get_startMode,
656 xslprocessor_get_startModeURI,
657 xslprocessor_put_output,
658 xslprocessor_get_output,
659 xslprocessor_transform,
660 xslprocessor_reset,
661 xslprocessor_get_readyState,
662 xslprocessor_addParameter,
663 xslprocessor_addObject,
664 xslprocessor_get_stylesheet
665 };
666
667 static const tid_t xslprocessor_iface_tids[] = {
668 IXSLProcessor_tid,
669 0
670 };
671
672 static dispex_static_data_t xslprocessor_dispex = {
673 NULL,
674 IXSLProcessor_tid,
675 NULL,
676 xslprocessor_iface_tids
677 };
678
679 HRESULT XSLProcessor_create(xsltemplate *template, IXSLProcessor **ppObj)
680 {
681 xslprocessor *This;
682
683 TRACE("(%p)\n", ppObj);
684
685 This = heap_alloc( sizeof (*This) );
686 if(!This)
687 return E_OUTOFMEMORY;
688
689 This->IXSLProcessor_iface.lpVtbl = &XSLProcessorVtbl;
690 This->ref = 1;
691 This->input = NULL;
692 This->output = NULL;
693 This->outstr = NULL;
694 list_init(&This->params.list);
695 This->params.count = 0;
696 This->stylesheet = template;
697 IXSLTemplate_AddRef(&template->IXSLTemplate_iface);
698 init_dispex(&This->dispex, (IUnknown*)&This->IXSLProcessor_iface, &xslprocessor_dispex);
699
700 *ppObj = &This->IXSLProcessor_iface;
701
702 TRACE("returning iface %p\n", *ppObj);
703
704 return S_OK;
705 }