xref: /reactos/dll/win32/oleacc/main.c (revision c2c66aff)
1 /*
2  * Implementation of the OLEACC dll
3  *
4  * Copyright 2003 Mike McCormack 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 "oleacc_private.h"
22 
23 #include <commctrl.h>
24 #include <rpcproxy.h>
25 
26 #include <wine/unicode.h>
27 
28 #include "resource.h"
29 
30 static const WCHAR lresult_atom_prefix[] = {'w','i','n','e','_','o','l','e','a','c','c',':'};
31 
32 static const WCHAR menuW[] = {'#','3','2','7','6','8',0};
33 static const WCHAR desktopW[] = {'#','3','2','7','6','9',0};
34 static const WCHAR dialogW[] = {'#','3','2','7','7','0',0};
35 static const WCHAR winswitchW[] = {'#','3','2','7','7','1',0};
36 static const WCHAR mdi_clientW[] = {'M','D','I','C','l','i','e','n','t',0};
37 static const WCHAR richeditW[] = {'R','I','C','H','E','D','I','T',0};
38 static const WCHAR richedit20aW[] = {'R','i','c','h','E','d','i','t','2','0','A',0};
39 static const WCHAR richedit20wW[] = {'R','i','c','h','E','d','i','t','2','0','W',0};
40 
41 typedef HRESULT (WINAPI *accessible_create)(HWND, const IID*, void**);
42 
43 extern HRESULT WINAPI OLEACC_DllGetClassObject(REFCLSID, REFIID, void**) DECLSPEC_HIDDEN;
44 extern BOOL WINAPI OLEACC_DllMain(HINSTANCE, DWORD, void*) DECLSPEC_HIDDEN;
45 extern HRESULT WINAPI OLEACC_DllRegisterServer(void) DECLSPEC_HIDDEN;
46 extern HRESULT WINAPI OLEACC_DllUnregisterServer(void) DECLSPEC_HIDDEN;
47 
48 static struct {
49     const WCHAR *name;
50     DWORD idx;
51     accessible_create create_client;
52     accessible_create create_window;
53 } builtin_classes[] = {
54     {WC_LISTBOXW,           0x10000, NULL, NULL},
55     {menuW,                 0x10001, NULL, NULL},
56     {WC_BUTTONW,            0x10002, NULL, NULL},
57     {WC_STATICW,            0x10003, NULL, NULL},
58     {WC_EDITW,              0x10004, NULL, NULL},
59     {WC_COMBOBOXW,          0x10005, NULL, NULL},
60     {dialogW,               0x10006, NULL, NULL},
61     {winswitchW,            0x10007, NULL, NULL},
62     {mdi_clientW,           0x10008, NULL, NULL},
63     {desktopW,              0x10009, NULL, NULL},
64     {WC_SCROLLBARW,         0x1000a, NULL, NULL},
65     {STATUSCLASSNAMEW,      0x1000b, NULL, NULL},
66     {TOOLBARCLASSNAMEW,     0x1000c, NULL, NULL},
67     {PROGRESS_CLASSW,       0x1000d, NULL, NULL},
68     {ANIMATE_CLASSW,        0x1000e, NULL, NULL},
69     {WC_TABCONTROLW,        0x1000f, NULL, NULL},
70     {HOTKEY_CLASSW,         0x10010, NULL, NULL},
71     {WC_HEADERW,            0x10011, NULL, NULL},
72     {TRACKBAR_CLASSW,       0x10012, NULL, NULL},
73     {WC_LISTVIEWW,          0x10013, NULL, NULL},
74     {UPDOWN_CLASSW,         0x10016, NULL, NULL},
75     {TOOLTIPS_CLASSW,       0x10018, NULL, NULL},
76     {WC_TREEVIEWW,          0x10019, NULL, NULL},
77     {MONTHCAL_CLASSW,       0,       NULL, NULL},
78     {DATETIMEPICK_CLASSW,   0,       NULL, NULL},
79     {WC_IPADDRESSW,         0,       NULL, NULL},
80     {richeditW,             0x1001c, NULL, NULL},
81     {richedit20aW,          0,       NULL, NULL},
82     {richedit20wW,          0,       NULL, NULL},
83 };
84 
85 static HINSTANCE oleacc_handle = 0;
86 
87 int convert_child_id(VARIANT *v)
88 {
89     switch(V_VT(v)) {
90     case VT_I4:
91         return V_I4(v);
92     default:
93         FIXME("unhandled child ID variant type: %d\n", V_VT(v));
94         return -1;
95     }
96 }
97 
98 static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid)
99 {
100     WCHAR class_name[64];
101     int i, idx;
102 
103     if(!RealGetWindowClassW(hwnd, class_name, sizeof(class_name)/sizeof(WCHAR)))
104         return NULL;
105     TRACE("got window class: %s\n", debugstr_w(class_name));
106 
107     for(i=0; i<sizeof(builtin_classes)/sizeof(builtin_classes[0]); i++) {
108         if(!strcmpiW(class_name, builtin_classes[i].name)) {
109             accessible_create ret;
110 
111             ret = (objid==OBJID_CLIENT ?
112                     builtin_classes[i].create_client :
113                     builtin_classes[i].create_window);
114             if(!ret)
115                 FIXME("unhandled window class: %s\n", debugstr_w(class_name));
116             return ret;
117         }
118     }
119 
120     idx = SendMessageW(hwnd, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX);
121     if(idx) {
122         for(i=0; i<sizeof(builtin_classes)/sizeof(builtin_classes[0]); i++) {
123             if(idx == builtin_classes[i].idx) {
124                 accessible_create ret;
125 
126                 ret = (objid==OBJID_CLIENT ?
127                         builtin_classes[i].create_client :
128                         builtin_classes[i].create_window);
129                 if(!ret)
130                     FIXME("unhandled class name idx: %x\n", idx);
131                 return ret;
132             }
133         }
134 
135         WARN("unhandled class name idx: %x\n", idx);
136     }
137 
138     return NULL;
139 }
140 
141 HRESULT WINAPI CreateStdAccessibleObject( HWND hwnd, LONG idObject,
142         REFIID riidInterface, void** ppvObject )
143 {
144     accessible_create create;
145 
146     TRACE("%p %d %s %p\n", hwnd, idObject,
147           debugstr_guid( riidInterface ), ppvObject );
148 
149     switch(idObject) {
150     case OBJID_CLIENT:
151         create = get_builtin_accessible_obj(hwnd, idObject);
152         if(create) return create(hwnd, riidInterface, ppvObject);
153         return create_client_object(hwnd, riidInterface, ppvObject);
154     case OBJID_WINDOW:
155         create = get_builtin_accessible_obj(hwnd, idObject);
156         if(create) return create(hwnd, riidInterface, ppvObject);
157         return create_window_object(hwnd, riidInterface, ppvObject);
158     default:
159         FIXME("unhandled object id: %d\n", idObject);
160         return E_NOTIMPL;
161     }
162 }
163 
164 HRESULT WINAPI ObjectFromLresult( LRESULT result, REFIID riid, WPARAM wParam, void **ppObject )
165 {
166     WCHAR atom_str[sizeof(lresult_atom_prefix)/sizeof(WCHAR)+3*8+3];
167     HANDLE server_proc, server_mapping, mapping;
168     DWORD proc_id, size;
169     IStream *stream;
170     HGLOBAL data;
171     void *view;
172     HRESULT hr;
173     WCHAR *p;
174 
175     TRACE("%ld %s %ld %p\n", result, debugstr_guid(riid), wParam, ppObject );
176 
177     if(wParam)
178         FIXME("unsupported wParam = %lx\n", wParam);
179 
180     if(!ppObject)
181         return E_INVALIDARG;
182     *ppObject = NULL;
183 
184     if(result != (ATOM)result)
185         return E_FAIL;
186 
187     if(!GlobalGetAtomNameW(result, atom_str, sizeof(atom_str)/sizeof(WCHAR)))
188         return E_FAIL;
189     if(memcmp(atom_str, lresult_atom_prefix, sizeof(lresult_atom_prefix)))
190         return E_FAIL;
191     p = atom_str + sizeof(lresult_atom_prefix)/sizeof(WCHAR);
192     proc_id = strtoulW(p, &p, 16);
193     if(*p != ':')
194         return E_FAIL;
195     server_mapping = ULongToHandle( strtoulW(p+1, &p, 16) );
196     if(*p != ':')
197         return E_FAIL;
198     size = strtoulW(p+1, &p, 16);
199     if(*p != 0)
200         return E_FAIL;
201 
202     server_proc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, proc_id);
203     if(!server_proc)
204         return E_FAIL;
205 
206     if(!DuplicateHandle(server_proc, server_mapping, GetCurrentProcess(), &mapping,
207                 0, FALSE, DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS))
208         return E_FAIL;
209     CloseHandle(server_proc);
210     GlobalDeleteAtom(result);
211 
212     view = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
213     CloseHandle(mapping);
214     if(!view)
215         return E_FAIL;
216 
217     data = GlobalAlloc(GMEM_FIXED, size);
218     if(!data) {
219         UnmapViewOfFile(view);
220         return E_OUTOFMEMORY;
221     }
222     memcpy(data, view, size);
223     UnmapViewOfFile(view);
224 
225     hr = CreateStreamOnHGlobal(data, TRUE, &stream);
226     if(FAILED(hr)) {
227         GlobalFree(data);
228         return hr;
229     }
230 
231     hr = CoUnmarshalInterface(stream, riid, ppObject);
232     IStream_Release(stream);
233     return hr;
234 }
235 
236 LRESULT WINAPI LresultFromObject( REFIID riid, WPARAM wParam, LPUNKNOWN pAcc )
237 {
238     static const WCHAR atom_fmt[] = {'%','0','8','x',':','%','0','8','x',':','%','0','8','x',0};
239     static const LARGE_INTEGER seek_zero = {{0}};
240 
241     WCHAR atom_str[sizeof(lresult_atom_prefix)/sizeof(WCHAR)+3*8+3];
242     IStream *stream;
243     HANDLE mapping;
244     STATSTG stat;
245     HRESULT hr;
246     ATOM atom;
247     void *view;
248 
249     TRACE("%s %ld %p\n", debugstr_guid(riid), wParam, pAcc);
250 
251     if(wParam)
252         FIXME("unsupported wParam = %lx\n", wParam);
253 
254     if(!pAcc)
255         return E_INVALIDARG;
256 
257     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
258     if(FAILED(hr))
259         return hr;
260 
261     hr = CoMarshalInterface(stream, riid, pAcc, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
262     if(FAILED(hr)) {
263         IStream_Release(stream);
264         return hr;
265     }
266 
267     hr = IStream_Seek(stream, seek_zero, STREAM_SEEK_SET, NULL);
268     if(FAILED(hr)) {
269         IStream_Release(stream);
270         return hr;
271     }
272 
273     hr = IStream_Stat(stream, &stat, STATFLAG_NONAME);
274     if(FAILED(hr)) {
275         CoReleaseMarshalData(stream);
276         IStream_Release(stream);
277         return hr;
278     }else if(stat.cbSize.u.HighPart) {
279         FIXME("stream size to big\n");
280         CoReleaseMarshalData(stream);
281         IStream_Release(stream);
282         return E_NOTIMPL;
283     }
284 
285     mapping = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
286             stat.cbSize.u.HighPart, stat.cbSize.u.LowPart, NULL);
287     if(!mapping) {
288         CoReleaseMarshalData(stream);
289         IStream_Release(stream);
290         return hr;
291     }
292 
293     view = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
294     if(!view) {
295         CloseHandle(mapping);
296         CoReleaseMarshalData(stream);
297         IStream_Release(stream);
298         return E_FAIL;
299     }
300 
301     hr = IStream_Read(stream, view, stat.cbSize.u.LowPart, NULL);
302     UnmapViewOfFile(view);
303     if(FAILED(hr)) {
304         CloseHandle(mapping);
305         hr = IStream_Seek(stream, seek_zero, STREAM_SEEK_SET, NULL);
306         if(SUCCEEDED(hr))
307             CoReleaseMarshalData(stream);
308         IStream_Release(stream);
309         return hr;
310 
311     }
312 
313     memcpy(atom_str, lresult_atom_prefix, sizeof(lresult_atom_prefix));
314     sprintfW(atom_str+sizeof(lresult_atom_prefix)/sizeof(WCHAR),
315              atom_fmt, GetCurrentProcessId(), HandleToUlong(mapping), stat.cbSize.u.LowPart);
316     atom = GlobalAddAtomW(atom_str);
317     if(!atom) {
318         CloseHandle(mapping);
319         hr = IStream_Seek(stream, seek_zero, STREAM_SEEK_SET, NULL);
320         if(SUCCEEDED(hr))
321             CoReleaseMarshalData(stream);
322         IStream_Release(stream);
323         return E_FAIL;
324     }
325 
326     IStream_Release(stream);
327     return atom;
328 }
329 
330 HRESULT WINAPI AccessibleObjectFromPoint( POINT ptScreen, IAccessible** ppacc, VARIANT* pvarChild )
331 {
332     FIXME("{%d,%d} %p %p: stub\n", ptScreen.x, ptScreen.y, ppacc, pvarChild );
333     return E_NOTIMPL;
334 }
335 
336 HRESULT WINAPI AccessibleObjectFromWindow( HWND hwnd, DWORD dwObjectID,
337                              REFIID riid, void** ppvObject )
338 {
339     TRACE("%p %d %s %p\n", hwnd, dwObjectID,
340           debugstr_guid( riid ), ppvObject );
341 
342     if(!ppvObject)
343         return E_INVALIDARG;
344     *ppvObject = NULL;
345 
346     if(IsWindow(hwnd)) {
347         LRESULT lres;
348 
349         lres = SendMessageW(hwnd, WM_GETOBJECT, 0xffffffff, dwObjectID);
350         if(FAILED(lres))
351             return lres;
352         else if(lres)
353             return ObjectFromLresult(lres, riid, 0, ppvObject);
354     }
355 
356     return CreateStdAccessibleObject(hwnd, dwObjectID, riid, ppvObject);
357 }
358 
359 HRESULT WINAPI WindowFromAccessibleObject(IAccessible *acc, HWND *phwnd)
360 {
361     IDispatch *parent;
362     IOleWindow *ow;
363     HRESULT hres;
364 
365     TRACE("%p %p\n", acc, phwnd);
366 
367     IAccessible_AddRef(acc);
368     while(1) {
369         hres = IAccessible_QueryInterface(acc, &IID_IOleWindow, (void**)&ow);
370         if(SUCCEEDED(hres)) {
371             hres = IOleWindow_GetWindow(ow, phwnd);
372             IOleWindow_Release(ow);
373             IAccessible_Release(acc);
374             return hres;
375         }
376 
377         hres = IAccessible_get_accParent(acc, &parent);
378         IAccessible_Release(acc);
379         if(FAILED(hres))
380             return hres;
381         if(hres!=S_OK || !parent) {
382             *phwnd = NULL;
383             return hres;
384         }
385 
386         hres = IDispatch_QueryInterface(parent, &IID_IAccessible, (void**)&acc);
387         IDispatch_Release(parent);
388         if(FAILED(hres))
389             return hres;
390     }
391 }
392 
393 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
394                     LPVOID lpvReserved)
395 {
396     TRACE("%p, %d, %p\n", hinstDLL, fdwReason, lpvReserved);
397 
398     switch (fdwReason)
399     {
400         case DLL_PROCESS_ATTACH:
401             oleacc_handle = hinstDLL;
402             DisableThreadLibraryCalls(hinstDLL);
403             break;
404     }
405 
406     return OLEACC_DllMain(hinstDLL, fdwReason, lpvReserved);
407 }
408 
409 HRESULT WINAPI DllRegisterServer(void)
410 {
411     return OLEACC_DllRegisterServer();
412 }
413 
414 HRESULT WINAPI DllUnregisterServer(void)
415 {
416     return OLEACC_DllUnregisterServer();
417 }
418 
419 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, void **ppv)
420 {
421     if(IsEqualGUID(&CLSID_CAccPropServices, rclsid)) {
422         TRACE("(CLSID_CAccPropServices %s %p)\n", debugstr_guid(iid), ppv);
423         return get_accpropservices_factory(iid, ppv);
424     }
425 
426     if(IsEqualGUID(&CLSID_PSFactoryBuffer, rclsid)) {
427         TRACE("(CLSID_PSFactoryBuffer %s %p)\n", debugstr_guid(iid), ppv);
428         return OLEACC_DllGetClassObject(rclsid, iid, ppv);
429     }
430 
431     FIXME("%s %s %p: stub\n", debugstr_guid(rclsid), debugstr_guid(iid), ppv);
432     return E_NOTIMPL;
433 }
434 
435 void WINAPI GetOleaccVersionInfo(DWORD* pVersion, DWORD* pBuild)
436 {
437 #ifdef __REACTOS__
438     *pVersion = MAKELONG(2,4); /* Windows XP version of oleacc: 4.2.5406.0 */
439     *pBuild = MAKELONG(0,5406);
440 #else
441     *pVersion = MAKELONG(0,7); /* Windows 7 version of oleacc: 7.0.0.0 */
442     *pBuild = MAKELONG(0,0);
443 #endif
444 }
445 
446 HANDLE WINAPI GetProcessHandleFromHwnd(HWND hwnd)
447 {
448     DWORD proc_id;
449 
450     TRACE("%p\n", hwnd);
451 
452     if(!GetWindowThreadProcessId(hwnd, &proc_id))
453         return NULL;
454     return OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION |
455             PROCESS_VM_READ | PROCESS_VM_WRITE | SYNCHRONIZE, TRUE, proc_id);
456 }
457 
458 UINT WINAPI GetRoleTextW(DWORD role, LPWSTR lpRole, UINT rolemax)
459 {
460     INT ret;
461     WCHAR *resptr;
462 
463     TRACE("%u %p %u\n", role, lpRole, rolemax);
464 
465     /* return role text length */
466     if(!lpRole)
467         return LoadStringW(oleacc_handle, role, (LPWSTR)&resptr, 0);
468 
469     ret = LoadStringW(oleacc_handle, role, lpRole, rolemax);
470     if(!(ret > 0)){
471         if(rolemax > 0) lpRole[0] = '\0';
472         return 0;
473     }
474 
475     return ret;
476 }
477 
478 UINT WINAPI GetRoleTextA(DWORD role, LPSTR lpRole, UINT rolemax)
479 {
480     UINT length;
481     WCHAR *roletextW;
482 
483     TRACE("%u %p %u\n", role, lpRole, rolemax);
484 
485     if(lpRole && !rolemax)
486         return 0;
487 
488     length = GetRoleTextW(role, NULL, 0);
489     if(!length) {
490         if(lpRole && rolemax)
491             lpRole[0] = 0;
492         return 0;
493     }
494 
495     roletextW = HeapAlloc(GetProcessHeap(), 0, (length + 1)*sizeof(WCHAR));
496     if(!roletextW)
497         return 0;
498 
499     GetRoleTextW(role, roletextW, length + 1);
500 
501     length = WideCharToMultiByte( CP_ACP, 0, roletextW, -1, NULL, 0, NULL, NULL );
502 
503     if(!lpRole){
504         HeapFree(GetProcessHeap(), 0, roletextW);
505         return length - 1;
506     }
507 
508     if(rolemax < length) {
509         HeapFree(GetProcessHeap(), 0, roletextW);
510         lpRole[0] = 0;
511         return 0;
512     }
513 
514     WideCharToMultiByte( CP_ACP, 0, roletextW, -1, lpRole, rolemax, NULL, NULL );
515 
516     if(rolemax < length){
517         lpRole[rolemax-1] = '\0';
518         length = rolemax;
519     }
520 
521     HeapFree(GetProcessHeap(), 0, roletextW);
522 
523     return length - 1;
524 }
525 
526 UINT WINAPI GetStateTextW(DWORD state_bit, WCHAR *state_str, UINT state_str_len)
527 {
528     DWORD state_id;
529 
530     TRACE("%x %p %u\n", state_bit, state_str, state_str_len);
531 
532     if(state_bit & ~(STATE_SYSTEM_VALID | STATE_SYSTEM_HASPOPUP)) {
533         if(state_str && state_str_len)
534             state_str[0] = 0;
535         return 0;
536     }
537 
538     state_id = IDS_STATE_NORMAL;
539     while(state_bit) {
540         state_id++;
541         state_bit /= 2;
542     }
543 
544     if(state_str) {
545         UINT ret = LoadStringW(oleacc_handle, state_id, state_str, state_str_len);
546         if(!ret && state_str_len)
547             state_str[0] = 0;
548         return ret;
549     }else {
550         WCHAR *tmp;
551         return LoadStringW(oleacc_handle, state_id, (WCHAR*)&tmp, 0);
552     }
553 
554 }
555 
556 UINT WINAPI GetStateTextA(DWORD state_bit, CHAR *state_str, UINT state_str_len)
557 {
558     DWORD state_id;
559 
560     TRACE("%x %p %u\n", state_bit, state_str, state_str_len);
561 
562     if(state_str && !state_str_len)
563         return 0;
564 
565     if(state_bit & ~(STATE_SYSTEM_VALID | STATE_SYSTEM_HASPOPUP)) {
566         if(state_str && state_str_len)
567             state_str[0] = 0;
568         return 0;
569     }
570 
571     state_id = IDS_STATE_NORMAL;
572     while(state_bit) {
573         state_id++;
574         state_bit /= 2;
575     }
576 
577     if(state_str) {
578         UINT ret = LoadStringA(oleacc_handle, state_id, state_str, state_str_len);
579         if(!ret && state_str_len)
580             state_str[0] = 0;
581         return ret;
582     }else {
583         CHAR tmp[256];
584         return LoadStringA(oleacc_handle, state_id, tmp, sizeof(tmp));
585     }
586 }
587 
588 HRESULT WINAPI AccessibleChildren(IAccessible *container,
589         LONG start, LONG count, VARIANT *children, LONG *children_cnt)
590 {
591     IEnumVARIANT *ev;
592     LONG i, child_no;
593     HRESULT hr;
594 
595     TRACE("%p %d %d %p %p\n", container, start, count, children, children_cnt);
596 
597     if(!container || !children || !children_cnt)
598         return E_INVALIDARG;
599 
600     for(i=0; i<count; i++)
601         VariantInit(children+i);
602 
603     hr = IAccessible_QueryInterface(container, &IID_IEnumVARIANT, (void**)&ev);
604     if(SUCCEEDED(hr)) {
605         hr = IEnumVARIANT_Reset(ev);
606         if(SUCCEEDED(hr))
607             hr = IEnumVARIANT_Skip(ev, start);
608         if(SUCCEEDED(hr))
609             hr = IEnumVARIANT_Next(ev, count, children, (ULONG*)children_cnt);
610         IEnumVARIANT_Release(ev);
611         return hr;
612     }
613 
614     hr = IAccessible_get_accChildCount(container, &child_no);
615     if(FAILED(hr))
616         return hr;
617 
618     for(i=0; i<count && start+i+1<=child_no; i++) {
619         IDispatch *disp;
620 
621         V_VT(children+i) = VT_I4;
622         V_I4(children+i) = start+i+1;
623 
624         hr = IAccessible_get_accChild(container, children[i], &disp);
625         if(SUCCEEDED(hr) && disp) {
626             V_VT(children+i) = VT_DISPATCH;
627             V_DISPATCH(children+i) = disp;
628         }
629     }
630 
631     *children_cnt = i;
632     return i==count ? S_OK : S_FALSE;
633 }
634