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