]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/accessibility/Accessible.java
Work around SWT 4.13 - 4.18 Win32 DnD bug 567422
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / accessibility / Accessible.java
diff --git a/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/accessibility/Accessible.java b/bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/accessibility/Accessible.java
new file mode 100644 (file)
index 0000000..05f6930
--- /dev/null
@@ -0,0 +1,5556 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2017 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.accessibility;
+
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.internal.*;
+import org.eclipse.swt.internal.ole.win32.*;
+import org.eclipse.swt.internal.win32.*;
+import org.eclipse.swt.ole.win32.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * 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 {
+       static final int MAX_RELATION_TYPES = 15;
+       static final int TABLE_MODEL_CHANGE_SIZE = 5;
+       static final int TEXT_CHANGE_SIZE = 4;
+       static final int SCROLL_RATE = 100;
+       static final boolean DEBUG = false;
+       static final String PROPERTY_USEIA2 = "org.eclipse.swt.accessibility.UseIA2"; //$NON-NLS-1$
+       static boolean UseIA2 = true;
+       static int UniqueID = -0x10;
+       int refCount = 0, enumIndex = 0;
+       Runnable timer;
+       COMObject objIAccessible, objIEnumVARIANT, objIServiceProvider,
+               objIAccessibleApplication, /*objIAccessibleComponent,*/ objIAccessibleEditableText, objIAccessibleHyperlink,
+               objIAccessibleHypertext, /*objIAccessibleImage,*/ objIAccessibleTable2, objIAccessibleTableCell,
+               objIAccessibleValue; /* objIAccessibleRelation is defined in Relation class */
+       IAccessible iaccessible;
+       List<AccessibleListener> accessibleListeners;
+       List<AccessibleControlListener> accessibleControlListeners;
+       List<AccessibleTextListener> accessibleTextListeners;
+       List<AccessibleActionListener> accessibleActionListeners;
+       List<AccessibleEditableTextListener> accessibleEditableTextListeners;
+       List<AccessibleHyperlinkListener> accessibleHyperlinkListeners;
+       List<AccessibleTableListener> accessibleTableListeners;
+       List<AccessibleTableCellListener> accessibleTableCellListeners;
+       List<AccessibleTextExtendedListener> accessibleTextExtendedListeners;
+       List<AccessibleValueListener> accessibleValueListeners;
+       List<AccessibleAttributeListener> accessibleAttributeListeners;
+       Relation relations[] = new Relation[MAX_RELATION_TYPES];
+       Object[] variants;
+       Accessible parent;
+       List<Accessible> children = new ArrayList<>();
+       Control control;
+       int uniqueID = -1;
+       int [] tableChange; // type, rowStart, rowCount, columnStart, columnCount
+       Object [] textDeleted; // type, start, end, text
+       Object [] textInserted; // type, start, end, text
+       ToolItem item;
+
+       static {
+               String property = System.getProperty (PROPERTY_USEIA2);
+               if (property != null && property.equalsIgnoreCase ("false")) { //$NON-NLS-1$
+                       UseIA2 = false;
+               }
+       }
+
+       /**
+        * Constructs a new instance of this class given its parent.
+        *
+        * @param parent the Accessible parent, which must not be null
+        *
+        * @exception IllegalArgumentException <ul>
+        *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
+        * </ul>
+        *
+        * @see #dispose
+        * @see Control#getAccessible
+        *
+        * @since 3.6
+        */
+       public Accessible(Accessible parent) {
+               this.parent = checkNull(parent);
+               this.control = parent.control;
+               parent.children.add(this);
+               AddRef();
+       }
+
+       /**
+        * @since 3.5
+        * @deprecated
+        */
+       @Deprecated
+       protected Accessible() {
+       }
+
+       Accessible(Control control) {
+               this.control = control;
+               long[] ppvObject = new 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.
+                */
+               int result = (int)COM.CreateStdAccessibleObject(control.handle, OS.OBJID_CLIENT, COM.IIDIAccessible, ppvObject);
+               /* The object needs to be checked, because if the CreateStdAccessibleObject()
+                * symbol is not found, the return value is S_OK.
+                */
+               if (ppvObject[0] == 0) return;
+               if (result != COM.S_OK) OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);
+               iaccessible = new IAccessible(ppvObject[0]);
+               createIAccessible();
+               AddRef();
+       }
+
+       Accessible(Accessible parent, long iaccessible_address) {
+               this(parent);
+               iaccessible = new IAccessible(iaccessible_address);
+       }
+
+       static Accessible checkNull (Accessible parent) {
+               if (parent == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+               return parent;
+       }
+
+       void createIAccessible() {
+               objIAccessible = new COMObject(new int[] {2,0,0,/*IA>>*/1,3,5,8,1,1,2,2,2,2,2,2,2,3,2,1,1,2,2,5,3,3,1,2,2,/*<<IA*/1,2,3,1,1,3,3,1,1,1,1,3,3,1,1,1,1,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       // method3 GetTypeInfoCount - not implemented
+                       // method4 GetTypeInfo - not implemented
+                       // method5 GetIDsOfNames - not implemented
+                       // method6 Invoke - not implemented
+                       @Override
+                       public long method7(long[] args) {return get_accParent(args[0]);}
+                       @Override
+                       public long method8(long[] args) {return get_accChildCount(args[0]);}
+                       @Override
+                       public long method9(long[] args) {return get_accChild(args[0], args[1]);}
+                       @Override
+                       public long method10(long[] args) {return get_accName(args[0], args[1]);}
+                       @Override
+                       public long method11(long[] args) {return get_accValue(args[0], args[1]);}
+                       @Override
+                       public long method12(long[] args) {return get_accDescription(args[0], args[1]);}
+                       @Override
+                       public long method13(long[] args) {return get_accRole(args[0], args[1]);}
+                       @Override
+                       public long method14(long[] args) {return get_accState(args[0], args[1]);}
+                       @Override
+                       public long method15(long[] args) {return get_accHelp(args[0], args[1]);}
+                       @Override
+                       public long method16(long[] args) {return get_accHelpTopic(args[0], args[1], args[2]);}
+                       @Override
+                       public long method17(long[] args) {return get_accKeyboardShortcut(args[0], args[1]);}
+                       @Override
+                       public long method18(long[] args) {return get_accFocus(args[0]);}
+                       @Override
+                       public long method19(long[] args) {return get_accSelection(args[0]);}
+                       @Override
+                       public long method20(long[] args) {return get_accDefaultAction(args[0], args[1]);}
+                       @Override
+                       public long method21(long[] args) {return accSelect((int)args[0], args[1]);}
+                       @Override
+                       public long method22(long[] args) {return accLocation(args[0], args[1], args[2], args[3], args[4]);}
+                       @Override
+                       public long method23(long[] args) {return accNavigate((int)args[0], args[1], args[2]);}
+                       @Override
+                       public long method24(long[] args) {return accHitTest((int)args[0], (int)args[1], args[2]);}
+                       @Override
+                       public long method25(long[] args) {return accDoDefaultAction(args[0]);}
+                       @Override
+                       public long method26(long[] args) {return put_accName(args[0], args[1]);}
+                       @Override
+                       public long method27(long[] args) {return put_accValue(args[0], args[1]);}
+
+                       // IAccessible2 methods
+                       @Override
+                       public long method28(long[] args) {return get_nRelations(args[0]);}
+                       @Override
+                       public long method29(long[] args) {return get_relation((int)args[0], args[1]);}
+                       @Override
+                       public long method30(long[] args) {return get_relations((int)args[0], args[1], args[2]);}
+                       @Override
+                       public long method31(long[] args) {return get_role(args[0]);}
+                       @Override
+                       public long method32(long[] args) {return scrollTo((int)args[0]);}
+                       @Override
+                       public long method33(long[] args) {return scrollToPoint((int)args[0], (int)args[1], (int)args[2]);}
+                       @Override
+                       public long method34(long[] args) {return get_groupPosition(args[0], args[1], args[2]);}
+                       @Override
+                       public long method35(long[] args) {return get_states(args[0]);}
+                       @Override
+                       public long method36(long[] args) {return get_extendedRole(args[0]);}
+                       @Override
+                       public long method37(long[] args) {return get_localizedExtendedRole(args[0]);}
+                       @Override
+                       public long method38(long[] args) {return get_nExtendedStates(args[0]);}
+                       @Override
+                       public long method39(long[] args) {return get_extendedStates((int)args[0], args[1], args[2]);}
+                       @Override
+                       public long method40(long[] args) {return get_localizedExtendedStates((int)args[0], args[1], args[2]);}
+                       @Override
+                       public long method41(long[] args) {return get_uniqueID(args[0]);}
+                       @Override
+                       public long method42(long[] args) {return get_windowHandle(args[0]);}
+                       @Override
+                       public long method43(long[] args) {return get_indexInParent(args[0]);}
+                       @Override
+                       public long method44(long[] args) {return get_locale(args[0]);}
+                       @Override
+                       public long method45(long[] args) {return get_attributes(args[0]);}
+               };
+       }
+
+       void createIAccessibleApplication() {
+               objIAccessibleApplication = new COMObject(new int[] {2,0,0,1,1,1,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return get_appName(args[0]);}
+                       @Override
+                       public long method4(long[] args) {return get_appVersion(args[0]);}
+                       @Override
+                       public long method5(long[] args) {return get_toolkitName(args[0]);}
+                       @Override
+                       public long method6(long[] args) {return get_toolkitVersion(args[0]);}
+               };
+       }
+
+       // This method is intentionally commented. We are not providing IAccessibleComponent at this time.
+//     void createIAccessibleComponent() {
+//             objIAccessibleComponent = new COMObject(new int[] {2,0,0,2,1,1}) {
+//                     public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+//                     public long method1(long[] args) {return AddRef();}
+//                     public long method2(long[] args) {return Release();}
+//                     public long method3(long[] args) {return get_locationInParent(args[0], args[1]);}
+//                     public long method4(long[] args) {return get_foreground(args[0]);}
+//                     public long method5(long[] args) {return get_background(args[0]);}
+//             };
+//     }
+
+       void createIAccessibleEditableText() {
+               objIAccessibleEditableText = new COMObject(new int[] {2,0,0,2,2,2,2,1,3,3}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return copyText((int)args[0], (int)args[1]);}
+                       @Override
+                       public long method4(long[] args) {return deleteText((int)args[0], (int)args[1]);}
+                       @Override
+                       public long method5(long[] args) {return insertText((int)args[0], args[1]);}
+                       @Override
+                       public long method6(long[] args) {return cutText((int)args[0], (int)args[1]);}
+                       @Override
+                       public long method7(long[] args) {return pasteText((int)args[0]);}
+                       @Override
+                       public long method8(long[] args) {return replaceText((int)args[0], (int)args[1], args[2]);}
+                       @Override
+                       public long method9(long[] args) {return setAttributes((int)args[0], (int)args[1], args[2]);}
+               };
+       }
+
+       void createIAccessibleHyperlink() {
+               objIAccessibleHyperlink = new COMObject(new int[] {2,0,0,/*IAA>>*/1,1,2,4,2,2,/*<<IAA*/2,2,1,1,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       // IAccessibleAction
+                       @Override
+                       public long method3(long[] args) {return get_nActions(args[0]);}
+                       @Override
+                       public long method4(long[] args) {return doAction((int)args[0]);}
+                       @Override
+                       public long method5(long[] args) {return get_description((int)args[0], args[1]);}
+                       @Override
+                       public long method6(long[] args) {return get_keyBinding((int)args[0], (int)args[1], args[2], args[3]);}
+                       @Override
+                       public long method7(long[] args) {return get_name((int)args[0], args[1]);}
+                       @Override
+                       public long method8(long[] args) {return get_localizedName((int)args[0], args[1]);}
+                       // IAccessibleHyperlink
+                       @Override
+                       public long method9(long[] args) {return get_anchor((int)args[0], args[1]);}
+                       @Override
+                       public long method10(long[] args) {return get_anchorTarget((int)args[0], args[1]);}
+                       @Override
+                       public long method11(long[] args) {return get_startIndex(args[0]);}
+                       @Override
+                       public long method12(long[] args) {return get_endIndex(args[0]);}
+                       @Override
+                       public long method13(long[] args) {return get_valid(args[0]);}
+               };
+       }
+
+       void createIAccessibleHypertext() {
+               objIAccessibleHypertext = new COMObject(new int[] {2,0,0,/*IAT>>*/2,4,1,6,1,4,3,3,5,5,5,1,1,3,1,3,5,1,1,/*<<IAT*/1,2,2}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       // IAccessibleText
+                       @Override
+                       public long method3(long[] args) {return addSelection((int)args[0], (int)args[1]);}
+                       @Override
+                       public long method4(long[] args) {return get_attributes((int)args[0], args[1], args[2], args[3]);}
+                       @Override
+                       public long method5(long[] args) {return get_caretOffset(args[0]);}
+                       @Override
+                       public long method6(long[] args) {return get_characterExtents((int)args[0], (int)args[1], args[2], args[3], args[4], args[5]);}
+                       @Override
+                       public long method7(long[] args) {return get_nSelections(args[0]);}
+                       @Override
+                       public long method8(long[] args) {return get_offsetAtPoint((int)args[0], (int)args[1], (int)args[2], args[3]);}
+                       @Override
+                       public long method9(long[] args) {return get_selection((int)args[0], args[1], args[2]);}
+                       @Override
+                       public long method10(long[] args) {return get_text((int)args[0], (int)args[1], args[2]);}
+                       @Override
+                       public long method11(long[] args) {return get_textBeforeOffset((int)args[0], (int)args[1], args[2], args[3], args[4]);}
+                       @Override
+                       public long method12(long[] args) {return get_textAfterOffset((int)args[0], (int)args[1], args[2], args[3], args[4]);}
+                       @Override
+                       public long method13(long[] args) {return get_textAtOffset((int)args[0], (int)args[1], args[2], args[3], args[4]);}
+                       @Override
+                       public long method14(long[] args) {return removeSelection((int)args[0]);}
+                       @Override
+                       public long method15(long[] args) {return setCaretOffset((int)args[0]);}
+                       @Override
+                       public long method16(long[] args) {return setSelection((int)args[0], (int)args[1], (int)args[2]);}
+                       @Override
+                       public long method17(long[] args) {return get_nCharacters(args[0]);}
+                       @Override
+                       public long method18(long[] args) {return scrollSubstringTo((int)args[0], (int)args[1], (int)args[2]);}
+                       @Override
+                       public long method19(long[] args) {return scrollSubstringToPoint((int)args[0], (int)args[1], (int)args[2], (int)args[3], (int)args[4]);}
+                       @Override
+                       public long method20(long[] args) {return get_newText(args[0]);}
+                       @Override
+                       public long method21(long[] args) {return get_oldText(args[0]);}
+                       // IAccessibleHypertext
+                       @Override
+                       public long method22(long[] args) {return get_nHyperlinks(args[0]);}
+                       @Override
+                       public long method23(long[] args) {return get_hyperlink((int)args[0], args[1]);}
+                       @Override
+                       public long method24(long[] args) {return get_hyperlinkIndex((int)args[0], args[1]);}
+               };
+       }
+
+       // This method is intentionally commented. We are not providing IAccessibleImage at this time.
+//     void createIAccessibleImage() {
+//             objIAccessibleImage = new COMObject(new int[] {2,0,0,1,3,2}) {
+//                     public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+//                     public long method1(long[] args) {return AddRef();}
+//                     public long method2(long[] args) {return Release();}
+//                     public long method3(long[] args) {return get_description(args[0]);}
+//                     public long method4(long[] args) {return get_imagePosition((int)args[0], args[1], args[2]);}
+//                     public long method5(long[] args) {return get_imageSize(args[0], args[1]);}
+//             };
+//     }
+
+       void createIAccessibleTable2() {
+               objIAccessibleTable2 = new COMObject(new int[] {2,0,0,3,1,2,1,1,1,1,1,2,2,2,2,1,2,2,1,1,1,1,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return get_cellAt((int)args[0], (int)args[1], args[2]);}
+                       @Override
+                       public long method4(long[] args) {return get_caption(args[0]);}
+                       @Override
+                       public long method5(long[] args) {return get_columnDescription((int)args[0], args[1]);}
+                       @Override
+                       public long method6(long[] args) {return get_nColumns(args[0]);}
+                       @Override
+                       public long method7(long[] args) {return get_nRows(args[0]);}
+                       @Override
+                       public long method8(long[] args) {return get_nSelectedCells(args[0]);}
+                       @Override
+                       public long method9(long[] args) {return get_nSelectedColumns(args[0]);}
+                       @Override
+                       public long method10(long[] args) {return get_nSelectedRows(args[0]);}
+                       @Override
+                       public long method11(long[] args) {return get_rowDescription((int)args[0], args[1]);}
+                       @Override
+                       public long method12(long[] args) {return get_selectedCells(args[0], args[1]);}
+                       @Override
+                       public long method13(long[] args) {return get_selectedColumns(args[0], args[1]);}
+                       @Override
+                       public long method14(long[] args) {return get_selectedRows(args[0], args[1]);}
+                       @Override
+                       public long method15(long[] args) {return get_summary(args[0]);}
+                       @Override
+                       public long method16(long[] args) {return get_isColumnSelected((int)args[0], args[1]);}
+                       @Override
+                       public long method17(long[] args) {return get_isRowSelected((int)args[0], args[1]);}
+                       @Override
+                       public long method18(long[] args) {return selectRow((int)args[0]);}
+                       @Override
+                       public long method19(long[] args) {return selectColumn((int)args[0]);}
+                       @Override
+                       public long method20(long[] args) {return unselectRow((int)args[0]);}
+                       @Override
+                       public long method21(long[] args) {return unselectColumn((int)args[0]);}
+                       @Override
+                       public long method22(long[] args) {return get_modelChange(args[0]);}
+               };
+       }
+
+       void createIAccessibleTableCell() {
+               objIAccessibleTableCell = new COMObject(new int[] {2,0,0,1,2,1,1,2,1,1,5,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return get_columnExtent(args[0]);}
+                       @Override
+                       public long method4(long[] args) {return get_columnHeaderCells(args[0], args[1]);}
+                       @Override
+                       public long method5(long[] args) {return get_columnIndex(args[0]);}
+                       @Override
+                       public long method6(long[] args) {return get_rowExtent(args[0]);}
+                       @Override
+                       public long method7(long[] args) {return get_rowHeaderCells(args[0], args[1]);}
+                       @Override
+                       public long method8(long[] args) {return get_rowIndex(args[0]);}
+                       @Override
+                       public long method9(long[] args) {return get_isSelected(args[0]);}
+                       @Override
+                       public long method10(long[] args) {return get_rowColumnExtents(args[0], args[1], args[2], args[3], args[4]);}
+                       @Override
+                       public long method11(long[] args) {return get_table(args[0]);}
+               };
+       }
+
+       void createIAccessibleValue() {
+               objIAccessibleValue = new COMObject(new int[] {2,0,0,1,1,1,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return get_currentValue(args[0]);}
+                       @Override
+                       public long method4(long[] args) {return setCurrentValue(args[0]);}
+                       @Override
+                       public long method5(long[] args) {return get_maximumValue(args[0]);}
+                       @Override
+                       public long method6(long[] args) {return get_minimumValue(args[0]);}
+               };
+       }
+
+       void createIEnumVARIANT() {
+               objIEnumVARIANT = new COMObject(new int[] {2,0,0,3,1,0,1}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return Next((int)args[0], args[1], args[2]);}
+                       @Override
+                       public long method4(long[] args) {return Skip((int)args[0]);}
+                       @Override
+                       public long method5(long[] args) {return Reset();}
+                       @Override
+                       public long method6(long[] args) {return Clone(args[0]);}
+               };
+       }
+
+       void createIServiceProvider() {
+               objIServiceProvider = new COMObject(new int[] {2,0,0,3}) {
+                       @Override
+                       public long method0(long[] args) {return QueryInterface(args[0], args[1]);}
+                       @Override
+                       public long method1(long[] args) {return AddRef();}
+                       @Override
+                       public long method2(long[] args) {return Release();}
+                       @Override
+                       public long method3(long[] args) {return QueryService(args[0], args[1], args[2]);}
+               };
+       }
+
+       /**
+        * 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
+        *
+        * @noreference This method is not intended to be referenced by clients.
+        */
+       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 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleListeners == null) accessibleListeners = new ArrayList<>();
+               accessibleListeners.add(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 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleControlListeners == null) accessibleControlListeners = new ArrayList<>();
+               accessibleControlListeners.add(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>
+        * and <code>AccessibleTextExtendedListener</code> interfaces.
+        *
+        * @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 AccessibleTextExtendedListener
+        * @see #removeAccessibleTextListener
+        *
+        * @since 3.0
+        */
+       public void addAccessibleTextListener (AccessibleTextListener listener) {
+               checkWidget ();
+               if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+               if (listener instanceof AccessibleTextExtendedListener) {
+                       if (accessibleTextExtendedListeners == null) accessibleTextExtendedListeners = new ArrayList<>();
+                       accessibleTextExtendedListeners.add ((AccessibleTextExtendedListener) listener);
+               } else {
+                       if (accessibleTextListeners == null) accessibleTextListeners = new ArrayList<>();
+                       accessibleTextListeners.add (listener);
+               }
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleActionListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleActionListener</code> interface properties
+        *
+        * @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 AccessibleActionListener
+        * @see #removeAccessibleActionListener
+        *
+        * @since 3.6
+        */
+       public void addAccessibleActionListener(AccessibleActionListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleActionListeners == null) accessibleActionListeners = new ArrayList<>();
+               accessibleActionListeners.add(listener);
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleEditableTextListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleEditableTextListener</code> interface properties
+        *
+        * @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 AccessibleEditableTextListener
+        * @see #removeAccessibleEditableTextListener
+        *
+        * @since 3.7
+        */
+       public void addAccessibleEditableTextListener(AccessibleEditableTextListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleEditableTextListeners == null) accessibleEditableTextListeners = new ArrayList<>();
+               accessibleEditableTextListeners.add(listener);
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleHyperlinkListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleHyperlinkListener</code> interface properties
+        *
+        * @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 AccessibleHyperlinkListener
+        * @see #removeAccessibleHyperlinkListener
+        *
+        * @since 3.6
+        */
+       public void addAccessibleHyperlinkListener(AccessibleHyperlinkListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleHyperlinkListeners == null) accessibleHyperlinkListeners = new ArrayList<>();
+               accessibleHyperlinkListeners.add(listener);
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleTableListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleTableListener</code> interface properties
+        *
+        * @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 AccessibleTableListener
+        * @see #removeAccessibleTableListener
+        *
+        * @since 3.6
+        */
+       public void addAccessibleTableListener(AccessibleTableListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleTableListeners == null) accessibleTableListeners = new ArrayList<>();
+               accessibleTableListeners.add(listener);
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleTableCellListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleTableCellListener</code> interface properties
+        *
+        * @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 AccessibleTableCellListener
+        * @see #removeAccessibleTableCellListener
+        *
+        * @since 3.6
+        */
+       public void addAccessibleTableCellListener(AccessibleTableCellListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleTableCellListeners == null) accessibleTableCellListeners = new ArrayList<>();
+               accessibleTableCellListeners.add(listener);
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleValueListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleValueListener</code> interface properties
+        *
+        * @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 AccessibleValueListener
+        * @see #removeAccessibleValueListener
+        *
+        * @since 3.6
+        */
+       public void addAccessibleValueListener(AccessibleValueListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleValueListeners == null) accessibleValueListeners = new ArrayList<>();
+               accessibleValueListeners.add(listener);
+       }
+
+       /**
+        * Adds the listener to the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleAttributeListener</code> interface.
+        *
+        * @param listener the listener that should be notified when the receiver
+        * is asked for <code>AccessibleAttributeListener</code> interface properties
+        *
+        * @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 AccessibleAttributeListener
+        * @see #removeAccessibleAttributeListener
+        *
+        * @since 3.6
+        */
+       public void addAccessibleAttributeListener(AccessibleAttributeListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleAttributeListeners == null) accessibleAttributeListeners = new ArrayList<>();
+               accessibleAttributeListeners.add(listener);
+       }
+
+       /**
+        * Adds a relation with the specified type and target
+        * to the receiver's set of relations.
+        *
+        * @param type an <code>ACC</code> constant beginning with RELATION_* indicating the type of relation
+        * @param target the accessible that is the target for this relation
+        * @exception IllegalArgumentException ERROR_NULL_ARGUMENT - if the Accessible target is null
+        * @since 3.6
+        */
+       public void addRelation(int type, Accessible target) {
+               checkWidget();
+               if (target == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+               if (relations[type] == null) {
+                       relations[type] = new Relation(this, type);
+               }
+               relations[type].addTarget(target);
+       }
+
+       /**
+        * Disposes of the operating system resources associated with
+        * the receiver, and removes the receiver from its parent's
+        * list of children.
+        * <p>
+        * This method should be called when an accessible that was created
+        * with the public constructor <code>Accessible(Accessible parent)</code>
+        * is no longer needed. You do not need to call this when the receiver's
+        * control is disposed, because all <code>Accessible</code> instances
+        * associated with a control are released when the control is disposed.
+        * It is also not necessary to call this for instances of <code>Accessible</code>
+        * that were retrieved with <code>Control.getAccessible()</code>.
+        * </p>
+        *
+        * @since 3.6
+        */
+       public void dispose () {
+               if (parent == null) return;
+               Release();
+               parent.children.remove(this);
+               parent = null;
+       }
+
+       long getAddress() {
+               /* The address of an Accessible is the address of its IAccessible COMObject. */
+               if (objIAccessible == null) createIAccessible();
+               return objIAccessible.getAddress();
+       }
+
+       /**
+        * 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>
+        *
+        * @noreference This method is not intended to be referenced by clients.
+        */
+       public void internal_dispose_Accessible() {
+               if (iaccessible != null) {
+                       iaccessible.Release();
+               }
+               iaccessible = null;
+               Release();
+               for (int i = 0; i < children.size(); i++) {
+                       Accessible child = children.get(i);
+                       child.dispose();
+               }
+       }
+
+       /**
+        * 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>
+        *
+        * @noreference This method is not intended to be referenced by clients.
+        */
+       public long internal_WM_GETOBJECT (long wParam, long lParam) {
+               if (objIAccessible == null) return 0;
+               if ((int)lParam == OS.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, objIAccessible.getAddress());
+               }
+               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 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleListeners != null) {
+                       accessibleListeners.remove(listener);
+                       if (accessibleListeners.isEmpty()) accessibleListeners = null;
+               }
+       }
+
+       /**
+        * 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 == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleControlListeners != null) {
+                       accessibleControlListeners.remove(listener);
+                       if (accessibleControlListeners.isEmpty()) accessibleControlListeners = null;
+               }
+       }
+
+       /**
+        * 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 AccessibleTextExtendedListener
+        * @see #addAccessibleTextListener
+        *
+        * @since 3.0
+        */
+       public void removeAccessibleTextListener (AccessibleTextListener listener) {
+               checkWidget ();
+               if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+               if (listener instanceof AccessibleTextExtendedListener) {
+                       if (accessibleTextExtendedListeners != null) {
+                               accessibleTextExtendedListeners.remove (listener);
+                               if (accessibleTextExtendedListeners.isEmpty()) accessibleTextExtendedListeners = null;
+                       }
+               } else {
+                       if (accessibleTextListeners != null) {
+                               accessibleTextListeners.remove (listener);
+                               if (accessibleTextListeners.isEmpty()) accessibleTextListeners = null;
+                       }
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleActionListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleActionListener</code> interface properties
+        *
+        * @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 AccessibleActionListener
+        * @see #addAccessibleActionListener
+        *
+        * @since 3.6
+        */
+       public void removeAccessibleActionListener(AccessibleActionListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleActionListeners != null) {
+                       accessibleActionListeners.remove(listener);
+                       if (accessibleActionListeners.isEmpty()) accessibleActionListeners = null;
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleEditableTextListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleEditableTextListener</code> interface properties
+        *
+        * @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 AccessibleEditableTextListener
+        * @see #addAccessibleEditableTextListener
+        *
+        * @since 3.7
+        */
+       public void removeAccessibleEditableTextListener(AccessibleEditableTextListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleEditableTextListeners != null) {
+                       accessibleEditableTextListeners.remove(listener);
+                       if (accessibleEditableTextListeners.isEmpty()) accessibleEditableTextListeners = null;
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleHyperlinkListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleHyperlinkListener</code> interface properties
+        *
+        * @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 AccessibleHyperlinkListener
+        * @see #addAccessibleHyperlinkListener
+        *
+        * @since 3.6
+        */
+       public void removeAccessibleHyperlinkListener(AccessibleHyperlinkListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleHyperlinkListeners != null) {
+                       accessibleHyperlinkListeners.remove(listener);
+                       if (accessibleHyperlinkListeners.isEmpty()) accessibleHyperlinkListeners = null;
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleTableListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleTableListener</code> interface properties
+        *
+        * @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 AccessibleTableListener
+        * @see #addAccessibleTableListener
+        *
+        * @since 3.6
+        */
+       public void removeAccessibleTableListener(AccessibleTableListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleTableListeners != null) {
+                       accessibleTableListeners.remove(listener);
+                       if (accessibleTableListeners.isEmpty()) accessibleTableListeners = null;
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleTableCellListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleTableCellListener</code> interface properties
+        *
+        * @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 AccessibleTableCellListener
+        * @see #addAccessibleTableCellListener
+        *
+        * @since 3.6
+        */
+       public void removeAccessibleTableCellListener(AccessibleTableCellListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleTableCellListeners != null) {
+                       accessibleTableCellListeners.remove(listener);
+                       if (accessibleTableCellListeners.isEmpty()) accessibleTableCellListeners = null;
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleValueListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleValueListener</code> interface properties
+        *
+        * @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 AccessibleValueListener
+        * @see #addAccessibleValueListener
+        *
+        * @since 3.6
+        */
+       public void removeAccessibleValueListener(AccessibleValueListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleValueListeners != null) {
+                       accessibleValueListeners.remove(listener);
+                       if (accessibleValueListeners.isEmpty()) accessibleValueListeners = null;
+               }
+       }
+
+       /**
+        * Removes the listener from the collection of listeners that will be
+        * notified when an accessible client asks for any of the properties
+        * defined in the <code>AccessibleAttributeListener</code> interface.
+        *
+        * @param listener the listener that should no longer be notified when the receiver
+        * is asked for <code>AccessibleAttributeListener</code> interface properties
+        *
+        * @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 AccessibleAttributeListener
+        * @see #addAccessibleAttributeListener
+        *
+        * @since 3.6
+        */
+       public void removeAccessibleAttributeListener(AccessibleAttributeListener listener) {
+               checkWidget();
+               if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
+               if (accessibleAttributeListeners != null) {
+                       accessibleAttributeListeners.remove(listener);
+                       if (accessibleAttributeListeners.isEmpty()) accessibleAttributeListeners = null;
+               }
+       }
+
+       /**
+        * Removes the relation with the specified type and target
+        * from the receiver's set of relations.
+        *
+        * @param type an <code>ACC</code> constant beginning with RELATION_* indicating the type of relation
+        * @param target the accessible that is the target for this relation
+        * @exception IllegalArgumentException ERROR_NULL_ARGUMENT - if the Accessible target is null
+        * @since 3.6
+        */
+       public void removeRelation(int type, Accessible target) {
+               checkWidget();
+               if (target == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
+               Relation relation = relations[type];
+               if (relation != null) {
+                       relation.removeTarget(target);
+                       if (!relation.hasTargets()) {
+                               relations[type].Release();
+                               relations[type] = null;
+                       }
+               }
+       }
+
+       /**
+        * Sends a message with event-specific data to accessible clients
+        * indicating that something has changed within a custom control.
+        *
+        * @param event an <code>ACC</code> constant beginning with EVENT_* indicating the message to send
+        * @param eventData an object containing event-specific data, or null if there is no event-specific data
+        * (eventData is specified in the documentation for individual ACC.EVENT_* constants)
+        *
+        * @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#EVENT_ACTION_CHANGED
+        * @see ACC#EVENT_ATTRIBUTE_CHANGED
+        * @see ACC#EVENT_DESCRIPTION_CHANGED
+        * @see ACC#EVENT_DOCUMENT_LOAD_COMPLETE
+        * @see ACC#EVENT_DOCUMENT_LOAD_STOPPED
+        * @see ACC#EVENT_DOCUMENT_RELOAD
+        * @see ACC#EVENT_HYPERLINK_ACTIVATED
+        * @see ACC#EVENT_HYPERLINK_ANCHOR_COUNT_CHANGED
+        * @see ACC#EVENT_HYPERLINK_END_INDEX_CHANGED
+        * @see ACC#EVENT_HYPERLINK_SELECTED_LINK_CHANGED
+        * @see ACC#EVENT_HYPERLINK_START_INDEX_CHANGED
+        * @see ACC#EVENT_HYPERTEXT_LINK_COUNT_CHANGED
+        * @see ACC#EVENT_HYPERTEXT_LINK_SELECTED
+        * @see ACC#EVENT_LOCATION_CHANGED
+        * @see ACC#EVENT_NAME_CHANGED
+        * @see ACC#EVENT_PAGE_CHANGED
+        * @see ACC#EVENT_SECTION_CHANGED
+        * @see ACC#EVENT_SELECTION_CHANGED
+        * @see ACC#EVENT_STATE_CHANGED
+        * @see ACC#EVENT_TABLE_CAPTION_CHANGED
+        * @see ACC#EVENT_TABLE_CHANGED
+        * @see ACC#EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED
+        * @see ACC#EVENT_TABLE_COLUMN_HEADER_CHANGED
+        * @see ACC#EVENT_TABLE_ROW_DESCRIPTION_CHANGED
+        * @see ACC#EVENT_TABLE_ROW_HEADER_CHANGED
+        * @see ACC#EVENT_TABLE_SUMMARY_CHANGED
+        * @see ACC#EVENT_TEXT_ATTRIBUTE_CHANGED
+        * @see ACC#EVENT_TEXT_CARET_MOVED
+        * @see ACC#EVENT_TEXT_CHANGED
+        * @see ACC#EVENT_TEXT_COLUMN_CHANGED
+        * @see ACC#EVENT_TEXT_SELECTION_CHANGED
+        * @see ACC#EVENT_VALUE_CHANGED
+        *
+        * @since 3.6
+        */
+       public void sendEvent(int event, Object eventData) {
+               checkWidget();
+               if (!isATRunning ()) return;
+               if (!UseIA2) return;
+               if (DEBUG) print(this + ".NotifyWinEvent " + getEventString(event) + " hwnd=" + control.handle + " childID=" + eventChildID());
+               switch (event) {
+                       case ACC.EVENT_TABLE_CHANGED: {
+                               if (!(eventData instanceof int[] && ((int[])eventData).length == TABLE_MODEL_CHANGE_SIZE)) break;
+                               tableChange = (int[])eventData;
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID());
+                               break;
+                       }
+                       case ACC.EVENT_TEXT_CHANGED: {
+                               if (!(eventData instanceof Object[] && ((Object[])eventData).length == TEXT_CHANGE_SIZE)) break;
+                               Object[] data = (Object[])eventData;
+                               int type = ((Integer)data[0]).intValue();
+                               switch (type) {
+                                       case ACC.DELETE:
+                                               textDeleted = (Object[])eventData;
+                                               OS.NotifyWinEvent (COM.IA2_EVENT_TEXT_REMOVED, control.handle, OS.OBJID_CLIENT, eventChildID());
+                                               break;
+                                       case ACC.INSERT:
+                                               textInserted = (Object[])eventData;
+                                               OS.NotifyWinEvent (COM.IA2_EVENT_TEXT_INSERTED, control.handle, OS.OBJID_CLIENT, eventChildID());
+                                               break;
+                               }
+                               break;
+                       }
+                       case ACC.EVENT_HYPERTEXT_LINK_SELECTED: {
+                               if (!(eventData instanceof Integer)) break;
+       //                      int index = ((Integer)eventData).intValue();
+                               // TODO: IA2 currently does not use the index, however the plan is to use it in future
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERTEXT_LINK_SELECTED, control.handle, OS.OBJID_CLIENT, eventChildID());
+                               break;
+                       }
+                       case ACC.EVENT_VALUE_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_STATE_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_STATECHANGE, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_SELECTION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TEXT_SELECTION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_TEXTSELECTIONCHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_LOCATION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_NAME_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_NAMECHANGE, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_DESCRIPTION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_DESCRIPTIONCHANGE, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_DOCUMENT_LOAD_COMPLETE:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_DOCUMENT_LOAD_COMPLETE, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_DOCUMENT_LOAD_STOPPED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_DOCUMENT_LOAD_STOPPED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_DOCUMENT_RELOAD:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_DOCUMENT_RELOAD, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_PAGE_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_PAGE_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_SECTION_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_SECTION_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_ACTION_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_ACTION_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_HYPERLINK_START_INDEX_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERLINK_START_INDEX_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_HYPERLINK_END_INDEX_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERLINK_END_INDEX_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_HYPERLINK_ANCHOR_COUNT_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERLINK_ANCHOR_COUNT_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_HYPERLINK_SELECTED_LINK_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_HYPERLINK_ACTIVATED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERLINK_ACTIVATED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_HYPERTEXT_LINK_COUNT_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_HYPERTEXT_LINK_COUNT_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_ATTRIBUTE_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_ATTRIBUTE_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TABLE_CAPTION_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_CAPTION_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TABLE_COLUMN_HEADER_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_COLUMN_HEADER_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TABLE_ROW_DESCRIPTION_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_ROW_DESCRIPTION_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TABLE_ROW_HEADER_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_ROW_HEADER_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TABLE_SUMMARY_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TABLE_SUMMARY_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TEXT_ATTRIBUTE_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TEXT_ATTRIBUTE_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TEXT_CARET_MOVED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TEXT_CARET_MOVED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+                       case ACC.EVENT_TEXT_COLUMN_CHANGED:
+                               OS.NotifyWinEvent (COM.IA2_EVENT_TEXT_COLUMN_CHANGED, control.handle, OS.OBJID_CLIENT, eventChildID()); break;
+               }
+       }
+
+       /**
+        * Sends a message with event-specific data and a childID
+        * to accessible clients, indicating that something has changed
+        * within a custom control.
+        *
+        * NOTE: This API is intended for applications that are still using childIDs.
+        * Moving forward, applications should use accessible objects instead of childIDs.
+        *
+        * @param event an <code>ACC</code> constant beginning with EVENT_* indicating the message to send
+        * @param eventData an object containing event-specific data, or null if there is no event-specific data
+        * (eventData is specified in the documentation for individual ACC.EVENT_* constants)
+        * @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>
+        *
+        * @see ACC#EVENT_DESCRIPTION_CHANGED
+        * @see ACC#EVENT_LOCATION_CHANGED
+        * @see ACC#EVENT_NAME_CHANGED
+        * @see ACC#EVENT_SELECTION_CHANGED
+        * @see ACC#EVENT_STATE_CHANGED
+        * @see ACC#EVENT_TEXT_SELECTION_CHANGED
+        * @see ACC#EVENT_VALUE_CHANGED
+        *
+        * @since 3.8
+        */
+       public void sendEvent(int event, Object eventData, int childID) {
+               checkWidget();
+               if (!isATRunning ()) return;
+               if (!UseIA2) return;
+               int osChildID = childID == ACC.CHILDID_SELF ? eventChildID() : childIDToOs(childID);
+               if (DEBUG) print(this + ".NotifyWinEvent " + getEventString(event) + " hwnd=" + control.handle + " childID=" + osChildID);
+               switch (event) {
+                       case ACC.EVENT_STATE_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_STATECHANGE, control.handle, OS.OBJID_CLIENT, osChildID); break;
+                       case ACC.EVENT_NAME_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_NAMECHANGE, control.handle, OS.OBJID_CLIENT, osChildID); break;
+                       case ACC.EVENT_VALUE_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, OS.OBJID_CLIENT, osChildID); break;
+                       case ACC.EVENT_LOCATION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, OS.OBJID_CLIENT, osChildID); break;
+                       case ACC.EVENT_SELECTION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, OS.OBJID_CLIENT, osChildID); break;
+                       case ACC.EVENT_TEXT_SELECTION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_TEXTSELECTIONCHANGED, control.handle, OS.OBJID_CLIENT, osChildID); break;
+                       case ACC.EVENT_DESCRIPTION_CHANGED:
+                               OS.NotifyWinEvent (COM.EVENT_OBJECT_DESCRIPTIONCHANGE, control.handle, OS.OBJID_CLIENT, osChildID); break;
+               }
+       }
+
+
+       /**
+        * 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();
+               if (!isATRunning ()) return;
+               if (DEBUG) print(this + ".NotifyWinEvent EVENT_OBJECT_SELECTIONWITHIN hwnd=" + control.handle + " childID=" + eventChildID());
+               OS.NotifyWinEvent (COM.EVENT_OBJECT_SELECTIONWITHIN, control.handle, OS.OBJID_CLIENT, eventChildID());
+       }
+
+       /**
+        * 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();
+               if (!isATRunning ()) return;
+               int osChildID = childID == ACC.CHILDID_SELF ? eventChildID() : childIDToOs(childID);
+               if (DEBUG) print(this + ".NotifyWinEvent EVENT_OBJECT_FOCUS hwnd=" + control.handle + " childID=" + osChildID);
+               OS.NotifyWinEvent (OS.EVENT_OBJECT_FOCUS, control.handle, OS.OBJID_CLIENT, osChildID);
+       }
+
+       /**
+        * 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();
+               if (timer == null) {
+                       timer = new Runnable() {
+                               @Override
+                               public void run() {
+                                       if (!isATRunning ()) return;
+                                       if (DEBUG) print(this + ".NotifyWinEvent EVENT_OBJECT_LOCATIONCHANGE hwnd=" + control.handle + " childID=" + eventChildID());
+                                       OS.NotifyWinEvent (COM.EVENT_OBJECT_LOCATIONCHANGE, control.handle, OS.OBJID_CARET, eventChildID());
+                                       if (!UseIA2) return;
+                                       if (DEBUG) print(this + ".NotifyWinEvent IA2_EVENT_TEXT_CARET_MOVED hwnd=" + control.handle + " childID=" + eventChildID());
+                                       OS.NotifyWinEvent (COM.IA2_EVENT_TEXT_CARET_MOVED, control.handle, OS.OBJID_CLIENT, eventChildID());
+                               }
+                       };
+               }
+               control.getDisplay ().timerExec(SCROLL_RATE, timer);
+       }
+
+       /**
+        * 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.TEXT_INSERT</code>
+        * or <code>ACC.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();
+               if (!isATRunning ()) return;
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.start = startIndex;
+               event.end = startIndex + length;
+               event.count = 0;
+               event.type = ACC.TEXT_BOUNDARY_ALL;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getText(event);
+               }
+               if (event.result != null) {
+                       Object[] eventData = new Object[] {
+                                       Integer.valueOf(type),
+                                       Integer.valueOf(startIndex),
+                                       Integer.valueOf(startIndex + length),
+                                       event.result};
+                       sendEvent(ACC.EVENT_TEXT_CHANGED, eventData);
+                       return;
+               }
+               if (DEBUG) print(this + ".NotifyWinEvent EVENT_OBJECT_VALUECHANGE hwnd=" + control.handle + " childID=" + eventChildID());
+               OS.NotifyWinEvent (COM.EVENT_OBJECT_VALUECHANGE, control.handle, OS.OBJID_CLIENT, eventChildID());
+       }
+
+       /**
+        * 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();
+               if (!isATRunning ()) return;
+               if (DEBUG) print(this + ".NotifyWinEvent EVENT_OBJECT_TEXTSELECTIONCHANGED hwnd=" + control.handle + " childID=" + eventChildID());
+               OS.NotifyWinEvent (COM.EVENT_OBJECT_TEXTSELECTIONCHANGED, control.handle, OS.OBJID_CLIENT, eventChildID());
+       }
+
+       /* 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.
+        */
+       int QueryInterface(long iid, long ppvObject) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               OS.MoveMemory(ppvObject, new long[] { 0 }, C.PTR_SIZEOF);
+               GUID guid = new GUID();
+               COM.MoveMemory(guid, iid, GUID.sizeof);
+
+               if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDispatch) || COM.IsEqualGUID(guid, COM.IIDIAccessible)) {
+                       if (objIAccessible == null) createIAccessible();
+                       OS.MoveMemory(ppvObject, new long[] { objIAccessible.getAddress() }, C.PTR_SIZEOF);
+                       AddRef();
+                       if (DEBUG) print(this + ".QueryInterface guid=" + guidString(guid) + " returning " + objIAccessible.getAddress() + hresult(COM.S_OK));
+                       return COM.S_OK;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIEnumVARIANT)) {
+                       if (objIEnumVARIANT == null) createIEnumVARIANT();
+                       OS.MoveMemory(ppvObject, new long[] { objIEnumVARIANT.getAddress() }, C.PTR_SIZEOF);
+                       AddRef();
+                       enumIndex = 0;
+                       if (DEBUG) print(this + ".QueryInterface guid=" + guidString(guid) + " returning " + objIEnumVARIANT.getAddress() + hresult(COM.S_OK));
+                       return COM.S_OK;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIServiceProvider)) {
+                       if (!UseIA2) return COM.E_NOINTERFACE;
+                       if (accessibleActionListenersSize() > 0 || accessibleAttributeListenersSize() > 0 ||
+                               accessibleHyperlinkListenersSize() > 0 || accessibleTableListenersSize() > 0 ||
+                               accessibleTableCellListenersSize() > 0 || accessibleTextExtendedListenersSize() > 0 ||
+                               accessibleValueListenersSize() > 0 || accessibleControlListenersSize() > 0 || getRelationCount() > 0
+                               || (control instanceof Button && ((control.getStyle() & SWT.RADIO) != 0)) || (control instanceof Composite)) {
+                               if (objIServiceProvider == null) createIServiceProvider();
+                               OS.MoveMemory(ppvObject, new long[] { objIServiceProvider.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               if (DEBUG) print(this + ".QueryInterface guid=" + guidString(guid) + " returning " + objIServiceProvider.getAddress() + hresult(COM.S_OK));
+                               return COM.S_OK;
+                       }
+                       if (DEBUG) if (interesting(guid)) print("QueryInterface guid=" + guidString(guid) + " returning" + hresult(COM.E_NOINTERFACE));
+                       return COM.E_NOINTERFACE;
+               }
+
+               int code = queryAccessible2Interfaces(guid, ppvObject);
+               if (code != COM.S_FALSE) {
+                       if (DEBUG) print(this + ".QueryInterface guid=" + guidString(guid) + " returning" + hresult(code));
+                       return code;
+               }
+
+               if (iaccessible != null) {
+                       /* Forward any other GUIDs to the OS proxy. */
+                       long[] ppv = new long[1];
+                       code = iaccessible.QueryInterface(guid, ppv);
+                       OS.MoveMemory(ppvObject, ppv, C.PTR_SIZEOF);
+                       if (DEBUG) if (interesting(guid)) print("QueryInterface guid=" + guidString(guid) + " returning super" + hresult(code));
+                       return code;
+               }
+
+               if (DEBUG) if (interesting(guid)) print("QueryInterface guid=" + guidString(guid) + " returning" + hresult(COM.E_NOINTERFACE));
+               return COM.E_NOINTERFACE;
+       }
+
+       int accessibleListenersSize() {
+               return accessibleListeners == null ? 0 : accessibleListeners.size();
+       }
+
+       int accessibleControlListenersSize() {
+               return accessibleControlListeners == null ? 0 : accessibleControlListeners.size();
+       }
+
+       int accessibleValueListenersSize() {
+               return accessibleValueListeners == null ? 0 : accessibleValueListeners.size();
+       }
+
+       int accessibleTextExtendedListenersSize() {
+               return accessibleTextExtendedListeners == null ? 0 : accessibleTextExtendedListeners.size();
+       }
+
+       int accessibleTextListenersSize() {
+               return accessibleTextListeners == null ? 0 : accessibleTextListeners.size();
+       }
+
+       int accessibleTableCellListenersSize() {
+               return accessibleTableCellListeners == null ? 0 : accessibleTableCellListeners.size();
+       }
+
+       int accessibleTableListenersSize() {
+               return accessibleTableListeners == null ? 0 : accessibleTableListeners.size();
+       }
+
+       int accessibleHyperlinkListenersSize() {
+               return accessibleHyperlinkListeners == null ? 0 : accessibleHyperlinkListeners.size();
+       }
+
+       int accessibleEditableTextListenersSize() {
+               return accessibleEditableTextListeners == null ? 0 : accessibleEditableTextListeners.size();
+       }
+
+       int accessibleAttributeListenersSize() {
+               return accessibleAttributeListeners == null ? 0 : accessibleAttributeListeners.size();
+       }
+
+       int accessibleActionListenersSize() {
+               return accessibleActionListeners == null ? 0 : accessibleActionListeners.size();
+       }
+
+       int AddRef() {
+               refCount++;
+               return refCount;
+       }
+
+       int Release() {
+               refCount--;
+
+               if (refCount == 0) {
+                       if (objIAccessible != null)
+                               objIAccessible.dispose();
+                       objIAccessible = null;
+
+                       if (objIEnumVARIANT != null)
+                               objIEnumVARIANT.dispose();
+                       objIEnumVARIANT = null;
+
+                       if (objIServiceProvider != null)
+                               objIServiceProvider.dispose();
+                       objIServiceProvider = null;
+
+                       if (objIAccessibleApplication != null)
+                               objIAccessibleApplication.dispose();
+                       objIAccessibleApplication = null;
+
+                       // The following lines are intentionally commented. We are not providing IAccessibleComponent at this time.
+//                     if (objIAccessibleComponent != null)
+//                             objIAccessibleComponent.dispose();
+//                     objIAccessibleComponent = null;
+
+                       if (objIAccessibleEditableText != null)
+                               objIAccessibleEditableText.dispose();
+                       objIAccessibleEditableText = null;
+
+                       if (objIAccessibleHyperlink != null)
+                               objIAccessibleHyperlink.dispose();
+                       objIAccessibleHyperlink = null;
+
+                       if (objIAccessibleHypertext != null)
+                               objIAccessibleHypertext.dispose();
+                       objIAccessibleHypertext = null;
+
+                       // The following lines are intentionally commented. We are not providing IAccessibleImage at this time.
+//                     if (objIAccessibleImage != null)
+//                             objIAccessibleImage.dispose();
+//                     objIAccessibleImage = null;
+
+                       if (objIAccessibleTable2 != null)
+                               objIAccessibleTable2.dispose();
+                       objIAccessibleTable2 = null;
+
+                       if (objIAccessibleTableCell != null)
+                               objIAccessibleTableCell.dispose();
+                       objIAccessibleTableCell = null;
+
+                       if (objIAccessibleValue != null)
+                               objIAccessibleValue.dispose();
+                       objIAccessibleValue = null;
+
+                       for (int i = 0; i < relations.length; i++) {
+                               if (relations[i] != null) relations[i].Release();
+                       }
+                       // TODO: also remove all relations for which 'this' is a target??
+               }
+               return refCount;
+       }
+
+       /* QueryService([in] guidService, [in] riid, [out] ppvObject) */
+       int QueryService(long guidService, long riid, long ppvObject) {
+               OS.MoveMemory(ppvObject, new long[] { 0 }, C.PTR_SIZEOF);
+               GUID service = new GUID();
+               COM.MoveMemory(service, guidService, GUID.sizeof);
+               GUID guid = new GUID();
+               COM.MoveMemory(guid, riid, GUID.sizeof);
+
+               if (COM.IsEqualGUID(service, COM.IIDIAccessible)) {
+                       if (COM.IsEqualGUID(guid, COM.IIDIUnknown) || COM.IsEqualGUID(guid, COM.IIDIDispatch) || COM.IsEqualGUID(guid, COM.IIDIAccessible)) {
+                               if (objIAccessible == null) createIAccessible();
+                               if (DEBUG) print(this + ".QueryService service=" + guidString(service) + " guid=" + guidString(guid) + " returning " + objIAccessible.getAddress() + hresult(COM.S_OK));
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessible.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       int code = queryAccessible2Interfaces(guid, ppvObject);
+                       if (code != COM.S_FALSE) {
+                               if (DEBUG) print(this + ".QueryService service=" + guidString(service) + " guid=" + guidString(guid) + " returning" + hresult(code));
+                               return code;
+                       }
+               }
+
+               if (COM.IsEqualGUID(service, COM.IIDIAccessible2)) {
+                       int code = queryAccessible2Interfaces(guid, ppvObject);
+                       if (code != COM.S_FALSE) {
+                               if (DEBUG) print(this + ".*QueryService service=" + guidString(service) + " guid=" + guidString(guid) + " returning" + hresult(code));
+                               return code;
+                       }
+               }
+
+               if (iaccessible != null) {
+                       /* Forward any other GUIDs to the OS proxy. */
+                       long [] ppv = new long [1];
+                       int code = iaccessible.QueryInterface(COM.IIDIServiceProvider, ppv);
+                       if (code == COM.S_OK) {
+                               IServiceProvider iserviceProvider = new IServiceProvider(ppv[0]);
+                               long [] ppvx = new long [1];
+                               code = iserviceProvider.QueryService(service, guid, ppvx);
+                               OS.MoveMemory(ppvObject, ppvx, C.PTR_SIZEOF);
+                               if (DEBUG) if (interesting(service) && interesting(guid)) print("QueryService service=" + guidString(service) + " guid=" + guidString(guid) + " returning super" + hresult(code));
+                               return code;
+                       }
+               }
+
+               if (DEBUG) if (interesting(service) && interesting(guid)) print("QueryService service=" + guidString(service) + " guid=" + guidString(guid) + " returning" + hresult(COM.E_NOINTERFACE));
+               return COM.E_NOINTERFACE;
+       }
+
+       int queryAccessible2Interfaces(GUID guid, long ppvObject) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessible2)) {
+                       if (accessibleActionListenersSize() > 0 || accessibleAttributeListenersSize() > 0 ||
+                                       accessibleHyperlinkListenersSize() > 0 || accessibleTableListenersSize() > 0 ||
+                                       accessibleTableCellListenersSize() > 0 || accessibleTextExtendedListenersSize() > 0 ||
+                                       accessibleValueListenersSize() > 0 || accessibleControlListenersSize() > 0 || getRelationCount() > 0
+                                       || (control instanceof Button && ((control.getStyle() & SWT.RADIO) != 0)) || (control instanceof Composite)) {
+                               // NOTE: IAccessible2 vtable is shared with IAccessible
+                               if (objIAccessible == null) createIAccessible();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessible.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleAction)) {
+                       if (accessibleActionListenersSize() > 0) {
+                               // NOTE: IAccessibleAction vtable is shared with IAccessibleHyperlink
+                               if (objIAccessibleHyperlink == null) createIAccessibleHyperlink();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleHyperlink.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleApplication)) {
+                       if (objIAccessibleApplication == null) createIAccessibleApplication();
+                       OS.MoveMemory(ppvObject, new long[] { objIAccessibleApplication.getAddress() }, C.PTR_SIZEOF);
+                       AddRef();
+                       return COM.S_OK;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleComponent)) {
+                       // The following lines are intentionally commented. We are not supporting IAccessibleComponent at this time.
+//                     if (accessibleControlListenersSize() > 0) { // TO DO: can we reduce the scope of this somehow?
+//                             if (objIAccessibleComponent == null) createIAccessibleComponent();
+//                             COM.MoveMemory(ppvObject, new long[] { objIAccessibleComponent.getAddress() }, OS.PTR_SIZEOF);
+//                             AddRef();
+//                             return COM.S_OK;
+//                     }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleEditableText)) {
+                       if (accessibleEditableTextListenersSize() > 0) {
+                               if (objIAccessibleEditableText == null) createIAccessibleEditableText();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleEditableText.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleHyperlink)) {
+                       if (accessibleHyperlinkListenersSize() > 0) {
+                               if (objIAccessibleHyperlink == null) createIAccessibleHyperlink();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleHyperlink.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleHypertext)) {
+                       if (accessibleTextExtendedListenersSize() > 0) {
+                               if (objIAccessibleHypertext == null) createIAccessibleHypertext();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleHypertext.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleImage)) {
+                       // The following lines are intentionally commented. We are not supporting IAccessibleImage at this time.
+//                     if (getRole() == ACC.ROLE_GRAPHIC && (accessibleAccessibleListenersSize() > 0 || accessibleControlListenersSize() > 0)) {
+//                             if (objIAccessibleImage == null) createIAccessibleImage();
+//                             COM.MoveMemory(ppvObject, new long[] { objIAccessibleImage.getAddress() }, OS.PTR_SIZEOF);
+//                             AddRef();
+//                             return COM.S_OK;
+//                     }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTable)) {
+                       // We are not supporting IAccessibleTable at this time.
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTable2)) {
+                       if (accessibleTableListenersSize() > 0) {
+                               if (objIAccessibleTable2 == null) createIAccessibleTable2();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleTable2.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTableCell)) {
+                       if (accessibleTableCellListenersSize() > 0) {
+                               if (objIAccessibleTableCell == null) createIAccessibleTableCell();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleTableCell.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleText)) {
+                       if (accessibleTextExtendedListenersSize() > 0 || accessibleAttributeListenersSize() > 0) {
+                               // NOTE: IAccessibleText vtable is shared with IAccessibleHypertext
+                               if (objIAccessibleHypertext == null) createIAccessibleHypertext();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleHypertext.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleValue)) {
+                       if (accessibleValueListenersSize() > 0) {
+                               if (objIAccessibleValue == null) createIAccessibleValue();
+                               OS.MoveMemory(ppvObject, new long[] { objIAccessibleValue.getAddress() }, C.PTR_SIZEOF);
+                               AddRef();
+                               return COM.S_OK;
+                       }
+                       return COM.E_NOINTERFACE;
+               }
+
+               return COM.S_FALSE;
+       }
+
+       /* IAccessible::accDoDefaultAction([in] varChild) */
+       int accDoDefaultAction(long varChild) {
+               if (DEBUG) print(this + ".IAccessible::accDoDefaultAction");
+               if (accessibleActionListenersSize() > 0) {
+                       VARIANT v = getVARIANT(varChild);
+                       if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+                       if (v.lVal == COM.CHILDID_SELF) return doAction(0);
+               }
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               if (iaccessible != null) {
+                       /* If there were no action listeners, forward to the proxy. */
+                       code = iaccessible.accDoDefaultAction(varChild);
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+               }
+               return code;
+       }
+
+       /* IAccessible::accHitTest([in] xLeft, [in] yTop, [out] pvarChild) */
+       int accHitTest(int xLeft, int yTop, long pvarChild) {
+               int osChild = ACC.CHILDID_NONE;
+               long osChildObject = 0;
+               if (iaccessible != null) {
+                       /* Get the default child at point (left, top) from the OS. */
+                       int code = iaccessible.accHitTest(xLeft, yTop, pvarChild);
+                       if (code == COM.S_OK) {
+                               VARIANT v = getVARIANT(pvarChild);
+                               if (v.vt == COM.VT_I4) osChild = v.lVal;
+                               else if (v.vt == COM.VT_DISPATCH) {
+                                       osChildObject = v.lVal; // TODO: don't use struct. lVal is an int.
+                                       if (DEBUG) print(this + ".IAccessible::accHitTest() super returned VT_DISPATCH");
+                               }
+                       }
+                       if (accessibleControlListenersSize() == 0) {
+                               if (DEBUG) print(this + ".IAccessible::accHitTest returning childID=" + osChild + " from super" + hresult(code));
+                               return code;
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = osChild == ACC.CHILDID_NONE ? ACC.CHILDID_NONE : osToChildID(osChild);
+               // TODO: event.accessible = Accessible for osChildObject;
+               event.x = xLeft;
+               event.y = yTop;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getChildAtPoint(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible != null) {
+                       if (DEBUG) print(this + ".IAccessible::accHitTest returning " + accessible.getAddress() + hresult(COM.S_OK));
+                       accessible.AddRef();
+                       setPtrVARIANT(pvarChild, COM.VT_DISPATCH, accessible.getAddress());
+                       return COM.S_OK;
+               }
+               int childID = event.childID;
+               if (childID == ACC.CHILDID_NONE) {
+                       if (osChildObject != 0) {
+                               if (DEBUG) print(this + ".IAccessible::accHitTest returning osChildObject " + osChildObject + " from super" + hresult(COM.S_OK));
+                               return COM.S_OK;
+                       }
+                       if (DEBUG) print(this + ".IAccessible::accHitTest returning VT_EMPTY" + hresult(COM.S_FALSE));
+                       setIntVARIANT(pvarChild, COM.VT_EMPTY, 0);
+                       return COM.S_FALSE;
+               }
+               if (DEBUG) print(this + ".IAccessible::accHitTest returning " + childIDToOs(childID) + hresult(COM.S_OK));
+               setIntVARIANT(pvarChild, COM.VT_I4, childIDToOs(childID));
+               return COM.S_OK;
+       }
+
+       /* IAccessible::accLocation([out] pxLeft, [out] pyTop, [out] pcxWidth, [out] pcyHeight, [in] varChild) */
+       int accLocation(long pxLeft, long pyTop, long pcxWidth, long pcyHeight, long varChild) {
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int osLeft = 0, osTop = 0, osWidth = 0, osHeight = 0;
+               if (iaccessible != null) {
+                       /* Get the default location from the OS. */
+                       int code = iaccessible.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+                       if (accessibleControlListenersSize() == 0) {
+                               if (DEBUG) print(this + ".IAccessible::accLocation returning from super" + hresult(code));
+                               return code;
+                       }
+                       if (code == COM.S_OK) {
+                               int[] pLeft = new int[1], pTop = new int[1], pWidth = new int[1], pHeight = new int[1];
+                               OS.MoveMemory(pLeft, pxLeft, 4);
+                               OS.MoveMemory(pTop, pyTop, 4);
+                               OS.MoveMemory(pWidth, pcxWidth, 4);
+                               OS.MoveMemory(pHeight, 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 < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getLocation(event);
+               }
+               if (DEBUG) print(this + ".IAccessible::accLocation(" + v.lVal + ") returning x=" + event.x + " y=" + event.y + "w=" + event.width + "h=" + event.height + hresult(COM.S_OK));
+               OS.MoveMemory(pxLeft, new int[] { event.x }, 4);
+               OS.MoveMemory(pyTop, new int[] { event.y }, 4);
+               OS.MoveMemory(pcxWidth, new int[] { event.width }, 4);
+               OS.MoveMemory(pcyHeight, new int[] { event.height }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::accNavigate([in] navDir, [in] varStart, [out] pvarEndUpAt) */
+       int accNavigate(int navDir, long varStart, long pvarEndUpAt) {
+               if (DEBUG) print(this + ".IAccessible::accNavigate");
+               /* MSAA: "The accNavigate method is deprecated and should not be used." */
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               if (iaccessible != null) {
+                       /* Since many of the native controls still handle accNavigate,
+                        * we will continue to send this through to the proxy. */
+                       code = iaccessible.accNavigate(navDir, varStart, pvarEndUpAt);
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+               }
+               return code;
+       }
+
+       // TODO: Consider supporting this in future.
+       /* IAccessible::accSelect([in] flagsSelect, [in] varChild) */
+       int accSelect(int flagsSelect, long varChild) {
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               if (iaccessible != null) {
+                       /* Currently, we don't expose this as API. Forward to the proxy. */
+                       code = iaccessible.accSelect(flagsSelect, varChild);
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+               }
+               if (DEBUG) print(this + ".IAccessible::accSelect(" + flagsSelect + ") returning" + hresult(code));
+               return code;
+       }
+
+       /* IAccessible::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.
+        */
+       int get_accChild(long varChild, long ppdispChild) {
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               if (v.lVal == COM.CHILDID_SELF) {
+                       if (DEBUG) print(this + ".IAccessible::get_accChild(" + v.lVal + ") returning " + getAddress() + hresult(COM.S_OK));
+                       AddRef();
+                       OS.MoveMemory(ppdispChild, new long[] { getAddress() }, C.PTR_SIZEOF);
+                       return COM.S_OK;
+               }
+               final int childID = osToChildID(v.lVal);
+               int code = COM.S_FALSE;
+               Accessible osAccessible = null;
+               if (iaccessible != null) {
+                       /* Get the default child from the OS. */
+                       code = iaccessible.get_accChild(varChild, ppdispChild);
+                       if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+                       if (code == COM.S_OK && control instanceof ToolBar) {
+                               ToolBar toolBar = (ToolBar) control;
+                               final ToolItem item = toolBar.getItem(childID);
+                               if (item != null && (item.getStyle() & SWT.DROP_DOWN) != 0) {
+                                       long[] addr = new long[1];
+                                       OS.MoveMemory(addr, ppdispChild, C.PTR_SIZEOF);
+                                       boolean found = false;
+                                       for (int i = 0; i < children.size(); i++) {
+                                               Accessible accChild = children.get(i);
+                                               if (accChild.item == item) {
+                                                       /*
+                                                        * MSAA uses a new accessible for the child
+                                                        * so we dispose the old and use the new.
+                                                        */
+                                                       accChild.dispose();
+                                                       accChild.item = null;
+                                                       found = true;
+                                                       break;
+                                               }
+                                       }
+                                       osAccessible = new Accessible(this, addr[0]);
+                                       osAccessible.item = item;
+                                       if (!found) {
+                                               item.addListener(SWT.Dispose, e -> {
+                                                       for (int i = 0; i < children.size(); i++) {
+                                                               Accessible accChild = children.get(i);
+                                                               if (accChild.item == item) {
+                                                                       accChild.dispose();
+                                                               }
+                                                       }
+                                               });
+                                       }
+                                       osAccessible.addAccessibleListener(new AccessibleAdapter() {
+                                               @Override
+                                               public void getName(AccessibleEvent e) {
+                                                       if (e.childID == ACC.CHILDID_SELF) {
+                                                               AccessibleEvent event = new AccessibleEvent(Accessible.this);
+                                                               event.childID = childID;
+                                                               for (int i = 0; i < accessibleListenersSize(); i++) {
+                                                                       AccessibleListener listener = accessibleListeners.get(i);
+                                                                       listener.getName(event);
+                                                               }
+                                                               e.result = event.result;
+                                                       }
+                                               }
+                                       });
+                               }
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = childID;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getChild(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible == null) accessible = osAccessible;
+               if (accessible != null) {
+                       if (DEBUG) print(this + ".IAccessible::get_accChild(" + v.lVal + ") returning " + accessible.getAddress() + hresult(COM.S_OK));
+                       accessible.AddRef();
+                       OS.MoveMemory(ppdispChild, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+                       return COM.S_OK;
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accChild(" + v.lVal + ") returning from super" + hresult(code));
+               return code;
+       }
+
+       /* IAccessible::get_accChildCount([out] pcountChildren) */
+       int get_accChildCount(long pcountChildren) {
+               int osChildCount = 0;
+               if (iaccessible != null) {
+                       /* Get the default child count from the OS. */
+                       int code = iaccessible.get_accChildCount(pcountChildren);
+                       if (code == COM.S_OK) {
+                               int[] pChildCount = new int[1];
+                               OS.MoveMemory(pChildCount, pcountChildren, 4);
+                               osChildCount = pChildCount[0];
+                       }
+                       if (accessibleControlListenersSize() == 0) {
+                               if (DEBUG) print(this + ".IAccessible::get_accChildCount() returning " + osChildCount + " from super" + hresult(code));
+                               return code;
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = ACC.CHILDID_SELF;
+               event.detail = osChildCount;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getChildCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accChildCount() returning " + event.detail + hresult(COM.S_OK));
+               OS.MoveMemory(pcountChildren, new int[] { event.detail }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accDefaultAction([in] varChild, [out] pszDefaultAction) */
+       int get_accDefaultAction(long varChild, long pszDefaultAction) {
+               if (DEBUG) print(this + ".IAccessible::get_accDefaultAction");
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               String osDefaultAction = null;
+               if (iaccessible != null) {
+                       /* Get the default defaultAction from the OS. */
+                       code = iaccessible.get_accDefaultAction(varChild, pszDefaultAction);
+                       if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+                       if (accessibleControlListenersSize() == 0) return code;
+                       if (code == COM.S_OK) {
+                               long[] pDefaultAction = new long[1];
+                               OS.MoveMemory(pDefaultAction, pszDefaultAction, C.PTR_SIZEOF);
+                               int size = COM.SysStringByteLen(pDefaultAction[0]);
+                               if (size > 0) {
+                                       char[] buffer = new char[(size + 1) /2];
+                                       OS.MoveMemory(buffer, pDefaultAction[0], size);
+                                       osDefaultAction = new String(buffer);
+                               }
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = osToChildID(v.lVal);
+               event.result = osDefaultAction;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getDefaultAction(event);
+               }
+               if ((event.result == null || event.result.length() == 0) && v.lVal == COM.CHILDID_SELF) {
+                       code = get_name(0, pszDefaultAction);
+               }
+               if (event.result == null) return code;
+               if (event.result.length() == 0) return COM.S_FALSE;
+               setString(pszDefaultAction, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accDescription([in] varChild, [out] pszDescription) */
+       int get_accDescription(long varChild, long pszDescription) {
+               /*
+                * MSAA: "The accDescription property is not supported in the transition to
+                * UI Automation. MSAA servers and applications should not use it."
+                *
+                * TODO: Description was exposed as SWT API. We will need to either deprecate this (?),
+                * or find a suitable replacement. Also, check description property on other platforms.
+                * If it is truly deprecated for MSAA, then perhaps it can be reused for IAccessibleImage.
+                * Note that the trick to expose tree columns (below) was not supported by screen readers,
+                * so it should be replaced.
+                */
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               String osDescription = null;
+               if (iaccessible != null) {
+                       /* Get the default description from the OS. */
+                       code = iaccessible.get_accDescription(varChild, pszDescription);
+                       if (code == 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 (accessibleListenersSize() == 0 && !(control instanceof Tree)) {
+                               if (DEBUG) print(this + ".IAccessible::get_accDescription(" + v.lVal + ") returning super" + hresult(code));
+                               return code;
+                       }
+                       if (code == COM.S_OK) {
+                               long[] pDescription = new long[1];
+                               OS.MoveMemory(pDescription, pszDescription, C.PTR_SIZEOF);
+                               int size = COM.SysStringByteLen(pDescription[0]);
+                               if (size > 0) {
+                                       char[] buffer = new char[(size + 1) /2];
+                                       OS.MoveMemory(buffer, pDescription[0], size);
+                                       osDescription = new String(buffer);
+                               }
+                       }
+               }
+
+               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 != COM.CHILDID_SELF) {
+                       if (control instanceof Tree) {
+                               Tree tree = (Tree) control;
+                               int columnCount = tree.getColumnCount ();
+                               if (columnCount > 1) {
+                                       long hwnd = control.handle, hItem = 0;
+                                       hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
+                                       Widget widget = tree.getDisplay ().findWidget (hwnd, hItem);
+                                       event.result = "";
+                                       if (widget != null && widget instanceof TreeItem) {
+                                               TreeItem item = (TreeItem) widget;
+                                               for (int i = 1; i < columnCount; i++) {
+                                                       if (tree.isDisposed() || item.isDisposed()) {
+                                                               event.result = "";
+                                                               return COM.S_OK;
+                                                       }
+                                                       event.result += tree.getColumn(i).getText() + ": " + item.getText(i);
+                                                       if (i + 1 < columnCount) event.result += ", ";
+                                               }
+                                       }
+                               }
+                       }
+               }
+               for (int i = 0; i < accessibleListenersSize(); i++) {
+                       AccessibleListener listener = accessibleListeners.get(i);
+                       listener.getDescription(event);
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accDescription(" + v.lVal + ") returning " + event.result + hresult(event.result == null ? code : event.result.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               if (event.result == null) return code;
+               if (event.result.length() == 0) return COM.S_FALSE;
+               setString(pszDescription, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accFocus([out] 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.
+        */
+       int get_accFocus(long pvarChild) {
+               int osChild = ACC.CHILDID_NONE;
+               if (iaccessible != null) {
+                       /* Get the default focus child from the OS. */
+                       int code = iaccessible.get_accFocus(pvarChild);
+                       if (code == COM.S_OK) {
+                               VARIANT v = getVARIANT(pvarChild);
+                               if (v.vt == COM.VT_I4) osChild = v.lVal;
+                               // TODO: need to check VT_DISPATCH (don't use struct)
+                               if (DEBUG) if (v.vt == COM.VT_DISPATCH) print("IAccessible::get_accFocus() super returned VT_DISPATCH");
+                       }
+                       if (accessibleControlListenersSize() == 0) {
+                               if (DEBUG) print(this + ".IAccessible::get_accFocus() returning childID=" + osChild + " from super" + hresult(code));
+                               return code;
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = osChild == ACC.CHILDID_NONE ? ACC.CHILDID_NONE : osToChildID(osChild);
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getFocus(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible != null) {
+                       if (DEBUG) print(this + ".IAccessible::get_accFocus() returning accessible " + accessible.getAddress() + hresult(COM.S_OK));
+                       accessible.AddRef();
+                       setPtrVARIANT(pvarChild, COM.VT_DISPATCH, accessible.getAddress());
+                       return COM.S_OK;
+               }
+               int childID = event.childID;
+               if (childID == ACC.CHILDID_NONE) {
+                       if (DEBUG) print(this + ".IAccessible::get_accFocus() returning VT_EMPTY" + hresult(COM.S_FALSE));
+                       setIntVARIANT(pvarChild, COM.VT_EMPTY, 0);
+                       return COM.S_FALSE;
+               }
+               if (childID == ACC.CHILDID_SELF) {
+                       if (DEBUG) print(this + ".IAccessible::get_accFocus() returning CHILDID_SELF " + hresult(COM.S_OK));
+                       AddRef();
+                       setIntVARIANT(pvarChild, COM.VT_I4, COM.CHILDID_SELF);
+                       return COM.S_OK;
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accFocus() returning childID " + childIDToOs(childID) + hresult(COM.S_OK));
+               setIntVARIANT(pvarChild, COM.VT_I4, childIDToOs(childID));
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accHelp([in] varChild, [out] pszHelp) */
+       int get_accHelp(long varChild, long pszHelp) {
+               if (DEBUG) print(this + ".IAccessible::get_accHelp");
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               String osHelp = null;
+               if (iaccessible != null) {
+                       /* Get the default help string from the OS. */
+                       code = iaccessible.get_accHelp(varChild, pszHelp);
+                       if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+                       if (accessibleListenersSize() == 0) return code;
+                       if (code == COM.S_OK) {
+                               long[] pHelp = new long[1];
+                               OS.MoveMemory(pHelp, pszHelp, C.PTR_SIZEOF);
+                               int size = COM.SysStringByteLen(pHelp[0]);
+                               if (size > 0) {
+                                       char[] buffer = new char[(size + 1) /2];
+                                       OS.MoveMemory(buffer, pHelp[0], size);
+                                       osHelp = new String(buffer);
+                               }
+                       }
+               }
+
+               AccessibleEvent event = new AccessibleEvent(this);
+               event.childID = osToChildID(v.lVal);
+               event.result = osHelp;
+               for (int i = 0; i < accessibleListenersSize(); i++) {
+                       AccessibleListener listener = accessibleListeners.get(i);
+                       listener.getHelp(event);
+               }
+               if (event.result == null) return code;
+               if (event.result.length() == 0) return COM.S_FALSE;
+               setString(pszHelp, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accHelpTopic([out] pszHelpFile, [in] varChild, [out] pidTopic) */
+       int get_accHelpTopic(long pszHelpFile, long varChild, long pidTopic) {
+               if (DEBUG) print(this + ".IAccessible::get_accHelpTopic");
+               /* MSAA: "The accHelpTopic property is deprecated and should not be used." */
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               if (iaccessible != null) {
+                       /* Since it is possible that a native control might still handle get_accHelpTopic,
+                        * we will continue to send this through to the proxy. */
+                       code = iaccessible.get_accHelpTopic(pszHelpFile, varChild, pidTopic);
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+               }
+               return code;
+       }
+
+       /* IAccessible::get_accKeyboardShortcut([in] varChild, [out] pszKeyboardShortcut) */
+       int get_accKeyboardShortcut(long varChild, long pszKeyboardShortcut) {
+               if (DEBUG) print(this + ".IAccessible::get_accKeyboardShortcut");
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               String osKeyboardShortcut = null;
+               if (iaccessible != null) {
+                       /* Get the default keyboard shortcut from the OS. */
+                       code = iaccessible.get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
+                       if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+                       /* Process TabFolder even if there are no apps listening. */
+                       if (accessibleListenersSize() == 0 && !(control instanceof TabFolder)) return code;
+                       if (code == COM.S_OK) {
+                               long[] pKeyboardShortcut = new long[1];
+                               OS.MoveMemory(pKeyboardShortcut, pszKeyboardShortcut, C.PTR_SIZEOF);
+                               int size = COM.SysStringByteLen(pKeyboardShortcut[0]);
+                               if (size > 0) {
+                                       char[] buffer = new char[(size + 1) /2];
+                                       OS.MoveMemory(buffer, pKeyboardShortcut[0], size);
+                                       osKeyboardShortcut = new String(buffer);
+                               }
+                       }
+               }
+
+               AccessibleEvent event = new AccessibleEvent(this);
+               event.childID = osToChildID(v.lVal);
+               event.result = osKeyboardShortcut;
+               /* SWT TabFolders use Ctrl+PageDown to switch pages (not Ctrl+Tab). */
+               if (v.lVal == COM.CHILDID_SELF && control instanceof TabFolder) {
+                       event.result = SWT.getMessage ("SWT_SwitchPage_Shortcut"); //$NON-NLS-1$
+               }
+               for (int i = 0; i < accessibleListenersSize(); i++) {
+                       AccessibleListener listener = accessibleListeners.get(i);
+                       listener.getKeyboardShortcut(event);
+               }
+               if (event.result == null) return code;
+               if (event.result.length() == 0) return COM.S_FALSE;
+               setString(pszKeyboardShortcut, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accName([in] varChild, [out] pszName) */
+       int get_accName(long varChild, long pszName) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.S_FALSE;
+               String osName = null;
+               if (iaccessible != null) {
+                       /* Get the default name from the OS. */
+                       code = iaccessible.get_accName(varChild, pszName);
+                       if (code == COM.S_OK) {
+                               long[] pName = new long[1];
+                               OS.MoveMemory(pName, pszName, C.PTR_SIZEOF);
+                               int size = COM.SysStringByteLen(pName[0]);
+                               if (size > 0) {
+                                       char[] buffer = new char[(size + 1) /2];
+                                       OS.MoveMemory(buffer, pName[0], size);
+                                       osName = new String(buffer);
+                               }
+                       }
+                       if (code == COM.E_INVALIDARG) code = COM.S_FALSE; // proxy doesn't know about app childID
+                       /* Process Text even if there are no apps listening. */
+                       if (accessibleListenersSize() == 0 && !(control instanceof Text)) {
+                               if (DEBUG) print(this + ".IAccessible::get_accName(" + v.lVal + ") returning name=" + osName + " from super" + hresult(code));
+                               return code;
+                       }
+               }
+
+               AccessibleEvent event = new AccessibleEvent(this);
+               event.childID = osToChildID(v.lVal);
+               event.result = osName;
+               /*
+               * Bug in Windows:  A Text with SWT.SEARCH style uses EM_SETCUEBANNER
+               * to set the message text. This text should be used as the control's
+               * accessible name, however it is not. The fix is to return the message
+               * text here as the accName (unless there is a preceding label).
+               */
+               if (control instanceof Text && (control.getStyle() & SWT.SEARCH) != 0 && osName == null) {
+                       event.result = ((Text) control).getMessage();
+               }
+               for (int i = 0; i < accessibleListenersSize(); i++) {
+                       AccessibleListener listener = accessibleListeners.get(i);
+                       listener.getName(event);
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accName(" + v.lVal + ") returning " + event.result + hresult(event.result == null ? code : event.result.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               if (event.result == null) return code;
+               if (event.result.length() == 0) return COM.S_FALSE;
+               setString(pszName, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::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.
+        */
+       int get_accParent(long ppdispParent) {
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               if (iaccessible != null) {
+                       /* Currently, we don't expose this as API. Forward to the proxy. */
+                       code = iaccessible.get_accParent(ppdispParent);
+               }
+               if (parent != null) {
+                       /* For lightweight accessibles, return the accessible's parent. */
+                       parent.AddRef();
+                       OS.MoveMemory(ppdispParent, new long[] { parent.getAddress() }, C.PTR_SIZEOF);
+                       code = COM.S_OK;
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accParent() returning" + (parent != null ? " " + parent.getAddress() : " from super") + hresult(code));
+               return code;
+       }
+
+       /* IAccessible::get_accRole([in] varChild, [out] pvarRole) */
+       int get_accRole(long varChild, long pvarRole) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int osRole = COM.ROLE_SYSTEM_CLIENT;
+               if (iaccessible != null) {
+                       /* Get the default role from the OS. */
+                       int code = iaccessible.get_accRole(varChild, pvarRole);
+                       if (code == COM.S_OK) {
+                               VARIANT v2 = getVARIANT(pvarRole);
+                               if (v2.vt == COM.VT_I4) osRole = v2.lVal;
+                       }
+               }
+
+               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 (control instanceof Tree || control instanceof Table) {
+                       if (v.lVal != COM.CHILDID_SELF && (control.getStyle() & SWT.CHECK) != 0) event.detail = ACC.ROLE_CHECKBUTTON;
+               }
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getRole(event);
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accRole(" + v.lVal + ") returning " + getRoleString(roleToOs(event.detail)) + hresult(COM.S_OK));
+               setIntVARIANT(pvarRole, COM.VT_I4, roleToOs(event.detail));
+               return COM.S_OK;
+       }
+
+       /* IAccessible::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.
+        */
+       int get_accSelection(long pvarChildren) {
+               if (DEBUG) print(this + ".IAccessible::get_accSelection");
+               int osChild = ACC.CHILDID_NONE;
+               long osChildObject = 0;
+               if (iaccessible != null) {
+                       /* Get the default selection from the OS. */
+                       int code = iaccessible.get_accSelection(pvarChildren);
+                       if (accessibleControlListenersSize() == 0) return code;
+                       if (code == COM.S_OK) {
+                               VARIANT v = getVARIANT(pvarChildren);
+                               if (v.vt == COM.VT_I4) {
+                                       osChild = osToChildID(v.lVal);
+                               } else if (v.vt == COM.VT_DISPATCH) {
+                                       osChildObject = v.lVal; // TODO: don't use struct; lVal is an int
+                               } else if (v.vt == COM.VT_UNKNOWN) {
+                                       osChild = ACC.CHILDID_MULTIPLE;
+                                       // TODO: Should get IEnumVARIANT from punkVal field, and enumerate children...
+                               }
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = osChild;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getSelection(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible != null) {
+                       accessible.AddRef();
+                       setPtrVARIANT(pvarChildren, COM.VT_DISPATCH, accessible.getAddress());
+                       return COM.S_OK;
+               }
+               int childID = event.childID;
+               if (childID == ACC.CHILDID_NONE) {
+                       if (osChildObject != 0) return COM.S_OK;
+                       setIntVARIANT(pvarChildren, COM.VT_EMPTY, 0);
+                       return COM.S_FALSE;
+               }
+               if (childID == ACC.CHILDID_MULTIPLE) {
+                       // TODO: return an enumeration for event.children (currently just returns enumeration from proxy)
+                       //AddRef();
+                       //setPtrVARIANT(pvarChildren, COM.VT_UNKNOWN, getAddress());
+                       return COM.S_OK;
+               }
+               if (childID == ACC.CHILDID_SELF) {
+                       AddRef();
+                       setPtrVARIANT(pvarChildren, COM.VT_DISPATCH, getAddress());
+                       return COM.S_OK;
+               }
+               setIntVARIANT(pvarChildren, COM.VT_I4, childIDToOs(childID));
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accState([in] varChild, [out] pvarState) */
+       int get_accState(long varChild, long pvarState) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int osState = 0;
+               if (iaccessible != null) {
+                       /* Get the default state from the OS. */
+                       int code = iaccessible.get_accState(varChild, pvarState);
+                       if (code == COM.S_OK) {
+                               VARIANT v2 = getVARIANT(pvarState);
+                               if (v2.vt == COM.VT_I4) osState = v2.lVal;
+                       }
+               }
+
+               boolean 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 != COM.CHILDID_SELF) {
+                       if (control instanceof Tree && (control.getStyle() & SWT.CHECK) != 0) {
+                               long hwnd = control.handle;
+                               TVITEM tvItem = new TVITEM ();
+                               tvItem.mask = OS.TVIF_HANDLE | OS.TVIF_STATE;
+                               tvItem.stateMask = OS.TVIS_STATEIMAGEMASK;
+                               tvItem.hItem = OS.SendMessage (hwnd, OS.TVM_MAPACCIDTOHTREEITEM, v.lVal, 0);
+                               long result = OS.SendMessage (hwnd, OS.TVM_GETITEM, 0, tvItem);
+                               boolean checked = (result != 0) && (((tvItem.state >> 12) & 1) == 0);
+                               if (checked) event.detail |= ACC.STATE_CHECKED;
+                               grayed = tvItem.state >> 12 > 2;
+                       } else if (control instanceof Table && (control.getStyle() & SWT.CHECK) != 0) {
+                               Table table = (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 < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getState(event);
+               }
+               int state = stateToOs(event.detail);
+               if ((state & ACC.STATE_CHECKED) != 0 && grayed) {
+                       state &= ~ COM.STATE_SYSTEM_CHECKED;
+                       state |= COM.STATE_SYSTEM_MIXED;
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accState(" + v.lVal + ") returning" + getStateString(state) + hresult(COM.S_OK));
+               setIntVARIANT(pvarState, COM.VT_I4, state);
+               return COM.S_OK;
+       }
+
+       /* IAccessible::get_accValue([in] varChild, [out] pszValue) */
+       int get_accValue(long varChild, long pszValue) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               String osValue = null;
+               if (iaccessible != null) {
+                       /* Get the default value string from the OS. */
+                       code = iaccessible.get_accValue(varChild, pszValue);
+                       if (code == COM.S_OK) {
+                               long[] pValue = new long[1];
+                               OS.MoveMemory(pValue, pszValue, C.PTR_SIZEOF);
+                               int size = COM.SysStringByteLen(pValue[0]);
+                               if (size > 0) {
+                                       char[] buffer = new char[(size + 1) /2];
+                                       OS.MoveMemory(buffer, pValue[0], size);
+                                       osValue = new String(buffer);
+                               }
+                       }
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+                       /* Process Text even if there are no apps listening. */
+                       if (accessibleControlListenersSize() == 0 && !(control instanceof Text)) {
+                               if (DEBUG) print(this + ".IAccessible::get_accValue(" + v.lVal + ") returning value=" + osValue + " from super" + hresult(code));
+                               return code;
+                       }
+               }
+
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = osToChildID(v.lVal);
+               event.result = osValue;
+               /*
+               * Bug in Windows:  A Text with SWT.SEARCH style uses EM_SETCUEBANNER
+               * to set the message text. This text should be used as the control's
+               * accessible value when the control does not have focus, however it
+               * is not. The fix is to return the message text here as the accValue.
+               */
+               if (control instanceof Text && (control.getStyle() & SWT.SEARCH) != 0 && !control.isFocusControl()) {
+                       event.result = ((Text) control).getMessage();
+               }
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getValue(event);
+               }
+               if (DEBUG) print(this + ".IAccessible::get_accValue(" + v.lVal + ") returning " + event.result + hresult(event.result == null ? code : COM.S_OK));
+               if (event.result == null) return code;
+               // empty string is a valid value, so do not test for it
+               setString(pszValue, event.result);
+               return COM.S_OK;
+       }
+
+       /* put_accName([in] varChild, [in] szName) */
+       int put_accName(long varChild, long szName) {
+               /* MSAA: "The IAccessible::put_accName method is no longer supported. Servers should return E_NOTIMPL." */
+               return COM.E_NOTIMPL;
+       }
+
+       /* put_accValue([in] varChild, [in] szValue) */
+       int put_accValue(long varChild, long szValue) {
+               /* MSAA: this method is supported for some UI elements (usually edit controls). */
+               VARIANT v = getVARIANT(varChild);
+               if (v.vt != COM.VT_I4) return COM.E_INVALIDARG;
+               int code = COM.DISP_E_MEMBERNOTFOUND;
+               if (v.lVal == COM.CHILDID_SELF && accessibleEditableTextListenersSize() > 0) {
+                       /*
+                        * If the object supports AccessibleEditableTextListener.replaceText,
+                        * then give the object a chance to handle this event.
+                        */
+                       AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+                       event.start = 0;
+                       event.end = getCharacterCount();
+                       if (event.end >= 0) {
+                               int size = COM.SysStringByteLen(szValue);
+                               char [] buffer = new char [(size + 1) / 2];
+                               OS.MoveMemory (buffer, szValue, size);
+                               event.string = new String (buffer);
+                               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                                       listener.replaceText(event);
+                               }
+                               if (event.result != null && event.result.equals(ACC.OK)) code = COM.S_OK;
+                               if (DEBUG) print(this + ".IAccessible::put_accValue(" + v.lVal + ", \"" + event.string + "\") returning " + hresult(code));
+                       }
+               }
+               if (code != COM.S_OK && iaccessible != null) {
+                       /* If the object did not handle the event, then forward to the proxy. */
+                       code = iaccessible.put_accValue(varChild, szValue);
+                       if (code == COM.E_INVALIDARG) code = COM.DISP_E_MEMBERNOTFOUND; // proxy doesn't know about app childID
+                       if (DEBUG) print(this + ".IAccessible::put_accValue(" + v.lVal + ") returning " + hresult(code) + " from proxy");
+               }
+               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).
+        */
+
+       /* IEnumVARIANT::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.
+        */
+       int Next(int celt, long rgvar, long pceltFetched) {
+               if (DEBUG) print(this + ".IEnumVARIANT::Next");
+               /* If there are no listeners, query the proxy for
+                * its IEnumVariant, and get the Next items from it.
+                */
+               if (iaccessible != null && accessibleControlListenersSize() == 0) {
+                       long[] ppvObject = new long[1];
+                       int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+                       if (code != COM.S_OK) return code;
+                       IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+                       int[] celtFetched = new int[1];
+                       code = ienumvariant.Next(celt, rgvar, celtFetched);
+                       ienumvariant.Release();
+                       OS.MoveMemory(pceltFetched, celtFetched, 4);
+                       return code;
+               }
+
+               if (rgvar == 0) return COM.E_INVALIDARG;
+               if (pceltFetched == 0 && celt != 1) return COM.E_INVALIDARG;
+               if (enumIndex == 0) {
+                       AccessibleControlEvent event = new AccessibleControlEvent(this);
+                       event.childID = ACC.CHILDID_SELF;
+                       for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                               AccessibleControlListener listener = accessibleControlListeners.get(i);
+                               listener.getChildren(event);
+                       }
+                       variants = event.children;
+               }
+               Object[] nextItems = null;
+               if (variants != 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 (child instanceof Integer) {
+                                               nextItems[i] = Integer.valueOf(childIDToOs(((Integer)child).intValue()));
+                                       } else {
+                                               nextItems[i] = child;
+                                       }
+                                       enumIndex++;
+                               }
+                       }
+               }
+               if (nextItems != null) {
+                       for (int i = 0; i < nextItems.length; i++) {
+                               Object nextItem = nextItems[i];
+                               if (nextItem instanceof Integer) {
+                                       int item = ((Integer) nextItem).intValue();
+                                       setIntVARIANT(rgvar + i * VARIANT.sizeof, COM.VT_I4, item);
+                               } else {
+                                       Accessible accessible = (Accessible) nextItem;
+                                       accessible.AddRef();
+                                       setPtrVARIANT(rgvar + i * VARIANT.sizeof, COM.VT_DISPATCH, accessible.getAddress());
+                               }
+                       }
+                       if (pceltFetched != 0)
+                               OS.MoveMemory(pceltFetched, new int[] {nextItems.length}, 4);
+                       if (nextItems.length == celt) return COM.S_OK;
+               } else {
+                       if (pceltFetched != 0)
+                               OS.MoveMemory(pceltFetched, new int[] {0}, 4);
+               }
+               return COM.S_FALSE;
+       }
+
+       /* IEnumVARIANT::Skip([in] celt) over the specified number of elements in the enumeration sequence. */
+       int Skip(int celt) {
+               if (DEBUG) print(this + ".IEnumVARIANT::Skip");
+               /* If there are no listeners, query the proxy
+                * for its IEnumVariant, and tell it to Skip.
+                */
+               if (iaccessible != null && accessibleControlListenersSize() == 0) {
+                       long[] ppvObject = new long[1];
+                       int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+                       if (code != COM.S_OK) return code;
+                       IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+                       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;
+       }
+
+       /* IEnumVARIANT::Reset() the enumeration sequence to the beginning. */
+       int Reset() {
+               if (DEBUG) print(this + ".IEnumVARIANT::Reset");
+               /* If there are no listeners, query the proxy
+                * for its IEnumVariant, and tell it to Reset.
+                */
+               if (iaccessible != null && accessibleControlListenersSize() == 0) {
+                       long[] ppvObject = new long[1];
+                       int code = (int)iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+                       if (code != COM.S_OK) return code;
+                       IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+                       code = ienumvariant.Reset();
+                       ienumvariant.Release();
+                       return code;
+               }
+
+               enumIndex = 0;
+               return COM.S_OK;
+       }
+
+       /* IEnumVARIANT::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(long ppEnum) {
+               if (DEBUG) print(this + ".IEnumVARIANT::Clone");
+               /* If there are no listeners, query the proxy for
+                * its IEnumVariant, and get the Clone from it.
+                */
+               if (iaccessible != null && accessibleControlListenersSize() == 0) {
+                       long[] ppvObject = new long[1];
+                       int code = iaccessible.QueryInterface(COM.IIDIEnumVARIANT, ppvObject);
+                       if (code != COM.S_OK) return code;
+                       IEnumVARIANT ienumvariant = new IEnumVARIANT(ppvObject[0]);
+                       long [] pEnum = new long [1];
+                       code = ienumvariant.Clone(pEnum);
+                       ienumvariant.Release();
+                       OS.MoveMemory(ppEnum, pEnum, C.PTR_SIZEOF);
+                       return code;
+               }
+
+               if (ppEnum == 0) return COM.E_INVALIDARG;
+               OS.MoveMemory(ppEnum, new long[] { objIEnumVARIANT.getAddress() }, C.PTR_SIZEOF);
+               AddRef();
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_nRelations([out] pNRelations) */
+       int get_nRelations(long pNRelations) {
+               int count = getRelationCount();
+               if (DEBUG) print(this + ".IAccessible2::get_nRelations returning " + count + hresult(COM.S_OK));
+               OS.MoveMemory(pNRelations, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_relation([in] relationIndex, [out] ppRelation) */
+       int get_relation(int relationIndex, long ppRelation) {
+               int i = -1;
+               for (int type = 0; type < MAX_RELATION_TYPES; type++) {
+                       Relation relation = relations[type];
+                       if (relation != null) i++;
+                       if (i == relationIndex) {
+                               if (DEBUG) print(this + ".IAccessible2::get_relation(" + relationIndex + ") returning " + relation.getAddress() + hresult(COM.S_OK));
+                               relation.AddRef();
+                               OS.MoveMemory(ppRelation, new long[] { relation.getAddress() }, C.PTR_SIZEOF);
+                               return COM.S_OK;
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessible2::get_relation(" + relationIndex + ") returning" + hresult(COM.E_INVALIDARG));
+               return COM.E_INVALIDARG;
+       }
+
+       /* IAccessible2::get_relations([in] maxRelations, [out] ppRelations, [out] pNRelations) */
+       int get_relations(int maxRelations, long ppRelations, long pNRelations) {
+               int count = 0;
+               for (int type = 0; type < MAX_RELATION_TYPES; type++) {
+                       if (count == maxRelations) break;
+                       Relation relation = relations[type];
+                       if (relation != null) {
+                               relation.AddRef();
+                               OS.MoveMemory(ppRelations + count * C.PTR_SIZEOF, new long[] { relation.getAddress() }, C.PTR_SIZEOF);
+                               count++;
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessible2::get_relations(" + maxRelations + ") returning " + count + hresult(COM.S_OK));
+               OS.MoveMemory(pNRelations, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_role([out] pRole) */
+       int get_role(long pRole) {
+               int role = getRole();
+               if (role == 0) role = getDefaultRole();
+               if (DEBUG) print(this + ".IAccessible2::get_role() returning " + getRoleString(role) + hresult(COM.S_OK));
+               OS.MoveMemory(pRole, new int [] { role }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::scrollTo([in] scrollType) */
+       int scrollTo(int scrollType) {
+               if (DEBUG) print(this + ".IAccessible2::scrollTo");
+               if (scrollType < ACC.SCROLL_TYPE_LEFT_EDGE || scrollType > ACC.SCROLL_TYPE_ANYWHERE) return COM.E_INVALIDARG;
+               return COM.E_NOTIMPL;
+       }
+
+       /* IAccessible2::scrollToPoint([in] coordinateType, [in] x, [in] y) */
+       int scrollToPoint(int coordinateType, int x, int y) {
+               if (DEBUG) print(this + ".IAccessible2::scrollToPoint");
+               if (coordinateType != COM.IA2_COORDTYPE_SCREEN_RELATIVE) return COM.E_INVALIDARG;
+               return COM.E_NOTIMPL;
+       }
+
+       /* IAccessible2::get_groupPosition([out] pGroupLevel, [out] pSimilarItemsInGroup, [out] pPositionInGroup) */
+       int get_groupPosition(long pGroupLevel, long pSimilarItemsInGroup, long pPositionInGroup) {
+               if (control != null && control.isDisposed()) return COM.CO_E_OBJNOTCONNECTED;
+               AccessibleAttributeEvent event = new AccessibleAttributeEvent(this);
+               event.groupLevel = event.groupCount = event.groupIndex = -1;
+               for (int i = 0; i < accessibleAttributeListenersSize(); i++) {
+                       AccessibleAttributeListener listener = accessibleAttributeListeners.get(i);
+                       listener.getAttributes(event);
+               }
+               int groupLevel = (event.groupLevel != -1) ? event.groupLevel : 0;
+               int similarItemsInGroup = (event.groupCount != -1) ? event.groupCount : 0;
+               int positionInGroup = (event.groupIndex != -1) ? event.groupIndex : 0;
+               if (similarItemsInGroup == 0 && positionInGroup == 0) {
+                       /* Determine position and count for radio buttons. */
+                       if (control instanceof Button && ((control.getStyle() & SWT.RADIO) != 0)) {
+                               Control [] children = control.getParent().getChildren();
+                               positionInGroup = 1;
+                               similarItemsInGroup = 1;
+                               for (int i = 0; i < children.length; i++) {
+                                       Control child = children[i];
+                                       if (child instanceof Button && ((child.getStyle() & SWT.RADIO) != 0)) {
+                                               if (child == control) positionInGroup = similarItemsInGroup;
+                                               else similarItemsInGroup++;
+                                       }
+                               }
+                       }
+               }
+               OS.MoveMemory(pGroupLevel, new int [] { groupLevel }, 4);
+               OS.MoveMemory(pSimilarItemsInGroup, new int [] { similarItemsInGroup }, 4);
+               OS.MoveMemory(pPositionInGroup, new int [] { positionInGroup }, 4);
+               if (DEBUG) print(this + ".IAccessible2::get_groupPosition() returning level=" + groupLevel + ", count=" + similarItemsInGroup + ", index=" + positionInGroup + hresult(groupLevel == 0 && similarItemsInGroup == 0 && positionInGroup == 0 ? COM.S_FALSE : COM.S_OK));
+               if (groupLevel == 0 && similarItemsInGroup == 0 && positionInGroup == 0) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_states([out] pStates) */
+       int get_states(long pStates) {
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = ACC.CHILDID_SELF;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getState(event);
+               }
+               int states = event.detail;
+               int ia2States = 0;
+               if ((states & ACC.STATE_ACTIVE) != 0) ia2States |= COM.IA2_STATE_ACTIVE;
+               if ((states & ACC.STATE_SINGLELINE) != 0) ia2States |= COM.IA2_STATE_SINGLE_LINE;
+               if ((states & ACC.STATE_MULTILINE) != 0) ia2States |= COM.IA2_STATE_MULTI_LINE;
+               if ((states & ACC.STATE_REQUIRED) != 0) ia2States |= COM.IA2_STATE_REQUIRED;
+               if ((states & ACC.STATE_INVALID_ENTRY) != 0) ia2States |= COM.IA2_STATE_INVALID_ENTRY;
+               if ((states & ACC.STATE_SUPPORTS_AUTOCOMPLETION) != 0) ia2States |= COM.IA2_STATE_SUPPORTS_AUTOCOMPLETION;
+
+               /* If the role is text and there are TextExtendedListeners, then set IA2_STATE_EDITABLE.
+                * Note that IA2_STATE_EDITABLE is not the opposite of STATE_READONLY.
+                * Instead, it means: "has a caret, supports IAccessibleText, and is a text editing environment".
+                */
+               if (getRole() == ACC.ROLE_TEXT && accessibleTextExtendedListenersSize() > 0) {
+                       ia2States |= COM.IA2_STATE_EDITABLE;
+               }
+               if (DEBUG) print(this + ".IAccessible2::get_states returning" + getIA2StatesString(ia2States) + hresult(COM.S_OK));
+               OS.MoveMemory(pStates, new int [] { ia2States }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_extendedRole([out] pbstrExtendedRole) */
+       int get_extendedRole(long pbstrExtendedRole) {
+               /* This feature is not supported. */
+               setString(pbstrExtendedRole, null);
+               return COM.S_FALSE;
+       }
+
+       /* IAccessible2::get_localizedExtendedRole([out] pbstrLocalizedExtendedRole) */
+       int get_localizedExtendedRole(long pbstrLocalizedExtendedRole) {
+               /* This feature is not supported. */
+               setString(pbstrLocalizedExtendedRole, null);
+               return COM.S_FALSE;
+       }
+
+       /* IAccessible2::get_nExtendedStates([out] pNExtendedStates) */
+       int get_nExtendedStates(long pNExtendedStates) {
+               /* This feature is not supported. */
+               OS.MoveMemory(pNExtendedStates, new int [] { 0 }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_extendedStates([in] maxExtendedStates, [out] ppbstrExtendedStates, [out] pNExtendedStates) */
+       int get_extendedStates(int maxExtendedStates, long ppbstrExtendedStates, long pNExtendedStates) {
+               /* This feature is not supported. */
+               setString(ppbstrExtendedStates, null);
+               OS.MoveMemory(pNExtendedStates, new int [] { 0 }, 4);
+               return COM.S_FALSE;
+       }
+
+       /* IAccessible2::get_localizedExtendedStates([in] maxLocalizedExtendedStates, [out] ppbstrLocalizedExtendedStates, [out] pNLocalizedExtendedStates) */
+       int get_localizedExtendedStates(int maxLocalizedExtendedStates, long ppbstrLocalizedExtendedStates, long pNLocalizedExtendedStates) {
+               /* This feature is not supported. */
+               setString(ppbstrLocalizedExtendedStates, null);
+               OS.MoveMemory(pNLocalizedExtendedStates, new int [] { 0 }, 4);
+               return COM.S_FALSE;
+       }
+
+       /* IAccessible2::get_uniqueID([out] pUniqueID) */
+       int get_uniqueID(long pUniqueID) {
+               if (uniqueID == -1) uniqueID = UniqueID--;
+               if (DEBUG) print(this + ".IAccessible2::get_uniqueID returning " + uniqueID + hresult(COM.S_OK));
+               OS.MoveMemory(pUniqueID, new long [] { uniqueID }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_windowHandle([out] pWindowHandle) */
+       int get_windowHandle(long pWindowHandle) {
+               if (DEBUG) print(this + ".IAccessible2::get_windowHandle returning " + control.handle + hresult(COM.S_OK));
+               OS.MoveMemory(pWindowHandle, new long [] { control.handle }, C.PTR_SIZEOF);
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_indexInParent([out] pIndexInParent) */
+       int get_indexInParent(long pIndexInParent) {
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = ACC.CHILDID_CHILD_INDEX;
+               event.detail = -1;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getChild(event);
+               }
+               int indexInParent = event.detail;
+               if (indexInParent == -1) {
+//                     /* The application did not implement CHILDID_CHILD_INDEX,
+//                      * so determine the index by looping through the parent's
+//                      * children looking for this Accessible. This may be slow,
+//                      * so applications are strongly encouraged to implement
+//                      * getChild for CHILDID_CHILD_INDEX.
+//                      */
+//                     // TODO: finish this. See also get_groupPosition
+                       // this won't work because VARIANT.sizeof isn't big enough on 64-bit machines.
+                       // just create an  long [] ppdispParent - it's not a variant anyhow...
+//                     long ppdispParent = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof);
+//                     int code = get_accParent(ppdispParent);
+//                     if (code == COM.S_OK) {
+//                             VARIANT v = getVARIANT(ppdispParent);
+//                             if (v.vt == COM.VT_DISPATCH) {
+//                                     IAccessible accParent = new IAccessible(v.lVal);
+//                                     long pcountChildren = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, 4);
+//                                     code = accParent.get_accChildCount(pcountChildren);
+//                                     if (code == COM.S_OK) {
+//                                             int [] childCount = new int[1];
+//                                             OS.MoveMemory(childCount, pcountChildren, 4);
+//                                             int[] pcObtained = new int[1];
+//                                             long rgVarChildren = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof * childCount[0]);
+//                                             System.out.println("Asking for AccessibleChildren");
+//                                             code = COM.AccessibleChildren(accParent.getAddress(), 0, childCount[0], rgVarChildren, pcObtained);
+//                                             if (code == COM.S_OK) {
+//                                                     System.out.println("Got this far - now what?");
+//                                             } else {
+//                                                     System.out.println("AccessibleChildren failed? code=" + code);
+//                                             }
+//                                             OS.GlobalFree(rgVarChildren);
+//                                     } else {
+//                                             System.out.println("get_accChildCount failed? code=" + code);
+//                                     }
+//                                     OS.GlobalFree (pcountChildren);
+//                             } else {
+//                                     System.out.println("get_accParent did not return VT_DISPATCH? It returned: " + v.vt);
+//                             }
+//                             COM.VariantClear(ppdispParent);
+//                             OS.GlobalFree (ppdispParent);
+//                     } else {
+//                             System.out.println("get_accParent failed? code=" + code);
+//                     }
+               }
+
+               if (DEBUG) print(this + ".IAccessible2::get_indexInParent returning " + indexInParent + hresult(indexInParent == -1 ? COM.S_FALSE : COM.S_OK));
+               OS.MoveMemory(pIndexInParent, new int [] { indexInParent }, 4);
+               return indexInParent == -1 ? COM.S_FALSE : COM.S_OK;
+       }
+
+       /* IAccessible2::get_locale([out] pLocale) */
+       int get_locale(long pLocale) {
+               /* Return the default locale for the JVM. */
+               Locale locale = Locale.getDefault();
+
+               char[] data = (locale.getLanguage()+"\0").toCharArray();
+               long ptr = COM.SysAllocString(data);
+               OS.MoveMemory(pLocale, new long[] {ptr}, C.PTR_SIZEOF);
+
+               data = (locale.getCountry()+"\0").toCharArray();
+               ptr = COM.SysAllocString(data);
+               OS.MoveMemory(pLocale + C.PTR_SIZEOF, new long[] {ptr}, C.PTR_SIZEOF);
+
+               data = (locale.getVariant()+"\0").toCharArray();
+               ptr = COM.SysAllocString(data);
+               OS.MoveMemory(pLocale + 2 * C.PTR_SIZEOF, new long[] {ptr}, C.PTR_SIZEOF);
+
+               if (DEBUG) print(this + ".IAccessible2::get_locale() returning" + hresult(COM.S_OK));
+               return COM.S_OK;
+       }
+
+       /* IAccessible2::get_attributes([out] pbstrAttributes) */
+       int get_attributes(long pbstrAttributes) {
+               AccessibleAttributeEvent event = new AccessibleAttributeEvent(this);
+               for (int i = 0; i < accessibleAttributeListenersSize(); i++) {
+                       AccessibleAttributeListener listener = accessibleAttributeListeners.get(i);
+                       listener.getAttributes(event);
+               }
+               String attributes = "";
+               attributes += "margin-left:" + event.leftMargin + ";";
+               attributes += "margin-top:" + event.topMargin + ";";
+               attributes += "margin-right:" + event.rightMargin + ";";
+               attributes += "margin-bottom:" + event.bottomMargin + ";";
+               if (event.tabStops != null) {
+                       for (int i = 0; i < event.tabStops.length; i++) {
+                               attributes += "tab-stop:position=" + event.tabStops[i] + ";";
+                       }
+               }
+               if (event.justify) attributes += "text-align:justify;";
+               attributes += "text-align:" + (event.alignment == SWT.LEFT ? "left" : event.alignment == SWT.RIGHT ? "right" : "center") + ";";
+               attributes += "text-indent:" + event.indent + ";";
+               if (event.attributes != null) {
+                       for (int i = 0; i + 1 < event.attributes.length; i += 2) {
+                               attributes += event.attributes[i] + ":" + event.attributes[i+1] + ";";
+                       }
+               }
+
+               /* If the role is text, then specify the text model for JAWS. */
+               if (getRole() == ACC.ROLE_TEXT) {
+                       attributes += "text-model:a1;";
+               }
+               if (DEBUG) print(this + ".IAccessible2::get_attributes() returning " + attributes + hresult(attributes.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               setString(pbstrAttributes, attributes);
+               if (attributes.length() == 0) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleAction::get_nActions([out] pNActions) */
+       int get_nActions(long pNActions) {
+               AccessibleActionEvent event = new AccessibleActionEvent(this);
+               for (int i = 0; i < accessibleActionListenersSize(); i++) {
+                       AccessibleActionListener listener = accessibleActionListeners.get(i);
+                       listener.getActionCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleAction::get_nActions() returning " + event.count + hresult(COM.S_OK));
+               OS.MoveMemory(pNActions, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleAction::doAction([in] actionIndex) */
+       int doAction(int actionIndex) {
+               AccessibleActionEvent event = new AccessibleActionEvent(this);
+               event.index = actionIndex;
+               for (int i = 0; i < accessibleActionListenersSize(); i++) {
+                       AccessibleActionListener listener = accessibleActionListeners.get(i);
+                       listener.doAction(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleAction::doAction(" + actionIndex + ") returning" + hresult(event.result == null || !event.result.equals(ACC.OK) ? COM.E_INVALIDARG : COM.S_OK));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleAction::get_description([in] actionIndex, [out] pbstrDescription) */
+       int get_description(int actionIndex, long pbstrDescription) {
+               AccessibleActionEvent event = new AccessibleActionEvent(this);
+               event.index = actionIndex;
+               for (int i = 0; i < accessibleActionListenersSize(); i++) {
+                       AccessibleActionListener listener = accessibleActionListeners.get(i);
+                       listener.getDescription(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleAction::get_description(" + actionIndex + ") returning " + event.result + hresult(event.result == null || event.result.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               setString(pbstrDescription, event.result);
+               if (event.result == null || event.result.length() == 0) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleAction::get_keyBinding([in] actionIndex, [in] nMaxBindings, [out] ppbstrKeyBindings, [out] pNBindings) */
+       int get_keyBinding(int actionIndex, int nMaxBindings, long ppbstrKeyBindings, long pNBindings) {
+               AccessibleActionEvent event = new AccessibleActionEvent(this);
+               event.index = actionIndex;
+               for (int i = 0; i < accessibleActionListenersSize(); i++) {
+                       AccessibleActionListener listener = accessibleActionListeners.get(i);
+                       listener.getKeyBinding(event);
+               }
+               String keyBindings = event.result;
+               int length = 0;
+               if (keyBindings != null) length = keyBindings.length();
+               int i = 0, count = 0;
+               while (i < length) {
+                       if (count == nMaxBindings) break;
+                       int j = keyBindings.indexOf(';', i);
+                       if (j == -1) j = length;
+                       String keyBinding = keyBindings.substring(i, j);
+                       if (keyBinding.length() > 0) {
+                               setString(ppbstrKeyBindings + count * C.PTR_SIZEOF, keyBinding);
+                               count++;
+                       }
+                       i = j + 1;
+               }
+               if (DEBUG) print(this + ".IAccessibleAction::get_keyBinding(index=" + actionIndex + " max=" + nMaxBindings + ") returning count=" + count + hresult(count == 0 ? COM.S_FALSE : COM.S_OK));
+               OS.MoveMemory(pNBindings, new int [] { count }, 4);
+               if (count == 0) {
+                       setString(ppbstrKeyBindings, null);
+                       return COM.S_FALSE;
+               }
+               return COM.S_OK;
+       }
+
+       /* IAccessibleAction::get_name([in] actionIndex, [out] pbstrName) */
+       int get_name(int actionIndex, long pbstrName) {
+               AccessibleActionEvent event = new AccessibleActionEvent(this);
+               event.index = actionIndex;
+               event.localized = false;
+               for (int i = 0; i < accessibleActionListenersSize(); i++) {
+                       AccessibleActionListener listener = accessibleActionListeners.get(i);
+                       listener.getName(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleAction::get_name(" + actionIndex + ") returning " + event.result + hresult(event.result == null || event.result.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               if (event.result == null || event.result.length() == 0) {
+                       setString(pbstrName, null);
+                       return COM.S_FALSE;
+               }
+               setString(pbstrName, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleAction::get_localizedName([in] actionIndex, [out] pbstrLocalizedName) */
+       int get_localizedName(int actionIndex, long pbstrLocalizedName) {
+               AccessibleActionEvent event = new AccessibleActionEvent(this);
+               event.index = actionIndex;
+               event.localized = true;
+               for (int i = 0; i < accessibleActionListenersSize(); i++) {
+                       AccessibleActionListener listener = accessibleActionListeners.get(i);
+                       listener.getName(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleAction::get_localizedName(" + actionIndex + ") returning " + event.result + hresult(event.result == null || event.result.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               if (event.result == null || event.result.length() == 0) {
+                       setString(pbstrLocalizedName, null);
+                       return COM.S_FALSE;
+               }
+               setString(pbstrLocalizedName, event.result);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleApplication::get_appName([out] pbstrName) */
+       int get_appName(long pbstrName) {
+               String appName = Display.getAppName();
+               if (DEBUG) print(this + ".IAccessibleApplication::get_appName() returning " + appName + hresult(appName == null || appName.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               if (appName == null || appName.length() == 0) {
+                       setString(pbstrName, null);
+                       return COM.S_FALSE;
+               }
+               setString(pbstrName, appName);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleApplication::get_appVersion([out] pbstrVersion) */
+       int get_appVersion(long pbstrVersion) {
+               String appVersion = Display.getAppVersion();
+               if (DEBUG) print(this + ".IAccessibleApplication::get_appVersion() returning" + appVersion + hresult(appVersion == null || appVersion.length() == 0 ? COM.S_FALSE : COM.S_OK));
+               if (appVersion == null || appVersion.length() == 0) {
+                       setString(pbstrVersion, null);
+                       return COM.S_FALSE;
+               }
+               setString(pbstrVersion, appVersion);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleApplication::get_toolkitName([out] pbstrName) */
+       int get_toolkitName(long pbstrName) {
+               String toolkitName = "SWT";
+               if (DEBUG) print(this + ".IAccessibleApplication::get_toolkitName() returning" + toolkitName + hresult(COM.S_OK));
+               setString(pbstrName, toolkitName);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleApplication::get_toolkitVersion([out] pbstrVersion) */
+       int get_toolkitVersion(long pbstrVersion) {
+               String toolkitVersion = "" + SWT.getVersion(); //$NON-NLS-1$
+               if (DEBUG) print(this + ".IAccessibleApplication::get_toolkitVersion() returning" + toolkitVersion + hresult(COM.S_OK));
+               setString(pbstrVersion, toolkitVersion);
+               return COM.S_OK;
+       }
+
+       // The following 3 method are intentionally commented. We are not providing IAccessibleComponent at this time.
+//     /* IAccessibleComponent::get_locationInParent([out] pX, [out] pY) */
+//     int get_locationInParent(long pX, long pY) {
+//             if (DEBUG) print(this + ".IAccessibleComponent::get_locationInParent");
+//             // TO DO: support transparently (hard for lightweight parents - screen vs. parent coords)
+//             AccessibleControlEvent event = new AccessibleControlEvent(this);
+//             for (int i = 0; i < accessibleControlListenersSize(); i++) {
+//                     AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.get(i);
+//                     listener.getLocation (event);
+//             }
+//             COM.MoveMemory(pX, new int [] { event.x }, 4);
+//             COM.MoveMemory(pY, new int [] { event.y }, 4);
+//             return COM.S_OK;
+//     }
+//
+//     /* IAccessibleComponent::get_foreground([out] pForeground) */
+//     int get_foreground(long pForeground) {
+//             Color color = control.getForeground();
+//             if (DEBUG) print(this + ".IAccessibleComponent::get_foreground returning " + color.handle);
+//             COM.MoveMemory(pForeground, new int [] { color.handle }, 4);
+//             return COM.S_OK;
+//     }
+//
+//     /* IAccessibleComponent::get_background([out] pBackground) */
+//     int get_background(long pBackground) {
+//             Color color = control.getBackground();
+//             if (DEBUG) print(this + ".IAccessibleComponent::get_background returning " + color.handle);
+//             COM.MoveMemory(pBackground, new int [] { color.handle }, 4);
+//             return COM.S_OK;
+//     }
+
+       /* IAccessibleEditableText::copyText([in] startOffset, [in] endOffset) */
+       int copyText(int startOffset, int endOffset) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::copyText, start=" + startOffset + ", end=" + endOffset);
+               AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                       listener.copyText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleEditableText::deleteText([in] startOffset, [in] endOffset) */
+       int deleteText(int startOffset, int endOffset) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::deleteText, start=" + startOffset + ", end=" + endOffset);
+               AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               event.string = "";
+               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                       listener.replaceText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleEditableText::insertText([in] offset, [in] pbstrText) */
+       int insertText(int offset, long pbstrText) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::insertText, offset=" + offset + ", pbstrText=" + pbstrText);
+               AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+               event.start = offset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : offset;
+               event.end = event.start;
+               event.string = getString(pbstrText);
+               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                       listener.replaceText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleEditableText::cutText([in] startOffset, [in] endOffset) */
+       int cutText(int startOffset, int endOffset) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::cutText, start=" + startOffset + ", end=" + endOffset);
+               AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                       listener.cutText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleEditableText::pasteText([in] offset) */
+       int pasteText(int offset) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::pasteText, offset=" + offset);
+               AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+               event.start = offset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : offset;
+               event.end = event.start;
+               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                       listener.pasteText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleEditableText::replaceText([in] startOffset, [in] endOffset, [in] pbstrText) */
+       int replaceText(int startOffset, int endOffset, long pbstrText) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::replaceText, start=" + startOffset + ", end=" + endOffset + ", pbstrText=" + pbstrText);
+               AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(this);
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               event.string = getString(pbstrText);
+               for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                       AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                       listener.replaceText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleEditableText::setAttributes([in] startOffset, [in] endOffset, [in] pbstrAttributes) */
+       int setAttributes(int startOffset, int endOffset, long pbstrAttributes) {
+               if (DEBUG) print(this + ".IAccessibleEditableText::setAttributes, start=" + startOffset + ", end=" + endOffset + ", pbstrAttributes=" + pbstrAttributes);
+               AccessibleTextAttributeEvent event = new AccessibleTextAttributeEvent(this);
+               String string = getString(pbstrAttributes);
+               if (string != null && string.length() > 0) {
+                       event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+                       event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+                       TextStyle style = new TextStyle();
+                       FontData fontData = null;
+                       int points = 10; // used for default rise
+                       String [] attributes = new String [0];
+                       int begin = 0;
+                       int end = string.indexOf(';');
+                       while (end != -1 && end < string.length()) {
+                               String keyValue = string.substring(begin, end).trim();
+                               int colonIndex = keyValue.indexOf(':');
+                               if (colonIndex != -1 && colonIndex + 1 < keyValue.length()) {
+                                       String [] newAttributes = new String [attributes.length + 2];
+                                       System.arraycopy (attributes, 0, newAttributes, 0, attributes.length);
+                                       newAttributes[attributes.length] = keyValue.substring(0, colonIndex).trim();
+                                       newAttributes[attributes.length + 1] = keyValue.substring(colonIndex + 1).trim();
+                                       attributes = newAttributes;
+                               }
+                               begin = end + 1;
+                               end = string.indexOf(';', begin);
+                       }
+                       for (int i = 0; i+1 < attributes.length; i+=2) {
+                               String key = attributes[i];
+                               String value = attributes[i+1];
+                               if (key.equals("text-position")) {
+                                       if (value.equals("super")) style.rise = points / 2;
+                                       else if (value.equals("sub")) style.rise = - points / 2;
+                               } else if (key.equals("text-underline-type")) {
+                                       style.underline = true;
+                                       if (value.equals("double")) style.underlineStyle = SWT.UNDERLINE_DOUBLE;
+                                       else if (value.equals("single")) {
+                                               if (style.underlineStyle != SWT.UNDERLINE_SQUIGGLE && style.underlineStyle != SWT.UNDERLINE_ERROR) {
+                                                       style.underlineStyle = SWT.UNDERLINE_SINGLE;
+                                               }
+                                       }
+                               } else if (key.equals("text-underline-style") && value.equals("wave")) {
+                                       style.underline = true;
+                                       style.underlineStyle = SWT.UNDERLINE_SQUIGGLE;
+                               } else if (key.equals("invalid") && value.equals("true")) {
+                                       style.underline = true;
+                                       style.underlineStyle = SWT.UNDERLINE_ERROR;
+                               } else if (key.equals("text-line-through-type")) {
+                                       if (value.equals("single")) style.strikeout = true;
+                               } else if (key.equals("font-family")) {
+                                       if (fontData == null) fontData = new FontData ();
+                                       fontData.setName(value);
+                               } else if (key.equals("font-size")) {
+                                       try {
+                                               String pts = value.endsWith("pt") ? value.substring(0, value.length() - 2) : value;
+                                               points = Integer.parseInt(pts);
+                                               if (fontData == null) fontData = new FontData ();
+                                               fontData.setHeight(points);
+                                               if (style.rise > 0) style.rise = points / 2;
+                                               else if (style.rise < 0) style.rise = - points / 2;
+                                       } catch (NumberFormatException ex) {}
+                               } else if (key.equals("font-style")) {
+                                       if (value.equals("italic")) {
+                                               if (fontData == null) fontData = new FontData ();
+                                               fontData.setStyle(fontData.getStyle() | SWT.ITALIC);
+                                       }
+                               } else if (key.equals("font-weight")) {
+                                       if (value.equals("bold")) {
+                                               if (fontData == null) fontData = new FontData ();
+                                               fontData.setStyle(fontData.getStyle() | SWT.BOLD);
+                                       } else {
+                                               try {
+                                                       int weight = Integer.parseInt(value);
+                                                       if (fontData == null) fontData = new FontData ();
+                                                       if (weight > 400) fontData.setStyle(fontData.getStyle() | SWT.BOLD);
+                                               } catch (NumberFormatException ex) {}
+                                       }
+                               } else if (key.equals("color")) {
+                                       style.foreground = colorFromString(value);
+                               } else if (key.equals("background-color")) {
+                                       style.background = colorFromString(value);
+                               }
+                       }
+                       if (attributes.length > 0) {
+                               event.attributes = attributes;
+                               if (fontData != null) {
+                                       style.font = new Font(control.getDisplay(), fontData);
+                               }
+                               if (!style.equals(new TextStyle())) event.textStyle = style;
+                       }
+                       for (int i = 0; i < accessibleEditableTextListenersSize(); i++) {
+                               AccessibleEditableTextListener listener = accessibleEditableTextListeners.get(i);
+                               listener.setTextAttributes(event);
+                       }
+                       if (style.font != null) {
+                               style.font.dispose();
+                       }
+                       if (style.foreground != null) {
+                               style.foreground.dispose();
+                       }
+                       if (style.background != null) {
+                               style.background.dispose();
+                       }
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHyperlink::get_anchor([in] index, [out] pAnchor) */
+       int get_anchor(int index, long pAnchor) {
+               if (DEBUG) print(this + ".IAccessibleHyperlink::get_anchor");
+               AccessibleHyperlinkEvent event = new AccessibleHyperlinkEvent(this);
+               event.index = index;
+               for (int i = 0; i < accessibleHyperlinkListenersSize(); i++) {
+                       AccessibleHyperlinkListener listener = accessibleHyperlinkListeners.get(i);
+                       listener.getAnchor(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible != null) {
+                       accessible.AddRef();
+                       setPtrVARIANT(pAnchor, COM.VT_DISPATCH, accessible.getAddress());
+                       return COM.S_OK;
+               }
+               setStringVARIANT(pAnchor, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHyperlink::get_anchorTarget([in] index, [out] pAnchorTarget) */
+       int get_anchorTarget(int index, long pAnchorTarget) {
+               if (DEBUG) print(this + ".IAccessibleHyperlink::get_anchorTarget");
+               AccessibleHyperlinkEvent event = new AccessibleHyperlinkEvent(this);
+               event.index = index;
+               for (int i = 0; i < accessibleHyperlinkListenersSize(); i++) {
+                       AccessibleHyperlinkListener listener = accessibleHyperlinkListeners.get(i);
+                       listener.getAnchorTarget(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible != null) {
+                       accessible.AddRef();
+                       setPtrVARIANT(pAnchorTarget, COM.VT_DISPATCH, accessible.getAddress());
+                       return COM.S_OK;
+               }
+               setStringVARIANT(pAnchorTarget, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHyperlink::get_startIndex([out] pIndex) */
+       int get_startIndex(long pIndex) {
+               if (DEBUG) print(this + ".IAccessibleHyperlink::get_startIndex");
+               AccessibleHyperlinkEvent event = new AccessibleHyperlinkEvent(this);
+               for (int i = 0; i < accessibleHyperlinkListenersSize(); i++) {
+                       AccessibleHyperlinkListener listener = accessibleHyperlinkListeners.get(i);
+                       listener.getStartIndex(event);
+               }
+               OS.MoveMemory(pIndex, new int [] { event.index }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHyperlink::get_endIndex([out] pIndex) */
+       int get_endIndex(long pIndex) {
+               if (DEBUG) print(this + ".IAccessibleHyperlink::get_endIndex");
+               AccessibleHyperlinkEvent event = new AccessibleHyperlinkEvent(this);
+               for (int i = 0; i < accessibleHyperlinkListenersSize(); i++) {
+                       AccessibleHyperlinkListener listener = accessibleHyperlinkListeners.get(i);
+                       listener.getEndIndex(event);
+               }
+               OS.MoveMemory(pIndex, new int [] { event.index }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHyperlink::get_valid([out] pValid) */
+       int get_valid(long pValid) {
+               /* Deprecated. */
+               return COM.E_NOTIMPL;
+       }
+
+       /* IAccessibleHypertext::get_nHyperlinks([out] pHyperlinkCount) */
+       int get_nHyperlinks(long pHyperlinkCount) {
+               if (DEBUG) print(this + ".IAccessibleHypertext::get_nHyperlinks");
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getHyperlinkCount(event);
+               }
+               OS.MoveMemory(pHyperlinkCount, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHypertext::get_hyperlink([in] index, [out] ppHyperlink) */
+       int get_hyperlink(int index, long ppHyperlink) {
+               if (DEBUG) print(this + ".IAccessibleHypertext::get_hyperlink");
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.index = index;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getHyperlink(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible == null) {
+                       setIntVARIANT(ppHyperlink, COM.VT_EMPTY, 0);
+                       return COM.E_INVALIDARG;
+               }
+               accessible.AddRef();
+               OS.MoveMemory(ppHyperlink, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleHypertext::get_hyperlinkIndex([in] charIndex, [out] pHyperlinkIndex) */
+       int get_hyperlinkIndex(int charIndex, long pHyperlinkIndex) {
+               if (DEBUG) print(this + ".IAccessibleHypertext::get_hyperlinkIndex");
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.offset = charIndex;
+               event.index = -1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getHyperlinkIndex(event);
+               }
+               OS.MoveMemory(pHyperlinkIndex, new int [] { event.index }, 4);
+               if (event.index == -1) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       // The following 3 method are intentionally commented. We are not providing IAccessibleImage at this time.
+//     /* IAccessibleImage::get_description([out] pbstrDescription) */
+//     int get_description(long pbstrDescription) {
+//             if (DEBUG) print(this + ".IAccessibleImage::get_description");
+//             // TO DO: Does it make sense to just reuse description?
+//             AccessibleEvent event = new AccessibleEvent(this);
+//             event.childID = ACC.CHILDID_SELF;
+//             for (int i = 0; i < accessibleListenersSize(); i++) {
+//                     AccessibleListener listener = (AccessibleListener) accessibleListeners.get(i);
+//                     listener.getDescription(event);
+//             }
+//             setString(pbstrDescription, event.result);
+//             if (event.result == null) return COM.S_FALSE;
+//             return COM.S_OK;
+//     }
+//
+//     /* IAccessibleImage::get_imagePosition([in] coordinateType, [out] pX, [out] pY) */
+//     int get_imagePosition(int coordinateType, long pX, long pY) {
+//             if (DEBUG) print(this + ".IAccessibleImage::get_imagePosition");
+//             // TO DO: does it make sense to just reuse getLocation?
+//             AccessibleControlEvent event = new AccessibleControlEvent(this);
+//             event.childID = ACC.CHILDID_SELF;
+//             for (int i = 0; i < accessibleControlListenersSize(); i++) {
+//                     AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.get(i);
+//                     listener.getLocation(event);
+//             }
+//             COM.MoveMemory(pX, new int [] { event.x }, 4);
+//             COM.MoveMemory(pY, new int [] { event.y }, 4);
+//             return COM.S_OK;
+//     }
+//
+//     /* IAccessibleImage::get_imageSize([out] pHeight, [out] pWidth) */
+//     int get_imageSize(long pHeight, long pWidth) {
+//             if (DEBUG) print(this + ".IAccessibleImage::get_imageSize");
+//             // TO DO: does it make sense to just reuse getLocation?
+//             AccessibleControlEvent event = new AccessibleControlEvent(this);
+//             for (int i = 0; i < accessibleControlListenersSize(); i++) {
+//                     AccessibleControlListener listener = (AccessibleControlListener) accessibleControlListeners.get(i);
+//                     listener.getLocation(event);
+//             }
+//             COM.MoveMemory(pHeight, new int [] { event.height }, 4);
+//             COM.MoveMemory(pWidth, new int [] { event.width }, 4);
+//             return COM.S_OK;
+//     }
+
+       /* IAccessibleTable2::get_cellAt([in] row, [in] column, [out] ppCell) */
+       int get_cellAt(int row, int column, long ppCell) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.row = row;
+               event.column = column;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getCell(event);
+               }
+               Accessible accessible = event.accessible;
+               if (DEBUG) print(this + ".IAccessibleTable2::get_cellAt(row=" + row + ", column=" + column + ") returning " + accessible);
+               if (accessible == null) return COM.E_INVALIDARG;
+               accessible.AddRef();
+               OS.MoveMemory(ppCell, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_caption([out] ppAccessible) */
+       int get_caption(long ppAccessible) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getCaption(event);
+               }
+               Accessible accessible = event.accessible;
+               if (DEBUG) print(this + ".IAccessibleTable2::get_caption() returning " + accessible);
+               if (accessible == null) {
+                       OS.MoveMemory(ppAccessible, new long[] { 0 }, C.PTR_SIZEOF);
+                       return COM.S_FALSE;
+               }
+               accessible.AddRef();
+               OS.MoveMemory(ppAccessible, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_columnDescription([in] column, [out] pbstrDescription) */
+       int get_columnDescription(int column, long pbstrDescription) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.column = column;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getColumnDescription(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_columnDescription(column=" + column + ") returning " + event.result);
+               setString(pbstrDescription, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_nColumns([out] pColumnCount) */
+       int get_nColumns(long pColumnCount) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getColumnCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_nColumns() returning " + event.count);
+               OS.MoveMemory(pColumnCount, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_nRows([out] pRowCount) */
+       int get_nRows(long pRowCount) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getRowCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_nRows() returning " + event.count);
+               OS.MoveMemory(pRowCount, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_nSelectedCells([out] pCellCount) */
+       int get_nSelectedCells(long pCellCount) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSelectedCellCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_nSelectedCells() returning " + event.count);
+               OS.MoveMemory(pCellCount, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_nSelectedColumns([out] pColumnCount) */
+       int get_nSelectedColumns(long pColumnCount) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSelectedColumnCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_nSelectedColumns() returning " + event.count);
+               OS.MoveMemory(pColumnCount, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_nSelectedRows([out] pRowCount) */
+       int get_nSelectedRows(long pRowCount) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSelectedRowCount(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_nSelectedRows() returning " + event.count);
+               OS.MoveMemory(pRowCount, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_rowDescription([in] row, [out] pbstrDescription) */
+       int get_rowDescription(int row, long pbstrDescription) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.row = row;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getRowDescription(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_rowDescription(row=" + row + ") returning " + event.result);
+               setString(pbstrDescription, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_selectedCells([out] ppCells, [out] pNSelectedCells) */
+       int get_selectedCells(long ppCells, long pNSelectedCells) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSelectedCells(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_selectedCells() returning " + (event.accessibles == null ? "null" : "accessibles[" + event.accessibles.length + "]"));
+               if (event.accessibles == null || event.accessibles.length == 0) {
+                       OS.MoveMemory(ppCells, new long[] { 0 }, C.PTR_SIZEOF);
+                       OS.MoveMemory(pNSelectedCells, new int [] { 0 }, 4);
+                       return COM.S_FALSE;
+               }
+               int length = event.accessibles.length;
+               long pv = OS.CoTaskMemAlloc(length * C.PTR_SIZEOF);
+               int count = 0;
+               for (int i = 0; i < length; i++) {
+                       Accessible accessible = event.accessibles[i];
+                       if (accessible != null) {
+                               accessible.AddRef();
+                               OS.MoveMemory(pv + i * C.PTR_SIZEOF, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+                               count++;
+                       }
+               }
+               OS.MoveMemory(ppCells, new long [] { pv }, C.PTR_SIZEOF);
+               OS.MoveMemory(pNSelectedCells, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_selectedColumns([out] ppSelectedColumns, [out] pNColumns) */
+       int get_selectedColumns(long ppSelectedColumns, long pNColumns) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSelectedColumns(event);
+               }
+               int count = event.selected == null ? 0 : event.selected.length;
+               if (DEBUG) print(this + ".IAccessibleTable2::get_selectedColumns() returning " + (count == 0 ? "null" : "selected[" + count + "]"));
+               if (count == 0) {
+                       OS.MoveMemory(ppSelectedColumns, new long[] { 0 }, C.PTR_SIZEOF);
+                       OS.MoveMemory(pNColumns, new int [] { 0 }, 4);
+                       return COM.S_FALSE;
+               }
+               long pv = OS.CoTaskMemAlloc(count * 4);
+               OS.MoveMemory(pv, event.selected, count * 4);
+               OS.MoveMemory(ppSelectedColumns, new long [] { pv }, C.PTR_SIZEOF);
+               OS.MoveMemory(pNColumns, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_selectedRows([out] ppSelectedRows, [out] pNRows) */
+       int get_selectedRows(long ppSelectedRows, long pNRows) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSelectedRows(event);
+               }
+               int count = event.selected == null ? 0 : event.selected.length;
+               if (DEBUG) print(this + ".IAccessibleTable2::get_selectedRows() returning " + (count == 0 ? "null" : "selected[" + count + "]"));
+               if (count == 0) {
+                       OS.MoveMemory(ppSelectedRows, new long[] { 0 }, C.PTR_SIZEOF);
+                       OS.MoveMemory(pNRows, new int [] { 0 }, 4);
+                       return COM.S_FALSE;
+               }
+               long pv = OS.CoTaskMemAlloc(count * 4);
+               OS.MoveMemory(pv, event.selected, count * 4);
+               OS.MoveMemory(ppSelectedRows, new long [] { pv }, C.PTR_SIZEOF);
+               OS.MoveMemory(pNRows, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_summary([out] ppAccessible) */
+       int get_summary(long ppAccessible) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.getSummary(event);
+               }
+               Accessible accessible = event.accessible;
+               if (DEBUG) print(this + ".IAccessibleTable2::get_summary() returning " + accessible);
+               if (accessible == null) {
+                       OS.MoveMemory(ppAccessible, new long[] { 0 }, C.PTR_SIZEOF);
+                       return COM.S_FALSE;
+               }
+               accessible.AddRef();
+               OS.MoveMemory(ppAccessible, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_isColumnSelected([in] column, [out] pIsSelected) */
+       int get_isColumnSelected(int column, long pIsSelected) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.column = column;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.isColumnSelected(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_isColumnSelected() returning " + event.isSelected);
+               OS.MoveMemory(pIsSelected, new int [] {event.isSelected ? 1 : 0}, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_isRowSelected([in] row, [out] pIsSelected) */
+       int get_isRowSelected(int row, long pIsSelected) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.row = row;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.isRowSelected(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::get_isRowSelected() returning " + event.isSelected);
+               OS.MoveMemory(pIsSelected, new int [] {event.isSelected ? 1 : 0}, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::selectRow([in] row) */
+       int selectRow(int row) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.row = row;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.setSelectedRow(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::selectRow() returning " + (event.result == null ? "E_INVALIDARG" : event.result));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::selectColumn([in] column) */
+       int selectColumn(int column) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.column = column;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.setSelectedColumn(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::selectColumn() returning " + (event.result == null ? "E_INVALIDARG" : event.result));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::unselectRow([in] row) */
+       int unselectRow(int row) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.row = row;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.deselectRow(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::unselectRow() returning " + (event.result == null ? "E_INVALIDARG" : event.result));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::unselectColumn([in] column) */
+       int unselectColumn(int column) {
+               AccessibleTableEvent event = new AccessibleTableEvent(this);
+               event.column = column;
+               for (int i = 0; i < accessibleTableListenersSize(); i++) {
+                       AccessibleTableListener listener = accessibleTableListeners.get(i);
+                       listener.deselectColumn(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTable2::unselectColumn() returning " + (event.result == null ? "E_INVALIDARG" : event.result));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTable2::get_modelChange([out] pModelChange) */
+       int get_modelChange(long pModelChange) {
+               if (DEBUG) print(this + ".IAccessibleTable2::get_modelChange() returning " + (tableChange == null ? "null" : "tableChange=" + tableChange[0] + ", " + tableChange[1] + ", " + tableChange[2] + ", " + tableChange[3]));
+               if (tableChange == null) {
+                       OS.MoveMemory(pModelChange, new long [] { 0 }, C.PTR_SIZEOF);
+                       return COM.S_FALSE;
+               }
+               OS.MoveMemory(pModelChange, tableChange, tableChange.length * 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_columnExtent([out] pNColumnsSpanned) */
+       int get_columnExtent(long pNColumnsSpanned) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getColumnSpan(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_columnExtent() returning " + event.count);
+               OS.MoveMemory(pNColumnsSpanned, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_columnHeaderCells([out] ppCellAccessibles, [out] pNColumnHeaderCells) */
+       int get_columnHeaderCells(long ppCellAccessibles, long pNColumnHeaderCells) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getColumnHeaders(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_columnHeaderCells() returning " + (event.accessibles == null ? "null" : "accessibles[" + event.accessibles.length + "]"));
+               if (event.accessibles == null || event.accessibles.length == 0) {
+                       OS.MoveMemory(ppCellAccessibles, new long[] { 0 }, C.PTR_SIZEOF);
+                       OS.MoveMemory(pNColumnHeaderCells, new int [] { 0 }, 4);
+                       return COM.S_FALSE;
+               }
+               int length = event.accessibles.length;
+               long pv = OS.CoTaskMemAlloc(length * C.PTR_SIZEOF);
+               int count = 0;
+               for (int i = 0; i < length; i++) {
+                       Accessible accessible = event.accessibles[i];
+                       if (accessible != null) {
+                               accessible.AddRef();
+                               OS.MoveMemory(pv + i * C.PTR_SIZEOF, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+                               count++;
+                       }
+               }
+               OS.MoveMemory(ppCellAccessibles, new long [] { pv }, C.PTR_SIZEOF);
+               OS.MoveMemory(pNColumnHeaderCells, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_columnIndex([out] pColumnIndex) */
+       int get_columnIndex(long pColumnIndex) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getColumnIndex(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_columnIndex() returning " + event.index);
+               OS.MoveMemory(pColumnIndex, new int [] { event.index }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_rowExtent([out] pNRowsSpanned) */
+       int get_rowExtent(long pNRowsSpanned) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getRowSpan(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_rowExtent() returning " + event.count);
+               OS.MoveMemory(pNRowsSpanned, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_rowHeaderCells([out] ppCellAccessibles, [out] pNRowHeaderCells) */
+       int get_rowHeaderCells(long ppCellAccessibles, long pNRowHeaderCells) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getRowHeaders(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_rowHeaderCells() returning " + (event.accessibles == null ? "null" : "accessibles[" + event.accessibles.length + "]"));
+               if (event.accessibles == null || event.accessibles.length == 0) {
+                       OS.MoveMemory(ppCellAccessibles, new long[] { 0 }, C.PTR_SIZEOF);
+                       OS.MoveMemory(pNRowHeaderCells, new int [] { 0 }, 4);
+                       return COM.S_FALSE;
+               }
+               int length = event.accessibles.length;
+               long pv = OS.CoTaskMemAlloc(length * C.PTR_SIZEOF);
+               int count = 0;
+               for (int i = 0; i < length; i++) {
+                       Accessible accessible = event.accessibles[i];
+                       if (accessible != null) {
+                               accessible.AddRef();
+                               OS.MoveMemory(pv + i * C.PTR_SIZEOF, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+                               count++;
+                       }
+               }
+               OS.MoveMemory(ppCellAccessibles, new long [] { pv }, C.PTR_SIZEOF);
+               OS.MoveMemory(pNRowHeaderCells, new int [] { count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_rowIndex([out] pRowIndex) */
+       int get_rowIndex(long pRowIndex) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getRowIndex(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_rowIndex() returning " + event.index);
+               OS.MoveMemory(pRowIndex, new int [] { event.index }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_isSelected([out] pIsSelected) */
+       int get_isSelected(long pIsSelected) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.isSelected(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_isSelected() returning " + event.isSelected);
+               OS.MoveMemory(pIsSelected, new int [] {event.isSelected ? 1 : 0}, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_rowColumnExtents([out] pRow, [out] pColumn, [out] pRowExtents, [out] pColumnExtents, [out] pIsSelected) */
+       int get_rowColumnExtents(long pRow, long pColumn, long pRowExtents, long pColumnExtents, long pIsSelected) {
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_rowColumnExtents");
+               // TODO: should we implement this? It is just a convenience function.
+               return COM.DISP_E_MEMBERNOTFOUND;
+//             AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+//             for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+//                     AccessibleTableCellListener listener = (AccessibleTableCellListener) accessibleTableCellListeners.get(i);
+//                     listener.getRowColumnExtents(event);
+//             }
+//             COM.MoveMemory(pRow, new int [] { event.row }, 4);
+//             COM.MoveMemory(pColumn, new int [] { event.column }, 4);
+//             COM.MoveMemory(pRowExtents, new int [] { event.rowExtents }, 4);
+//             COM.MoveMemory(pColumnExtents, new int [] { event.columnExtents }, 4);
+//             return COM.S_OK;
+       }
+
+       /* IAccessibleTableCell::get_table([out] ppTable) */
+       int get_table(long ppTable) {
+               AccessibleTableCellEvent event = new AccessibleTableCellEvent(this);
+               for (int i = 0; i < accessibleTableCellListenersSize(); i++) {
+                       AccessibleTableCellListener listener = accessibleTableCellListeners.get(i);
+                       listener.getTable(event);
+               }
+               Accessible accessible = event.accessible;
+               if (DEBUG) print(this + ".IAccessibleTableCell::get_table() returning " + accessible);
+               if (accessible == null) {
+                       // TODO: This is not supposed to return S_FALSE. We need to lookup the table role parent and return that.
+                       OS.MoveMemory(ppTable, new long[] { 0 }, C.PTR_SIZEOF);
+                       return COM.S_FALSE;
+               }
+               accessible.AddRef();
+               OS.MoveMemory(ppTable, new long[] { accessible.getAddress() }, C.PTR_SIZEOF);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::addSelection([in] startOffset, [in] endOffset) */
+       int addSelection(int startOffset, int endOffset) {
+               if (DEBUG) print(this + ".IAccessibleText::addSelection(" + startOffset + ", " + endOffset + ")");
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.addSelection(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_attributes([in] offset, [out] pStartOffset, [out] pEndOffset, [out] pbstrTextAttributes) */
+       int get_attributes(int offset, long pStartOffset, long pEndOffset, long pbstrTextAttributes) {
+               AccessibleTextAttributeEvent event = new AccessibleTextAttributeEvent(this);
+               event.offset = offset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : offset;
+               for (int i = 0; i < accessibleAttributeListenersSize(); i++) {
+                       AccessibleAttributeListener listener = accessibleAttributeListeners.get(i);
+                       listener.getTextAttributes(event);
+               }
+               String textAttributes = "";
+               TextStyle style = event.textStyle;
+               if (style != null) {
+                       if (style.rise != 0) {
+                               textAttributes += "text-position:";
+                               if (style.rise > 0) textAttributes += "super";
+                               else textAttributes += "sub";
+                       }
+                       if (style.underline) {
+                               textAttributes += "text-underline-type:";
+                               switch (style.underlineStyle) {
+                                       case SWT.UNDERLINE_SINGLE: textAttributes += "single;"; break;
+                                       case SWT.UNDERLINE_DOUBLE: textAttributes += "double;"; break;
+                                       case SWT.UNDERLINE_SQUIGGLE: textAttributes += "single;text-underline-style:wave;"; break;
+                                       case SWT.UNDERLINE_ERROR: textAttributes += "single;text-underline-style:wave;invalid:true;"; break;
+                                       default: textAttributes += "none;"; break;
+                               }
+                               // style.underlineColor is not currently part of the IA2 spec. If provided, it would be "text-underline-color:rgb(n,n,n);"
+                       }
+                       if (style.strikeout) {
+                               textAttributes += "text-line-through-type:single;";
+                               // style.strikeoutColor is not currently part of the IA2 spec. If provided, it would be "text-line-through-color:rgb(n,n,n);"
+                       }
+                       Font font = style.font;
+                       if (font != null && !font.isDisposed()) {
+                               FontData fontData = font.getFontData()[0];
+                               textAttributes += "font-family:" + fontData.getName() + ";";
+                               textAttributes += "font-size:" + fontData.getHeight() + "pt;";
+                               textAttributes += "font-style:" + (fontData.data.lfItalic != 0 ? "italic" : "normal") + ";";
+                               textAttributes += "font-weight:" + fontData.data.lfWeight + ";";
+                       }
+                       Color color = style.foreground;
+                       if (color != null && !color.isDisposed()) {
+                               textAttributes += "color:rgb(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ");";
+                       }
+                       color = style.background;
+                       if (color != null && !color.isDisposed()) {
+                               textAttributes += "background-color:rgb(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ");";
+                       }
+               }
+               if (event.attributes != null) {
+                       for (int i = 0; i + 1 < event.attributes.length; i += 2) {
+                               textAttributes += event.attributes[i] + ":" + event.attributes[i+1] + ";";
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_attributes(" + offset + ") returning start = " + event.start + ", end = " + event.end + ", attributes = " + textAttributes);
+               OS.MoveMemory(pStartOffset, new int [] { event.start }, 4);
+               OS.MoveMemory(pEndOffset, new int [] { event.end }, 4);
+               setString(pbstrTextAttributes, textAttributes);
+               if (textAttributes.length() == 0) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_caretOffset([out] pOffset) */
+       int get_caretOffset(long pOffset) {
+               int offset = getCaretOffset();
+               if (DEBUG) print(this + ".IAccessibleText::get_caretOffset returning " + offset + hresult(offset == -1 ? COM.S_FALSE : COM.S_OK));
+               OS.MoveMemory(pOffset, new int [] { offset }, 4);
+               if (offset == -1) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_characterExtents([in] offset, [in] coordType, [out] pX, [out] pY, [out] pWidth, [out] pHeight) */
+       int get_characterExtents(int offset, int coordType, long pX, long pY, long pWidth, long pHeight) {
+               int length = getCharacterCount();
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.start = offset == COM.IA2_TEXT_OFFSET_LENGTH ? length : offset < 0 ? 0 : offset;
+               event.end = offset == COM.IA2_TEXT_OFFSET_LENGTH || offset >= length ? length : offset + 1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getTextBounds(event);
+               }
+               /* Note: event.rectangles is not used here, because IAccessibleText::get_characterExtents is just for one character. */
+               if (DEBUG) print(this + ".IAccessibleText::get_characterExtents(" + offset + ") returning " + event.x + ", " + event.y + ", " + event.width + ", " + event.height);
+               OS.MoveMemory(pX, new int [] { event.x }, 4);
+               OS.MoveMemory(pY, new int [] { event.y }, 4);
+               OS.MoveMemory(pWidth, new int [] { event.width }, 4);
+               OS.MoveMemory(pHeight, new int [] { event.height }, 4);
+               if (event.width == 0 && event.height == 0) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_nSelections([out] pNSelections) */
+       int get_nSelections(long pNSelections) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.count = -1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getSelectionCount(event);
+               }
+               if (event.count == -1) {
+                       event.childID = ACC.CHILDID_SELF;
+                       event.offset = -1;
+                       event.length = 0;
+                       for (int i = 0; i < accessibleTextListenersSize(); i++) {
+                               AccessibleTextListener listener = accessibleTextListeners.get(i);
+                               listener.getSelectionRange (event);
+                       }
+                       event.count = event.offset != -1 && event.length > 0 ? 1 : 0;
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_nSelections returning " + event.count);
+               OS.MoveMemory(pNSelections, new int [] { event.count }, 4);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_offsetAtPoint([in] x, [in] y, [in] coordType, [out] pOffset) */
+       int get_offsetAtPoint(int x, int y, int coordType, long pOffset) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.x = x;
+               event.y = y;
+               event.offset = -1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getOffsetAtPoint(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_offsetAtPoint(" + x + ", " + y + ") returning " + event.offset + hresult(event.offset == -1 ? COM.S_FALSE : COM.S_OK));
+               /*
+                * Note that the current IA2 spec says to return 0 when there's nothing to return,
+                * but since 0 is a valid return value, the spec is going to be updated to return -1.
+                */
+               OS.MoveMemory(pOffset, new int [] { event.offset }, 4);
+               if (event.offset == -1) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_selection([in] selectionIndex, [out] pStartOffset, [out] pEndOffset) */
+       int get_selection(int selectionIndex, long pStartOffset, long pEndOffset) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.index = selectionIndex;
+               event.start = -1;
+               event.end = -1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getSelection(event);
+               }
+               if (event.start == -1 && selectionIndex == 0) {
+                       event.childID = ACC.CHILDID_SELF;
+                       event.offset = -1;
+                       event.length = 0;
+                       for (int i = 0; i < accessibleTextListenersSize(); i++) {
+                               AccessibleTextListener listener = accessibleTextListeners.get(i);
+                               listener.getSelectionRange (event);
+                       }
+                       event.start = event.offset;
+                       event.end = event.offset + event.length;
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_selection(" + selectionIndex + ") returning " + event.start + ", " + event.end);
+               OS.MoveMemory(pStartOffset, new int [] { event.start }, 4);
+               OS.MoveMemory(pEndOffset, new int [] { event.end }, 4);
+               /*
+                * Note that the current IA2 spec says to return 0,0 when there's nothing to return,
+                * but since 0 is a valid return value, the spec is going to be updated to return -1,-1.
+                */
+               if (event.start == -1) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_text([in] startOffset, [in] endOffset, [out] pbstrText) */
+       int get_text(int startOffset, int endOffset, long pbstrText) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               if (event.start > event.end) {
+                       /* IA2 spec says that indices can be exchanged. */
+                       int temp = event.start;
+                       event.start = event.end;
+                       event.end = temp;
+               }
+               event.count = 0;
+               event.type = ACC.TEXT_BOUNDARY_ALL;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getText(event);
+               }
+               if (event.result == null) {
+                       AccessibleControlEvent e = new AccessibleControlEvent(this);
+                       e.childID = ACC.CHILDID_SELF;
+                       for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                               AccessibleControlListener listener = accessibleControlListeners.get(i);
+                               listener.getRole(e);
+                               listener.getValue(e);
+                       }
+                       // TODO: Consider passing the value through for other roles as well (i.e. combo, etc). Keep in sync with get_nCharacters.
+                       if (e.detail == ACC.ROLE_TEXT) {
+                               event.result = e.result;
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_text(" + startOffset + ", " + endOffset + ") returning " + event.result + hresult(event.result == null ? COM.E_INVALIDARG : COM.S_OK));
+               setString(pbstrText, event.result);
+               if (event.result == null) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_textBeforeOffset([in] offset, [in] boundaryType, [out] pStartOffset, [out] pEndOffset, [out] pbstrText) */
+       int get_textBeforeOffset(int offset, int boundaryType, long pStartOffset, long pEndOffset, long pbstrText) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               int charCount = getCharacterCount();
+               event.start = offset == COM.IA2_TEXT_OFFSET_LENGTH ? charCount : offset == COM.IA2_TEXT_OFFSET_CARET ? getCaretOffset() : offset;
+               event.end = event.start;
+               event.count = -1;
+               switch (boundaryType) {
+                       case COM.IA2_TEXT_BOUNDARY_CHAR: event.type = ACC.TEXT_BOUNDARY_CHAR; break;
+                       case COM.IA2_TEXT_BOUNDARY_WORD: event.type = ACC.TEXT_BOUNDARY_WORD; break;
+                       case COM.IA2_TEXT_BOUNDARY_SENTENCE: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
+                       case COM.IA2_TEXT_BOUNDARY_PARAGRAPH: event.type = ACC.TEXT_BOUNDARY_PARAGRAPH; break;
+                       case COM.IA2_TEXT_BOUNDARY_LINE: event.type = ACC.TEXT_BOUNDARY_LINE; break;
+                       default: return COM.E_INVALIDARG;
+               }
+               int eventStart = event.start;
+               int eventEnd = event.end;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getText(event);
+               }
+               if (event.end < charCount) {
+                       switch (boundaryType) {
+                               case COM.IA2_TEXT_BOUNDARY_WORD:
+                               case COM.IA2_TEXT_BOUNDARY_SENTENCE:
+                               case COM.IA2_TEXT_BOUNDARY_PARAGRAPH:
+                               case COM.IA2_TEXT_BOUNDARY_LINE:
+                                       int start = event.start;
+                                       event.start = eventStart;
+                                       event.end = eventEnd;
+                                       event.count = 0;
+                                       for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                                               AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                                               listener.getText(event);
+                                       }
+                                       event.end = event.start;
+                                       event.start = start;
+                                       event.type = ACC.TEXT_BOUNDARY_ALL;
+                                       event.count = 0;
+                                       for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                                               AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                                               listener.getText(event);
+                                       }
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_textBeforeOffset(" + offset + ") returning start=" + event.start + ", end=" + event.end + " " + event.result + hresult(event.result == null ? COM.S_FALSE : COM.S_OK));
+               OS.MoveMemory(pStartOffset, new int [] { event.start }, 4);
+               OS.MoveMemory(pEndOffset, new int [] { event.end }, 4);
+               setString(pbstrText, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_textAfterOffset([in] offset, [in] boundaryType, [out] pStartOffset, [out] pEndOffset, [out] pbstrText) */
+       int get_textAfterOffset(int offset, int boundaryType, long pStartOffset, long pEndOffset, long pbstrText) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               int charCount = getCharacterCount();
+               event.start = offset == COM.IA2_TEXT_OFFSET_LENGTH ? charCount : offset == COM.IA2_TEXT_OFFSET_CARET ? getCaretOffset() : offset;
+               event.end = event.start;
+               event.count = 1;
+               switch (boundaryType) {
+                       case COM.IA2_TEXT_BOUNDARY_CHAR: event.type = ACC.TEXT_BOUNDARY_CHAR; break;
+                       case COM.IA2_TEXT_BOUNDARY_WORD: event.type = ACC.TEXT_BOUNDARY_WORD; break;
+                       case COM.IA2_TEXT_BOUNDARY_SENTENCE: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
+                       case COM.IA2_TEXT_BOUNDARY_PARAGRAPH: event.type = ACC.TEXT_BOUNDARY_PARAGRAPH; break;
+                       case COM.IA2_TEXT_BOUNDARY_LINE: event.type = ACC.TEXT_BOUNDARY_LINE; break;
+                       default: return COM.E_INVALIDARG;
+               }
+               int eventStart = event.start;
+               int eventEnd = event.end;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getText(event);
+               }
+               if (event.end < charCount) {
+                       switch (boundaryType) {
+                               case COM.IA2_TEXT_BOUNDARY_WORD:
+                               case COM.IA2_TEXT_BOUNDARY_SENTENCE:
+                               case COM.IA2_TEXT_BOUNDARY_PARAGRAPH:
+                               case COM.IA2_TEXT_BOUNDARY_LINE:
+                                       int start = event.start;
+                                       event.start = eventStart;
+                                       event.end = eventEnd;
+                                       event.count = 2;
+                                       for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                                               AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                                               listener.getText(event);
+                                       }
+                                       event.end = event.start;
+                                       event.start = start;
+                                       event.type = ACC.TEXT_BOUNDARY_ALL;
+                                       event.count = 0;
+                                       for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                                               AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                                               listener.getText(event);
+                                       }
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_textAfterOffset(" + offset + ") returning start=" + event.start + ", end=" + event.end + " " + event.result + hresult(event.result == null ? COM.S_FALSE : COM.S_OK));
+               OS.MoveMemory(pStartOffset, new int [] { event.start }, 4);
+               OS.MoveMemory(pEndOffset, new int [] { event.end }, 4);
+               setString(pbstrText, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_textAtOffset([in] offset, [in] boundaryType, [out] pStartOffset, [out] pEndOffset, [out] pbstrText) */
+       int get_textAtOffset(int offset, int boundaryType, long pStartOffset, long pEndOffset, long pbstrText) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               int charCount = getCharacterCount();
+               event.start = offset == COM.IA2_TEXT_OFFSET_LENGTH ? charCount : offset == COM.IA2_TEXT_OFFSET_CARET ? getCaretOffset() : offset;
+               event.end = event.start;
+               event.count = 0;
+               switch (boundaryType) {
+                       case COM.IA2_TEXT_BOUNDARY_CHAR: event.type = ACC.TEXT_BOUNDARY_CHAR; break;
+                       case COM.IA2_TEXT_BOUNDARY_WORD: event.type = ACC.TEXT_BOUNDARY_WORD; break;
+                       case COM.IA2_TEXT_BOUNDARY_SENTENCE: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
+                       case COM.IA2_TEXT_BOUNDARY_PARAGRAPH: event.type = ACC.TEXT_BOUNDARY_PARAGRAPH; break;
+                       case COM.IA2_TEXT_BOUNDARY_LINE: event.type = ACC.TEXT_BOUNDARY_LINE; break;
+                       case COM.IA2_TEXT_BOUNDARY_ALL: {
+                               event.type = ACC.TEXT_BOUNDARY_ALL;
+                               event.start = 0;
+                               event.end = charCount;
+                               event.count = 0;
+                               break;
+                       }
+                       default: return COM.E_INVALIDARG;
+               }
+               int eventStart = event.start;
+               int eventEnd = event.end;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getText(event);
+               }
+               if (event.end < charCount) {
+                       switch (boundaryType) {
+                               case COM.IA2_TEXT_BOUNDARY_WORD:
+                               case COM.IA2_TEXT_BOUNDARY_SENTENCE:
+                               case COM.IA2_TEXT_BOUNDARY_PARAGRAPH:
+                               case COM.IA2_TEXT_BOUNDARY_LINE:
+                                       int start = event.start;
+                                       event.start = eventStart;
+                                       event.end = eventEnd;
+                                       event.count = 1;
+                                       for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                                               AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                                               listener.getText(event);
+                                       }
+                                       event.end = event.start;
+                                       event.start = start;
+                                       event.type = ACC.TEXT_BOUNDARY_ALL;
+                                       event.count = 0;
+                                       for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                                               AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                                               listener.getText(event);
+                                       }
+                       }
+               }
+               if (DEBUG) print(this + ".IAccessibleText::get_textAtOffset(" + offset + ") returning start=" + event.start + ", end=" + event.end + " " + event.result + hresult(event.result == null ? COM.S_FALSE : COM.S_OK));
+               OS.MoveMemory(pStartOffset, new int [] { event.start }, 4);
+               OS.MoveMemory(pEndOffset, new int [] { event.end }, 4);
+               setString(pbstrText, event.result);
+               if (event.result == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::removeSelection([in] selectionIndex) */
+       int removeSelection(int selectionIndex) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.index = selectionIndex;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.removeSelection(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleText::removeSelection(" + selectionIndex + ") returning" + hresult(event.result == null || !event.result.equals(ACC.OK) ? COM.E_INVALIDARG : COM.S_OK));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::setCaretOffset([in] offset) */
+       int setCaretOffset(int offset) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.offset = offset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : offset;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.setCaretOffset(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleText::setCaretOffset(" + offset + ") returning" + hresult(event.result == null || !event.result.equals(ACC.OK) ? COM.E_INVALIDARG : COM.S_OK));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG; // TODO: @retval E_FAIL if the caret cannot be set ?
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::setSelection([in] selectionIndex, [in] startOffset, [in] endOffset) */
+       int setSelection(int selectionIndex, int startOffset, int endOffset) {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.index = selectionIndex;
+               event.start = startOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : startOffset;
+               event.end = endOffset == COM.IA2_TEXT_OFFSET_LENGTH ? getCharacterCount() : endOffset;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.setSelection(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleText::setSelection(index=" + selectionIndex + ", start=" + event.start + ", end=" + event.end + ") returning " + (event.result.equals(ACC.OK) ? "OK" : "INVALIDARG"));
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_nCharacters([out] pNCharacters) */
+       int get_nCharacters(long pNCharacters) {
+               int count = getCharacterCount();
+               OS.MoveMemory(pNCharacters, new int [] { count }, 4);
+               if (DEBUG) print(this + ".IAccessibleText::get_nCharacters returning " + count);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::scrollSubstringTo([in] startIndex, [in] endIndex, [in] scrollType) */
+       int scrollSubstringTo(int startIndex, int endIndex, int scrollType) {
+               if (DEBUG) print(this + ".IAccessibleText::scrollSubstringTo");
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.start = startIndex;
+               event.end = endIndex;
+               switch (scrollType) {
+                       case COM.IA2_SCROLL_TYPE_TOP_LEFT: event.type = ACC.SCROLL_TYPE_TOP_LEFT; break;
+                       case COM.IA2_SCROLL_TYPE_BOTTOM_RIGHT: event.type = ACC.SCROLL_TYPE_BOTTOM_RIGHT; break;
+                       case COM.IA2_SCROLL_TYPE_TOP_EDGE: event.type = ACC.SCROLL_TYPE_TOP_EDGE; break;
+                       case COM.IA2_SCROLL_TYPE_BOTTOM_EDGE: event.type = ACC.SCROLL_TYPE_BOTTOM_EDGE; break;
+                       case COM.IA2_SCROLL_TYPE_LEFT_EDGE: event.type = ACC.SCROLL_TYPE_LEFT_EDGE; break;
+                       case COM.IA2_SCROLL_TYPE_RIGHT_EDGE: event.type = ACC.SCROLL_TYPE_RIGHT_EDGE; break;
+                       case COM.IA2_SCROLL_TYPE_ANYWHERE: event.type = ACC.SCROLL_TYPE_ANYWHERE; break;
+               }
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.scrollText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::scrollSubstringToPoint([in] startIndex, [in] endIndex, [in] coordinateType, [in] x, [in] y) */
+       int scrollSubstringToPoint(int startIndex, int endIndex, int coordinateType, int x, int y) {
+               if (DEBUG) print(this + ".IAccessibleText::scrollSubstringToPoint");
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.start = startIndex;
+               event.end = endIndex;
+               event.type = ACC.SCROLL_TYPE_POINT;
+               event.x = x;
+               event.y = y;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.scrollText(event);
+               }
+               if (event.result == null || !event.result.equals(ACC.OK)) return COM.E_INVALIDARG; // TODO: @retval S_FALSE if the object is already at the specified location.
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_newText([out] pNewText) */
+       int get_newText(long pNewText) {
+               if (DEBUG) print(this + ".IAccessibleText::get_newText");
+               String text = null;
+               int start = 0;
+               int end = 0;
+               if (textInserted != null) {
+                       text = (String) textInserted[3];
+                       start = ((Integer)textInserted[1]).intValue();
+                       end = ((Integer)textInserted[2]).intValue();
+               }
+               setString(pNewText, text);
+               OS.MoveMemory(pNewText + C.PTR_SIZEOF, new int [] {start}, 4);
+               OS.MoveMemory(pNewText + C.PTR_SIZEOF + 4, new int [] {end}, 4);
+               if (textInserted == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleText::get_oldText([out] pOldText) */
+       int get_oldText(long pOldText) {
+               if (DEBUG) print(this + ".IAccessibleText::get_oldText");
+               String text = null;
+               int start = 0;
+               int end = 0;
+               if (textDeleted != null) {
+                       text = (String) textDeleted[3];
+                       start = ((Integer)textDeleted[1]).intValue();
+                       end = ((Integer)textDeleted[2]).intValue();
+               }
+               setString(pOldText, text);
+               OS.MoveMemory(pOldText + C.PTR_SIZEOF, new int [] {start}, 4);
+               OS.MoveMemory(pOldText + C.PTR_SIZEOF + 4, new int [] {end}, 4);
+               if (textDeleted == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleValue::get_currentValue([out] pCurrentValue) */
+       int get_currentValue(long pCurrentValue) {
+               AccessibleValueEvent event = new AccessibleValueEvent(this);
+               for (int i = 0; i < accessibleValueListenersSize(); i++) {
+                       AccessibleValueListener listener = accessibleValueListeners.get(i);
+                       listener.getCurrentValue(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleValue::get_currentValue returning " + event.value + hresult(event.value == null ? COM.S_FALSE : COM.S_OK));
+               setNumberVARIANT(pCurrentValue, event.value);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleValue::setCurrentValue([in] value) */
+       int setCurrentValue(long value) {
+               if (DEBUG) print(this + ".IAccessibleValue::setCurrentValue");
+               AccessibleValueEvent event = new AccessibleValueEvent(this);
+               event.value = getNumberVARIANT(value);
+               for (int i = 0; i < accessibleValueListenersSize(); i++) {
+                       AccessibleValueListener listener = accessibleValueListeners.get(i);
+                       listener.setCurrentValue(event);
+               }
+               //if (event.value == null) return COM.S_FALSE;
+               return COM.S_OK;
+       }
+
+       /* IAccessibleValue::get_maximumValue([out] pMaximumValue) */
+       int get_maximumValue(long pMaximumValue) {
+               AccessibleValueEvent event = new AccessibleValueEvent(this);
+               for (int i = 0; i < accessibleValueListenersSize(); i++) {
+                       AccessibleValueListener listener = accessibleValueListeners.get(i);
+                       listener.getMaximumValue(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleValue::get_maximumValue returning " + event.value + hresult(event.value == null ? COM.S_FALSE : COM.S_OK));
+               setNumberVARIANT(pMaximumValue, event.value);
+               return COM.S_OK;
+       }
+
+       /* IAccessibleValue::get_minimumValue([out] pMinimumValue) */
+       int get_minimumValue(long pMinimumValue) {
+               AccessibleValueEvent event = new AccessibleValueEvent(this);
+               for (int i = 0; i < accessibleValueListenersSize(); i++) {
+                       AccessibleValueListener listener = accessibleValueListeners.get(i);
+                       listener.getMinimumValue(event);
+               }
+               if (DEBUG) print(this + ".IAccessibleValue::get_minimumValue returning " + event.value + hresult(event.value == null ? COM.S_FALSE : COM.S_OK));
+               setNumberVARIANT(pMinimumValue, event.value);
+               return COM.S_OK;
+       }
+
+       int eventChildID() {
+               if (parent == null) return COM.CHILDID_SELF;
+               if (uniqueID == -1) uniqueID = UniqueID--;
+               return uniqueID;
+       }
+
+       void checkUniqueID(int childID) {
+               /* If the application is using child ids, check whether there's a corresponding
+                * accessible, and if so, use the child id as that accessible's unique id. */
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = childID;
+               for (int l = 0; l < accessibleControlListenersSize(); l++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(l);
+                       listener.getChild(event);
+               }
+               Accessible accessible = event.accessible;
+               if (accessible != null && accessible.uniqueID == -1) {
+                       accessible.uniqueID = childID;
+               }
+       }
+
+       int childIDToOs(int childID) {
+               if (childID == ACC.CHILDID_SELF) return COM.CHILDID_SELF;
+               /* ChildIDs are 1-based indices. */
+               int osChildID = childID + 1;
+               if (control instanceof Tree) {
+                       osChildID = (int)OS.SendMessage (control.handle, OS.TVM_MAPHTREEITEMTOACCID, childID, 0);
+               }
+               checkUniqueID(osChildID);
+               return osChildID;
+       }
+
+       int osToChildID(int osChildID) {
+               if (osChildID == COM.CHILDID_SELF) return ACC.CHILDID_SELF;
+               /*
+               * Feature of Windows:
+               * Before Windows XP, tree item ids were 1-based indices.
+               * Windows XP and later use the tree item handle for the
+               * accessible child ID. For backward compatibility, we still
+               * take 1-based childIDs for tree items prior to Windows XP.
+               * All other childIDs are 1-based indices.
+               */
+               if (!(control instanceof Tree)) return osChildID - 1;
+               return (int)OS.SendMessage (control.handle, OS.TVM_MAPACCIDTOHTREEITEM, osChildID, 0);
+       }
+
+       int stateToOs(int state) {
+               int osState = 0;
+               if ((state & ACC.STATE_SELECTED) != 0) osState |= COM.STATE_SYSTEM_SELECTED;
+               if ((state & ACC.STATE_SELECTABLE) != 0) osState |= COM.STATE_SYSTEM_SELECTABLE;
+               if ((state & ACC.STATE_MULTISELECTABLE) != 0) osState |= COM.STATE_SYSTEM_MULTISELECTABLE;
+               if ((state & ACC.STATE_FOCUSED) != 0) osState |= COM.STATE_SYSTEM_FOCUSED;
+               if ((state & ACC.STATE_FOCUSABLE) != 0) osState |= COM.STATE_SYSTEM_FOCUSABLE;
+               if ((state & ACC.STATE_PRESSED) != 0) osState |= COM.STATE_SYSTEM_PRESSED;
+               if ((state & ACC.STATE_CHECKED) != 0) osState |= COM.STATE_SYSTEM_CHECKED;
+               if ((state & ACC.STATE_EXPANDED) != 0) osState |= COM.STATE_SYSTEM_EXPANDED;
+               if ((state & ACC.STATE_COLLAPSED) != 0) osState |= COM.STATE_SYSTEM_COLLAPSED;
+               if ((state & ACC.STATE_HOTTRACKED) != 0) osState |= COM.STATE_SYSTEM_HOTTRACKED;
+               if ((state & ACC.STATE_BUSY) != 0) osState |= COM.STATE_SYSTEM_BUSY;
+               if ((state & ACC.STATE_READONLY) != 0) osState |= COM.STATE_SYSTEM_READONLY;
+               if ((state & ACC.STATE_INVISIBLE) != 0) osState |= COM.STATE_SYSTEM_INVISIBLE;
+               if ((state & ACC.STATE_OFFSCREEN) != 0) osState |= COM.STATE_SYSTEM_OFFSCREEN;
+               if ((state & ACC.STATE_SIZEABLE) != 0) osState |= COM.STATE_SYSTEM_SIZEABLE;
+               if ((state & ACC.STATE_LINKED) != 0) osState |= COM.STATE_SYSTEM_LINKED;
+               if ((state & ACC.STATE_DISABLED) != 0) osState |= COM.STATE_SYSTEM_UNAVAILABLE;
+               return osState;
+       }
+
+       int osToState(int osState) {
+               int state = ACC.STATE_NORMAL;
+               if ((osState & COM.STATE_SYSTEM_SELECTED) != 0) state |= ACC.STATE_SELECTED;
+               if ((osState & COM.STATE_SYSTEM_SELECTABLE) != 0) state |= ACC.STATE_SELECTABLE;
+               if ((osState & COM.STATE_SYSTEM_MULTISELECTABLE) != 0) state |= ACC.STATE_MULTISELECTABLE;
+               if ((osState & COM.STATE_SYSTEM_FOCUSED) != 0) state |= ACC.STATE_FOCUSED;
+               if ((osState & COM.STATE_SYSTEM_FOCUSABLE) != 0) state |= ACC.STATE_FOCUSABLE;
+               if ((osState & COM.STATE_SYSTEM_PRESSED) != 0) state |= ACC.STATE_PRESSED;
+               if ((osState & COM.STATE_SYSTEM_CHECKED) != 0) state |= ACC.STATE_CHECKED;
+               if ((osState & COM.STATE_SYSTEM_EXPANDED) != 0) state |= ACC.STATE_EXPANDED;
+               if ((osState & COM.STATE_SYSTEM_COLLAPSED) != 0) state |= ACC.STATE_COLLAPSED;
+               if ((osState & COM.STATE_SYSTEM_HOTTRACKED) != 0) state |= ACC.STATE_HOTTRACKED;
+               if ((osState & COM.STATE_SYSTEM_BUSY) != 0) state |= ACC.STATE_BUSY;
+               if ((osState & COM.STATE_SYSTEM_READONLY) != 0) state |= ACC.STATE_READONLY;
+               if ((osState & COM.STATE_SYSTEM_INVISIBLE) != 0) state |= ACC.STATE_INVISIBLE;
+               if ((osState & COM.STATE_SYSTEM_OFFSCREEN) != 0) state |= ACC.STATE_OFFSCREEN;
+               if ((osState & COM.STATE_SYSTEM_SIZEABLE) != 0) state |= ACC.STATE_SIZEABLE;
+               if ((osState & COM.STATE_SYSTEM_LINKED) != 0) state |= ACC.STATE_LINKED;
+               if ((osState & COM.STATE_SYSTEM_UNAVAILABLE) != 0) state |= ACC.STATE_DISABLED;
+               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_SPLITBUTTON: return COM.ROLE_SYSTEM_SPLITBUTTON;
+                       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;
+                       case ACC.ROLE_ALERT: return COM.ROLE_SYSTEM_ALERT;
+                       case ACC.ROLE_ANIMATION: return COM.ROLE_SYSTEM_ANIMATION;
+                       case ACC.ROLE_COLUMN: return COM.ROLE_SYSTEM_COLUMN;
+                       case ACC.ROLE_DOCUMENT: return COM.ROLE_SYSTEM_DOCUMENT;
+                       case ACC.ROLE_GRAPHIC: return COM.ROLE_SYSTEM_GRAPHIC;
+                       case ACC.ROLE_GROUP: return COM.ROLE_SYSTEM_GROUPING;
+                       case ACC.ROLE_ROW: return COM.ROLE_SYSTEM_ROW;
+                       case ACC.ROLE_SPINBUTTON: return COM.ROLE_SYSTEM_SPINBUTTON;
+                       case ACC.ROLE_STATUSBAR: return COM.ROLE_SYSTEM_STATUSBAR;
+                       case ACC.ROLE_CLOCK: return COM.ROLE_SYSTEM_CLOCK;
+                       case ACC.ROLE_CALENDAR: return COM.ROLE_SYSTEM_DROPLIST;
+
+                       /* The rest are IA2 roles, so return the closest match. */
+                       case ACC.ROLE_CANVAS: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_CHECKMENUITEM: return COM.ROLE_SYSTEM_MENUITEM;
+                       case ACC.ROLE_RADIOMENUITEM: return COM.ROLE_SYSTEM_MENUITEM;
+                       case ACC.ROLE_DATETIME: return COM.ROLE_SYSTEM_DROPLIST;
+                       case ACC.ROLE_FOOTER: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_FORM: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_HEADER: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_HEADING: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_PAGE: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_PARAGRAPH: return COM.ROLE_SYSTEM_CLIENT;
+                       case ACC.ROLE_SECTION: return COM.ROLE_SYSTEM_CLIENT;
+               }
+               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_SPLITBUTTON: return ACC.ROLE_SPLITBUTTON;
+                       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;
+                       case COM.ROLE_SYSTEM_ALERT: return ACC.ROLE_ALERT;
+                       case COM.ROLE_SYSTEM_ANIMATION: return ACC.ROLE_ANIMATION;
+                       case COM.ROLE_SYSTEM_COLUMN: return ACC.ROLE_COLUMN;
+                       case COM.ROLE_SYSTEM_DOCUMENT: return ACC.ROLE_DOCUMENT;
+                       case COM.ROLE_SYSTEM_GRAPHIC: return ACC.ROLE_GRAPHIC;
+                       case COM.ROLE_SYSTEM_GROUPING: return ACC.ROLE_GROUP;
+                       case COM.ROLE_SYSTEM_ROW: return ACC.ROLE_ROW;
+                       case COM.ROLE_SYSTEM_SPINBUTTON: return ACC.ROLE_SPINBUTTON;
+                       case COM.ROLE_SYSTEM_STATUSBAR: return ACC.ROLE_STATUSBAR;
+                       case COM.ROLE_SYSTEM_CLOCK: return ACC.ROLE_CLOCK;
+                       case COM.ROLE_SYSTEM_DROPLIST: return ACC.ROLE_CALENDAR;
+               }
+               return ACC.ROLE_CLIENT_AREA;
+       }
+
+       /*
+        * Return a Color given a string of the form "rgb(n,n,n)".
+        */
+       Color colorFromString(String rgbString) {
+               try {
+                       int open = rgbString.indexOf('(');
+                       int comma1 = rgbString.indexOf(',');
+                       int comma2 = rgbString.indexOf(',', comma1 + 1);
+                       int close = rgbString.indexOf(')');
+                       int r = Integer.parseInt(rgbString.substring(open + 1, comma1));
+                       int g = Integer.parseInt(rgbString.substring(comma1 + 1, comma2));
+                       int b = Integer.parseInt(rgbString.substring(comma2 + 1, close));
+                       return new Color(control.getDisplay(), r, g, b);
+               } catch (NumberFormatException ex) {}
+               return null;
+       }
+
+       int getCaretOffset() {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.offset = -1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getCaretOffset (event);
+               }
+               if (event.offset == -1) {
+                       for (int i = 0; i < accessibleTextListenersSize(); i++) {
+                               event.childID = ACC.CHILDID_SELF;
+                               AccessibleTextListener listener = accessibleTextListeners.get(i);
+                               listener.getCaretOffset (event);
+                       }
+               }
+               return event.offset;
+       }
+
+       int getCharacterCount() {
+               AccessibleTextEvent event = new AccessibleTextEvent(this);
+               event.count = -1;
+               for (int i = 0; i < accessibleTextExtendedListenersSize(); i++) {
+                       AccessibleTextExtendedListener listener = accessibleTextExtendedListeners.get(i);
+                       listener.getCharacterCount(event);
+               }
+               if (event.count == -1) {
+                       AccessibleControlEvent e = new AccessibleControlEvent(this);
+                       e.childID = ACC.CHILDID_SELF;
+                       for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                               AccessibleControlListener listener = accessibleControlListeners.get(i);
+                               listener.getRole(e);
+                               listener.getValue(e);
+                       }
+                       // TODO: Consider passing the value through for other roles as well (i.e. combo, etc). Keep in sync with get_text.
+                       event.count = e.detail == ACC.ROLE_TEXT && e.result != null ? e.result.length() : 0;
+               }
+               return event.count;
+       }
+
+       int getRelationCount() {
+               int count = 0;
+               for (int type = 0; type < MAX_RELATION_TYPES; type++) {
+                       if (relations[type] != null) count++;
+               }
+               return count;
+       }
+
+       int getRole() {
+               AccessibleControlEvent event = new AccessibleControlEvent(this);
+               event.childID = ACC.CHILDID_SELF;
+               for (int i = 0; i < accessibleControlListenersSize(); i++) {
+                       AccessibleControlListener listener = accessibleControlListeners.get(i);
+                       listener.getRole(event);
+               }
+               return event.detail;
+       }
+
+       int getDefaultRole() {
+               int role;
+               role = COM.ROLE_SYSTEM_CLIENT;
+               if (iaccessible != null) {
+                       /* Get the default role from the OS. */
+                       long varChild = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof);
+                       setIntVARIANT(varChild, COM.VT_I4, COM.CHILDID_SELF);
+                       long pvarRole = OS.GlobalAlloc (OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof);
+                       int code = iaccessible.get_accRole(varChild, pvarRole);
+                       if (code == COM.S_OK) {
+                               VARIANT v = getVARIANT(pvarRole);
+                               if (v.vt == COM.VT_I4) role = v.lVal;
+                       }
+                       OS.GlobalFree(varChild);
+                       OS.GlobalFree(pvarRole);
+               }
+               return role;
+       }
+
+       String getString(long psz) {
+               long [] ptr = new long [1];
+               OS.MoveMemory (ptr, psz, C.PTR_SIZEOF);
+               int size = COM.SysStringByteLen(ptr [0]);
+               if (size == 0) return "";
+               char [] buffer = new char [(size + 1) / 2];
+               OS.MoveMemory (buffer, ptr [0], size);
+               return new String (buffer);
+       }
+
+       VARIANT getVARIANT(long variant) {
+               VARIANT v = new VARIANT();
+               COM.MoveMemory(v, variant, VARIANT.sizeof);
+               return v;
+       }
+
+       Number getNumberVARIANT(long variant) {
+               VARIANT v = new VARIANT();
+               COM.MoveMemory(v, variant, VARIANT.sizeof);
+               if (v.vt == COM.VT_I8) return Long.valueOf(v.lVal); // TODO: Fix this - v.lVal is an int - don't use struct
+               return Integer.valueOf(v.lVal);
+       }
+
+       void setIntVARIANT(long variant, short vt, int lVal) {
+               if (vt == COM.VT_I4 || vt == COM.VT_EMPTY) {
+                       OS.MoveMemory(variant, new short[] { vt }, 2);
+                       OS.MoveMemory(variant + 8, new int[] { lVal }, 4);
+               }
+       }
+
+       void setPtrVARIANT(long variant, short vt, long lVal) {
+               if (vt == COM.VT_DISPATCH || vt == COM.VT_UNKNOWN) {
+                       OS.MoveMemory(variant, new short[] { vt }, 2);
+                       OS.MoveMemory(variant + 8, new long [] { lVal }, C.PTR_SIZEOF);
+               }
+       }
+
+       void setNumberVARIANT(long variant, Number number) {
+               if (number == null) {
+                       OS.MoveMemory(variant, new short[] { COM.VT_EMPTY }, 2);
+                       OS.MoveMemory(variant + 8, new int[] { 0 }, 4);
+               } else if (number instanceof Double) {
+                       OS.MoveMemory(variant, new short[] { COM.VT_R8 }, 2);
+                       OS.MoveMemory(variant + 8, new double[] { number.doubleValue() }, 8);
+               } else if (number instanceof Float) {
+                       OS.MoveMemory(variant, new short[] { COM.VT_R4 }, 2);
+                       OS.MoveMemory(variant + 8, new float[] { number.floatValue() }, 4);
+               } else if (number instanceof Long) {
+                       OS.MoveMemory(variant, new short[] { COM.VT_I8 }, 2);
+                       OS.MoveMemory(variant + 8, new long[] { number.longValue() }, 8);
+               } else {
+                       OS.MoveMemory(variant, new short[] { COM.VT_I4 }, 2);
+                       OS.MoveMemory(variant + 8, new int[] { number.intValue() }, 4);
+               }
+       }
+
+       void setString(long psz, String string) {
+               long ptr = 0;
+               if (string != null) {
+                       char[] data = (string + "\0").toCharArray();
+                       ptr = COM.SysAllocString(data);
+               }
+               OS.MoveMemory(psz, new long [] { ptr }, C.PTR_SIZEOF);
+       }
+
+       void setStringVARIANT(long variant, String string) {
+               long ptr = 0;
+               if (string != null) {
+                       char[] data = (string + "\0").toCharArray();
+                       ptr = COM.SysAllocString(data);
+               }
+               OS.MoveMemory(variant, new short[] { ptr == 0 ? COM.VT_EMPTY : COM.VT_BSTR }, 2);
+               OS.MoveMemory(variant + 8, new long [] { ptr }, C.PTR_SIZEOF);
+       }
+
+       /* 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);
+       }
+
+       boolean isATRunning () {
+               /*
+                * Currently there is no accurate way to check if AT is running from 'refCount'.
+                * JAWS screen reader cannot be detected using 'refCount' approach,
+                * because 'refCount' continues to be 1 even when JAWS is running.
+                */
+               // if (refCount <= 1) return false;
+               return true;
+       }
+
+       /* isValidThread was copied from Widget, and rewritten to work in this package */
+       boolean isValidThread () {
+               return control.getDisplay ().getThread () == Thread.currentThread ();
+       }
+
+       // START DEBUG CODE
+       static void print (String str) {
+               if (DEBUG) System.out.println (str);
+       }
+       String getRoleString(int role) {
+               if (DEBUG) switch (role) {
+                       case COM.ROLE_SYSTEM_CLIENT: return "ROLE_SYSTEM_CLIENT";
+                       case COM.ROLE_SYSTEM_WINDOW: return "ROLE_SYSTEM_WINDOW";
+                       case COM.ROLE_SYSTEM_MENUBAR: return "ROLE_SYSTEM_MENUBAR";
+                       case COM.ROLE_SYSTEM_MENUPOPUP: return "ROLE_SYSTEM_MENUPOPUP";
+                       case COM.ROLE_SYSTEM_MENUITEM: return "ROLE_SYSTEM_MENUITEM";
+                       case COM.ROLE_SYSTEM_SEPARATOR: return "ROLE_SYSTEM_SEPARATOR";
+                       case COM.ROLE_SYSTEM_TOOLTIP: return "ROLE_SYSTEM_TOOLTIP";
+                       case COM.ROLE_SYSTEM_SCROLLBAR: return "ROLE_SYSTEM_SCROLLBAR";
+                       case COM.ROLE_SYSTEM_DIALOG: return "ROLE_SYSTEM_DIALOG";
+                       case COM.ROLE_SYSTEM_STATICTEXT: return "ROLE_SYSTEM_STATICTEXT";
+                       case COM.ROLE_SYSTEM_PUSHBUTTON: return "ROLE_SYSTEM_PUSHBUTTON";
+                       case COM.ROLE_SYSTEM_CHECKBUTTON: return "ROLE_SYSTEM_CHECKBUTTON";
+                       case COM.ROLE_SYSTEM_RADIOBUTTON: return "ROLE_SYSTEM_RADIOBUTTON";
+                       case COM.ROLE_SYSTEM_SPLITBUTTON: return "ROLE_SYSTEM_SPLITBUTTON";
+                       case COM.ROLE_SYSTEM_COMBOBOX: return "ROLE_SYSTEM_COMBOBOX";
+                       case COM.ROLE_SYSTEM_TEXT: return "ROLE_SYSTEM_TEXT";
+                       case COM.ROLE_SYSTEM_TOOLBAR: return "ROLE_SYSTEM_TOOLBAR";
+                       case COM.ROLE_SYSTEM_LIST: return "ROLE_SYSTEM_LIST";
+                       case COM.ROLE_SYSTEM_LISTITEM: return "ROLE_SYSTEM_LISTITEM";
+                       case COM.ROLE_SYSTEM_TABLE: return "ROLE_SYSTEM_TABLE";
+                       case COM.ROLE_SYSTEM_CELL: return "ROLE_SYSTEM_CELL";
+                       case COM.ROLE_SYSTEM_COLUMNHEADER: return "ROLE_SYSTEM_COLUMNHEADER";
+                       case COM.ROLE_SYSTEM_ROWHEADER: return "ROLE_SYSTEM_ROWHEADER";
+                       case COM.ROLE_SYSTEM_OUTLINE: return "ROLE_SYSTEM_OUTLINE";
+                       case COM.ROLE_SYSTEM_OUTLINEITEM: return "ROLE_SYSTEM_OUTLINEITEM";
+                       case COM.ROLE_SYSTEM_PAGETABLIST: return "ROLE_SYSTEM_PAGETABLIST";
+                       case COM.ROLE_SYSTEM_PAGETAB: return "ROLE_SYSTEM_PAGETAB";
+                       case COM.ROLE_SYSTEM_PROGRESSBAR: return "ROLE_SYSTEM_PROGRESSBAR";
+                       case COM.ROLE_SYSTEM_SLIDER: return "ROLE_SYSTEM_SLIDER";
+                       case COM.ROLE_SYSTEM_LINK: return "ROLE_SYSTEM_LINK";
+                       case COM.ROLE_SYSTEM_ALERT: return "ROLE_SYSTEM_ALERT";
+                       case COM.ROLE_SYSTEM_ANIMATION: return "ROLE_SYSTEM_ANIMATION";
+                       case COM.ROLE_SYSTEM_COLUMN: return "ROLE_SYSTEM_COLUMN";
+                       case COM.ROLE_SYSTEM_DOCUMENT: return "ROLE_SYSTEM_DOCUMENT";
+                       case COM.ROLE_SYSTEM_GRAPHIC: return "ROLE_SYSTEM_GRAPHIC";
+                       case COM.ROLE_SYSTEM_GROUPING: return "ROLE_SYSTEM_GROUPING";
+                       case COM.ROLE_SYSTEM_ROW: return "ROLE_SYSTEM_ROW";
+                       case COM.ROLE_SYSTEM_SPINBUTTON: return "ROLE_SYSTEM_SPINBUTTON";
+                       case COM.ROLE_SYSTEM_STATUSBAR: return "ROLE_SYSTEM_STATUSBAR";
+                       case COM.ROLE_SYSTEM_CLOCK: return "ROLE_SYSTEM_CLOCK";
+                       case COM.ROLE_SYSTEM_DROPLIST: return "ROLE_SYSTEM_DROPLIST";
+                       // IA2 roles
+                       case ACC.ROLE_CANVAS: return "IA2_ROLE_CANVAS";
+                       case ACC.ROLE_CHECKMENUITEM: return "IA2_ROLE_CHECKMENUITEM";
+                       case ACC.ROLE_RADIOMENUITEM: return "IA2_ROLE_RADIOMENUITEM";
+                       case ACC.ROLE_DATETIME: return "IA2_ROLE_DATETIME";
+                       case ACC.ROLE_FOOTER: return "IA2_ROLE_FOOTER";
+                       case ACC.ROLE_FORM: return "IA2_ROLE_FORM";
+                       case ACC.ROLE_HEADER: return "IA2_ROLE_HEADER";
+                       case ACC.ROLE_HEADING: return "IA2_ROLE_HEADING";
+                       case ACC.ROLE_PAGE: return "IA2_ROLE_PAGE";
+                       case ACC.ROLE_PARAGRAPH: return "IA2_ROLE_PARAGRAPH";
+                       case ACC.ROLE_SECTION: return "IA2_ROLE_SECTION";
+               }
+               return "Unknown role (" + role + ")";
+       }
+       String getStateString(int state) {
+               if (state == 0) return " no state bits set";
+               StringBuilder stateString = new StringBuilder();
+               if (DEBUG) {
+               if ((state & COM.STATE_SYSTEM_SELECTED) != 0) stateString.append(" STATE_SYSTEM_SELECTED");
+               if ((state & COM.STATE_SYSTEM_SELECTABLE) != 0) stateString.append(" STATE_SYSTEM_SELECTABLE");
+               if ((state & COM.STATE_SYSTEM_MULTISELECTABLE) != 0) stateString.append(" STATE_SYSTEM_MULTISELECTABLE");
+               if ((state & COM.STATE_SYSTEM_FOCUSED) != 0) stateString.append(" STATE_SYSTEM_FOCUSED");
+               if ((state & COM.STATE_SYSTEM_FOCUSABLE) != 0) stateString.append(" STATE_SYSTEM_FOCUSABLE");
+               if ((state & COM.STATE_SYSTEM_PRESSED) != 0) stateString.append(" STATE_SYSTEM_PRESSED");
+               if ((state & COM.STATE_SYSTEM_CHECKED) != 0) stateString.append(" STATE_SYSTEM_CHECKED");
+               if ((state & COM.STATE_SYSTEM_EXPANDED) != 0) stateString.append(" STATE_SYSTEM_EXPANDED");
+               if ((state & COM.STATE_SYSTEM_COLLAPSED) != 0) stateString.append(" STATE_SYSTEM_COLLAPSED");
+               if ((state & COM.STATE_SYSTEM_HOTTRACKED) != 0) stateString.append(" STATE_SYSTEM_HOTTRACKED");
+               if ((state & COM.STATE_SYSTEM_BUSY) != 0) stateString.append(" STATE_SYSTEM_BUSY");
+               if ((state & COM.STATE_SYSTEM_READONLY) != 0) stateString.append(" STATE_SYSTEM_READONLY");
+               if ((state & COM.STATE_SYSTEM_INVISIBLE) != 0) stateString.append(" STATE_SYSTEM_INVISIBLE");
+               if ((state & COM.STATE_SYSTEM_OFFSCREEN) != 0) stateString.append(" STATE_SYSTEM_OFFSCREEN");
+               if ((state & COM.STATE_SYSTEM_SIZEABLE) != 0) stateString.append(" STATE_SYSTEM_SIZEABLE");
+               if ((state & COM.STATE_SYSTEM_LINKED) != 0) stateString.append(" STATE_SYSTEM_LINKED");
+               if ((state & COM.STATE_SYSTEM_UNAVAILABLE) != 0) stateString.append(" STATE_SYSTEM_UNAVAILABLE");
+               if (stateString.length() == 0) stateString.append(" Unknown state[s] (" + Integer.toHexString(state) + ")");
+               }
+               return stateString.toString();
+       }
+       String getIA2StatesString(int ia2States) {
+               if (ia2States == 0) return " no state bits set";
+               StringBuilder stateString = new StringBuilder();
+               if (DEBUG) {
+               if ((ia2States & COM.IA2_STATE_ACTIVE) != 0) stateString.append(" IA2_STATE_ACTIVE");
+               if ((ia2States & COM.IA2_STATE_EDITABLE) != 0) stateString.append(" IA2_STATE_EDITABLE");
+               if ((ia2States & COM.IA2_STATE_SINGLE_LINE) != 0) stateString.append(" IA2_STATE_SINGLE_LINE");
+               if ((ia2States & COM.IA2_STATE_MULTI_LINE) != 0) stateString.append(" IA2_STATE_MULTI_LINE");
+               if ((ia2States & COM.IA2_STATE_REQUIRED) != 0) stateString.append(" IA2_STATE_REQUIRED");
+               if ((ia2States & COM.IA2_STATE_INVALID_ENTRY) != 0) stateString.append(" IA2_STATE_INVALID_ENTRY");
+               if ((ia2States & COM.IA2_STATE_SUPPORTS_AUTOCOMPLETION) != 0) stateString.append(" IA2_STATE_SUPPORTS_AUTOCOMPLETION");
+               if (stateString.length() == 0) stateString.append(" Unknown IA2 state[s] (" + ia2States + ")");
+               }
+               return stateString.toString();
+       }
+       String getEventString(int event) {
+               if (DEBUG) switch (event) {
+                       case ACC.EVENT_TABLE_CHANGED: return "IA2_EVENT_TABLE_CHANGED";
+                       case ACC.EVENT_TEXT_CHANGED: return "IA2_EVENT_TEXT_REMOVED or IA2_EVENT_TEXT_INSERTED";
+                       case ACC.EVENT_HYPERTEXT_LINK_SELECTED: return "IA2_EVENT_HYPERTEXT_LINK_SELECTED";
+                       case ACC.EVENT_VALUE_CHANGED: return "EVENT_OBJECT_VALUECHANGE";
+                       case ACC.EVENT_STATE_CHANGED: return "EVENT_OBJECT_STATECHANGE";
+                       case ACC.EVENT_SELECTION_CHANGED: return "EVENT_OBJECT_SELECTIONWITHIN";
+                       case ACC.EVENT_TEXT_SELECTION_CHANGED: return "EVENT_OBJECT_TEXTSELECTIONCHANGED";
+                       case ACC.EVENT_LOCATION_CHANGED: return "EVENT_OBJECT_LOCATIONCHANGE";
+                       case ACC.EVENT_NAME_CHANGED: return "EVENT_OBJECT_NAMECHANGE";
+                       case ACC.EVENT_DESCRIPTION_CHANGED: return "EVENT_OBJECT_DESCRIPTIONCHANGE";
+                       case ACC.EVENT_DOCUMENT_LOAD_COMPLETE: return "IA2_EVENT_DOCUMENT_LOAD_COMPLETE";
+                       case ACC.EVENT_DOCUMENT_LOAD_STOPPED: return "IA2_EVENT_DOCUMENT_LOAD_STOPPED";
+                       case ACC.EVENT_DOCUMENT_RELOAD: return "IA2_EVENT_DOCUMENT_RELOAD";
+                       case ACC.EVENT_PAGE_CHANGED: return "IA2_EVENT_PAGE_CHANGED";
+                       case ACC.EVENT_SECTION_CHANGED: return "IA2_EVENT_SECTION_CHANGED";
+                       case ACC.EVENT_ACTION_CHANGED: return "IA2_EVENT_ACTION_CHANGED";
+                       case ACC.EVENT_HYPERLINK_START_INDEX_CHANGED: return "IA2_EVENT_HYPERLINK_START_INDEX_CHANGED";
+                       case ACC.EVENT_HYPERLINK_END_INDEX_CHANGED: return "IA2_EVENT_HYPERLINK_END_INDEX_CHANGED";
+                       case ACC.EVENT_HYPERLINK_ANCHOR_COUNT_CHANGED: return "IA2_EVENT_HYPERLINK_ANCHOR_COUNT_CHANGED";
+                       case ACC.EVENT_HYPERLINK_SELECTED_LINK_CHANGED: return "IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED";
+                       case ACC.EVENT_HYPERLINK_ACTIVATED: return "IA2_EVENT_HYPERLINK_ACTIVATED";
+                       case ACC.EVENT_HYPERTEXT_LINK_COUNT_CHANGED: return "IA2_EVENT_HYPERTEXT_LINK_COUNT_CHANGED";
+                       case ACC.EVENT_ATTRIBUTE_CHANGED: return "IA2_EVENT_ATTRIBUTE_CHANGED";
+                       case ACC.EVENT_TABLE_CAPTION_CHANGED: return "IA2_EVENT_TABLE_CAPTION_CHANGED";
+                       case ACC.EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED: return "IA2_EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED";
+                       case ACC.EVENT_TABLE_COLUMN_HEADER_CHANGED: return "IA2_EVENT_TABLE_COLUMN_HEADER_CHANGED";
+                       case ACC.EVENT_TABLE_ROW_DESCRIPTION_CHANGED: return "IA2_EVENT_TABLE_ROW_DESCRIPTION_CHANGED";
+                       case ACC.EVENT_TABLE_ROW_HEADER_CHANGED: return "IA2_EVENT_TABLE_ROW_HEADER_CHANGED";
+                       case ACC.EVENT_TABLE_SUMMARY_CHANGED: return "IA2_EVENT_TABLE_SUMMARY_CHANGED";
+                       case ACC.EVENT_TEXT_ATTRIBUTE_CHANGED: return "IA2_EVENT_TEXT_ATTRIBUTE_CHANGED";
+                       case ACC.EVENT_TEXT_CARET_MOVED: return "IA2_EVENT_TEXT_CARET_MOVED";
+                       case ACC.EVENT_TEXT_COLUMN_CHANGED: return "IA2_EVENT_TEXT_COLUMN_CHANGED";
+               }
+               return "Unknown event (" + event + ")";
+       }
+       private String hresult(int code) {
+               if (DEBUG) switch (code) {
+                       case COM.S_OK: return " S_OK";
+                       case COM.S_FALSE: return " S_FALSE";
+                       case COM.E_ACCESSDENIED: return " E_ACCESSDENIED";
+                       case COM.E_FAIL: return " E_FAIL";
+                       case COM.E_INVALIDARG: return " E_INVALIDARG";
+                       case COM.E_NOINTERFACE: return " E_NOINTERFACE";
+                       case COM.E_NOTIMPL: return " E_NOTIMPL";
+                       case COM.E_NOTSUPPORTED: return " E_NOTSUPPORTED";
+                       case COM.E_OUTOFMEMORY: return " E_OUTOFMEMORY";
+                       case OS.E_POINTER: return " E_POINTER";
+                       case COM.DISP_E_EXCEPTION: return " DISP_E_EXCEPTION";
+                       case COM.DISP_E_MEMBERNOTFOUND: return " DISP_E_MEMBERNOTFOUND";
+                       case COM.DISP_E_UNKNOWNINTERFACE: return " DISP_E_UNKNOWNINTERFACE";
+                       case COM.DISP_E_UNKNOWNNAME: return " DISP_E_UNKNOWNNAME";
+               }
+               return " HRESULT=" + code;
+       }
+       boolean interesting(GUID guid) {
+               if (DEBUG) {
+               if (COM.IsEqualGUID(guid, COM.IIDIUnknown)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessible)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIEnumVARIANT)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIServiceProvider)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessible2)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleRelation)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleAction)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleComponent)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleValue)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleText)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleEditableText)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleHyperlink)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleHypertext)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTable)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTable2)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTableCell)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleImage)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleApplication)) return true;
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleContext)) return true;
+               }
+               return false;
+       }
+       String guidString(GUID guid) {
+               if (DEBUG) {
+               final GUID IIDIAccessibleHandler = IIDFromString("{03022430-ABC4-11D0-BDE2-00AA001A1953}"); //$NON-NLS-1$
+               final GUID IIDIAccessor = IIDFromString("{0C733A8C-2A1C-11CE-ADE5-00AA0044773D}"); //$NON-NLS-1$
+               final GUID IIDIAdviseSink2 = IIDFromString("{00000125-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIBindCtx = IIDFromString("{0000000E-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDICreateErrorInfo = IIDFromString("{22F03340-547D-101B-8E65-08002B2BD119}"); //$NON-NLS-1$
+               final GUID IIDICreateTypeInfo = IIDFromString("{00020405-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDICreateTypeLib = IIDFromString("{00020406-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIDataAdviseHolder = IIDFromString("{00000110-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIEnumConnectionPoints = IIDFromString("{B196B285-BAB4-101A-B69C-00AA00341D07}"); //$NON-NLS-1$
+               final GUID IIDIEnumConnections = IIDFromString("{B196B287-BAB4-101A-B69C-00AA00341D07}"); //$NON-NLS-1$
+               final GUID IIDIEnumMoniker = IIDFromString("{00000102-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIEnumOLEVERB = IIDFromString("{00000104-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIEnumSTATDATA = IIDFromString("{00000105-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIEnumSTATSTG = IIDFromString("{0000000D-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIEnumString = IIDFromString("{00000101-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIEnumUnknown = IIDFromString("{00000100-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIErrorInfo = IIDFromString("{1CF2B120-547D-101B-8E65-08002B2BD119}"); //$NON-NLS-1$
+               final GUID IIDIErrorLog = IIDFromString("{3127CA40-446E-11CE-8135-00AA004BB851}"); //$NON-NLS-1$
+               final GUID IIDIExternalConnection = IIDFromString("{00000019-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIFontDisp = IIDFromString("{BEF6E003-A874-101A-8BBA-00AA00300CAB}"); //$NON-NLS-1$
+               final GUID IIDILockBytes = IIDFromString("{0000000A-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIMalloc = IIDFromString("{00000002-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIMallocSpy = IIDFromString("{0000001D-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIMarshal = IIDFromString("{00000003-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIMessageFilter = IIDFromString("{00000016-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIMoniker = IIDFromString("{0000000F-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIOleAdviseHolder = IIDFromString("{00000111-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIOleCache = IIDFromString("{0000011E-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIOleCache2 = IIDFromString("{00000128-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIOleCacheControl = IIDFromString("{00000129-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIOleItemContainer = IIDFromString("{0000011C-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIParseDisplayName = IIDFromString("{0000011A-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIPerPropertyBrowsing = IIDFromString("{376BD3AA-3845-101B-84ED-08002B2EC713}"); //$NON-NLS-1$
+               final GUID IIDIPersistMemory = IIDFromString("{BD1AE5E0-A6AE-11CE-BD37-504200C10000}"); //$NON-NLS-1$
+               final GUID IIDIPersistPropertyBag = IIDFromString("{37D84F60-42CB-11CE-8135-00AA004BB851}"); //$NON-NLS-1$
+               final GUID IIDIPicture = IIDFromString("{7BF80980-BF32-101A-8BBB-00AA00300CAB}"); //$NON-NLS-1$
+               final GUID IIDIPictureDisp = IIDFromString("{7BF80981-BF32-101A-8BBB-00AA00300CAB}"); //$NON-NLS-1$
+               final GUID IIDIPropertyBag = IIDFromString("{55272A00-42CB-11CE-8135-00AA004BB851}"); //$NON-NLS-1$
+               final GUID IIDIPropertyPage = IIDFromString("{B196B28D-BAB4-101A-B69C-00AA00341D07}"); //$NON-NLS-1$
+               final GUID IIDIPropertyPage2 = IIDFromString("{01E44665-24AC-101B-84ED-08002B2EC713}"); //$NON-NLS-1$
+               final GUID IIDIPropertyPageSite = IIDFromString("{B196B28C-BAB4-101A-B69C-00AA00341D07}"); //$NON-NLS-1$
+               final GUID IIDIPSFactoryBuffer = IIDFromString("{D5F569D0-593B-101A-B569-08002B2DBF7A}"); //$NON-NLS-1$
+               final GUID IIDIRootStorage = IIDFromString("{00000012-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIROTData = IIDFromString("{F29F6BC0-5021-11CE-AA15-00006901293F}"); //$NON-NLS-1$
+               final GUID IIDIRpcChannelBuffer = IIDFromString("{D5F56B60-593B-101A-B569-08002B2DBF7A}"); //$NON-NLS-1$
+               final GUID IIDIRpcProxyBuffer = IIDFromString("{D5F56A34-593B-101A-B569-08002B2DBF7A}"); //$NON-NLS-1$
+               final GUID IIDIRpcStubBuffer = IIDFromString("{D5F56AFC-593B-101A-B569-08002B2DBF7A}"); //$NON-NLS-1$
+               final GUID IIDIRunnableObject = IIDFromString("{00000126-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIRunningObjectTable = IIDFromString("{00000010-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDISimpleFrameSite = IIDFromString("{742B0E01-14E6-101B-914E-00AA00300CAB}"); //$NON-NLS-1$
+               final GUID IIDIStdMarshalInfo = IIDFromString("{00000018-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDISupportErrorInfo = IIDFromString("{DF0B3D60-548F-101B-8E65-08002B2BD119}"); //$NON-NLS-1$
+               final GUID IIDITypeComp = IIDFromString("{00020403-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDITypeLib = IIDFromString("{00020402-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIViewObject = IIDFromString("{0000010D-0000-0000-C000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDIdentityUnmarshal = IIDFromString("{0000001b-0000-0000-c000-000000000046}"); //$NON-NLS-1$
+               final GUID IIDInternalMSMarshaller = IIDFromString("{4c1e39e1-e3e3-4296-aa86-ec938d896e92}"); //$NON-NLS-1$
+               final GUID IIDIAccIdentity = IIDFromString("{7852B78D-1CFD-41C1-A615-9C0C85960B5F}"); //$NON-NLS-1$
+               final GUID IIDIAccPropServer = IIDFromString("{76C0DBBB-15E0-4E7B-B61B-20EEEA2001E0}"); //$NON-NLS-1$
+               final GUID IIDIAccPropServices = IIDFromString("{6E26E776-04F0-495D-80E4-3330352E3169}"); //$NON-NLS-1$
+               if (COM.IsEqualGUID(guid, COM.IID_IDropTargetHelper)) return "IID_IDropTargetHelper";
+               if (COM.IsEqualGUID(guid, COM.IIDJavaBeansBridge)) return "IIDJavaBeansBridge";
+               if (COM.IsEqualGUID(guid, COM.IIDShockwaveActiveXControl)) return "IIDShockwaveActiveXControl";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessible)) return "IIDIAccessible";
+               if (COM.IsEqualGUID(guid, IIDIAccessibleHandler)) return "IIDIAccessibleHandler";
+               if (COM.IsEqualGUID(guid, IIDIAccessor)) return "IIDIAccessor";
+               if (COM.IsEqualGUID(guid, COM.IIDIAdviseSink)) return "IIDIAdviseSink";
+               if (COM.IsEqualGUID(guid, IIDIAdviseSink2)) return "IIDIAdviseSink2";
+               if (COM.IsEqualGUID(guid, IIDIBindCtx)) return "IIDIBindCtx";
+               if (COM.IsEqualGUID(guid, COM.IIDIClassFactory)) return "IIDIClassFactory";
+               if (COM.IsEqualGUID(guid, COM.IIDIClassFactory2)) return "IIDIClassFactory2";
+               if (COM.IsEqualGUID(guid, COM.IIDIConnectionPointContainer)) return "IIDIConnectionPointContainer";
+               if (COM.IsEqualGUID(guid, IIDICreateErrorInfo)) return "IIDICreateErrorInfo";
+               if (COM.IsEqualGUID(guid, IIDICreateTypeInfo)) return "IIDICreateTypeInfo";
+               if (COM.IsEqualGUID(guid, IIDICreateTypeLib)) return "IIDICreateTypeLib";
+               if (COM.IsEqualGUID(guid, IIDIDataAdviseHolder)) return "IIDIDataAdviseHolder";
+               if (COM.IsEqualGUID(guid, COM.IIDIDataObject)) return "IIDIDataObject";
+               if (COM.IsEqualGUID(guid, COM.IIDIDispatch)) return "IIDIDispatch";
+               if (COM.IsEqualGUID(guid, COM.IIDIDispatchEx)) return "IIDIDispatchEx";
+               if (COM.IsEqualGUID(guid, COM.IIDIDocHostUIHandler)) return "IIDIDocHostUIHandler";
+               if (COM.IsEqualGUID(guid, COM.IIDIDocHostShowUI)) return "IIDIDocHostShowUI";
+               if (COM.IsEqualGUID(guid, COM.IIDIDropSource)) return "IIDIDropSource";
+               if (COM.IsEqualGUID(guid, COM.IIDIDropTarget)) return "IIDIDropTarget";
+               if (COM.IsEqualGUID(guid, IIDIEnumConnectionPoints)) return "IIDIEnumConnectionPoints";
+               if (COM.IsEqualGUID(guid, IIDIEnumConnections)) return "IIDIEnumConnections";
+               if (COM.IsEqualGUID(guid, COM.IIDIEnumFORMATETC)) return "IIDIEnumFORMATETC";
+               if (COM.IsEqualGUID(guid, IIDIEnumMoniker)) return "IIDIEnumMoniker";
+               if (COM.IsEqualGUID(guid, IIDIEnumOLEVERB)) return "IIDIEnumOLEVERB";
+               if (COM.IsEqualGUID(guid, IIDIEnumSTATDATA)) return "IIDIEnumSTATDATA";
+               if (COM.IsEqualGUID(guid, IIDIEnumSTATSTG)) return "IIDIEnumSTATSTG";
+               if (COM.IsEqualGUID(guid, IIDIEnumString)) return "IIDIEnumString";
+               if (COM.IsEqualGUID(guid, IIDIEnumUnknown)) return "IIDIEnumUnknown";
+               if (COM.IsEqualGUID(guid, COM.IIDIEnumVARIANT)) return "IIDIEnumVARIANT";
+               if (COM.IsEqualGUID(guid, IIDIErrorInfo)) return "IIDIErrorInfo";
+               if (COM.IsEqualGUID(guid, IIDIErrorLog)) return "IIDIErrorLog";
+               if (COM.IsEqualGUID(guid, IIDIExternalConnection)) return "IIDIExternalConnection";
+               if (COM.IsEqualGUID(guid, IIDIFontDisp)) return "IIDIFontDisp";
+       //      if (COM.IsEqualGUID(guid, COM.IIDIHTMLDocumentEvents2)) return "IIDIHTMLDocumentEvents2";
+               if (COM.IsEqualGUID(guid, COM.IIDIInternetSecurityManager)) return "IIDIInternetSecurityManager";
+               if (COM.IsEqualGUID(guid, COM.IIDIAuthenticate)) return "IIDIAuthenticate";
+               if (COM.IsEqualGUID(guid, COM.IIDIJScriptTypeInfo)) return "IIDIJScriptTypeInfo";
+               if (COM.IsEqualGUID(guid, IIDILockBytes)) return "IIDILockBytes";
+               if (COM.IsEqualGUID(guid, IIDIMalloc)) return "IIDIMalloc";
+               if (COM.IsEqualGUID(guid, IIDIMallocSpy)) return "IIDIMallocSpy";
+               if (COM.IsEqualGUID(guid, IIDIMarshal)) return "IIDIMarshal";
+               if (COM.IsEqualGUID(guid, IIDIMessageFilter)) return "IIDIMessageFilter";
+               if (COM.IsEqualGUID(guid, IIDIMoniker)) return "IIDIMoniker";
+               if (COM.IsEqualGUID(guid, IIDIOleAdviseHolder)) return "IIDIOleAdviseHolder";
+               if (COM.IsEqualGUID(guid, IIDIOleCache)) return "IIDIOleCache";
+               if (COM.IsEqualGUID(guid, IIDIOleCache2)) return "IIDIOleCache2";
+               if (COM.IsEqualGUID(guid, IIDIOleCacheControl)) return "IIDIOleCacheControl";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleClientSite)) return "IIDIOleClientSite";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleCommandTarget)) return "IIDIOleCommandTarget";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleControl)) return "IIDIOleControl";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleControlSite)) return "IIDIOleControlSite";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleDocument)) return "IIDIOleDocument";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleDocumentSite)) return "IIDIOleDocumentSite";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleInPlaceFrame)) return "IIDIOleInPlaceFrame";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleInPlaceObject)) return "IIDIOleInPlaceObject";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleInPlaceSite)) return "IIDIOleInPlaceSite";
+               if (COM.IsEqualGUID(guid, IIDIOleItemContainer)) return "IIDIOleItemContainer";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleLink)) return "IIDIOleLink";
+               if (COM.IsEqualGUID(guid, COM.IIDIOleObject)) return "IIDIOleObject";
+               if (COM.IsEqualGUID(guid, IIDIParseDisplayName)) return "IIDIParseDisplayName";
+               if (COM.IsEqualGUID(guid, IIDIPerPropertyBrowsing)) return "IIDIPerPropertyBrowsing";
+               if (COM.IsEqualGUID(guid, COM.IIDIPersist)) return "IIDIPersist";
+               if (COM.IsEqualGUID(guid, COM.IIDIPersistFile)) return "IIDIPersistFile";
+               if (COM.IsEqualGUID(guid, IIDIPersistMemory)) return "IIDIPersistMemory";
+               if (COM.IsEqualGUID(guid, IIDIPersistPropertyBag)) return "IIDIPersistPropertyBag";
+               if (COM.IsEqualGUID(guid, COM.IIDIPersistStorage)) return "IIDIPersistStorage";
+               if (COM.IsEqualGUID(guid, COM.IIDIPersistStreamInit)) return "IIDIPersistStreamInit";
+               if (COM.IsEqualGUID(guid, IIDIPicture)) return "IIDIPicture";
+               if (COM.IsEqualGUID(guid, IIDIPictureDisp)) return "IIDIPictureDisp";
+               if (COM.IsEqualGUID(guid, IIDIPropertyBag)) return "IIDIPropertyBag";
+               if (COM.IsEqualGUID(guid, COM.IIDIPropertyNotifySink)) return "IIDIPropertyNotifySink";
+               if (COM.IsEqualGUID(guid, IIDIPropertyPage)) return "IIDIPropertyPage";
+               if (COM.IsEqualGUID(guid, IIDIPropertyPage2)) return "IIDIPropertyPage2";
+               if (COM.IsEqualGUID(guid, IIDIPropertyPageSite)) return "IIDIPropertyPageSite";
+               if (COM.IsEqualGUID(guid, COM.IIDIProvideClassInfo)) return "IIDIProvideClassInfo";
+               if (COM.IsEqualGUID(guid, COM.IIDIProvideClassInfo2)) return "IIDIProvideClassInfo2";
+               if (COM.IsEqualGUID(guid, IIDIPSFactoryBuffer)) return "IIDIPSFactoryBuffer";
+               if (COM.IsEqualGUID(guid, IIDIRootStorage)) return "IIDIRootStorage";
+               if (COM.IsEqualGUID(guid, IIDIROTData)) return "IIDIROTData";
+               if (COM.IsEqualGUID(guid, IIDIRpcChannelBuffer)) return "IIDIRpcChannelBuffer";
+               if (COM.IsEqualGUID(guid, IIDIRpcProxyBuffer)) return "IIDIRpcProxyBuffer";
+               if (COM.IsEqualGUID(guid, IIDIRpcStubBuffer)) return "IIDIRpcStubBuffer";
+               if (COM.IsEqualGUID(guid, IIDIRunnableObject)) return "IIDIRunnableObject";
+               if (COM.IsEqualGUID(guid, IIDIRunningObjectTable)) return "IIDIRunningObjectTable";
+               if (COM.IsEqualGUID(guid, IIDISimpleFrameSite)) return "IIDISimpleFrameSite";
+               if (COM.IsEqualGUID(guid, COM.IIDIServiceProvider)) return "IIDIServiceProvider";
+               if (COM.IsEqualGUID(guid, COM.IIDISpecifyPropertyPages)) return "IIDISpecifyPropertyPages";
+               if (COM.IsEqualGUID(guid, IIDIStdMarshalInfo)) return "IIDIStdMarshalInfo";
+               if (COM.IsEqualGUID(guid, IIDISupportErrorInfo)) return "IIDISupportErrorInfo";
+               if (COM.IsEqualGUID(guid, IIDITypeComp)) return "IIDITypeComp";
+               if (COM.IsEqualGUID(guid, IIDITypeLib)) return "IIDITypeLib";
+               if (COM.IsEqualGUID(guid, COM.IIDIUnknown)) return "IIDIUnknown";
+               if (COM.IsEqualGUID(guid, IIDIViewObject)) return "IIDIViewObject";
+               if (COM.IsEqualGUID(guid, COM.IIDIViewObject2)) return "IIDIViewObject2";
+               if (COM.IsEqualGUID(guid, COM.CGID_DocHostCommandHandler)) return "CGID_DocHostCommandHandler";
+               if (COM.IsEqualGUID(guid, COM.CGID_Explorer)) return "CGID_Explorer";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessible2)) return "IIDIAccessible2";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleRelation)) return "IIDIAccessibleRelation";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleAction)) return "IIDIAccessibleAction";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleComponent)) return "IIDIAccessibleComponent";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleValue)) return "IIDIAccessibleValue";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleText)) return "IIDIAccessibleText";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleEditableText)) return "IIDIAccessibleEditableText";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleHyperlink)) return "IIDIAccessibleHyperlink";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleHypertext)) return "IIDIAccessibleHypertext";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTable)) return "IIDIAccessibleTable";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTable2)) return "IIDIAccessibleTable2";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleTableCell)) return "IIDIAccessibleTableCell";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleImage)) return "IIDIAccessibleImage";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleApplication)) return "IIDIAccessibleApplication";
+               if (COM.IsEqualGUID(guid, COM.IIDIAccessibleContext)) return "IIDIAccessibleContext";
+               if (COM.IsEqualGUID(guid, IIDIdentityUnmarshal)) return "IIDIdentityUnmarshal";
+               if (COM.IsEqualGUID(guid, IIDInternalMSMarshaller)) return "IIDInternalMSMarshaller";
+               if (COM.IsEqualGUID(guid, IIDIAccIdentity)) return "IIDIAccIdentity";
+               if (COM.IsEqualGUID(guid, IIDIAccPropServer)) return "IIDIAccPropServer";
+               if (COM.IsEqualGUID(guid, IIDIAccPropServices)) return "IIDIAccPropServices";
+               }
+               return guid.toString();
+       }
+       static GUID IIDFromString(String lpsz) {
+               if (DEBUG) {
+               int length = lpsz.length();
+               char[] buffer = new char[length + 1];
+               lpsz.getChars(0, length, buffer, 0);
+               GUID lpiid = new GUID();
+               if (COM.IIDFromString(buffer, lpiid) == COM.S_OK) return lpiid;
+               }
+               return null;
+       }
+       @Override
+       public String toString () {
+               String toString = super.toString();
+               if (DEBUG) {
+                       int role = getRole();
+                       if (role == 0) role = getDefaultRole();
+                       return toString.substring(toString.lastIndexOf('.') + 1) + "(" + getRoleString(role) + ")";
+               }
+               return toString;
+       }
+       // END DEBUG CODE
+}