shell32: Updated French translation.
[wine] / dlls / mshtml / dispex.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "ole2.h"
27
28 #include "wine/debug.h"
29
30 #include "mshtml_private.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
33
34 typedef struct {
35     DISPID id;
36     BSTR name;
37     tid_t tid;
38 } func_info_t;
39
40 struct dispex_data_t {
41     DWORD func_cnt;
42     func_info_t *funcs;
43     func_info_t **name_table;
44
45     struct list entry;
46 };
47
48 typedef struct {
49     VARIANT var;
50     LPWSTR name;
51 } dynamic_prop_t;
52
53 struct dispex_dynamic_data_t {
54     DWORD buf_size;
55     DWORD prop_cnt;
56     dynamic_prop_t *props;
57 };
58
59 #define DISPID_DYNPROP_0    0x50000000
60 #define DISPID_DYNPROP_MAX  0x5fffffff
61
62 static ITypeLib *typelib;
63 static ITypeInfo *typeinfos[LAST_tid];
64 static struct list dispex_data_list = LIST_INIT(dispex_data_list);
65
66 static REFIID tid_ids[] = {
67     &IID_NULL,
68     &DIID_DispCEventObj,
69     &DIID_DispDOMChildrenCollection,
70     &DIID_DispHTMLBody,
71     &DIID_DispHTMLCommentElement,
72     &DIID_DispHTMLCurrentStyle,
73     &DIID_DispHTMLDocument,
74     &DIID_DispHTMLDOMTextNode,
75     &DIID_DispHTMLElementCollection,
76     &DIID_DispHTMLGenericElement,
77     &DIID_DispHTMLIFrame,
78     &DIID_DispHTMLImg,
79     &DIID_DispHTMLInputElement,
80     &DIID_DispHTMLOptionElement,
81     &DIID_DispHTMLSelectElement,
82     &DIID_DispHTMLStyle,
83     &DIID_DispHTMLTable,
84     &DIID_DispHTMLTableRow,
85     &DIID_DispHTMLUnknownElement,
86     &DIID_DispHTMLWindow2,
87     &IID_IHTMLBodyElement,
88     &IID_IHTMLBodyElement2,
89     &IID_IHTMLCommentElement,
90     &IID_IHTMLCurrentStyle,
91     &IID_IHTMLDocument2,
92     &IID_IHTMLDocument3,
93     &IID_IHTMLDocument4,
94     &IID_IHTMLDocument5,
95     &IID_IHTMLDOMChildrenCollection,
96     &IID_IHTMLDOMNode,
97     &IID_IHTMLDOMNode2,
98     &IID_IHTMLDOMTextNode,
99     &IID_IHTMLElement,
100     &IID_IHTMLElement2,
101     &IID_IHTMLElement3,
102     &IID_IHTMLElement4,
103     &IID_IHTMLElementCollection,
104     &IID_IHTMLEventObj,
105     &IID_IHTMLFrameBase2,
106     &IID_IHTMLGenericElement,
107     &IID_IHTMLImgElement,
108     &IID_IHTMLInputElement,
109     &IID_IHTMLOptionElement,
110     &IID_IHTMLSelectElement,
111     &IID_IHTMLStyle,
112     &IID_IHTMLStyle2,
113     &IID_IHTMLTable,
114     &IID_IHTMLTableRow,
115     &IID_IHTMLTextContainer,
116     &IID_IHTMLUniqueName,
117     &IID_IHTMLWindow2,
118     &IID_IHTMLWindow3,
119     &IID_IOmNavigator
120 };
121
122 static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
123 {
124     HRESULT hres;
125
126     if(!typelib) {
127         ITypeLib *tl;
128
129         hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
130         if(FAILED(hres)) {
131             ERR("LoadRegTypeLib failed: %08x\n", hres);
132             return hres;
133         }
134
135         if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
136             ITypeLib_Release(tl);
137     }
138
139     if(!typeinfos[tid]) {
140         ITypeInfo *typeinfo;
141
142         hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &typeinfo);
143         if(FAILED(hres)) {
144             ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres);
145             return hres;
146         }
147
148         if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), typeinfo, NULL))
149             ITypeInfo_Release(typeinfo);
150     }
151
152     *typeinfo = typeinfos[tid];
153     return S_OK;
154 }
155
156 void release_typelib(void)
157 {
158     dispex_data_t *iter;
159     unsigned i;
160
161     while(!list_empty(&dispex_data_list)) {
162         iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
163         list_remove(&iter->entry);
164
165         for(i=0; i < iter->func_cnt; i++)
166             SysFreeString(iter->funcs[i].name);
167
168         heap_free(iter->funcs);
169         heap_free(iter->name_table);
170         heap_free(iter);
171     }
172
173     if(!typelib)
174         return;
175
176     for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
177         if(typeinfos[i])
178             ITypeInfo_Release(typeinfos[i]);
179
180     ITypeLib_Release(typelib);
181 }
182
183 static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, DISPID id, ITypeInfo *dti)
184 {
185     HRESULT hres;
186
187     if(data->func_cnt && data->funcs[data->func_cnt-1].id == id)
188         return;
189
190     if(data->func_cnt == *size)
191         data->funcs = heap_realloc(data->funcs, (*size <<= 1)*sizeof(func_info_t));
192
193     hres = ITypeInfo_GetDocumentation(dti, id, &data->funcs[data->func_cnt].name, NULL, NULL, NULL);
194     if(FAILED(hres))
195         return;
196
197     data->funcs[data->func_cnt].id = id;
198     data->funcs[data->func_cnt].tid = tid;
199
200     data->func_cnt++;
201 }
202
203 static int dispid_cmp(const void *p1, const void *p2)
204 {
205     return ((func_info_t*)p1)->id - ((func_info_t*)p2)->id;
206 }
207
208 static int func_name_cmp(const void *p1, const void *p2)
209 {
210     return strcmpiW((*(func_info_t**)p1)->name, (*(func_info_t**)p2)->name);
211 }
212
213 static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
214 {
215     const tid_t *tid = This->data->iface_tids;
216     FUNCDESC *funcdesc;
217     dispex_data_t *data;
218     DWORD size = 16, i;
219     ITypeInfo *ti, *dti;
220     HRESULT hres;
221
222     TRACE("(%p)\n", This);
223
224     hres = get_typeinfo(This->data->disp_tid, &dti);
225     if(FAILED(hres)) {
226         ERR("Could not get disp type info: %08x\n", hres);
227         return NULL;
228     }
229
230     data = heap_alloc(sizeof(dispex_data_t));
231     data->func_cnt = 0;
232     data->funcs = heap_alloc(size*sizeof(func_info_t));
233     list_add_tail(&dispex_data_list, &data->entry);
234
235     while(*tid) {
236         hres = get_typeinfo(*tid, &ti);
237         if(FAILED(hres))
238             break;
239
240         i=7;
241         while(1) {
242             hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
243             if(FAILED(hres))
244                 break;
245
246             add_func_info(data, &size, *tid, funcdesc->memid, dti);
247             ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
248         }
249
250         tid++;
251     }
252
253     if(!data->func_cnt) {
254         heap_free(data->funcs);
255         data->funcs = NULL;
256     }else if(data->func_cnt != size) {
257         data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
258     }
259
260     qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
261
262     if(data->funcs) {
263         data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
264         for(i=0; i < data->func_cnt; i++)
265             data->name_table[i] = data->funcs+i;
266         qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
267     }else {
268         data->name_table = NULL;
269     }
270
271     return data;
272 }
273
274 static CRITICAL_SECTION cs_dispex_static_data;
275 static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
276 {
277     0, 0, &cs_dispex_static_data,
278     { &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
279       0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
280 };
281 static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
282
283
284 static dispex_data_t *get_dispex_data(DispatchEx *This)
285 {
286     if(This->data->data)
287         return This->data->data;
288
289     EnterCriticalSection(&cs_dispex_static_data);
290
291     if(!This->data->data)
292         This->data->data = preprocess_dispex_data(This);
293
294     LeaveCriticalSection(&cs_dispex_static_data);
295
296     return This->data->data;
297 }
298
299 void call_disp_func(HTMLDocument *doc, IDispatch *disp, IDispatch *this_obj)
300 {
301     DISPID named_arg = DISPID_THIS;
302     VARIANTARG arg;
303     DISPPARAMS params = {&arg, &named_arg, 1, 1};
304     EXCEPINFO ei;
305     IDispatchEx *dispex;
306     VARIANT res;
307     HRESULT hres;
308
309     hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
310     if(FAILED(hres)) {
311         FIXME("Could not get IDispatchEx interface: %08x\n", hres);
312         return;
313     }
314
315     V_VT(&arg) = VT_DISPATCH;
316     V_DISPATCH(&arg) = this_obj;
317     VariantInit(&res);
318     memset(&ei, 0, sizeof(ei));
319
320     hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, &params, &res, &ei, NULL);
321     IDispatchEx_Release(dispex);
322
323     TRACE("%p returned %08x\n", disp, hres);
324
325     VariantClear(&res);
326 }
327
328 static inline BOOL is_custom_dispid(DISPID id)
329 {
330     return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
331 }
332
333 static inline BOOL is_dynamic_dispid(DISPID id)
334 {
335     return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
336 }
337
338 static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, BOOL alloc, dynamic_prop_t **ret)
339 {
340     dispex_dynamic_data_t *data = This->dynamic_data;
341
342     if(data) {
343         unsigned i;
344
345         for(i=0; i < data->prop_cnt; i++) {
346             if(!strcmpW(data->props[i].name, name)) {
347                 *ret = data->props+i;
348                 return S_OK;
349             }
350         }
351     }
352
353     if(alloc) {
354         TRACE("creating dynamic prop %s\n", debugstr_w(name));
355
356         if(!data) {
357             data = This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t));
358             if(!data)
359                 return E_OUTOFMEMORY;
360         }
361
362         if(!data->buf_size) {
363             data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
364             if(!data->props)
365                 return E_OUTOFMEMORY;
366             data->buf_size = 4;
367         }else if(data->buf_size == data->prop_cnt) {
368             dynamic_prop_t *new_props;
369
370             new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
371             if(!new_props)
372                 return E_OUTOFMEMORY;
373
374             data->props = new_props;
375             data->buf_size <<= 1;
376         }
377
378         data->props[data->prop_cnt].name = heap_strdupW(name);
379         VariantInit(&data->props[data->prop_cnt].var);
380         *ret = data->props + data->prop_cnt++;
381
382         return S_OK;
383     }
384
385     TRACE("not found %s\n", debugstr_w(name));
386     return DISP_E_UNKNOWNNAME;
387 }
388
389 HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
390 {
391     dynamic_prop_t *prop;
392     HRESULT hres;
393
394     hres = get_dynamic_prop(This, name, alloc, &prop);
395     if(FAILED(hres))
396         return hres;
397
398     *ret = &prop->var;
399     return S_OK;
400 }
401
402 #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface)
403
404 static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
405 {
406     DispatchEx *This = DISPATCHEX_THIS(iface);
407
408     return IUnknown_QueryInterface(This->outer, riid, ppv);
409 }
410
411 static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
412 {
413     DispatchEx *This = DISPATCHEX_THIS(iface);
414
415     return IUnknown_AddRef(This->outer);
416 }
417
418 static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
419 {
420     DispatchEx *This = DISPATCHEX_THIS(iface);
421
422     return IUnknown_Release(This->outer);
423 }
424
425 static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
426 {
427     DispatchEx *This = DISPATCHEX_THIS(iface);
428
429     TRACE("(%p)->(%p)\n", This, pctinfo);
430
431     *pctinfo = 1;
432     return S_OK;
433 }
434
435 static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
436                                               LCID lcid, ITypeInfo **ppTInfo)
437 {
438     DispatchEx *This = DISPATCHEX_THIS(iface);
439     HRESULT hres;
440
441     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
442
443     hres = get_typeinfo(This->data->disp_tid, ppTInfo);
444     if(FAILED(hres))
445         return hres;
446
447     ITypeInfo_AddRef(*ppTInfo);
448     return S_OK;
449 }
450
451 static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
452                                                 LPOLESTR *rgszNames, UINT cNames,
453                                                 LCID lcid, DISPID *rgDispId)
454 {
455     DispatchEx *This = DISPATCHEX_THIS(iface);
456     UINT i;
457     HRESULT hres;
458
459     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
460           lcid, rgDispId);
461
462     for(i=0; i < cNames; i++) {
463         hres = IDispatchEx_GetDispID(DISPATCHEX(This), rgszNames[i], 0, rgDispId+i);
464         if(FAILED(hres))
465             return hres;
466     }
467
468     return S_OK;
469 }
470
471 static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
472                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
473                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
474 {
475     DispatchEx *This = DISPATCHEX_THIS(iface);
476
477     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
478           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
479
480     return IDispatchEx_InvokeEx(DISPATCHEX(This), dispIdMember, lcid, wFlags,
481                                 pDispParams, pVarResult, pExcepInfo, NULL);
482 }
483
484 static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
485 {
486     DispatchEx *This = DISPATCHEX_THIS(iface);
487     dynamic_prop_t *dprop;
488     dispex_data_t *data;
489     int min, max, n, c;
490     HRESULT hres;
491
492     TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
493
494     if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit))
495         FIXME("Unsupported grfdex %x\n", grfdex);
496
497     data = get_dispex_data(This);
498     if(!data)
499         return E_FAIL;
500
501     min = 0;
502     max = data->func_cnt-1;
503
504     while(min <= max) {
505         n = (min+max)/2;
506
507         c = strcmpiW(data->name_table[n]->name, bstrName);
508         if(!c) {
509             if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, bstrName))
510                 break;
511
512             *pid = data->name_table[n]->id;
513             return S_OK;
514         }
515
516         if(c > 0)
517             max = n-1;
518         else
519             min = n+1;
520     }
521
522     if(This->data->vtbl && This->data->vtbl->get_dispid) {
523         HRESULT hres;
524
525         hres = This->data->vtbl->get_dispid(This->outer, bstrName, grfdex, pid);
526         if(hres != DISP_E_UNKNOWNNAME)
527             return hres;
528     }
529
530     hres = get_dynamic_prop(This, bstrName, grfdex&fdexNameEnsure, &dprop);
531     if(FAILED(hres))
532         return hres;
533
534     *pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
535     return S_OK;
536 }
537
538 static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
539         VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
540 {
541     DispatchEx *This = DISPATCHEX_THIS(iface);
542     IUnknown *unk;
543     ITypeInfo *ti;
544     dispex_data_t *data;
545     UINT argerr=0;
546     int min, max, n;
547     HRESULT hres;
548
549     TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
550
551     if(is_custom_dispid(id) && This->data->vtbl && This->data->vtbl->invoke)
552         return This->data->vtbl->invoke(This->outer, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
553
554     if(wFlags == DISPATCH_CONSTRUCT) {
555         FIXME("DISPATCH_CONSTRUCT not implemented\n");
556         return E_NOTIMPL;
557     }
558
559     if(is_dynamic_dispid(id)) {
560         DWORD idx = id - DISPID_DYNPROP_0;
561         VARIANT *var;
562
563         if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
564             return DISP_E_UNKNOWNNAME;
565
566         var = &This->dynamic_data->props[idx].var;
567
568         switch(wFlags) {
569         case INVOKE_FUNC: {
570             DISPID named_arg = DISPID_THIS;
571             DISPPARAMS dp = {NULL, &named_arg, 0, 1};
572             IDispatchEx *dispex;
573
574             if(V_VT(var) != VT_DISPATCH) {
575                 FIXME("invoke vt %d\n", V_VT(var));
576                 return E_NOTIMPL;
577             }
578
579             if(pdp->cNamedArgs) {
580                 FIXME("named args not supported\n");
581                 return E_NOTIMPL;
582             }
583
584             dp.rgvarg = heap_alloc((pdp->cArgs+1)*sizeof(VARIANTARG));
585             if(!dp.rgvarg)
586                 return E_OUTOFMEMORY;
587
588             dp.cArgs = pdp->cArgs+1;
589             memcpy(dp.rgvarg+1, pdp->rgvarg, pdp->cArgs*sizeof(VARIANTARG));
590
591             V_VT(dp.rgvarg) = VT_DISPATCH;
592             V_DISPATCH(dp.rgvarg) = (IDispatch*)DISPATCHEX(This);
593
594             hres = IDispatch_QueryInterface(V_DISPATCH(var), &IID_IDispatchEx, (void**)&dispex);
595             TRACE("%s call\n", debugstr_w(This->dynamic_data->props[idx].name));
596             if(SUCCEEDED(hres)) {
597                 hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, wFlags, &dp, pvarRes, pei, pspCaller);
598                 IDispatchEx_Release(dispex);
599             }else {
600                 ULONG err = 0;
601                 hres = IDispatch_Invoke(V_DISPATCH(var), DISPID_VALUE, &IID_NULL, lcid, wFlags, pdp, pvarRes, pei, &err);
602             }
603             TRACE("%s ret %08x\n", debugstr_w(This->dynamic_data->props[idx].name), hres);
604
605             heap_free(dp.rgvarg);
606             return hres;
607         }
608         case INVOKE_PROPERTYGET:
609             return VariantCopy(pvarRes, var);
610         case INVOKE_PROPERTYPUT:
611             VariantClear(var);
612             return VariantCopy(var, pdp->rgvarg);
613         default:
614             FIXME("unhandled wFlags %x\n", wFlags);
615             return E_NOTIMPL;
616         }
617     }
618
619     data = get_dispex_data(This);
620     if(!data)
621         return E_FAIL;
622
623     min = 0;
624     max = data->func_cnt-1;
625
626     while(min <= max) {
627         n = (min+max)/2;
628
629         if(data->funcs[n].id == id)
630             break;
631
632         if(data->funcs[n].id < id)
633             min = n+1;
634         else
635             max = n-1;
636     }
637
638     if(min > max) {
639         WARN("invalid id %x\n", id);
640         return DISP_E_UNKNOWNNAME;
641     }
642
643     hres = get_typeinfo(data->funcs[n].tid, &ti);
644     if(FAILED(hres)) {
645         ERR("Could not get type info: %08x\n", hres);
646         return hres;
647     }
648
649     hres = IUnknown_QueryInterface(This->outer, tid_ids[data->funcs[n].tid], (void**)&unk);
650     if(FAILED(hres)) {
651         ERR("Could not get iface %s: %08x\n", debugstr_guid(tid_ids[data->funcs[n].tid]), hres);
652         return E_FAIL;
653     }
654
655     hres = ITypeInfo_Invoke(ti, unk, id, wFlags, pdp, pvarRes, pei, &argerr);
656
657     IUnknown_Release(unk);
658     return hres;
659 }
660
661 static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
662 {
663     DispatchEx *This = DISPATCHEX_THIS(iface);
664     FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
665     return E_NOTIMPL;
666 }
667
668 static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
669 {
670     DispatchEx *This = DISPATCHEX_THIS(iface);
671     FIXME("(%p)->(%x)\n", This, id);
672     return E_NOTIMPL;
673 }
674
675 static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
676 {
677     DispatchEx *This = DISPATCHEX_THIS(iface);
678     FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
679     return E_NOTIMPL;
680 }
681
682 static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
683 {
684     DispatchEx *This = DISPATCHEX_THIS(iface);
685     FIXME("(%p)->(%x %p)\n", This, id, pbstrName);
686     return E_NOTIMPL;
687 }
688
689 static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
690 {
691     DispatchEx *This = DISPATCHEX_THIS(iface);
692     FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
693     return E_NOTIMPL;
694 }
695
696 static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
697 {
698     DispatchEx *This = DISPATCHEX_THIS(iface);
699     FIXME("(%p)->(%p)\n", This, ppunk);
700     return E_NOTIMPL;
701 }
702
703 #undef DISPATCHEX_THIS
704
705 static IDispatchExVtbl DispatchExVtbl = {
706     DispatchEx_QueryInterface,
707     DispatchEx_AddRef,
708     DispatchEx_Release,
709     DispatchEx_GetTypeInfoCount,
710     DispatchEx_GetTypeInfo,
711     DispatchEx_GetIDsOfNames,
712     DispatchEx_Invoke,
713     DispatchEx_GetDispID,
714     DispatchEx_InvokeEx,
715     DispatchEx_DeleteMemberByName,
716     DispatchEx_DeleteMemberByDispID,
717     DispatchEx_GetMemberProperties,
718     DispatchEx_GetMemberName,
719     DispatchEx_GetNextDispID,
720     DispatchEx_GetNameSpaceParent
721 };
722
723 BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
724 {
725     static const IID IID_UndocumentedScriptIface =
726         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa0}};
727     static const IID IID_IDispatchJS =
728         {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
729
730     if(IsEqualGUID(&IID_IDispatch, riid)) {
731         TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
732         *ppv = DISPATCHEX(This);
733     }else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
734         TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
735         *ppv = DISPATCHEX(This);
736     }else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
737         TRACE("(%p)->(IID_IDispatchJS %p) returning NULL\n", This, ppv);
738         *ppv = NULL;
739     }else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) {
740         TRACE("(%p)->(IID_UndocumentedScriptIface %p) returning NULL\n", This, ppv);
741         *ppv = NULL;
742     }else {
743         return FALSE;
744     }
745
746     if(*ppv)
747         IUnknown_AddRef((IUnknown*)*ppv);
748     return TRUE;
749 }
750
751 void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
752 {
753     dispex->lpIDispatchExVtbl = &DispatchExVtbl;
754     dispex->outer = outer;
755     dispex->data = data;
756 }