diff org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/accessibility/Accessible.d @ 0:6dd524f61e62

add dwt win and basic java stuff
author Frank Benoit <benoit@tionex.de>
date Mon, 02 Mar 2009 14:44:16 +0100
parents
children 950d84783eac
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.swt.win32.win32.x86/src/org/eclipse/swt/accessibility/Accessible.d	Mon Mar 02 14:44:16 2009 +0100
@@ -0,0 +1,1487 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module org.eclipse.swt.accessibility.Accessible;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.internal.ole.win32.COM;
+import org.eclipse.swt.internal.ole.win32.OAIDL;
+import org.eclipse.swt.internal.ole.win32.ifs;
+import org.eclipse.swt.internal.ole.win32.extras;
+import org.eclipse.swt.internal.win32.OS;
+import org.eclipse.swt.internal.SWTEventListener;
+import org.eclipse.swt.ole.win32.OLE;
+import org.eclipse.swt.ole.win32.Variant;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+
+import org.eclipse.swt.accessibility.ACC;
+import org.eclipse.swt.accessibility.AccessibleControlListener;
+import org.eclipse.swt.accessibility.AccessibleListener;
+import org.eclipse.swt.accessibility.AccessibleTextListener;
+import org.eclipse.swt.accessibility.AccessibleControlEvent;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+
+import java.lang.all;
+import tango.core.Array;
+import tango.core.Thread;
+
+/**
+ * Instances of this class provide a bridge between application
+ * code and assistive technology clients. Many platforms provide
+ * default accessible behavior for most widgets, and this class
+ * allows that default behavior to be overridden. Applications
+ * can get the default Accessible object for a control by sending
+ * it <code>getAccessible</code>, and then add an accessible listener
+ * to override simple items like the name and help string, or they
+ * can add an accessible control listener to override complex items.
+ * As a rule of thumb, an application would only want to use the
+ * accessible control listener to implement accessibility for a
+ * custom control.
+ *
+ * @see Control#getAccessible
+ * @see AccessibleListener
+ * @see AccessibleEvent
+ * @see AccessibleControlListener
+ * @see AccessibleControlEvent
+ * @see <a href="http://www.eclipse.org/swt/snippets/#accessibility">Accessibility snippets</a>
+ * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
+ *
+ * @since 2.0
+ */
+public class Accessible {
+    int refCount = 0, enumIndex = 0;
+    _IAccessibleImpl objIAccessible;
+    _IEnumVARIANTImpl objIEnumVARIANT;
+    IAccessible iaccessible;
+    SWTEventListener[] accessibleListeners;
+    SWTEventListener[] accessibleControlListeners;
+    SWTEventListener[] textListeners;
+    Object[] variants;
+    Control control;
+
+    this(Control control) {
+        this.control = control;
+        int /*long*/[] ppvObject = new int /*long*/[1];
+        /* CreateStdAccessibleObject([in] hwnd, [in] idObject, [in] riidInterface, [out] ppvObject).
+         * AddRef has already been called on ppvObject by the callee and must be released by the caller.
+         */
+        HRESULT result = COM.CreateStdAccessibleObject(control.handle, COM.OBJID_CLIENT, &COM.IIDIAccessible, cast(void**)&iaccessible);
+        /* The object needs to be checked, because if the CreateStdAccessibleObject()
+         * symbol is not found, the return value is S_OK.
+         */
+        if (iaccessible is null) return;
+        if (result !is COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);
+
+        objIAccessible = new _IAccessibleImpl(this);
+
+//PORTING_FIXME: i don't understand this...
+/+
+        int /*long*/ ppVtable = objIAccessible.ppVtable;
+        int /*long*/[] pVtable = new int /*long*/[1];
+        COM.MoveMemory(pVtable, ppVtable, OS.PTR_SIZEOF);
+        int /*long*/[] funcs = new int /*long*/[28];
+        COM.MoveMemory(funcs, pVtable[0], OS.PTR_SIZEOF * funcs.length);
+        funcs[9] = COM.get_accChild_CALLBACK(funcs[9]);
+        funcs[10] = COM.get_accName_CALLBACK(funcs[10]);
+        funcs[11] = COM.get_accValue_CALLBACK(funcs[11]);
+        funcs[12] = COM.get_accDescription_CALLBACK(funcs[12]);
+        funcs[13] = COM.get_accRole_CALLBACK(funcs[13]);
+        funcs[14] = COM.get_accState_CALLBACK(funcs[14]);
+        funcs[15] = COM.get_accHelp_CALLBACK(funcs[15]);
+        funcs[16] = COM.get_accHelpTopic_CALLBACK(funcs[16]);
+        funcs[17] = COM.get_accKeyboardShortcut_CALLBACK(funcs[17]);
+        funcs[20] = COM.get_accDefaultAction_CALLBACK(funcs[20]);
+        funcs[21] = COM.accSelect_CALLBACK(funcs[21]);
+        funcs[22] = COM.accLocation_CALLBACK(funcs[22]);
+        funcs[23] = COM.accNavigate_CALLBACK(funcs[23]);
+        funcs[25] = COM.accDoDefaultAction_CALLBACK(funcs[25]);
+        funcs[26] = COM.put_accName_CALLBACK(funcs[26]);
+        funcs[27] = COM.put_accValue_CALLBACK(funcs[27]);
+        COM.MoveMemory(pVtable[0], funcs, OS.PTR_SIZEOF * funcs.length);
++/
+
+        objIEnumVARIANT = new _IEnumVARIANTImpl(this) ;
+        AddRef();
+    }
+
+    /**
+     * Invokes platform specific functionality to allocate a new accessible object.
+     * <p>
+     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+     * API for <code>Accessible</code>. It is marked public only so that it
+     * can be shared within the packages provided by SWT. It is not
+     * available on all platforms, and should never be called from
+     * application code.
+     * </p>
+     *
+     * @param control the control to get the accessible object for
+     * @return the platform specific accessible object
+     */
+    public static Accessible internal_new_Accessible(Control control) {
+        return new Accessible(control);
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when an accessible client asks for certain strings,
+     * such as name, description, help, or keyboard shortcut. The
+     * listener is notified by sending it one of the messages defined
+     * in the <code>AccessibleListener</code> interface.
+     *
+     * @param listener the listener that should be notified when the receiver
+     * is asked for a name, description, help, or keyboard shortcut string
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleListener
+     * @see #removeAccessibleListener
+     */
+    public void addAccessibleListener(AccessibleListener listener) {
+        checkWidget();
+        if (listener is null) SWT.error(__FILE__, __LINE__, SWT.ERROR_NULL_ARGUMENT);
+        accessibleListeners ~= listener;
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when an accessible client asks for custom control
+     * specific information. The listener is notified by sending it
+     * one of the messages defined in the <code>AccessibleControlListener</code>
+     * interface.
+     *
+     * @param listener the listener that should be notified when the receiver
+     * is asked for custom control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleControlListener
+     * @see #removeAccessibleControlListener
+     */
+    public void addAccessibleControlListener(AccessibleControlListener listener) {
+        checkWidget();
+        if (listener is null) SWT.error(__FILE__, __LINE__, SWT.ERROR_NULL_ARGUMENT);
+        accessibleControlListeners ~= listener;
+    }
+
+    /**
+     * Adds the listener to the collection of listeners who will
+     * be notified when an accessible client asks for custom text control
+     * specific information. The listener is notified by sending it
+     * one of the messages defined in the <code>AccessibleTextListener</code>
+     * interface.
+     *
+     * @param listener the listener that should be notified when the receiver
+     * is asked for custom text control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleTextListener
+     * @see #removeAccessibleTextListener
+     *
+     * @since 3.0
+     */
+    public void addAccessibleTextListener (AccessibleTextListener listener) {
+        checkWidget ();
+        if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+        textListeners ~= listener;
+    }
+
+    /**
+     * Returns the control for this Accessible object.
+     *
+     * @return the receiver's control
+     * @since 3.0
+     */
+    public Control getControl() {
+        return control;
+    }
+
+    /**
+     * Invokes platform specific functionality to dispose an accessible object.
+     * <p>
+     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+     * API for <code>Accessible</code>. It is marked public only so that it
+     * can be shared within the packages provided by SWT. It is not
+     * available on all platforms, and should never be called from
+     * application code.
+     * </p>
+     */
+    public void internal_dispose_Accessible() {
+        if (iaccessible !is null) {
+            iaccessible.Release();
+        }
+        iaccessible = null;
+        Release();
+    }
+
+    /**
+     * Invokes platform specific functionality to handle a window message.
+     * <p>
+     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
+     * API for <code>Accessible</code>. It is marked public only so that it
+     * can be shared within the packages provided by SWT. It is not
+     * available on all platforms, and should never be called from
+     * application code.
+     * </p>
+     */
+    public int /*long*/ internal_WM_GETOBJECT (int /*long*/ wParam, int /*long*/ lParam) {
+        if (objIAccessible is null) return 0;
+        if (lParam is COM.OBJID_CLIENT) {
+            /* LresultFromObject([in] riid, [in] wParam, [in] pAcc)
+             * The argument pAcc is owned by the caller so reference count does not
+             * need to be incremented.
+             */
+            return COM.LresultFromObject(&COM.IIDIAccessible, wParam, cast(IAccessible)objIAccessible);
+        }
+        return 0;
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when an accessible client asks for certain strings,
+     * such as name, description, help, or keyboard shortcut.
+     *
+     * @param listener the listener that should no longer be notified when the receiver
+     * is asked for a name, description, help, or keyboard shortcut string
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleListener
+     * @see #addAccessibleListener
+     */
+    public void removeAccessibleListener(AccessibleListener listener) {
+        checkWidget();
+        if (listener is null) SWT.error(__FILE__, __LINE__, SWT.ERROR_NULL_ARGUMENT);
+        accessibleListeners.length = accessibleListeners.remove(listener);
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when an accessible client asks for custom control
+     * specific information.
+     *
+     * @param listener the listener that should no longer be notified when the receiver
+     * is asked for custom control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleControlListener
+     * @see #addAccessibleControlListener
+     */
+    public void removeAccessibleControlListener(AccessibleControlListener listener) {
+        checkWidget();
+        if (listener is null) SWT.error(__FILE__, __LINE__, SWT.ERROR_NULL_ARGUMENT);
+        accessibleControlListeners.length = accessibleControlListeners.remove(listener);
+    }
+
+    /**
+     * Removes the listener from the collection of listeners who will
+     * be notified when an accessible client asks for custom text control
+     * specific information.
+     *
+     * @param listener the listener that should no longer be notified when the receiver
+     * is asked for custom text control specific information
+     *
+     * @exception IllegalArgumentException <ul>
+     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
+     * </ul>
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see AccessibleTextListener
+     * @see #addAccessibleTextListener
+     *
+     * @since 3.0
+     */
+    public void removeAccessibleTextListener (AccessibleTextListener listener) {
+        checkWidget ();
+        if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+        textListeners.length = textListeners.remove (listener);
+    }
+
+    /**
+     * Sends a message to accessible clients that the child selection
+     * within a custom container control has changed.
+     *
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @since 3.0
+     */
+    public void selectionChanged () {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
+    }
+
+    /**
+     * Sends a message to accessible clients indicating that the focus
+     * has changed within a custom control.
+     *
+     * @param childID an identifier specifying a child of the control
+     *
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     */
+    public void setFocus(int childID) {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_FOCUS, control.handle, COM.OBJID_CLIENT, childIDToOs(childID));
+    }
+
+    /**
+     * Sends a message to accessible clients that the text
+     * caret has moved within a custom control.
+     *
+     * @param index the new caret index within the control
+     *
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @since 3.0
+     */
+    public void textCaretMoved (int index) {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, COM.OBJID_CARET, COM.CHILDID_SELF);
+    }
+
+    /**
+     * Sends a message to accessible clients that the text
+     * within a custom control has changed.
+     *
+     * @param type the type of change, one of <code>ACC.NOTIFY_TEXT_INSERT</code>
+     * or <code>ACC.NOTIFY_TEXT_DELETE</code>
+     * @param startIndex the text index within the control where the insertion or deletion begins
+     * @param length the non-negative length in characters of the insertion or deletion
+     *
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @see ACC#TEXT_INSERT
+     * @see ACC#TEXT_DELETE
+     *
+     * @since 3.0
+     */
+    public void textChanged (int type, int startIndex, int length) {
+        checkWidget();
+        COM.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, COM.OBJID_CLIENT, COM.CHILDID_SELF);
+    }
+
+    /**
+     * Sends a message to accessible clients that the text
+     * selection has changed within a custom control.
+     *
+     * @exception SWTException <ul>
+     *    <li>ERROR_WIDGET_DISPOSED - if the receiver's control has been disposed</li>
+     *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver's control</li>
+     * </ul>
+     *
+     * @since 3.0
+     */
+    public void textSelectionChanged () {
+        checkWidget();
+        // not an MSAA event
+    }
+
+    /* QueryInterface([in] iid, [out] ppvObject)
+     * Ownership of ppvObject transfers from callee to caller so reference count on ppvObject
+     * must be incremented before returning.  Caller is responsible for releasing ppvObject.
+     */
+    HRESULT QueryInterface(REFIID riid, void** ppvObject) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        if (COM.IsEqualGUID(riid, &COM.IIDIUnknown)) {
+            *ppvObject = cast(void*)cast(IUnknown)  objIAccessible;
+            AddRef();
+            return COM.S_OK;
+        }
+
+        if (COM.IsEqualGUID(riid, &COM.IIDIDispatch)) {
+            *ppvObject = cast(void*)cast(IDispatch) objIAccessible;
+            AddRef();
+            return COM.S_OK;
+        }
+
+        if (COM.IsEqualGUID(riid, &COM.IIDIAccessible)) {
+            *ppvObject = cast(void*)cast(IAccessible) objIAccessible;
+            AddRef();
+            return COM.S_OK;
+        }
+
+        if (COM.IsEqualGUID(riid, &COM.IIDIEnumVARIANT)) {
+            *ppvObject = cast(void*)cast(IEnumVARIANT) objIEnumVARIANT;
+            AddRef();
+            enumIndex = 0;
+            return COM.S_OK;
+        }
+
+        HRESULT result = iaccessible.QueryInterface(riid, ppvObject);
+        return result;
+    }
+
+    ULONG AddRef() {
+        refCount++;
+        return refCount;
+    }
+
+    ULONG Release() {
+        refCount--;
+
+        if (refCount is 0) {
+            objIAccessible = null;
+            objIEnumVARIANT = null;
+        }
+        return refCount;
+    }
+
+    HRESULT accDoDefaultAction(VARIANT variant) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.accDoDefaultAction(variant);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    HRESULT accHitTest(LONG xLeft, LONG yTop, VARIANT* pvarChild) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        if (accessibleControlListeners.length is 0) {
+            return iaccessible.accHitTest(xLeft, yTop, pvarChild);
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = ACC.CHILDID_NONE;
+        event.x = xLeft;
+        event.y = yTop;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getChildAtPoint(event);
+        }
+        int childID = event.childID;
+        if (childID is ACC.CHILDID_NONE) {
+            return iaccessible.accHitTest(xLeft, yTop, pvarChild);
+        }
+        //TODO - use VARIANT structure
+        pvarChild.vt = COM.VT_I4;
+        pvarChild.lVal = childIDToOs(childID);
+        return COM.S_OK;
+    }
+
+    HRESULT accLocation(LONG* pxLeft, LONG* pyTop, LONG* pcxWidth, LONG* pcyHeight, VARIANT variant) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default location from the OS. */
+        int osLeft = 0, osTop = 0, osWidth = 0, osHeight = 0;
+        int code = iaccessible.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, variant);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleControlListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            int[1] pLeft, pTop, pWidth, pHeight;
+            COM.MoveMemory(pLeft.ptr, pxLeft, 4);
+            COM.MoveMemory(pTop.ptr, pyTop, 4);
+            COM.MoveMemory(pWidth.ptr, pcxWidth, 4);
+            COM.MoveMemory(pHeight.ptr, pcyHeight, 4);
+            osLeft = pLeft[0]; osTop = pTop[0]; osWidth = pWidth[0]; osHeight = pHeight[0];
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.x = osLeft;
+        event.y = osTop;
+        event.width = osWidth;
+        event.height = osHeight;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getLocation(event);
+        }
+        OS.MoveMemory(pxLeft, &event.x, 4);
+        OS.MoveMemory(pyTop, &event.y, 4);
+        OS.MoveMemory(pcxWidth, &event.width, 4);
+        OS.MoveMemory(pcyHeight, &event.height, 4);
+        return COM.S_OK;
+    }
+
+    HRESULT accNavigate(LONG navDir, VARIANT variant, VARIANT* pvarEndUpAt) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.accNavigate(navDir, variant, pvarEndUpAt);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    HRESULT accSelect(LONG flagsSelect, VARIANT variant) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.accSelect(flagsSelect, variant);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    /* get_accChild([in] varChild, [out] ppdispChild)
+     * Ownership of ppdispChild transfers from callee to caller so reference count on ppdispChild
+     * must be incremented before returning.  The caller is responsible for releasing ppdispChild.
+     */
+    HRESULT get_accChild(VARIANT variant, LPDISPATCH* ppdispChild) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+        if (accessibleControlListeners.length is 0) {
+            int code = iaccessible.get_accChild(variant, ppdispChild);
+            if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+            return code;
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID(v.lVal);
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getChild(event);
+        }
+        Accessible accessible = event.accessible;
+        if (accessible !is null) {
+            accessible.AddRef();
+            *ppdispChild = accessible.objIAccessible;
+            return COM.S_OK;
+        }
+        return COM.S_FALSE;
+    }
+
+    HRESULT get_accChildCount(LONG* pcountChildren) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        /* Get the default child count from the OS. */
+        int osChildCount = 0;
+        int code = iaccessible.get_accChildCount(pcountChildren);
+        if (accessibleControlListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            int[1] pChildCount;
+            COM.MoveMemory(pChildCount.ptr, pcountChildren, 4);
+            osChildCount = pChildCount[0];
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = ACC.CHILDID_SELF;
+        event.detail = osChildCount;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getChildCount(event);
+        }
+
+        *pcountChildren = event.detail;
+        return COM.S_OK;
+    }
+
+    HRESULT get_accDefaultAction(VARIANT variant, BSTR* pszDefaultAction) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default defaultAction from the OS. */
+        String osDefaultAction = null;
+        int code = iaccessible.get_accDefaultAction(variant, pszDefaultAction);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleControlListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            osDefaultAction = BSTRToStr( *pszDefaultAction, true );
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.result = osDefaultAction;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getDefaultAction(event);
+        }
+        if (event.result is null) return code;
+        auto ptr = COM.SysAllocString(StrToWCHARz(event.result));
+        *pszDefaultAction = ptr;
+        return COM.S_OK;
+    }
+
+    HRESULT get_accDescription(VARIANT variant, BSTR* pszDescription) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default description from the OS. */
+        String osDescription = null;
+        int code = iaccessible.get_accDescription(variant, pszDescription);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        // TEMPORARY CODE - process tree even if there are no apps listening
+        if (accessibleListeners.length is 0 && !( null is cast(Tree)control )) return code;
+        if (code is COM.S_OK) {
+            int size = COM.SysStringByteLen(*pszDescription);
+            if (size > 0) {
+                size = (size + 1) /2;
+                osDescription = WCHARzToStr(*pszDescription, size);
+            }
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.result = osDescription;
+
+        // TEMPORARY CODE
+        /* Currently our tree columns are emulated using custom draw,
+         * so we need to create the description using the tree column
+         * header text and tree item text. */
+        if (v.lVal !is COM.CHILDID_SELF) {
+            if (auto tree = cast(Tree)control ) {
+                int columnCount = tree.getColumnCount ();
+                if (columnCount > 1) {
+                    HWND hwnd = control.handle;
+                    int hItem;
+                    if (OS.COMCTL32_MAJOR >= 6) {
+                        hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
+                    } else {
+                        hItem = v.lVal;
+                    }
+                    Widget widget = tree.getDisplay ().findWidget (hwnd, hItem);
+                    event.result = "";
+                    if (widget !is null ) if( auto item = cast(TreeItem) widget ) {
+                        for (int i = 1; i < columnCount; i++) {
+                            event.result ~= tree.getColumn(i).getText() ~ ": " ~ item.getText(i);
+                            if (i + 1 < columnCount) event.result ~= ", ";
+                        }
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < accessibleListeners.length; i++) {
+            AccessibleListener listener = cast(AccessibleListener) accessibleListeners[i];
+            listener.getDescription(event);
+        }
+        if (event.result is null) return code;
+        auto ptr = COM.SysAllocString(StrToWCHARz(event.result));
+        *pszDescription = ptr;
+        return COM.S_OK;
+    }
+
+    /* get_accFocus([out] int pvarChild)
+     * Ownership of pvarChild transfers from callee to caller so reference count on pvarChild
+     * must be incremented before returning.  The caller is responsible for releasing pvarChild.
+     */
+    HRESULT get_accFocus(VARIANT* pvarChild) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        /* Get the default focus child from the OS. */
+        int osChild = ACC.CHILDID_NONE;
+        int code = iaccessible.get_accFocus(pvarChild);
+        if (accessibleControlListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            //TODO - use VARIANT structure
+            short[1] pvt;
+            COM.MoveMemory(pvt.ptr, pvarChild, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[1] pChild;
+                COM.MoveMemory(pChild.ptr, pvarChild + 8, 4);
+                osChild = osToChildID(pChild[0]);
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osChild;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getFocus(event);
+        }
+        Accessible accessible = event.accessible;
+        if (accessible !is null) {
+            accessible.AddRef();
+            pvarChild.vt = COM.VT_DISPATCH;
+            pvarChild.byRef = cast(void*)cast(IDispatch)accessible.objIAccessible;
+            return COM.S_OK;
+        }
+        int childID = event.childID;
+        if (childID is ACC.CHILDID_NONE) {
+            pvarChild.vt = COM.VT_EMPTY;
+            return COM.S_FALSE;
+        }
+        if (childID is ACC.CHILDID_SELF) {
+            AddRef();
+            pvarChild.vt = COM.VT_DISPATCH;
+            pvarChild.byRef = cast(void*)cast(IDispatch)objIAccessible;
+            return COM.S_OK;
+        }
+        pvarChild.vt = COM.VT_I4;
+        pvarChild.lVal = childIDToOs(childID);
+        return COM.S_OK;
+    }
+
+    HRESULT get_accHelp(VARIANT variant, BSTR* pszHelp) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default help string from the OS. */
+        String osHelp = null;
+        int code = iaccessible.get_accHelp(variant, pszHelp);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            // the original SysString is clearuped and bstr set to null
+            osHelp = BSTRToStr(*pszHelp, true);
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.result = osHelp;
+        for (int i = 0; i < accessibleListeners.length; i++) {
+            AccessibleListener listener = cast(AccessibleListener) accessibleListeners[i];
+            listener.getHelp(event);
+        }
+        if (event.result is null) return code;
+        auto ptr = COM.SysAllocString(StrToWCHARz(event.result));
+        *pszHelp = ptr;
+        return COM.S_OK;
+    }
+
+    HRESULT get_accHelpTopic(BSTR* pszHelpFile, VARIANT variant, LONG* pidTopic) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        int code = iaccessible.get_accHelpTopic(pszHelpFile, variant, pidTopic);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    HRESULT get_accKeyboardShortcut(VARIANT variant, BSTR* pszKeyboardShortcut) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default keyboard shortcut from the OS. */
+        String osKeyboardShortcut = null;
+        int code = iaccessible.get_accKeyboardShortcut(variant, pszKeyboardShortcut);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            // the original SysString is clearuped and bstr set to null
+            osKeyboardShortcut = BSTRToStr(*pszKeyboardShortcut, true);
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.result = osKeyboardShortcut;
+        for (int i = 0; i < accessibleListeners.length; i++) {
+            AccessibleListener listener = cast(AccessibleListener) accessibleListeners[i];
+            listener.getKeyboardShortcut(event);
+        }
+        if (event.result is null) return code;
+        auto ptr = COM.SysAllocString(StrToWCHARz(event.result));
+        *pszKeyboardShortcut = ptr;
+        return COM.S_OK;
+    }
+
+    HRESULT get_accName(VARIANT variant, BSTR* pszName) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default name from the OS. */
+        String osName = null;
+        int code = iaccessible.get_accName(variant, pszName);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            // the original SysString is clearuped and bstr set to null
+            osName = BSTRToStr(*pszName, true);
+        }
+
+        AccessibleEvent event = new AccessibleEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.result = osName;
+        for (int i = 0; i < accessibleListeners.length; i++) {
+            AccessibleListener listener = cast(AccessibleListener) accessibleListeners[i];
+            listener.getName(event);
+        }
+        if (event.result is null) return code;
+        auto ptr = COM.SysAllocString(StrToWCHARz(event.result));
+        *pszName = ptr;
+        return COM.S_OK;
+    }
+
+    /* get_accParent([out] ppdispParent)
+     * Ownership of ppdispParent transfers from callee to caller so reference count on ppdispParent
+     * must be incremented before returning.  The caller is responsible for releasing ppdispParent.
+     */
+    HRESULT get_accParent(LPDISPATCH* ppdispParent) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // Currently, we don't let the application override this. Forward to the proxy.
+        return iaccessible.get_accParent(ppdispParent);
+    }
+
+    HRESULT get_accRole(VARIANT variant, VARIANT* pvarRole) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default role from the OS. */
+        int osRole = COM.ROLE_SYSTEM_CLIENT;
+        int code = iaccessible.get_accRole(variant, pvarRole);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        // TEMPORARY CODE - process tree and table even if there are no apps listening
+        if (accessibleControlListeners.length is 0 && !( null !is cast(Tree)control || null !is cast(Table)control )) return code;
+        if (code is COM.S_OK) {
+            //TODO - use VARIANT structure
+            short[1] pvt;
+            COM.MoveMemory(pvt.ptr, pvarRole, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[1] pRole;
+                COM.MoveMemory(pRole.ptr, pvarRole + 8, 4);
+                osRole = pRole[0];
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.detail = osToRole(osRole);
+        // TEMPORARY CODE
+        /* Currently our checkbox table and tree are emulated using state mask
+         * images, so we need to specify 'checkbox' role for the items. */
+        if (v.lVal !is COM.CHILDID_SELF) {
+            if ( null !is cast(Tree)control || null !is cast(Table)control ) {
+                if ((control.getStyle() & SWT.CHECK) !is 0) event.detail = ACC.ROLE_CHECKBUTTON;
+            }
+        }
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getRole(event);
+        }
+        int role = roleToOs(event.detail);
+        pvarRole.vt = COM.VT_I4;
+        pvarRole.lVal = role;
+        return COM.S_OK;
+    }
+
+    /* get_accSelection([out] pvarChildren)
+     * Ownership of pvarChildren transfers from callee to caller so reference count on pvarChildren
+     * must be incremented before returning.  The caller is responsible for releasing pvarChildren.
+     */
+    HRESULT get_accSelection(VARIANT* pvarChildren) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+
+        /* Get the default selection from the OS. */
+        int osChild = ACC.CHILDID_NONE;
+        int code = iaccessible.get_accSelection(pvarChildren);
+        if (accessibleControlListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            //TODO - use VARIANT structure
+            short[1] pvt;
+            COM.MoveMemory(pvt.ptr, pvarChildren, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[1] pChild;
+                COM.MoveMemory(pChild.ptr, pvarChildren + 8, 4);
+                osChild = osToChildID(pChild[0]);
+            } else if (pvt[0] is COM.VT_UNKNOWN) {
+                osChild = ACC.CHILDID_MULTIPLE;
+                /* Should get IEnumVARIANT from punkVal field, and enumerate children... */
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osChild;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getSelection(event);
+        }
+        Accessible accessible = event.accessible;
+        if (accessible !is null) {
+            accessible.AddRef();
+            pvarChildren.vt = COM.VT_DISPATCH;
+            pvarChildren.byRef = cast(void*)cast(IDispatch)accessible.objIAccessible;
+            return COM.S_OK;
+        }
+        int childID = event.childID;
+        if (childID is ACC.CHILDID_NONE) {
+            pvarChildren.vt = COM.VT_EMPTY;
+            return COM.S_FALSE;
+        }
+        if (childID is ACC.CHILDID_MULTIPLE) {
+            AddRef();
+            pvarChildren.vt = COM.VT_UNKNOWN;
+            pvarChildren.byRef = cast(void*)cast(IUnknown)objIAccessible;
+            return COM.S_OK;
+        }
+        if (childID is ACC.CHILDID_SELF) {
+            AddRef();
+            pvarChildren.vt = COM.VT_DISPATCH;
+            pvarChildren.byRef = cast(void*)cast(IDispatch)objIAccessible;
+            return COM.S_OK;
+        }
+        pvarChildren.vt = COM.VT_I4;
+        pvarChildren.lVal = childIDToOs(childID);
+        return COM.S_OK;
+    }
+
+    HRESULT get_accState(VARIANT variant, VARIANT* pvarState) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default state from the OS. */
+        int osState = 0;
+        int code = iaccessible.get_accState(variant, pvarState);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        // TEMPORARY CODE - process tree and table even if there are no apps listening
+        if (accessibleControlListeners.length is 0 && !( null !is cast(Tree)control || null !is cast(Table)control )) return code;
+        if (code is COM.S_OK) {
+            //TODO - use VARIANT structure
+            short[1] pvt;
+            COM.MoveMemory(pvt.ptr, pvarState, 2);
+            if (pvt[0] is COM.VT_I4) {
+                int[1] pState;
+                COM.MoveMemory(pState.ptr, pvarState + 8, 4);
+                osState = pState[0];
+            }
+        }
+        bool grayed = false;
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.detail = osToState(osState);
+        // TEMPORARY CODE
+        /* Currently our checkbox table and tree are emulated using state mask
+         * images, so we need to determine if the item state is 'checked'. */
+        if (v.lVal !is COM.CHILDID_SELF) {
+            if (null !is cast(Tree)control && (control.getStyle() & SWT.CHECK) !is 0) {
+                auto hwnd = control.handle;
+                TVITEM tvItem;
+                tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+                if (OS.COMCTL32_MAJOR >= 6) {
+                    tvItem.hItem = cast(HTREEITEM) OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
+                } else {
+                    tvItem.hItem = cast(HTREEITEM) v.lVal;
+                }
+                auto result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, &tvItem);
+                bool checked = (result !is 0) && (((tvItem.state >> 12) & 1) is 0);
+                if (checked) event.detail |= ACC.STATE_CHECKED;
+                grayed = tvItem.state >> 12 > 2;
+            } else if (null !is cast(Table)control && (control.getStyle() & SWT.CHECK) !is 0) {
+                Table table = cast(Table) control;
+                int index = event.childID;
+                if (0 <= index && index < table.getItemCount()) {
+                    TableItem item = table.getItem(index);
+                    if (item.getChecked()) event.detail |= ACC.STATE_CHECKED;
+                    if (item.getGrayed()) grayed = true;
+                }
+            }
+        }
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getState(event);
+        }
+        int state = stateToOs(event.detail);
+        if ((state & ACC.STATE_CHECKED) !is 0 && grayed) {
+            state &= ~ COM.STATE_SYSTEM_CHECKED;
+            state |= COM.STATE_SYSTEM_MIXED;
+        }
+        pvarState.vt = COM.VT_I4;
+        pvarState.lVal = state;
+        return COM.S_OK;
+    }
+
+    HRESULT get_accValue(VARIANT variant, BSTR* pszValue) {
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        VARIANT* v = &variant;
+        //COM.MoveMemory(v, variant, VARIANT.sizeof);
+        if ((v.vt & 0xFFFF) !is COM.VT_I4) return COM.E_INVALIDARG;
+
+        /* Get the default value string from the OS. */
+        String osValue = null;
+        int code = iaccessible.get_accValue(variant, pszValue);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        if (accessibleControlListeners.length is 0) return code;
+        if (code is COM.S_OK) {
+            int size = COM.SysStringByteLen(*pszValue);
+            if (size > 0) {
+                osValue = WCHARsToStr((*pszValue)[ 0 .. size ]);
+            }
+        }
+
+        AccessibleControlEvent event = new AccessibleControlEvent(this);
+        event.childID = osToChildID(v.lVal);
+        event.result = osValue;
+        for (int i = 0; i < accessibleControlListeners.length; i++) {
+            AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+            listener.getValue(event);
+        }
+        if (event.result is null) return code;
+        auto ptr = COM.SysAllocString(StrToWCHARz(event.result));
+        *pszValue = ptr;
+        return COM.S_OK;
+    }
+
+    HRESULT put_accName(VARIANT variant, BSTR* szName) {
+        // MSAA: this method is no longer supported
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // We don't implement this. Forward to the proxy.
+        int code = iaccessible.put_accName(variant, szName);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    HRESULT put_accValue(VARIANT variant, BSTR* szValue) {
+        // MSAA: this method is typically only used for edit controls
+        if (iaccessible is null) return COM.CO_E_OBJNOTCONNECTED;
+        // We don't implement this. Forward to the proxy.
+        int code = iaccessible.put_accValue(variant, szValue);
+        if (code is COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+        return code;
+    }
+
+    /* IEnumVARIANT methods: Next, Skip, Reset, Clone */
+    /* Retrieve the next celt items in the enumeration sequence.
+     * If there are fewer than the requested number of elements left
+     * in the sequence, retrieve the remaining elements.
+     * The number of elements actually retrieved is returned in pceltFetched
+     * (unless the caller passed in NULL for that parameter).
+     */
+
+     /* Next([in] celt, [out] rgvar, [in, out] pceltFetched)
+     * Ownership of rgvar transfers from callee to caller so reference count on rgvar
+     * must be incremented before returning.  The caller is responsible for releasing rgvar.
+     */
+    HRESULT Next(ULONG celt, VARIANT *rgvar, ULONG *pceltFetched) {
+        /* If there are no listeners, query the proxy for
+         * its IEnumVariant, and get the Next items from it.
+         */
+        if (accessibleControlListeners.length is 0) {
+            IEnumVARIANT ienumvariant;
+            int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
+            if (code !is COM.S_OK) return code;
+            uint[1] celtFetched;
+            code = ienumvariant.Next(celt, rgvar, celtFetched.ptr);
+            ienumvariant.Release();
+            COM.MoveMemory(pceltFetched, celtFetched.ptr, 4);
+            return code;
+        }
+
+        if (rgvar is null) return COM.E_INVALIDARG;
+        if (pceltFetched is null && celt !is 1) return COM.E_INVALIDARG;
+        if (enumIndex is 0) {
+            AccessibleControlEvent event = new AccessibleControlEvent(this);
+            event.childID = ACC.CHILDID_SELF;
+            for (int i = 0; i < accessibleControlListeners.length; i++) {
+                AccessibleControlListener listener = cast(AccessibleControlListener) accessibleControlListeners[i];
+                listener.getChildren(event);
+            }
+            variants = event.children;
+        }
+        Object[] nextItems = null;
+        if (variants !is null && celt >= 1) {
+            int endIndex = enumIndex + celt - 1;
+            if (endIndex > (variants.length - 1)) endIndex = variants.length - 1;
+            if (enumIndex <= endIndex) {
+                nextItems = new Object[endIndex - enumIndex + 1];
+                for (int i = 0; i < nextItems.length; i++) {
+                    Object child = variants[enumIndex];
+                    if (auto val = cast(ValueWrapperInt)child ) {
+                        nextItems[i] = new ValueWrapperInt(childIDToOs(val.value));
+                    } else {
+                        nextItems[i] = child;
+                    }
+                    enumIndex++;
+                }
+            }
+        }
+        if (nextItems !is null) {
+            for (int i = 0; i < nextItems.length; i++) {
+                Object nextItem = nextItems[i];
+                if (auto val = cast(ValueWrapperInt)nextItem ) {
+                    int item = val.value;
+                    rgvar[i].vt = COM.VT_I4;
+                    rgvar[i].byRef = cast(void*)item;
+                    COM.MoveMemory(rgvar + i * VARIANT.sizeof + 8, &item, 4);
+                } else {
+                    Accessible accessible = cast(Accessible) nextItem;
+                    accessible.AddRef();
+                    rgvar[i].vt = COM.VT_DISPATCH;
+                    rgvar[i].byRef = cast(void*)accessible.objIAccessible;
+                }
+            }
+            if (pceltFetched !is null)
+                *pceltFetched = nextItems.length;
+            if (nextItems.length is celt) return COM.S_OK;
+        } else {
+            if (pceltFetched !is null){
+                int zero = 0;
+                *pceltFetched = 0;
+            }
+        }
+        return COM.S_FALSE;
+    }
+
+    /* Skip over the specified number of elements in the enumeration sequence. */
+    HRESULT Skip(ULONG celt)  {
+        /* If there are no listeners, query the proxy
+         * for its IEnumVariant, and tell it to Skip.
+         */
+        if (accessibleControlListeners.length is 0) {
+            IEnumVARIANT ienumvariant;
+            int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
+            if (code !is COM.S_OK) return code;
+            code = ienumvariant.Skip(celt);
+            ienumvariant.Release();
+            return code;
+        }
+
+        if (celt < 1 ) return COM.E_INVALIDARG;
+        enumIndex += celt;
+        if (enumIndex > (variants.length - 1)) {
+            enumIndex = variants.length - 1;
+            return COM.S_FALSE;
+        }
+        return COM.S_OK;
+    }
+
+    /* Reset the enumeration sequence to the beginning. */
+    HRESULT Reset() {
+        /* If there are no listeners, query the proxy
+         * for its IEnumVariant, and tell it to Reset.
+         */
+        if (accessibleControlListeners.length is 0) {
+            IEnumVARIANT ienumvariant;
+            int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
+            if (code !is COM.S_OK) return code;
+            code = ienumvariant.Reset();
+            ienumvariant.Release();
+            return code;
+        }
+
+        enumIndex = 0;
+        return COM.S_OK;
+    }
+
+     /* Clone([out] ppEnum)
+     * Ownership of ppEnum transfers from callee to caller so reference count on ppEnum
+     * must be incremented before returning.  The caller is responsible for releasing ppEnum.
+     */
+    int Clone(IEnumVARIANT* ppEnum) {
+        /* If there are no listeners, query the proxy for
+         * its IEnumVariant, and get the Clone from it.
+         */
+        if (accessibleControlListeners.length is 0) {
+            IEnumVARIANT ienumvariant;
+            int code = iaccessible.QueryInterface(&COM.IIDIEnumVARIANT, cast(void**)&ienumvariant);
+            if (code !is COM.S_OK) return code;
+            IEnumVARIANT[1] pEnum;
+            code = ienumvariant.Clone(pEnum.ptr);
+            ienumvariant.Release();
+            COM.MoveMemory(ppEnum, pEnum.ptr, (void*).sizeof);
+            return code;
+        }
+
+        if (ppEnum is null) return COM.E_INVALIDARG;
+        *ppEnum = objIEnumVARIANT;
+        AddRef();
+        return COM.S_OK;
+    }
+
+    int childIDToOs(int childID) {
+        if (childID is ACC.CHILDID_SELF) return COM.CHILDID_SELF;
+        /*
+        * Feature of Windows:
+        * In Windows XP, tree item ids are 1-based indices. Previous versions
+        * of Windows use the tree item handle for the accessible child ID.
+        * For backward compatibility, we still take a handle childID for tree
+        * items on XP. All other childIDs are 1-based indices.
+        */
+        if (!(cast(Tree)control )) return childID + 1;
+        if (OS.COMCTL32_MAJOR < 6) return childID;
+        return OS.SendMessage (control.handle, OS.TVM_MAPHTREEITEMTOACCID, childID, 0);
+    }
+
+    int osToChildID(int osChildID) {
+        if (osChildID is COM.CHILDID_SELF) return ACC.CHILDID_SELF;
+        /*
+        * Feature of Windows:
+        * In Windows XP, tree item ids are 1-based indices. Previous versions
+        * of Windows use the tree item handle for the accessible child ID.
+        * For backward compatibility, we still take a handle childID for tree
+        * items on XP. All other childIDs are 1-based indices.
+        */
+        if (!(cast(Tree)control )) return osChildID - 1;
+        if (OS.COMCTL32_MAJOR < 6) return osChildID;
+        return OS.SendMessage (control.handle, OS.TVM_MAPACCIDTOHTREEITEM, osChildID, 0);
+    }
+
+    int stateToOs(int state) {
+        int osState = 0;
+        if ((state & ACC.STATE_SELECTED) !is 0) osState |= COM.STATE_SYSTEM_SELECTED;
+        if ((state & ACC.STATE_SELECTABLE) !is 0) osState |= COM.STATE_SYSTEM_SELECTABLE;
+        if ((state & ACC.STATE_MULTISELECTABLE) !is 0) osState |= COM.STATE_SYSTEM_MULTISELECTABLE;
+        if ((state & ACC.STATE_FOCUSED) !is 0) osState |= COM.STATE_SYSTEM_FOCUSED;
+        if ((state & ACC.STATE_FOCUSABLE) !is 0) osState |= COM.STATE_SYSTEM_FOCUSABLE;
+        if ((state & ACC.STATE_PRESSED) !is 0) osState |= COM.STATE_SYSTEM_PRESSED;
+        if ((state & ACC.STATE_CHECKED) !is 0) osState |= COM.STATE_SYSTEM_CHECKED;
+        if ((state & ACC.STATE_EXPANDED) !is 0) osState |= COM.STATE_SYSTEM_EXPANDED;
+        if ((state & ACC.STATE_COLLAPSED) !is 0) osState |= COM.STATE_SYSTEM_COLLAPSED;
+        if ((state & ACC.STATE_HOTTRACKED) !is 0) osState |= COM.STATE_SYSTEM_HOTTRACKED;
+        if ((state & ACC.STATE_BUSY) !is 0) osState |= COM.STATE_SYSTEM_BUSY;
+        if ((state & ACC.STATE_READONLY) !is 0) osState |= COM.STATE_SYSTEM_READONLY;
+        if ((state & ACC.STATE_INVISIBLE) !is 0) osState |= COM.STATE_SYSTEM_INVISIBLE;
+        if ((state & ACC.STATE_OFFSCREEN) !is 0) osState |= COM.STATE_SYSTEM_OFFSCREEN;
+        if ((state & ACC.STATE_SIZEABLE) !is 0) osState |= COM.STATE_SYSTEM_SIZEABLE;
+        if ((state & ACC.STATE_LINKED) !is 0) osState |= COM.STATE_SYSTEM_LINKED;
+        return osState;
+    }
+
+    int osToState(int osState) {
+        int state = ACC.STATE_NORMAL;
+        if ((osState & COM.STATE_SYSTEM_SELECTED) !is 0) state |= ACC.STATE_SELECTED;
+        if ((osState & COM.STATE_SYSTEM_SELECTABLE) !is 0) state |= ACC.STATE_SELECTABLE;
+        if ((osState & COM.STATE_SYSTEM_MULTISELECTABLE) !is 0) state |= ACC.STATE_MULTISELECTABLE;
+        if ((osState & COM.STATE_SYSTEM_FOCUSED) !is 0) state |= ACC.STATE_FOCUSED;
+        if ((osState & COM.STATE_SYSTEM_FOCUSABLE) !is 0) state |= ACC.STATE_FOCUSABLE;
+        if ((osState & COM.STATE_SYSTEM_PRESSED) !is 0) state |= ACC.STATE_PRESSED;
+        if ((osState & COM.STATE_SYSTEM_CHECKED) !is 0) state |= ACC.STATE_CHECKED;
+        if ((osState & COM.STATE_SYSTEM_EXPANDED) !is 0) state |= ACC.STATE_EXPANDED;
+        if ((osState & COM.STATE_SYSTEM_COLLAPSED) !is 0) state |= ACC.STATE_COLLAPSED;
+        if ((osState & COM.STATE_SYSTEM_HOTTRACKED) !is 0) state |= ACC.STATE_HOTTRACKED;
+        if ((osState & COM.STATE_SYSTEM_BUSY) !is 0) state |= ACC.STATE_BUSY;
+        if ((osState & COM.STATE_SYSTEM_READONLY) !is 0) state |= ACC.STATE_READONLY;
+        if ((osState & COM.STATE_SYSTEM_INVISIBLE) !is 0) state |= ACC.STATE_INVISIBLE;
+        if ((osState & COM.STATE_SYSTEM_OFFSCREEN) !is 0) state |= ACC.STATE_OFFSCREEN;
+        if ((osState & COM.STATE_SYSTEM_SIZEABLE) !is 0) state |= ACC.STATE_SIZEABLE;
+        if ((osState & COM.STATE_SYSTEM_LINKED) !is 0) state |= ACC.STATE_LINKED;
+        return state;
+    }
+
+    int roleToOs(int role) {
+        switch (role) {
+            case ACC.ROLE_CLIENT_AREA: return COM.ROLE_SYSTEM_CLIENT;
+            case ACC.ROLE_WINDOW: return COM.ROLE_SYSTEM_WINDOW;
+            case ACC.ROLE_MENUBAR: return COM.ROLE_SYSTEM_MENUBAR;
+            case ACC.ROLE_MENU: return COM.ROLE_SYSTEM_MENUPOPUP;
+            case ACC.ROLE_MENUITEM: return COM.ROLE_SYSTEM_MENUITEM;
+            case ACC.ROLE_SEPARATOR: return COM.ROLE_SYSTEM_SEPARATOR;
+            case ACC.ROLE_TOOLTIP: return COM.ROLE_SYSTEM_TOOLTIP;
+            case ACC.ROLE_SCROLLBAR: return COM.ROLE_SYSTEM_SCROLLBAR;
+            case ACC.ROLE_DIALOG: return COM.ROLE_SYSTEM_DIALOG;
+            case ACC.ROLE_LABEL: return COM.ROLE_SYSTEM_STATICTEXT;
+            case ACC.ROLE_PUSHBUTTON: return COM.ROLE_SYSTEM_PUSHBUTTON;
+            case ACC.ROLE_CHECKBUTTON: return COM.ROLE_SYSTEM_CHECKBUTTON;
+            case ACC.ROLE_RADIOBUTTON: return COM.ROLE_SYSTEM_RADIOBUTTON;
+            case ACC.ROLE_COMBOBOX: return COM.ROLE_SYSTEM_COMBOBOX;
+            case ACC.ROLE_TEXT: return COM.ROLE_SYSTEM_TEXT;
+            case ACC.ROLE_TOOLBAR: return COM.ROLE_SYSTEM_TOOLBAR;
+            case ACC.ROLE_LIST: return COM.ROLE_SYSTEM_LIST;
+            case ACC.ROLE_LISTITEM: return COM.ROLE_SYSTEM_LISTITEM;
+            case ACC.ROLE_TABLE: return COM.ROLE_SYSTEM_TABLE;
+            case ACC.ROLE_TABLECELL: return COM.ROLE_SYSTEM_CELL;
+            case ACC.ROLE_TABLECOLUMNHEADER: return COM.ROLE_SYSTEM_COLUMNHEADER;
+            case ACC.ROLE_TABLEROWHEADER: return COM.ROLE_SYSTEM_ROWHEADER;
+            case ACC.ROLE_TREE: return COM.ROLE_SYSTEM_OUTLINE;
+            case ACC.ROLE_TREEITEM: return COM.ROLE_SYSTEM_OUTLINEITEM;
+            case ACC.ROLE_TABFOLDER: return COM.ROLE_SYSTEM_PAGETABLIST;
+            case ACC.ROLE_TABITEM: return COM.ROLE_SYSTEM_PAGETAB;
+            case ACC.ROLE_PROGRESSBAR: return COM.ROLE_SYSTEM_PROGRESSBAR;
+            case ACC.ROLE_SLIDER: return COM.ROLE_SYSTEM_SLIDER;
+            case ACC.ROLE_LINK: return COM.ROLE_SYSTEM_LINK;
+            default:
+        }
+        return COM.ROLE_SYSTEM_CLIENT;
+    }
+
+    int osToRole(int osRole) {
+        switch (osRole) {
+            case COM.ROLE_SYSTEM_CLIENT: return ACC.ROLE_CLIENT_AREA;
+            case COM.ROLE_SYSTEM_WINDOW: return ACC.ROLE_WINDOW;
+            case COM.ROLE_SYSTEM_MENUBAR: return ACC.ROLE_MENUBAR;
+            case COM.ROLE_SYSTEM_MENUPOPUP: return ACC.ROLE_MENU;
+            case COM.ROLE_SYSTEM_MENUITEM: return ACC.ROLE_MENUITEM;
+            case COM.ROLE_SYSTEM_SEPARATOR: return ACC.ROLE_SEPARATOR;
+            case COM.ROLE_SYSTEM_TOOLTIP: return ACC.ROLE_TOOLTIP;
+            case COM.ROLE_SYSTEM_SCROLLBAR: return ACC.ROLE_SCROLLBAR;
+            case COM.ROLE_SYSTEM_DIALOG: return ACC.ROLE_DIALOG;
+            case COM.ROLE_SYSTEM_STATICTEXT: return ACC.ROLE_LABEL;
+            case COM.ROLE_SYSTEM_PUSHBUTTON: return ACC.ROLE_PUSHBUTTON;
+            case COM.ROLE_SYSTEM_CHECKBUTTON: return ACC.ROLE_CHECKBUTTON;
+            case COM.ROLE_SYSTEM_RADIOBUTTON: return ACC.ROLE_RADIOBUTTON;
+            case COM.ROLE_SYSTEM_COMBOBOX: return ACC.ROLE_COMBOBOX;
+            case COM.ROLE_SYSTEM_TEXT: return ACC.ROLE_TEXT;
+            case COM.ROLE_SYSTEM_TOOLBAR: return ACC.ROLE_TOOLBAR;
+            case COM.ROLE_SYSTEM_LIST: return ACC.ROLE_LIST;
+            case COM.ROLE_SYSTEM_LISTITEM: return ACC.ROLE_LISTITEM;
+            case COM.ROLE_SYSTEM_TABLE: return ACC.ROLE_TABLE;
+            case COM.ROLE_SYSTEM_CELL: return ACC.ROLE_TABLECELL;
+            case COM.ROLE_SYSTEM_COLUMNHEADER: return ACC.ROLE_TABLECOLUMNHEADER;
+            case COM.ROLE_SYSTEM_ROWHEADER: return ACC.ROLE_TABLEROWHEADER;
+            case COM.ROLE_SYSTEM_OUTLINE: return ACC.ROLE_TREE;
+            case COM.ROLE_SYSTEM_OUTLINEITEM: return ACC.ROLE_TREEITEM;
+            case COM.ROLE_SYSTEM_PAGETABLIST: return ACC.ROLE_TABFOLDER;
+            case COM.ROLE_SYSTEM_PAGETAB: return ACC.ROLE_TABITEM;
+            case COM.ROLE_SYSTEM_PROGRESSBAR: return ACC.ROLE_PROGRESSBAR;
+            case COM.ROLE_SYSTEM_SLIDER: return ACC.ROLE_SLIDER;
+            case COM.ROLE_SYSTEM_LINK: return ACC.ROLE_LINK;
+            default:
+        }
+        return ACC.ROLE_CLIENT_AREA;
+    }
+
+    /* checkWidget was copied from Widget, and rewritten to work in this package */
+    void checkWidget () {
+        if (!isValidThread ()) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS);
+        if (control.isDisposed ()) SWT.error (SWT.ERROR_WIDGET_DISPOSED);
+    }
+
+    /* isValidThread was copied from Widget, and rewritten to work in this package */
+    WINBOOL isValidThread () {
+        return control.getDisplay ().getThread () is Thread.getThis ();
+    }
+}
+
+class _IAccessibleImpl : IAccessible {
+
+    Accessible  parent;
+    this(Accessible p) { parent = p; }
+extern (Windows):
+    // interface of IUnknown
+    HRESULT QueryInterface(REFIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); }
+    ULONG AddRef()  { return parent.AddRef(); }
+    ULONG Release() { return parent.Release(); }
+
+    // interface of IDispatch
+    HRESULT GetTypeInfoCount(UINT * pctinfo) { return COM.E_NOTIMPL; }
+    HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo * ppTInfo) { return COM.E_NOTIMPL; }
+    HRESULT GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId) { return COM.E_NOTIMPL; }
+    HRESULT Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS* pDispParams,VARIANT* pVarResult,EXCEPINFO* pExcepInfo,UINT* puArgErr) { return COM.E_NOTIMPL; }
+
+    // interface of IAccessible
+    HRESULT get_accParent(LPDISPATCH * ppdispParent) { return parent.get_accParent(ppdispParent); }
+    HRESULT get_accChildCount(LONG* pcountChildren) { return parent.get_accChildCount(pcountChildren); }
+    HRESULT get_accChild(VARIANT varChildID, LPDISPATCH* ppdispChild) {
+        return parent.get_accChild(varChildID, ppdispChild);
+    }
+    HRESULT get_accName(VARIANT varID, BSTR* pszName) {
+        return parent.get_accName(varID, pszName);
+    }
+    HRESULT get_accValue(VARIANT varID, BSTR* pszValue) {
+        return parent.get_accValue(varID, pszValue);
+    }
+    HRESULT get_accDescription(VARIANT varID,BSTR* pszDescription) {
+        return parent.get_accDescription(varID, pszDescription);
+    }
+    HRESULT get_accRole(VARIANT varID, VARIANT* pvarRole) {
+        return parent.get_accRole(varID, pvarRole);
+    }
+    HRESULT get_accState(VARIANT varID, VARIANT* pvarState) {
+        return parent.get_accState(varID, pvarState);
+    }
+    HRESULT get_accHelp(VARIANT varID, BSTR* pszHelp) {
+        return parent.get_accHelp(varID, pszHelp);
+    }
+    HRESULT get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, LONG* pidTopic) {
+        return parent.get_accHelpTopic(pszHelpFile, varChild, pidTopic);
+    }
+    HRESULT get_accKeyboardShortcut(VARIANT varID, BSTR* pszKeyboardShortcut) {
+        return parent.get_accKeyboardShortcut(varID, pszKeyboardShortcut);
+    }
+    HRESULT get_accFocus(VARIANT* pvarID) { return parent.get_accFocus(pvarID); }
+    HRESULT get_accSelection(VARIANT* pvarChildren) { return parent.get_accSelection(pvarChildren); }
+    HRESULT get_accDefaultAction(VARIANT varID,BSTR* pszDefaultAction) {
+        return parent.get_accDefaultAction(varID, pszDefaultAction);
+    }
+    HRESULT accSelect(LONG flagsSelect, VARIANT varID) {
+        return parent.accSelect(flagsSelect, varID);
+    }
+    HRESULT accLocation(LONG* pxLeft, LONG* pyTop, LONG* pcxWidth, LONG* pcyHeight, VARIANT varID) {
+        return parent.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varID);
+    }
+    HRESULT accNavigate(LONG navDir, VARIANT varStart, VARIANT* pvarEnd) {
+        return parent.accNavigate(navDir, varStart, pvarEnd);
+    }
+    HRESULT accHitTest(LONG xLeft,  LONG yTop, VARIANT* pvarID) {
+        return parent.accHitTest(xLeft, yTop, pvarID);
+    }
+    HRESULT accDoDefaultAction(VARIANT varID) {
+        return parent.accDoDefaultAction(varID);
+    }
+    HRESULT put_accName(VARIANT varID, BSTR* szName) {
+        return parent.put_accName(varID, szName);
+    }
+    HRESULT put_accValue(VARIANT varID, BSTR* szValue) {
+        return parent.put_accValue(varID, szValue);
+    }
+}
+
+class _IEnumVARIANTImpl : IEnumVARIANT {
+
+    Accessible  parent;
+    this(Accessible a) { parent = a; }
+extern (Windows):
+    // interface of IUnknown
+    HRESULT QueryInterface(REFIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); }
+    ULONG AddRef()  { return parent.AddRef(); }
+    ULONG Release() { return parent.Release(); }
+
+    // interface of IEnumVARIANT
+    HRESULT Next(ULONG celt, VARIANT *rgvar, ULONG *pceltFetched) { return parent.Next(celt, rgvar, pceltFetched); }
+    HRESULT Skip(ULONG celt) { return parent.Skip(celt); }
+    HRESULT Reset() { return parent.Reset(); }
+    HRESULT Clone(LPENUMVARIANT * ppenum) { return COM.E_NOTIMPL;}
+}
+