[OLEACC] Sync with Wine Staging 4.0. CORE-15682
[reactos.git] / dll / win32 / oleacc / main.c
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 }