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