2008-07-08 16:39:29 +00:00
|
|
|
/*
|
2009-10-20 19:28:02 +00:00
|
|
|
* Copyright 2008-2009 Jacek Caban for CodeWeavers
|
2008-07-08 16:39:29 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
#define COBJMACROS
|
|
|
|
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "ole2.h"
|
|
|
|
|
|
|
|
#include "wine/debug.h"
|
|
|
|
|
|
|
|
#include "mshtml_private.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
|
|
|
|
|
2009-10-20 19:28:02 +00:00
|
|
|
static const WCHAR objectW[] = {'[','o','b','j','e','c','t',']',0};
|
|
|
|
|
2008-07-08 16:39:29 +00:00
|
|
|
typedef struct {
|
|
|
|
DISPID id;
|
|
|
|
BSTR name;
|
|
|
|
tid_t tid;
|
2009-09-05 15:02:49 +00:00
|
|
|
int func_disp_idx;
|
2008-07-08 16:39:29 +00:00
|
|
|
} func_info_t;
|
|
|
|
|
|
|
|
struct dispex_data_t {
|
|
|
|
DWORD func_cnt;
|
|
|
|
func_info_t *funcs;
|
|
|
|
func_info_t **name_table;
|
2009-09-05 15:02:49 +00:00
|
|
|
DWORD func_disp_cnt;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
struct list entry;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
VARIANT var;
|
|
|
|
LPWSTR name;
|
2010-04-20 17:16:57 +00:00
|
|
|
DWORD flags;
|
2008-07-08 16:39:29 +00:00
|
|
|
} dynamic_prop_t;
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
#define DYNPROP_DELETED 0x01
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
typedef struct {
|
|
|
|
DispatchEx dispex;
|
|
|
|
const IUnknownVtbl *lpIUnknownVtbl;
|
|
|
|
DispatchEx *obj;
|
|
|
|
func_info_t *info;
|
|
|
|
} func_disp_t;
|
|
|
|
|
|
|
|
#define FUNCUNKNOWN(x) ((IUnknown*) &(x)->lpIUnknownVtbl)
|
|
|
|
|
2008-07-08 16:39:29 +00:00
|
|
|
struct dispex_dynamic_data_t {
|
|
|
|
DWORD buf_size;
|
|
|
|
DWORD prop_cnt;
|
|
|
|
dynamic_prop_t *props;
|
2009-09-05 15:02:49 +00:00
|
|
|
func_disp_t **func_disps;
|
2008-07-08 16:39:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#define DISPID_DYNPROP_0 0x50000000
|
|
|
|
#define DISPID_DYNPROP_MAX 0x5fffffff
|
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
#define FDEX_VERSION_MASK 0xf0000000
|
|
|
|
|
2008-07-08 16:39:29 +00:00
|
|
|
static ITypeLib *typelib;
|
|
|
|
static ITypeInfo *typeinfos[LAST_tid];
|
|
|
|
static struct list dispex_data_list = LIST_INIT(dispex_data_list);
|
|
|
|
|
|
|
|
static REFIID tid_ids[] = {
|
|
|
|
&IID_NULL,
|
2009-02-04 14:35:06 +00:00
|
|
|
&DIID_DispCEventObj,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispDOMChildrenCollection,
|
2009-10-20 19:28:02 +00:00
|
|
|
&DIID_DispHTMLAnchorElement,
|
2008-09-10 07:44:02 +00:00
|
|
|
&DIID_DispHTMLBody,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLCommentElement,
|
2009-02-04 14:35:06 +00:00
|
|
|
&DIID_DispHTMLCurrentStyle,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLDocument,
|
|
|
|
&DIID_DispHTMLDOMTextNode,
|
|
|
|
&DIID_DispHTMLElementCollection,
|
2010-01-18 16:27:14 +00:00
|
|
|
&DIID_DispHTMLFormElement,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLGenericElement,
|
2010-03-02 14:08:15 +00:00
|
|
|
&DIID_DispHTMLFrameElement,
|
2009-02-04 14:35:06 +00:00
|
|
|
&DIID_DispHTMLIFrame,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLImg,
|
|
|
|
&DIID_DispHTMLInputElement,
|
2009-08-09 12:00:11 +00:00
|
|
|
&DIID_DispHTMLLocation,
|
|
|
|
&DIID_DispHTMLNavigator,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLOptionElement,
|
2010-01-18 16:27:14 +00:00
|
|
|
&DIID_DispHTMLScreen,
|
2010-03-02 14:08:15 +00:00
|
|
|
&DIID_DispHTMLScriptElement,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLSelectElement,
|
|
|
|
&DIID_DispHTMLStyle,
|
2009-02-04 14:35:06 +00:00
|
|
|
&DIID_DispHTMLTable,
|
|
|
|
&DIID_DispHTMLTableRow,
|
2010-03-02 14:08:15 +00:00
|
|
|
&DIID_DispHTMLTextAreaElement,
|
2008-07-08 16:39:29 +00:00
|
|
|
&DIID_DispHTMLUnknownElement,
|
|
|
|
&DIID_DispHTMLWindow2,
|
2010-01-18 16:27:14 +00:00
|
|
|
&DIID_HTMLDocumentEvents,
|
2009-10-20 19:28:02 +00:00
|
|
|
&IID_IHTMLAnchorElement,
|
2008-09-10 07:44:02 +00:00
|
|
|
&IID_IHTMLBodyElement,
|
|
|
|
&IID_IHTMLBodyElement2,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLCommentElement,
|
2009-02-04 14:35:06 +00:00
|
|
|
&IID_IHTMLCurrentStyle,
|
2009-04-29 17:16:20 +00:00
|
|
|
&IID_IHTMLCurrentStyle2,
|
|
|
|
&IID_IHTMLCurrentStyle3,
|
|
|
|
&IID_IHTMLCurrentStyle4,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLDocument2,
|
|
|
|
&IID_IHTMLDocument3,
|
|
|
|
&IID_IHTMLDocument4,
|
|
|
|
&IID_IHTMLDocument5,
|
|
|
|
&IID_IHTMLDOMChildrenCollection,
|
|
|
|
&IID_IHTMLDOMNode,
|
|
|
|
&IID_IHTMLDOMNode2,
|
|
|
|
&IID_IHTMLDOMTextNode,
|
|
|
|
&IID_IHTMLElement,
|
|
|
|
&IID_IHTMLElement2,
|
2008-09-10 07:44:02 +00:00
|
|
|
&IID_IHTMLElement3,
|
|
|
|
&IID_IHTMLElement4,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLElementCollection,
|
2009-02-04 14:35:06 +00:00
|
|
|
&IID_IHTMLEventObj,
|
2010-01-18 16:27:14 +00:00
|
|
|
&IID_IHTMLFiltersCollection,
|
|
|
|
&IID_IHTMLFormElement,
|
2009-11-15 23:44:20 +00:00
|
|
|
&IID_IHTMLFrameBase,
|
2009-02-04 14:35:06 +00:00
|
|
|
&IID_IHTMLFrameBase2,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLGenericElement,
|
2010-03-02 14:08:15 +00:00
|
|
|
&IID_IHTMLFrameElement3,
|
|
|
|
&IID_IHTMLIFrameElement,
|
2009-11-15 23:44:20 +00:00
|
|
|
&IID_IHTMLImageElementFactory,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLImgElement,
|
|
|
|
&IID_IHTMLInputElement,
|
2009-02-04 14:35:06 +00:00
|
|
|
&IID_IHTMLLocation,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLOptionElement,
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
&IID_IHTMLRect,
|
2010-01-18 16:27:14 +00:00
|
|
|
&IID_IHTMLScreen,
|
2010-03-02 14:08:15 +00:00
|
|
|
&IID_IHTMLScriptElement,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLSelectElement,
|
|
|
|
&IID_IHTMLStyle,
|
2009-02-04 14:35:06 +00:00
|
|
|
&IID_IHTMLStyle2,
|
2009-04-29 17:16:20 +00:00
|
|
|
&IID_IHTMLStyle3,
|
|
|
|
&IID_IHTMLStyle4,
|
2009-02-04 14:35:06 +00:00
|
|
|
&IID_IHTMLTable,
|
|
|
|
&IID_IHTMLTableRow,
|
2010-03-02 14:08:15 +00:00
|
|
|
&IID_IHTMLTextAreaElement,
|
2008-09-10 07:44:02 +00:00
|
|
|
&IID_IHTMLTextContainer,
|
|
|
|
&IID_IHTMLUniqueName,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IHTMLWindow2,
|
|
|
|
&IID_IHTMLWindow3,
|
2010-01-18 16:27:14 +00:00
|
|
|
&IID_IHTMLWindow4,
|
2008-07-08 16:39:29 +00:00
|
|
|
&IID_IOmNavigator
|
|
|
|
};
|
|
|
|
|
2009-02-04 14:35:06 +00:00
|
|
|
static HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
|
2008-07-08 16:39:29 +00:00
|
|
|
{
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
if(!typelib) {
|
|
|
|
ITypeLib *tl;
|
|
|
|
|
|
|
|
hres = LoadRegTypeLib(&LIBID_MSHTML, 4, 0, LOCALE_SYSTEM_DEFAULT, &tl);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
ERR("LoadRegTypeLib failed: %08x\n", hres);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL))
|
|
|
|
ITypeLib_Release(tl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!typeinfos[tid]) {
|
|
|
|
ITypeInfo *typeinfo;
|
|
|
|
|
|
|
|
hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &typeinfo);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), typeinfo, NULL))
|
|
|
|
ITypeInfo_Release(typeinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
*typeinfo = typeinfos[tid];
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void release_typelib(void)
|
|
|
|
{
|
|
|
|
dispex_data_t *iter;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
while(!list_empty(&dispex_data_list)) {
|
|
|
|
iter = LIST_ENTRY(list_head(&dispex_data_list), dispex_data_t, entry);
|
|
|
|
list_remove(&iter->entry);
|
|
|
|
|
|
|
|
for(i=0; i < iter->func_cnt; i++)
|
|
|
|
SysFreeString(iter->funcs[i].name);
|
|
|
|
|
|
|
|
heap_free(iter->funcs);
|
|
|
|
heap_free(iter->name_table);
|
|
|
|
heap_free(iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!typelib)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
|
|
|
|
if(typeinfos[i])
|
|
|
|
ITypeInfo_Release(typeinfos[i]);
|
|
|
|
|
|
|
|
ITypeLib_Release(typelib);
|
|
|
|
}
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti)
|
2008-07-08 16:39:29 +00:00
|
|
|
{
|
|
|
|
HRESULT hres;
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
if(data->func_cnt && data->funcs[data->func_cnt-1].id == desc->memid)
|
2008-07-08 16:39:29 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if(data->func_cnt == *size)
|
|
|
|
data->funcs = heap_realloc(data->funcs, (*size <<= 1)*sizeof(func_info_t));
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
hres = ITypeInfo_GetDocumentation(dti, desc->memid, &data->funcs[data->func_cnt].name, NULL, NULL, NULL);
|
2008-07-08 16:39:29 +00:00
|
|
|
if(FAILED(hres))
|
|
|
|
return;
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
data->funcs[data->func_cnt].id = desc->memid;
|
2008-07-08 16:39:29 +00:00
|
|
|
data->funcs[data->func_cnt].tid = tid;
|
2009-11-15 23:44:20 +00:00
|
|
|
data->funcs[data->func_cnt].func_disp_idx = (desc->invkind & DISPATCH_METHOD) ? data->func_disp_cnt++ : -1;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
data->func_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dispid_cmp(const void *p1, const void *p2)
|
|
|
|
{
|
2010-04-20 17:16:57 +00:00
|
|
|
return ((const func_info_t*)p1)->id - ((const func_info_t*)p2)->id;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int func_name_cmp(const void *p1, const void *p2)
|
|
|
|
{
|
2010-04-20 17:16:57 +00:00
|
|
|
return strcmpiW((*(func_info_t* const*)p1)->name, (*(func_info_t* const*)p2)->name);
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static dispex_data_t *preprocess_dispex_data(DispatchEx *This)
|
|
|
|
{
|
|
|
|
const tid_t *tid = This->data->iface_tids;
|
|
|
|
FUNCDESC *funcdesc;
|
|
|
|
dispex_data_t *data;
|
|
|
|
DWORD size = 16, i;
|
|
|
|
ITypeInfo *ti, *dti;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
TRACE("(%p)\n", This);
|
|
|
|
|
2009-10-20 19:28:02 +00:00
|
|
|
if(This->data->disp_tid) {
|
|
|
|
hres = get_typeinfo(This->data->disp_tid, &dti);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
ERR("Could not get disp type info: %08x\n", hres);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
data = heap_alloc(sizeof(dispex_data_t));
|
|
|
|
data->func_cnt = 0;
|
2009-09-05 15:02:49 +00:00
|
|
|
data->func_disp_cnt = 0;
|
2008-07-08 16:39:29 +00:00
|
|
|
data->funcs = heap_alloc(size*sizeof(func_info_t));
|
|
|
|
list_add_tail(&dispex_data_list, &data->entry);
|
|
|
|
|
|
|
|
while(*tid) {
|
|
|
|
hres = get_typeinfo(*tid, &ti);
|
|
|
|
if(FAILED(hres))
|
|
|
|
break;
|
|
|
|
|
|
|
|
i=7;
|
|
|
|
while(1) {
|
|
|
|
hres = ITypeInfo_GetFuncDesc(ti, i++, &funcdesc);
|
|
|
|
if(FAILED(hres))
|
|
|
|
break;
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
add_func_info(data, &size, *tid, funcdesc, dti);
|
2008-07-08 16:39:29 +00:00
|
|
|
ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
|
|
|
|
}
|
|
|
|
|
|
|
|
tid++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!data->func_cnt) {
|
|
|
|
heap_free(data->funcs);
|
|
|
|
data->funcs = NULL;
|
|
|
|
}else if(data->func_cnt != size) {
|
|
|
|
data->funcs = heap_realloc(data->funcs, data->func_cnt * sizeof(func_info_t));
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(data->funcs, data->func_cnt, sizeof(func_info_t), dispid_cmp);
|
|
|
|
|
|
|
|
if(data->funcs) {
|
|
|
|
data->name_table = heap_alloc(data->func_cnt * sizeof(func_info_t*));
|
|
|
|
for(i=0; i < data->func_cnt; i++)
|
|
|
|
data->name_table[i] = data->funcs+i;
|
|
|
|
qsort(data->name_table, data->func_cnt, sizeof(func_info_t*), func_name_cmp);
|
|
|
|
}else {
|
|
|
|
data->name_table = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:27:14 +00:00
|
|
|
static int id_cmp(const void *p1, const void *p2)
|
|
|
|
{
|
2010-04-20 17:16:57 +00:00
|
|
|
return *(const DISPID*)p1 - *(const DISPID*)p2;
|
2010-01-18 16:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT get_dispids(tid_t tid, DWORD *ret_size, DISPID **ret)
|
|
|
|
{
|
|
|
|
unsigned i, func_cnt;
|
|
|
|
FUNCDESC *funcdesc;
|
|
|
|
ITypeInfo *ti;
|
|
|
|
TYPEATTR *attr;
|
|
|
|
DISPID *ids;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
hres = get_typeinfo(tid, &ti);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
hres = ITypeInfo_GetTypeAttr(ti, &attr);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
ITypeInfo_Release(ti);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
func_cnt = attr->cFuncs;
|
|
|
|
ITypeInfo_ReleaseTypeAttr(ti, attr);
|
|
|
|
|
|
|
|
ids = heap_alloc(func_cnt*sizeof(DISPID));
|
|
|
|
if(!ids) {
|
|
|
|
ITypeInfo_Release(ti);
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i=0; i < func_cnt; i++) {
|
|
|
|
hres = ITypeInfo_GetFuncDesc(ti, i, &funcdesc);
|
|
|
|
if(FAILED(hres))
|
|
|
|
break;
|
|
|
|
|
|
|
|
ids[i] = funcdesc->memid;
|
|
|
|
ITypeInfo_ReleaseFuncDesc(ti, funcdesc);
|
|
|
|
}
|
|
|
|
|
|
|
|
ITypeInfo_Release(ti);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
heap_free(ids);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(ids, func_cnt, sizeof(DISPID), id_cmp);
|
|
|
|
|
|
|
|
*ret_size = func_cnt;
|
|
|
|
*ret = ids;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2008-07-08 16:39:29 +00:00
|
|
|
static CRITICAL_SECTION cs_dispex_static_data;
|
|
|
|
static CRITICAL_SECTION_DEBUG cs_dispex_static_data_dbg =
|
|
|
|
{
|
|
|
|
0, 0, &cs_dispex_static_data,
|
|
|
|
{ &cs_dispex_static_data_dbg.ProcessLocksList, &cs_dispex_static_data_dbg.ProcessLocksList },
|
|
|
|
0, 0, { (DWORD_PTR)(__FILE__ ": dispex_static_data") }
|
|
|
|
};
|
|
|
|
static CRITICAL_SECTION cs_dispex_static_data = { &cs_dispex_static_data_dbg, -1, 0, 0, 0, 0 };
|
|
|
|
|
|
|
|
|
|
|
|
static dispex_data_t *get_dispex_data(DispatchEx *This)
|
|
|
|
{
|
|
|
|
if(This->data->data)
|
|
|
|
return This->data->data;
|
|
|
|
|
|
|
|
EnterCriticalSection(&cs_dispex_static_data);
|
|
|
|
|
|
|
|
if(!This->data->data)
|
|
|
|
This->data->data = preprocess_dispex_data(This);
|
|
|
|
|
|
|
|
LeaveCriticalSection(&cs_dispex_static_data);
|
|
|
|
|
|
|
|
return This->data->data;
|
|
|
|
}
|
|
|
|
|
2009-10-20 19:28:02 +00:00
|
|
|
HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp)
|
2008-07-08 16:39:29 +00:00
|
|
|
{
|
|
|
|
EXCEPINFO ei;
|
|
|
|
IDispatchEx *dispex;
|
|
|
|
VARIANT res;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
VariantInit(&res);
|
|
|
|
memset(&ei, 0, sizeof(ei));
|
|
|
|
|
2010-03-02 14:08:15 +00:00
|
|
|
hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
|
|
|
|
if(SUCCEEDED(hres)) {
|
|
|
|
hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, &res, &ei, NULL);
|
|
|
|
IDispatchEx_Release(dispex);
|
|
|
|
}else {
|
|
|
|
TRACE("Could not get IDispatchEx interface: %08x\n", hres);
|
|
|
|
hres = IDispatch_Invoke(disp, 0, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD,
|
|
|
|
dp, &res, &ei, NULL);
|
|
|
|
}
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
VariantClear(&res);
|
2009-10-20 19:28:02 +00:00
|
|
|
return hres;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline BOOL is_custom_dispid(DISPID id)
|
|
|
|
{
|
|
|
|
return MSHTML_DISPID_CUSTOM_MIN <= id && id <= MSHTML_DISPID_CUSTOM_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline BOOL is_dynamic_dispid(DISPID id)
|
|
|
|
{
|
|
|
|
return DISPID_DYNPROP_0 <= id && id <= DISPID_DYNPROP_MAX;
|
|
|
|
}
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
static inline dispex_dynamic_data_t *get_dynamic_data(DispatchEx *This, BOOL alloc)
|
|
|
|
{
|
|
|
|
return !alloc || This->dynamic_data
|
|
|
|
? This->dynamic_data
|
|
|
|
: (This->dynamic_data = heap_alloc_zero(sizeof(dispex_dynamic_data_t)));
|
|
|
|
}
|
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
static HRESULT get_dynamic_prop(DispatchEx *This, const WCHAR *name, DWORD flags, dynamic_prop_t **ret)
|
2009-02-04 14:35:06 +00:00
|
|
|
{
|
2009-11-15 23:44:20 +00:00
|
|
|
const BOOL alloc = flags & fdexNameEnsure;
|
|
|
|
dispex_dynamic_data_t *data;
|
2010-04-20 17:16:57 +00:00
|
|
|
dynamic_prop_t *prop;
|
2009-02-04 14:35:06 +00:00
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
data = get_dynamic_data(This, alloc);
|
2009-09-05 15:02:49 +00:00
|
|
|
if(!data) {
|
|
|
|
if(alloc)
|
|
|
|
return E_OUTOFMEMORY;
|
2009-02-04 14:35:06 +00:00
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
TRACE("not found %s\n", debugstr_w(name));
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
2009-02-04 14:35:06 +00:00
|
|
|
}
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
for(prop = data->props; prop < data->props+data->prop_cnt; prop++) {
|
|
|
|
if(flags & fdexNameCaseInsensitive ? !strcmpiW(prop->name, name) : !strcmpW(prop->name, name)) {
|
|
|
|
if(prop->flags & DYNPROP_DELETED) {
|
|
|
|
if(!alloc)
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
prop->flags &= ~DYNPROP_DELETED;
|
|
|
|
}
|
|
|
|
*ret = prop;
|
2009-09-05 15:02:49 +00:00
|
|
|
return S_OK;
|
2009-02-04 14:35:06 +00:00
|
|
|
}
|
2009-09-05 15:02:49 +00:00
|
|
|
}
|
2009-02-04 14:35:06 +00:00
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
if(!alloc)
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
TRACE("creating dynamic prop %s\n", debugstr_w(name));
|
2009-02-04 14:35:06 +00:00
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
if(!data->buf_size) {
|
|
|
|
data->props = heap_alloc(sizeof(dynamic_prop_t)*4);
|
|
|
|
if(!data->props)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
data->buf_size = 4;
|
|
|
|
}else if(data->buf_size == data->prop_cnt) {
|
|
|
|
dynamic_prop_t *new_props;
|
2009-02-04 14:35:06 +00:00
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
new_props = heap_realloc(data->props, sizeof(dynamic_prop_t)*(data->buf_size<<1));
|
|
|
|
if(!new_props)
|
|
|
|
return E_OUTOFMEMORY;
|
2009-02-04 14:35:06 +00:00
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
data->props = new_props;
|
|
|
|
data->buf_size <<= 1;
|
2009-02-04 14:35:06 +00:00
|
|
|
}
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
prop = data->props + data->prop_cnt;
|
2009-09-05 15:02:49 +00:00
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
prop->name = heap_strdupW(name);
|
|
|
|
if(!prop->name)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
VariantInit(&prop->var);
|
|
|
|
prop->flags = 0;
|
|
|
|
data->prop_cnt++;
|
|
|
|
*ret = prop;
|
2009-09-05 15:02:49 +00:00
|
|
|
return S_OK;
|
2009-02-04 14:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT dispex_get_dprop_ref(DispatchEx *This, const WCHAR *name, BOOL alloc, VARIANT **ret)
|
|
|
|
{
|
|
|
|
dynamic_prop_t *prop;
|
|
|
|
HRESULT hres;
|
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
hres = get_dynamic_prop(This, name, alloc ? fdexNameEnsure : 0, &prop);
|
2009-02-04 14:35:06 +00:00
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
*ret = &prop->var;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
static HRESULT dispex_value(DispatchEx *This, LCID lcid, WORD flags, DISPPARAMS *params,
|
|
|
|
VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
|
|
|
|
{
|
|
|
|
if(This->data->vtbl && This->data->vtbl->value)
|
|
|
|
return This->data->vtbl->value(This->outer, lcid, flags, params, res, ei, caller);
|
|
|
|
|
|
|
|
switch(flags) {
|
|
|
|
case DISPATCH_PROPERTYGET:
|
|
|
|
V_VT(res) = VT_BSTR;
|
|
|
|
V_BSTR(res) = SysAllocString(objectW);
|
|
|
|
if(!V_BSTR(res))
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FIXME("Unimplemented flags %x\n", flags);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
|
|
|
|
EXCEPINFO *ei)
|
|
|
|
{
|
|
|
|
ITypeInfo *ti;
|
|
|
|
IUnknown *unk;
|
|
|
|
UINT argerr=0;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
hres = get_typeinfo(func->tid, &ti);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
ERR("Could not get type info: %08x\n", hres);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
hres = IUnknown_QueryInterface(This->outer, tid_ids[func->tid], (void**)&unk);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
ERR("Could not get iface %s: %08x\n", debugstr_guid(tid_ids[func->tid]), hres);
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
hres = ITypeInfo_Invoke(ti, unk, func->id, flags, dp, res, ei, &argerr);
|
|
|
|
|
|
|
|
IUnknown_Release(unk);
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FUNCTION_THIS(iface) DEFINE_THIS(func_disp_t, IUnknown, iface)
|
|
|
|
|
|
|
|
static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
|
|
|
|
{
|
|
|
|
func_disp_t *This = FUNCTION_THIS(iface);
|
|
|
|
|
|
|
|
if(IsEqualGUID(&IID_IUnknown, riid)) {
|
|
|
|
TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
|
|
|
|
*ppv = FUNCUNKNOWN(This);
|
|
|
|
}else if(dispex_query_interface(&This->dispex, riid, ppv)) {
|
|
|
|
return *ppv ? S_OK : E_NOINTERFACE;
|
|
|
|
}else {
|
|
|
|
*ppv = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown*)*ppv);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Function_AddRef(IUnknown *iface)
|
|
|
|
{
|
|
|
|
func_disp_t *This = FUNCTION_THIS(iface);
|
|
|
|
|
|
|
|
TRACE("(%p)\n", This);
|
|
|
|
|
|
|
|
return IDispatchEx_AddRef(DISPATCHEX(This->obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Function_Release(IUnknown *iface)
|
|
|
|
{
|
|
|
|
func_disp_t *This = FUNCTION_THIS(iface);
|
|
|
|
|
|
|
|
TRACE("(%p)\n", This);
|
|
|
|
|
|
|
|
return IDispatchEx_Release(DISPATCHEX(This->obj));
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT function_value(IUnknown *iface, LCID lcid, WORD flags, DISPPARAMS *params,
|
|
|
|
VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
|
|
|
|
{
|
|
|
|
func_disp_t *This = FUNCTION_THIS(iface);
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
switch(flags) {
|
2009-11-15 23:44:20 +00:00
|
|
|
case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
|
|
|
|
if(!res)
|
|
|
|
return E_INVALIDARG;
|
2009-09-05 15:02:49 +00:00
|
|
|
case DISPATCH_METHOD:
|
|
|
|
hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FIXME("Unimplemented flags %x\n", flags);
|
|
|
|
hres = E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef FUNCTION_THIS
|
|
|
|
|
|
|
|
static const IUnknownVtbl FunctionUnkVtbl = {
|
|
|
|
Function_QueryInterface,
|
|
|
|
Function_AddRef,
|
|
|
|
Function_Release
|
|
|
|
};
|
|
|
|
|
|
|
|
static const dispex_static_data_vtbl_t function_dispex_vtbl = {
|
|
|
|
function_value,
|
|
|
|
NULL,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2009-10-20 19:28:02 +00:00
|
|
|
static const tid_t function_iface_tids[] = {0};
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
static dispex_static_data_t function_dispex = {
|
|
|
|
&function_dispex_vtbl,
|
2009-10-20 19:28:02 +00:00
|
|
|
NULL_tid,
|
2009-09-05 15:02:49 +00:00
|
|
|
NULL,
|
2009-10-20 19:28:02 +00:00
|
|
|
function_iface_tids
|
2009-09-05 15:02:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info)
|
|
|
|
{
|
|
|
|
func_disp_t *ret;
|
|
|
|
|
|
|
|
ret = heap_alloc_zero(sizeof(func_disp_t));
|
|
|
|
if(!ret)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret->lpIUnknownVtbl = &FunctionUnkVtbl;
|
|
|
|
init_dispex(&ret->dispex, FUNCUNKNOWN(ret), &function_dispex);
|
|
|
|
ret->obj = obj;
|
|
|
|
ret->info = info;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res,
|
|
|
|
EXCEPINFO *ei)
|
|
|
|
{
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
switch(flags) {
|
2009-11-15 23:44:20 +00:00
|
|
|
case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
|
|
|
|
if(!res)
|
|
|
|
return E_INVALIDARG;
|
2009-09-05 15:02:49 +00:00
|
|
|
case DISPATCH_METHOD:
|
|
|
|
hres = typeinfo_invoke(This, func, flags, dp, res, ei);
|
|
|
|
break;
|
|
|
|
case DISPATCH_PROPERTYGET: {
|
|
|
|
dispex_dynamic_data_t *dynamic_data;
|
|
|
|
|
2009-10-20 19:28:02 +00:00
|
|
|
if(func->id == DISPID_VALUE) {
|
|
|
|
BSTR ret;
|
|
|
|
|
|
|
|
ret = SysAllocString(objectW);
|
|
|
|
if(!ret)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
V_VT(res) = VT_BSTR;
|
|
|
|
V_BSTR(res) = ret;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
dynamic_data = get_dynamic_data(This, TRUE);
|
|
|
|
if(!dynamic_data)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
if(!dynamic_data->func_disps) {
|
|
|
|
dynamic_data->func_disps = heap_alloc_zero(This->data->data->func_disp_cnt * sizeof(func_disp_t*));
|
|
|
|
if(!dynamic_data->func_disps)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!dynamic_data->func_disps[func->func_disp_idx]) {
|
|
|
|
dynamic_data->func_disps[func->func_disp_idx] = create_func_disp(This, func);
|
|
|
|
if(!dynamic_data->func_disps[func->func_disp_idx])
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
V_VT(res) = VT_DISPATCH;
|
|
|
|
V_DISPATCH(res) = (IDispatch*)DISPATCHEX(&dynamic_data->func_disps[func->func_disp_idx]->dispex);
|
|
|
|
IDispatch_AddRef(V_DISPATCH(res));
|
|
|
|
hres = S_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
FIXME("Unimplemented flags %x\n", flags);
|
|
|
|
case DISPATCH_PROPERTYPUT:
|
|
|
|
hres = E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
2009-10-20 19:28:02 +00:00
|
|
|
static HRESULT get_builtin_func(dispex_data_t *data, DISPID id, func_info_t **ret)
|
|
|
|
{
|
|
|
|
int min, max, n;
|
|
|
|
|
|
|
|
min = 0;
|
|
|
|
max = data->func_cnt-1;
|
|
|
|
|
|
|
|
while(min <= max) {
|
|
|
|
n = (min+max)/2;
|
|
|
|
|
|
|
|
if(data->funcs[n].id == id) {
|
|
|
|
*ret = data->funcs+n;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data->funcs[n].id < id)
|
|
|
|
min = n+1;
|
|
|
|
else
|
|
|
|
max = n-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN("invalid id %x\n", id);
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
}
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
static HRESULT get_builtin_id(DispatchEx *This, BSTR name, DWORD grfdex, DISPID *ret)
|
|
|
|
{
|
|
|
|
dispex_data_t *data;
|
|
|
|
int min, max, n, c;
|
|
|
|
|
|
|
|
data = get_dispex_data(This);
|
|
|
|
if(!data)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
min = 0;
|
|
|
|
max = data->func_cnt-1;
|
|
|
|
|
|
|
|
while(min <= max) {
|
|
|
|
n = (min+max)/2;
|
|
|
|
|
|
|
|
c = strcmpiW(data->name_table[n]->name, name);
|
|
|
|
if(!c) {
|
|
|
|
if((grfdex & fdexNameCaseSensitive) && strcmpW(data->name_table[n]->name, name))
|
|
|
|
break;
|
|
|
|
|
|
|
|
*ret = data->name_table[n]->id;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(c > 0)
|
|
|
|
max = n-1;
|
|
|
|
else
|
|
|
|
min = n+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(This->data->vtbl && This->data->vtbl->get_dispid) {
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
hres = This->data->vtbl->get_dispid(This->outer, name, grfdex, ret);
|
|
|
|
if(hres != DISP_E_UNKNOWNNAME)
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp,
|
|
|
|
VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller)
|
|
|
|
{
|
|
|
|
dispex_data_t *data;
|
|
|
|
func_info_t *func;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
data = get_dispex_data(This);
|
|
|
|
if(!data)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
hres = get_builtin_func(data, id, &func);
|
|
|
|
if(id == DISPID_VALUE && hres == DISP_E_UNKNOWNNAME)
|
|
|
|
return dispex_value(This, lcid, flags, dp, res, ei, caller);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
if(func->func_disp_idx == -1)
|
|
|
|
hres = typeinfo_invoke(This, func, flags, dp, res, ei);
|
|
|
|
else
|
|
|
|
hres = function_invoke(This, func, flags, dp, res, ei);
|
|
|
|
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT remove_prop(DispatchEx *This, BSTR name, VARIANT_BOOL *success)
|
|
|
|
{
|
|
|
|
dynamic_prop_t *prop;
|
|
|
|
DISPID id;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
hres = get_builtin_id(This, name, 0, &id);
|
|
|
|
if(hres == S_OK) {
|
|
|
|
DISPID named_id = DISPID_PROPERTYPUT;
|
|
|
|
VARIANT var;
|
|
|
|
DISPPARAMS dp = {&var,&named_id,1,1};
|
|
|
|
EXCEPINFO ei;
|
|
|
|
|
|
|
|
V_VT(&var) = VT_EMPTY;
|
|
|
|
memset(&ei, 0, sizeof(ei));
|
|
|
|
hres = invoke_builtin_prop(This, id, 0, DISPATCH_PROPERTYPUT, &dp, NULL, &ei, NULL);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
*success = VARIANT_TRUE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
hres = get_dynamic_prop(This, name, 0, &prop);
|
|
|
|
if(FAILED(hres)) {
|
|
|
|
if(hres != DISP_E_UNKNOWNNAME)
|
|
|
|
return hres;
|
|
|
|
*success = VARIANT_FALSE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
VariantClear(&prop->var);
|
|
|
|
prop->flags |= DYNPROP_DELETED;
|
|
|
|
*success = VARIANT_TRUE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2008-07-08 16:39:29 +00:00
|
|
|
#define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface)
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
|
|
|
|
return IUnknown_QueryInterface(This->outer, riid, ppv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
|
|
|
|
return IUnknown_AddRef(This->outer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
|
|
|
|
return IUnknown_Release(This->outer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
|
|
|
|
TRACE("(%p)->(%p)\n", This, pctinfo);
|
|
|
|
|
|
|
|
*pctinfo = 1;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo,
|
|
|
|
LCID lcid, ITypeInfo **ppTInfo)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
|
|
|
|
|
|
|
|
hres = get_typeinfo(This->data->disp_tid, ppTInfo);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
ITypeInfo_AddRef(*ppTInfo);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid,
|
|
|
|
LPOLESTR *rgszNames, UINT cNames,
|
|
|
|
LCID lcid, DISPID *rgDispId)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
UINT i;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
|
|
|
|
lcid, rgDispId);
|
|
|
|
|
|
|
|
for(i=0; i < cNames; i++) {
|
|
|
|
hres = IDispatchEx_GetDispID(DISPATCHEX(This), rgszNames[i], 0, rgDispId+i);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember,
|
|
|
|
REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
|
|
|
|
VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
|
|
|
|
TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
|
|
|
|
lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
|
|
|
|
|
|
|
|
return IDispatchEx_InvokeEx(DISPATCHEX(This), dispIdMember, lcid, wFlags,
|
|
|
|
pDispParams, pVarResult, pExcepInfo, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
2009-02-04 14:35:06 +00:00
|
|
|
dynamic_prop_t *dprop;
|
|
|
|
HRESULT hres;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid);
|
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
if(grfdex & ~(fdexNameCaseSensitive|fdexNameCaseInsensitive|fdexNameEnsure|fdexNameImplicit|FDEX_VERSION_MASK))
|
2008-07-08 16:39:29 +00:00
|
|
|
FIXME("Unsupported grfdex %x\n", grfdex);
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
hres = get_builtin_id(This, bstrName, grfdex, pid);
|
|
|
|
if(hres != DISP_E_UNKNOWNNAME)
|
|
|
|
return hres;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
2009-11-15 23:44:20 +00:00
|
|
|
hres = get_dynamic_prop(This, bstrName, grfdex, &dprop);
|
2009-02-04 14:35:06 +00:00
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
2009-02-04 14:35:06 +00:00
|
|
|
*pid = DISPID_DYNPROP_0 + (dprop - This->dynamic_data->props);
|
|
|
|
return S_OK;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
|
|
|
|
VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
|
|
|
|
|
|
|
|
if(is_custom_dispid(id) && This->data->vtbl && This->data->vtbl->invoke)
|
|
|
|
return This->data->vtbl->invoke(This->outer, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
|
|
|
|
|
|
|
|
if(wFlags == DISPATCH_CONSTRUCT) {
|
2009-11-15 23:44:20 +00:00
|
|
|
if(id == DISPID_VALUE) {
|
|
|
|
if(This->data->vtbl && This->data->vtbl->value) {
|
|
|
|
return This->data->vtbl->value(This->outer, lcid, wFlags, pdp,
|
|
|
|
pvarRes, pei, pspCaller);
|
|
|
|
}
|
|
|
|
FIXME("DISPATCH_CONSTRUCT flag but missing value function\n");
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
FIXME("DISPATCH_CONSTRUCT flag without DISPID_VALUE\n");
|
|
|
|
return E_FAIL;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(is_dynamic_dispid(id)) {
|
|
|
|
DWORD idx = id - DISPID_DYNPROP_0;
|
2010-04-20 17:16:57 +00:00
|
|
|
dynamic_prop_t *prop;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
prop = This->dynamic_data->props+idx;
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
switch(wFlags) {
|
2009-11-15 23:44:20 +00:00
|
|
|
case DISPATCH_METHOD|DISPATCH_PROPERTYGET:
|
|
|
|
if(!pvarRes)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
case DISPATCH_METHOD: {
|
2009-02-04 14:35:06 +00:00
|
|
|
DISPID named_arg = DISPID_THIS;
|
|
|
|
DISPPARAMS dp = {NULL, &named_arg, 0, 1};
|
|
|
|
IDispatchEx *dispex;
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
if(V_VT(&prop->var) != VT_DISPATCH) {
|
|
|
|
FIXME("invoke %s\n", debugstr_variant(&prop->var));
|
2009-02-04 14:35:06 +00:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pdp->cNamedArgs) {
|
|
|
|
FIXME("named args not supported\n");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dp.rgvarg = heap_alloc((pdp->cArgs+1)*sizeof(VARIANTARG));
|
|
|
|
if(!dp.rgvarg)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
dp.cArgs = pdp->cArgs+1;
|
|
|
|
memcpy(dp.rgvarg+1, pdp->rgvarg, pdp->cArgs*sizeof(VARIANTARG));
|
|
|
|
|
|
|
|
V_VT(dp.rgvarg) = VT_DISPATCH;
|
|
|
|
V_DISPATCH(dp.rgvarg) = (IDispatch*)DISPATCHEX(This);
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
hres = IDispatch_QueryInterface(V_DISPATCH(&prop->var), &IID_IDispatchEx, (void**)&dispex);
|
2009-02-04 14:35:06 +00:00
|
|
|
TRACE("%s call\n", debugstr_w(This->dynamic_data->props[idx].name));
|
|
|
|
if(SUCCEEDED(hres)) {
|
|
|
|
hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, wFlags, &dp, pvarRes, pei, pspCaller);
|
|
|
|
IDispatchEx_Release(dispex);
|
|
|
|
}else {
|
|
|
|
ULONG err = 0;
|
2010-04-20 17:16:57 +00:00
|
|
|
hres = IDispatch_Invoke(V_DISPATCH(&prop->var), DISPID_VALUE, &IID_NULL, lcid, wFlags, pdp, pvarRes, pei, &err);
|
2009-02-04 14:35:06 +00:00
|
|
|
}
|
|
|
|
TRACE("%s ret %08x\n", debugstr_w(This->dynamic_data->props[idx].name), hres);
|
|
|
|
|
|
|
|
heap_free(dp.rgvarg);
|
|
|
|
return hres;
|
|
|
|
}
|
2009-11-15 23:44:20 +00:00
|
|
|
case DISPATCH_PROPERTYGET:
|
2010-04-20 17:16:57 +00:00
|
|
|
if(prop->flags & DYNPROP_DELETED)
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
return VariantCopy(pvarRes, &prop->var);
|
2009-11-15 23:44:20 +00:00
|
|
|
case DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF:
|
|
|
|
case DISPATCH_PROPERTYPUT:
|
|
|
|
if(pdp->cArgs != 1 || (pdp->cNamedArgs == 1 && *pdp->rgdispidNamedArgs != DISPID_PROPERTYPUT)
|
|
|
|
|| pdp->cNamedArgs > 1) {
|
|
|
|
FIXME("invalid args\n");
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("put %s\n", debugstr_variant(pdp->rgvarg));
|
2010-04-20 17:16:57 +00:00
|
|
|
VariantClear(&prop->var);
|
|
|
|
hres = VariantCopy(&prop->var, pdp->rgvarg);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
prop->flags &= ~DYNPROP_DELETED;
|
|
|
|
return S_OK;
|
2008-07-08 16:39:29 +00:00
|
|
|
default:
|
|
|
|
FIXME("unhandled wFlags %x\n", wFlags);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
return invoke_builtin_prop(This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
2010-03-02 14:08:15 +00:00
|
|
|
|
|
|
|
TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex);
|
|
|
|
|
|
|
|
/* Not implemented by IE */
|
2008-07-08 16:39:29 +00:00
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
FIXME("(%p)->(%x)\n", This, id);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
2009-10-20 19:28:02 +00:00
|
|
|
dispex_data_t *data;
|
|
|
|
func_info_t *func;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
TRACE("(%p)->(%x %p)\n", This, id, pbstrName);
|
|
|
|
|
|
|
|
if(is_dynamic_dispid(id)) {
|
|
|
|
DWORD idx = id - DISPID_DYNPROP_0;
|
|
|
|
|
|
|
|
if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
|
|
|
|
*pbstrName = SysAllocString(This->dynamic_data->props[idx].name);
|
|
|
|
if(!*pbstrName)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = get_dispex_data(This);
|
|
|
|
if(!data)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
hres = get_builtin_func(data, id, &func);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
|
|
|
|
*pbstrName = SysAllocString(func->name);
|
|
|
|
if(!*pbstrName)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
2009-10-20 19:28:02 +00:00
|
|
|
dispex_data_t *data;
|
|
|
|
func_info_t *func;
|
|
|
|
HRESULT hres;
|
|
|
|
|
|
|
|
TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid);
|
|
|
|
|
|
|
|
if(is_dynamic_dispid(id)) {
|
|
|
|
DWORD idx = id - DISPID_DYNPROP_0;
|
|
|
|
|
|
|
|
if(!This->dynamic_data || This->dynamic_data->prop_cnt <= idx)
|
|
|
|
return DISP_E_UNKNOWNNAME;
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
while(++idx < This->dynamic_data->prop_cnt && This->dynamic_data->props[idx].flags & DYNPROP_DELETED);
|
|
|
|
|
|
|
|
if(idx == This->dynamic_data->prop_cnt) {
|
2009-10-20 19:28:02 +00:00
|
|
|
*pid = DISPID_STARTENUM;
|
|
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
|
2010-04-20 17:16:57 +00:00
|
|
|
*pid = DISPID_DYNPROP_0+idx;
|
2009-10-20 19:28:02 +00:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = get_dispex_data(This);
|
|
|
|
if(!data)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
if(id == DISPID_STARTENUM) {
|
|
|
|
func = data->funcs;
|
|
|
|
}else {
|
|
|
|
hres = get_builtin_func(data, id, &func);
|
|
|
|
if(FAILED(hres))
|
|
|
|
return hres;
|
|
|
|
func++;
|
|
|
|
}
|
|
|
|
|
|
|
|
while(func < data->funcs+data->func_cnt) {
|
|
|
|
/* FIXME: Skip hidden properties */
|
|
|
|
if(func->func_disp_idx == -1) {
|
|
|
|
*pid = func->id;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
func++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(This->dynamic_data && This->dynamic_data->prop_cnt) {
|
|
|
|
*pid = DISPID_DYNPROP_0;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pid = DISPID_STARTENUM;
|
|
|
|
return S_FALSE;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk)
|
|
|
|
{
|
|
|
|
DispatchEx *This = DISPATCHEX_THIS(iface);
|
|
|
|
FIXME("(%p)->(%p)\n", This, ppunk);
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef DISPATCHEX_THIS
|
|
|
|
|
|
|
|
static IDispatchExVtbl DispatchExVtbl = {
|
|
|
|
DispatchEx_QueryInterface,
|
|
|
|
DispatchEx_AddRef,
|
|
|
|
DispatchEx_Release,
|
|
|
|
DispatchEx_GetTypeInfoCount,
|
|
|
|
DispatchEx_GetTypeInfo,
|
|
|
|
DispatchEx_GetIDsOfNames,
|
|
|
|
DispatchEx_Invoke,
|
|
|
|
DispatchEx_GetDispID,
|
|
|
|
DispatchEx_InvokeEx,
|
|
|
|
DispatchEx_DeleteMemberByName,
|
|
|
|
DispatchEx_DeleteMemberByDispID,
|
|
|
|
DispatchEx_GetMemberProperties,
|
|
|
|
DispatchEx_GetMemberName,
|
|
|
|
DispatchEx_GetNextDispID,
|
|
|
|
DispatchEx_GetNameSpaceParent
|
|
|
|
};
|
|
|
|
|
|
|
|
BOOL dispex_query_interface(DispatchEx *This, REFIID riid, void **ppv)
|
|
|
|
{
|
|
|
|
static const IID IID_UndocumentedScriptIface =
|
|
|
|
{0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa0}};
|
2009-02-04 14:35:06 +00:00
|
|
|
static const IID IID_IDispatchJS =
|
|
|
|
{0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}};
|
2008-07-08 16:39:29 +00:00
|
|
|
|
|
|
|
if(IsEqualGUID(&IID_IDispatch, riid)) {
|
|
|
|
TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv);
|
|
|
|
*ppv = DISPATCHEX(This);
|
|
|
|
}else if(IsEqualGUID(&IID_IDispatchEx, riid)) {
|
|
|
|
TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv);
|
|
|
|
*ppv = DISPATCHEX(This);
|
2009-02-04 14:35:06 +00:00
|
|
|
}else if(IsEqualGUID(&IID_IDispatchJS, riid)) {
|
|
|
|
TRACE("(%p)->(IID_IDispatchJS %p) returning NULL\n", This, ppv);
|
|
|
|
*ppv = NULL;
|
2008-07-08 16:39:29 +00:00
|
|
|
}else if(IsEqualGUID(&IID_UndocumentedScriptIface, riid)) {
|
|
|
|
TRACE("(%p)->(IID_UndocumentedScriptIface %p) returning NULL\n", This, ppv);
|
|
|
|
*ppv = NULL;
|
|
|
|
}else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(*ppv)
|
|
|
|
IUnknown_AddRef((IUnknown*)*ppv);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-09-05 15:02:49 +00:00
|
|
|
void release_dispex(DispatchEx *This)
|
|
|
|
{
|
|
|
|
dynamic_prop_t *prop;
|
|
|
|
|
|
|
|
if(!This->dynamic_data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for(prop = This->dynamic_data->props; prop < This->dynamic_data->props + This->dynamic_data->prop_cnt; prop++) {
|
|
|
|
VariantClear(&prop->var);
|
|
|
|
heap_free(prop->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_free(This->dynamic_data->props);
|
|
|
|
|
|
|
|
if(This->dynamic_data->func_disps) {
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for(i=0; i < This->data->data->func_disp_cnt; i++) {
|
|
|
|
if(This->dynamic_data->func_disps[i]) {
|
|
|
|
release_dispex(&This->dynamic_data->func_disps[i]->dispex);
|
|
|
|
heap_free(This->dynamic_data->func_disps[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_free(This->dynamic_data->func_disps);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap_free(This->dynamic_data);
|
|
|
|
}
|
|
|
|
|
2008-07-08 16:39:29 +00:00
|
|
|
void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *data)
|
|
|
|
{
|
|
|
|
dispex->lpIDispatchExVtbl = &DispatchExVtbl;
|
|
|
|
dispex->outer = outer;
|
|
|
|
dispex->data = data;
|
2009-09-05 15:02:49 +00:00
|
|
|
dispex->dynamic_data = NULL;
|
2008-07-08 16:39:29 +00:00
|
|
|
}
|