xref: /reactos/dll/win32/oleacc/client.c (revision 53221834)
1 /*
2  * Copyright 2014 Piotr Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #define COBJMACROS
20 
21 #include "oleacc_private.h"
22 
23 #include "wine/debug.h"
24 #include "wine/heap.h"
25 
26 WINE_DEFAULT_DEBUG_CHANNEL(oleacc);
27 
28 typedef struct {
29     IAccessible IAccessible_iface;
30     IOleWindow IOleWindow_iface;
31     IEnumVARIANT IEnumVARIANT_iface;
32 
33     LONG ref;
34 
35     HWND hwnd;
36     HWND enum_pos;
37 } Client;
38 
39 static inline Client* impl_from_Client(IAccessible *iface)
40 {
41     return CONTAINING_RECORD(iface, Client, IAccessible_iface);
42 }
43 
44 static HRESULT WINAPI Client_QueryInterface(IAccessible *iface, REFIID riid, void **ppv)
45 {
46     Client *This = impl_from_Client(iface);
47 
48     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
49 
50     if(IsEqualIID(riid, &IID_IAccessible) ||
51             IsEqualIID(riid, &IID_IDispatch) ||
52             IsEqualIID(riid, &IID_IUnknown)) {
53         *ppv = iface;
54     }else if(IsEqualIID(riid, &IID_IOleWindow)) {
55         *ppv = &This->IOleWindow_iface;
56     }else if(IsEqualIID(riid, &IID_IEnumVARIANT)) {
57         *ppv = &This->IEnumVARIANT_iface;
58     }else {
59         WARN("no interface: %s\n", debugstr_guid(riid));
60         *ppv = NULL;
61         return E_NOINTERFACE;
62     }
63 
64     IAccessible_AddRef(iface);
65     return S_OK;
66 }
67 
68 static ULONG WINAPI Client_AddRef(IAccessible *iface)
69 {
70     Client *This = impl_from_Client(iface);
71     ULONG ref = InterlockedIncrement(&This->ref);
72 
73     TRACE("(%p) ref = %u\n", This, ref);
74     return ref;
75 }
76 
77 static ULONG WINAPI Client_Release(IAccessible *iface)
78 {
79     Client *This = impl_from_Client(iface);
80     ULONG ref = InterlockedDecrement(&This->ref);
81 
82     TRACE("(%p) ref = %u\n", This, ref);
83 
84     if(!ref)
85         heap_free(This);
86     return ref;
87 }
88 
89 static HRESULT WINAPI Client_GetTypeInfoCount(IAccessible *iface, UINT *pctinfo)
90 {
91     Client *This = impl_from_Client(iface);
92     FIXME("(%p)->(%p)\n", This, pctinfo);
93     return E_NOTIMPL;
94 }
95 
96 static HRESULT WINAPI Client_GetTypeInfo(IAccessible *iface,
97         UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
98 {
99     Client *This = impl_from_Client(iface);
100     FIXME("(%p)->(%u %x %p)\n", This, iTInfo, lcid, ppTInfo);
101     return E_NOTIMPL;
102 }
103 
104 static HRESULT WINAPI Client_GetIDsOfNames(IAccessible *iface, REFIID riid,
105         LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
106 {
107     Client *This = impl_from_Client(iface);
108     FIXME("(%p)->(%s %p %u %x %p)\n", This, debugstr_guid(riid),
109             rgszNames, cNames, lcid, rgDispId);
110     return E_NOTIMPL;
111 }
112 
113 static HRESULT WINAPI Client_Invoke(IAccessible *iface, DISPID dispIdMember,
114         REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
115         VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
116 {
117     Client *This = impl_from_Client(iface);
118     FIXME("(%p)->(%x %s %x %x %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
119             lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
120     return E_NOTIMPL;
121 }
122 
123 static HRESULT WINAPI Client_get_accParent(IAccessible *iface, IDispatch **ppdispParent)
124 {
125     Client *This = impl_from_Client(iface);
126 
127     TRACE("(%p)->(%p)\n", This, ppdispParent);
128 
129     return AccessibleObjectFromWindow(This->hwnd, OBJID_WINDOW,
130             &IID_IDispatch, (void**)ppdispParent);
131 }
132 
133 static HRESULT WINAPI Client_get_accChildCount(IAccessible *iface, LONG *pcountChildren)
134 {
135     Client *This = impl_from_Client(iface);
136     HWND cur;
137 
138     TRACE("(%p)->(%p)\n", This, pcountChildren);
139 
140     *pcountChildren = 0;
141     for(cur = GetWindow(This->hwnd, GW_CHILD); cur; cur = GetWindow(cur, GW_HWNDNEXT))
142         (*pcountChildren)++;
143 
144     return S_OK;
145 }
146 
147 static HRESULT WINAPI Client_get_accChild(IAccessible *iface,
148         VARIANT varChildID, IDispatch **ppdispChild)
149 {
150     Client *This = impl_from_Client(iface);
151 
152     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varChildID), ppdispChild);
153 
154     *ppdispChild = NULL;
155     return E_INVALIDARG;
156 }
157 
158 static HRESULT WINAPI Client_get_accName(IAccessible *iface, VARIANT varID, BSTR *pszName)
159 {
160     Client *This = impl_from_Client(iface);
161     WCHAR name[1024];
162     UINT i, len;
163 
164     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszName);
165 
166     *pszName = NULL;
167     if(convert_child_id(&varID) != CHILDID_SELF || !IsWindow(This->hwnd))
168         return E_INVALIDARG;
169 
170     len = SendMessageW(This->hwnd, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name);
171     if(!len)
172         return S_FALSE;
173 
174     for(i=0; i<len; i++) {
175         if(name[i] == '&') {
176             len--;
177             memmove(name+i, name+i+1, (len-i)*sizeof(WCHAR));
178             break;
179         }
180     }
181 
182     *pszName = SysAllocStringLen(name, len);
183     return *pszName ? S_OK : E_OUTOFMEMORY;
184 }
185 
186 static HRESULT WINAPI Client_get_accValue(IAccessible *iface, VARIANT varID, BSTR *pszValue)
187 {
188     Client *This = impl_from_Client(iface);
189 
190     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszValue);
191 
192     *pszValue = NULL;
193     if(convert_child_id(&varID) != CHILDID_SELF)
194         return E_INVALIDARG;
195     return S_FALSE;
196 }
197 
198 static HRESULT WINAPI Client_get_accDescription(IAccessible *iface,
199         VARIANT varID, BSTR *pszDescription)
200 {
201     Client *This = impl_from_Client(iface);
202 
203     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszDescription);
204 
205     *pszDescription = NULL;
206     if(convert_child_id(&varID) != CHILDID_SELF)
207         return E_INVALIDARG;
208     return S_FALSE;
209 }
210 
211 static HRESULT WINAPI Client_get_accRole(IAccessible *iface, VARIANT varID, VARIANT *pvarRole)
212 {
213     Client *This = impl_from_Client(iface);
214 
215     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarRole);
216 
217     if(convert_child_id(&varID) != CHILDID_SELF) {
218         V_VT(pvarRole) = VT_EMPTY;
219         return E_INVALIDARG;
220     }
221 
222     V_VT(pvarRole) = VT_I4;
223     V_I4(pvarRole) = ROLE_SYSTEM_CLIENT;
224     return S_OK;
225 }
226 
227 static HRESULT WINAPI Client_get_accState(IAccessible *iface, VARIANT varID, VARIANT *pvarState)
228 {
229     Client *This = impl_from_Client(iface);
230     LONG style;
231 
232     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pvarState);
233 
234     if(convert_child_id(&varID) != CHILDID_SELF) {
235         V_VT(pvarState) = VT_EMPTY;
236         return E_INVALIDARG;
237     }
238 
239     V_VT(pvarState) = VT_I4;
240     V_I4(pvarState) = 0;
241 
242     style = GetWindowLongW(This->hwnd, GWL_STYLE);
243     if(style & WS_DISABLED)
244         V_I4(pvarState) |= STATE_SYSTEM_UNAVAILABLE;
245     else if(IsWindow(This->hwnd))
246         V_I4(pvarState) |= STATE_SYSTEM_FOCUSABLE;
247     if(GetFocus() == This->hwnd)
248         V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
249     if(!(style & WS_VISIBLE))
250         V_I4(pvarState) |= STATE_SYSTEM_INVISIBLE;
251     return S_OK;
252 }
253 
254 static HRESULT WINAPI Client_get_accHelp(IAccessible *iface, VARIANT varID, BSTR *pszHelp)
255 {
256     Client *This = impl_from_Client(iface);
257 
258     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszHelp);
259 
260     *pszHelp = NULL;
261     if(convert_child_id(&varID) != CHILDID_SELF)
262         return E_INVALIDARG;
263     return S_FALSE;
264 }
265 
266 static HRESULT WINAPI Client_get_accHelpTopic(IAccessible *iface,
267         BSTR *pszHelpFile, VARIANT varID, LONG *pidTopic)
268 {
269     Client *This = impl_from_Client(iface);
270     FIXME("(%p)->(%p %s %p)\n", This, pszHelpFile, debugstr_variant(&varID), pidTopic);
271     return E_NOTIMPL;
272 }
273 
274 static HRESULT WINAPI Client_get_accKeyboardShortcut(IAccessible *iface,
275         VARIANT varID, BSTR *pszKeyboardShortcut)
276 {
277     static const WCHAR shortcut_fmt[] = {'A','l','t','+','!',0};
278     Client *This = impl_from_Client(iface);
279     WCHAR name[1024];
280     UINT i, len;
281 
282     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszKeyboardShortcut);
283 
284     *pszKeyboardShortcut = NULL;
285     if(convert_child_id(&varID) != CHILDID_SELF)
286         return E_INVALIDARG;
287 
288     len = SendMessageW(This->hwnd, WM_GETTEXT, ARRAY_SIZE(name), (LPARAM)name);
289     for(i=0; i<len; i++) {
290         if(name[i] == '&')
291             break;
292     }
293     if(i+1 >= len)
294         return S_FALSE;
295 
296     *pszKeyboardShortcut = SysAllocString(shortcut_fmt);
297     if(!*pszKeyboardShortcut)
298         return E_OUTOFMEMORY;
299 
300     (*pszKeyboardShortcut)[4] = name[i+1];
301     return S_OK;
302 }
303 
304 static HRESULT WINAPI Client_get_accFocus(IAccessible *iface, VARIANT *pvarID)
305 {
306     Client *This = impl_from_Client(iface);
307     FIXME("(%p)->(%p)\n", This, pvarID);
308     return E_NOTIMPL;
309 }
310 
311 static HRESULT WINAPI Client_get_accSelection(IAccessible *iface, VARIANT *pvarID)
312 {
313     Client *This = impl_from_Client(iface);
314     FIXME("(%p)->(%p)\n", This, pvarID);
315     return E_NOTIMPL;
316 }
317 
318 static HRESULT WINAPI Client_get_accDefaultAction(IAccessible *iface,
319         VARIANT varID, BSTR *pszDefaultAction)
320 {
321     Client *This = impl_from_Client(iface);
322 
323     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(&varID), pszDefaultAction);
324 
325     *pszDefaultAction = NULL;
326     if(convert_child_id(&varID) != CHILDID_SELF)
327         return E_INVALIDARG;
328     return S_FALSE;
329 }
330 
331 static HRESULT WINAPI Client_accSelect(IAccessible *iface, LONG flagsSelect, VARIANT varID)
332 {
333     Client *This = impl_from_Client(iface);
334     FIXME("(%p)->(%x %s)\n", This, flagsSelect, debugstr_variant(&varID));
335     return E_NOTIMPL;
336 }
337 
338 static HRESULT WINAPI Client_accLocation(IAccessible *iface, LONG *pxLeft,
339         LONG *pyTop, LONG *pcxWidth, LONG *pcyHeight, VARIANT varID)
340 {
341     Client *This = impl_from_Client(iface);
342     RECT rect;
343     POINT pt;
344 
345     TRACE("(%p)->(%p %p %p %p %s)\n", This, pxLeft, pyTop,
346             pcxWidth, pcyHeight, debugstr_variant(&varID));
347 
348     *pxLeft = *pyTop = *pcxWidth = *pcyHeight = 0;
349     if(convert_child_id(&varID) != CHILDID_SELF)
350         return E_INVALIDARG;
351 
352     if(!GetClientRect(This->hwnd, &rect))
353         return S_OK;
354 
355     pt.x = rect.left;
356     pt.y = rect.top;
357     MapWindowPoints(This->hwnd, NULL, &pt, 1);
358     *pxLeft = pt.x;
359     *pyTop = pt.y;
360 
361     pt.x = rect.right;
362     pt.y = rect.bottom;
363     MapWindowPoints(This->hwnd, NULL, &pt, 1);
364     *pcxWidth = pt.x - *pxLeft;
365     *pcyHeight = pt.y - *pyTop;
366     return S_OK;
367 }
368 
369 static HRESULT WINAPI Client_accNavigate(IAccessible *iface,
370         LONG navDir, VARIANT varStart, VARIANT *pvarEnd)
371 {
372     Client *This = impl_from_Client(iface);
373     FIXME("(%p)->(%d %s %p)\n", This, navDir, debugstr_variant(&varStart), pvarEnd);
374     return E_NOTIMPL;
375 }
376 
377 static HRESULT WINAPI Client_accHitTest(IAccessible *iface,
378         LONG xLeft, LONG yTop, VARIANT *pvarID)
379 {
380     Client *This = impl_from_Client(iface);
381     HWND child;
382     POINT pt;
383 
384     TRACE("(%p)->(%d %d %p)\n", This, xLeft, yTop, pvarID);
385 
386     V_VT(pvarID) = VT_I4;
387     V_I4(pvarID) = 0;
388 
389     pt.x = xLeft;
390     pt.y = yTop;
391     if(!IsWindowVisible(This->hwnd) || !ScreenToClient(This->hwnd, &pt))
392         return S_OK;
393 
394     child = ChildWindowFromPointEx(This->hwnd, pt, CWP_SKIPINVISIBLE);
395     if(!child || child==This->hwnd)
396         return S_OK;
397 
398     V_VT(pvarID) = VT_DISPATCH;
399     return AccessibleObjectFromWindow(child, OBJID_WINDOW,
400             &IID_IDispatch, (void**)&V_DISPATCH(pvarID));
401 }
402 
403 static HRESULT WINAPI Client_accDoDefaultAction(IAccessible *iface, VARIANT varID)
404 {
405     Client *This = impl_from_Client(iface);
406     FIXME("(%p)->(%s)\n", This, debugstr_variant(&varID));
407     return E_NOTIMPL;
408 }
409 
410 static HRESULT WINAPI Client_put_accName(IAccessible *iface, VARIANT varID, BSTR pszName)
411 {
412     Client *This = impl_from_Client(iface);
413     FIXME("(%p)->(%s %s)\n", This, debugstr_variant(&varID), debugstr_w(pszName));
414     return E_NOTIMPL;
415 }
416 
417 static HRESULT WINAPI Client_put_accValue(IAccessible *iface, VARIANT varID, BSTR pszValue)
418 {
419     Client *This = impl_from_Client(iface);
420     FIXME("(%p)->(%s %s)\n", This, debugstr_variant(&varID), debugstr_w(pszValue));
421     return E_NOTIMPL;
422 }
423 
424 static const IAccessibleVtbl ClientVtbl = {
425     Client_QueryInterface,
426     Client_AddRef,
427     Client_Release,
428     Client_GetTypeInfoCount,
429     Client_GetTypeInfo,
430     Client_GetIDsOfNames,
431     Client_Invoke,
432     Client_get_accParent,
433     Client_get_accChildCount,
434     Client_get_accChild,
435     Client_get_accName,
436     Client_get_accValue,
437     Client_get_accDescription,
438     Client_get_accRole,
439     Client_get_accState,
440     Client_get_accHelp,
441     Client_get_accHelpTopic,
442     Client_get_accKeyboardShortcut,
443     Client_get_accFocus,
444     Client_get_accSelection,
445     Client_get_accDefaultAction,
446     Client_accSelect,
447     Client_accLocation,
448     Client_accNavigate,
449     Client_accHitTest,
450     Client_accDoDefaultAction,
451     Client_put_accName,
452     Client_put_accValue
453 };
454 
455 static inline Client* impl_from_Client_OleWindow(IOleWindow *iface)
456 {
457     return CONTAINING_RECORD(iface, Client, IOleWindow_iface);
458 }
459 
460 static HRESULT WINAPI Client_OleWindow_QueryInterface(IOleWindow *iface, REFIID riid, void **ppv)
461 {
462     Client *This = impl_from_Client_OleWindow(iface);
463     return IAccessible_QueryInterface(&This->IAccessible_iface, riid, ppv);
464 }
465 
466 static ULONG WINAPI Client_OleWindow_AddRef(IOleWindow *iface)
467 {
468     Client *This = impl_from_Client_OleWindow(iface);
469     return IAccessible_AddRef(&This->IAccessible_iface);
470 }
471 
472 static ULONG WINAPI Client_OleWindow_Release(IOleWindow *iface)
473 {
474     Client *This = impl_from_Client_OleWindow(iface);
475     return IAccessible_Release(&This->IAccessible_iface);
476 }
477 
478 static HRESULT WINAPI Client_OleWindow_GetWindow(IOleWindow *iface, HWND *phwnd)
479 {
480     Client *This = impl_from_Client_OleWindow(iface);
481 
482     TRACE("(%p)->(%p)\n", This, phwnd);
483 
484     *phwnd = This->hwnd;
485     return S_OK;
486 }
487 
488 static HRESULT WINAPI Client_OleWindow_ContextSensitiveHelp(IOleWindow *iface, BOOL fEnterMode)
489 {
490     Client *This = impl_from_Client_OleWindow(iface);
491     FIXME("(%p)->(%x)\n", This, fEnterMode);
492     return E_NOTIMPL;
493 }
494 
495 static const IOleWindowVtbl ClientOleWindowVtbl = {
496     Client_OleWindow_QueryInterface,
497     Client_OleWindow_AddRef,
498     Client_OleWindow_Release,
499     Client_OleWindow_GetWindow,
500     Client_OleWindow_ContextSensitiveHelp
501 };
502 
503 static inline Client* impl_from_Client_EnumVARIANT(IEnumVARIANT *iface)
504 {
505     return CONTAINING_RECORD(iface, Client, IEnumVARIANT_iface);
506 }
507 
508 static HRESULT WINAPI Client_EnumVARIANT_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv)
509 {
510     Client *This = impl_from_Client_EnumVARIANT(iface);
511     return IAccessible_QueryInterface(&This->IAccessible_iface, riid, ppv);
512 }
513 
514 static ULONG WINAPI Client_EnumVARIANT_AddRef(IEnumVARIANT *iface)
515 {
516     Client *This = impl_from_Client_EnumVARIANT(iface);
517     return IAccessible_AddRef(&This->IAccessible_iface);
518 }
519 
520 static ULONG WINAPI Client_EnumVARIANT_Release(IEnumVARIANT *iface)
521 {
522     Client *This = impl_from_Client_EnumVARIANT(iface);
523     return IAccessible_Release(&This->IAccessible_iface);
524 }
525 
526 static HRESULT WINAPI Client_EnumVARIANT_Next(IEnumVARIANT *iface,
527         ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
528 {
529     Client *This = impl_from_Client_EnumVARIANT(iface);
530     HWND cur = This->enum_pos, next;
531     ULONG fetched = 0;
532     HRESULT hr;
533 
534     TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched);
535 
536     if(!celt) {
537         if(pCeltFetched)
538             *pCeltFetched = 0;
539         return S_OK;
540     }
541 
542     if(!This->enum_pos)
543         next = GetWindow(This->hwnd, GW_CHILD);
544     else
545         next = GetWindow(This->enum_pos, GW_HWNDNEXT);
546 
547     while(next) {
548         cur = next;
549 
550         V_VT(rgVar+fetched) = VT_DISPATCH;
551         hr = AccessibleObjectFromWindow(cur, OBJID_WINDOW,
552                 &IID_IDispatch, (void**)&V_DISPATCH(rgVar+fetched));
553         if(FAILED(hr)) {
554             V_VT(rgVar+fetched) = VT_EMPTY;
555             while(fetched > 0) {
556                 VariantClear(rgVar+fetched-1);
557                 fetched--;
558             }
559             if(pCeltFetched)
560                 *pCeltFetched = 0;
561             return hr;
562         }
563         fetched++;
564         if(fetched == celt)
565             break;
566 
567         next = GetWindow(cur, GW_HWNDNEXT);
568     }
569 
570     This->enum_pos = cur;
571     if(pCeltFetched)
572         *pCeltFetched = fetched;
573     return celt == fetched ? S_OK : S_FALSE;
574 }
575 
576 static HRESULT WINAPI Client_EnumVARIANT_Skip(IEnumVARIANT *iface, ULONG celt)
577 {
578     Client *This = impl_from_Client_EnumVARIANT(iface);
579     HWND next;
580 
581     TRACE("(%p)->(%u)\n", This, celt);
582 
583     while(celt) {
584         if(!This->enum_pos)
585             next = GetWindow(This->hwnd, GW_CHILD);
586         else
587             next = GetWindow(This->enum_pos, GW_HWNDNEXT);
588         if(!next)
589             return S_FALSE;
590 
591         This->enum_pos = next;
592         celt--;
593     }
594 
595     return S_OK;
596 }
597 
598 static HRESULT WINAPI Client_EnumVARIANT_Reset(IEnumVARIANT *iface)
599 {
600     Client *This = impl_from_Client_EnumVARIANT(iface);
601 
602     TRACE("(%p)\n", This);
603 
604     This->enum_pos = 0;
605     return S_OK;
606 }
607 
608 static HRESULT WINAPI Client_EnumVARIANT_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum)
609 {
610     Client *This = impl_from_Client_EnumVARIANT(iface);
611     FIXME("(%p)->(%p)\n", This, ppEnum);
612     return E_NOTIMPL;
613 }
614 
615 static const IEnumVARIANTVtbl ClientEnumVARIANTVtbl = {
616     Client_EnumVARIANT_QueryInterface,
617     Client_EnumVARIANT_AddRef,
618     Client_EnumVARIANT_Release,
619     Client_EnumVARIANT_Next,
620     Client_EnumVARIANT_Skip,
621     Client_EnumVARIANT_Reset,
622     Client_EnumVARIANT_Clone
623 };
624 
625 HRESULT create_client_object(HWND hwnd, const IID *iid, void **obj)
626 {
627     Client *client;
628     HRESULT hres;
629 
630     if(!IsWindow(hwnd))
631         return E_FAIL;
632 
633     client = heap_alloc_zero(sizeof(Client));
634     if(!client)
635         return E_OUTOFMEMORY;
636 
637     client->IAccessible_iface.lpVtbl = &ClientVtbl;
638     client->IOleWindow_iface.lpVtbl = &ClientOleWindowVtbl;
639     client->IEnumVARIANT_iface.lpVtbl = &ClientEnumVARIANTVtbl;
640     client->ref = 1;
641     client->hwnd = hwnd;
642     client->enum_pos = 0;
643 
644     hres = IAccessible_QueryInterface(&client->IAccessible_iface, iid, obj);
645     IAccessible_Release(&client->IAccessible_iface);
646     return hres;
647 }
648