org.eclipse.swt.ole.win32.OleControlSite.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.swt.ole.win32.OleControlSite.java

Source

/*******************************************************************************
 * 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.ole.win32;

import java.io.*;

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.widgets.*;

/**
 * OleControlSite provides a site to manage an embedded ActiveX Control within a container.
 *
 * <p>In addition to the behaviour provided by OleClientSite, this object provides the following:
 * <ul>
 *   <li>events from the ActiveX control
 *    <li>notification of property changes from the ActiveX control
 *   <li>simplified access to well known properties of the ActiveX Control (e.g. font, background color)
 *   <li>expose ambient properties of the container to the ActiveX Control
 * </ul>
 *
 * <p>This object implements the OLE Interfaces IOleControlSite, IDispatch, and IPropertyNotifySink.
 *
 * <p>Note that although this class is a subclass of <code>Composite</code>,
 * it does not make sense to add <code>Control</code> children to it,
 * or set a layout on it.
 * </p>
 * <dl>
 *   <dt><b>Styles</b> <dd>BORDER
 *   <dt><b>Events</b> <dd>Dispose, Move, Resize
 * </dl>
 *
 * @see <a href="http://www.eclipse.org/swt/snippets/#ole">OLE and ActiveX snippets</a>
 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Examples: OLEExample, OleWebBrowser</a>
 */
public class OleControlSite extends OleClientSite {
    // interfaces for this container
    private COMObject iOleControlSite;
    private COMObject iDispatch;

    // supporting Property Change attributes
    private OlePropertyChangeSink olePropertyChangeSink;

    // supporting Event Sink attributes
    private OleEventSink[] oleEventSink = new OleEventSink[0];
    private GUID[] oleEventSinkGUID = new GUID[0];
    private long[] oleEventSinkIUnknown = new long[0];

    // supporting information for the Control COM object
    private CONTROLINFO currentControlInfo;
    private int[] sitePropertyIds = new int[0];
    private Variant[] sitePropertyValues = new Variant[0];

    private Font font;

    // work around for IE destroying the caret
    static int SWT_RESTORECARET;

    static final String SHELL_PROG_ID = "Shell.Explorer"; //$NON-NLS-1$

    /**
     * Create an OleControlSite child widget using the OLE Document type associated with the
     * specified file.  The OLE Document type is determined either through header information in the file
     * or through a Registry entry for the file extension. Use style bits to select a particular look
     * or set of properties.
     *
     * @param parent a composite widget; must be an OleFrame
     * @param style the bitwise OR'ing of widget styles
     * @param file the file that is to be opened in this OLE Document
     *
     * @exception IllegalArgumentException
     * <ul><li>ERROR_NULL_ARGUMENT when the parent is null
     *     <li>ERROR_INVALID_ARGUMENT when the parent is not an OleFrame</ul>
     * @exception SWTException
     * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     *     <li>ERROR_CANNOT_CREATE_OBJECT when failed to create OLE Object
     *     <li>ERROR_CANNOT_OPEN_FILE when failed to open file
     *     <li>ERROR_INTERFACE_NOT_FOUND when unable to create callbacks for OLE Interfaces
     *     <li>ERROR_INVALID_CLASSID
     * </ul>
     *
     * @since 3.5
     */
    public OleControlSite(Composite parent, int style, File file) {
        super(parent, style, file);

        // Init site properties
        setSiteProperty(COM.DISPID_AMBIENT_USERMODE, new Variant(true));
        setSiteProperty(COM.DISPID_AMBIENT_UIDEAD, new Variant(false));
    }

    /**
     * Create an OleControlSite child widget using style bits
     * to select a particular look or set of properties.
     *
     * @param parent a composite widget; must be an OleFrame
     * @param style the bitwise OR'ing of widget styles
     * @param progId the unique program identifier which has been registered for this ActiveX Control;
     *               the value of the ProgID key or the value of the VersionIndependentProgID key specified
     *               in the registry for this Control (for example, the VersionIndependentProgID for
     *               Internet Explorer is Shell.Explorer)
     *
     * @exception IllegalArgumentException <ul>
     *     <li>ERROR_NULL_ARGUMENT when the parent is null
     *</ul>
     * @exception SWTException <ul>
     *     <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     *     <li>ERROR_INVALID_CLASSID when the progId does not map to a registered CLSID
     *     <li>ERROR_CANNOT_CREATE_OBJECT when failed to create OLE Object
     *     <li>ERROR_CANNOT_ACCESS_CLASSFACTORY when Class Factory could not be found
     *     <li>ERROR_CANNOT_CREATE_LICENSED_OBJECT when failed to create a licensed OLE Object
     * </ul>
     */
    public OleControlSite(Composite parent, int style, String progId) {
        super(parent, style);
        try {

            // check for licensing
            appClsid = getClassID(progId);
            if (appClsid == null)
                OLE.error(OLE.ERROR_INVALID_CLASSID);

            long licinfo = getLicenseInfo(appClsid);
            if (licinfo == 0) {

                // Open a storage object
                tempStorage = createTempStorage();

                // Create ole object with storage object
                long[] address = new long[1];
                /*
                * Bug in ICA Client 2.7. The creation of the IOleObject fails if the client
                * site is provided to OleCreate().  The fix is to detect that the program
                * id is an ICA Client and do not pass a client site to OleCreate().
                * IOleObject.SetClientSite() is called later on.
                */
                long clientSite = isICAClient() ? 0 : iOleClientSite.getAddress();
                int result = COM.OleCreate(appClsid, COM.IIDIUnknown, COM.OLERENDER_DRAW, null, clientSite,
                        tempStorage.getAddress(), address);
                if (result != COM.S_OK)
                    OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);

                objIUnknown = new IUnknown(address[0]);

            } else {
                // Prepare the ClassFactory
                long[] ppvObject = new long[1];
                try {
                    int result = COM.CoGetClassObject(appClsid,
                            COM.CLSCTX_INPROC_HANDLER | COM.CLSCTX_INPROC_SERVER, 0, COM.IIDIClassFactory2,
                            ppvObject);
                    if (result != COM.S_OK) {
                        OLE.error(OLE.ERROR_CANNOT_ACCESS_CLASSFACTORY, result);
                    }
                    IClassFactory2 classFactory = new IClassFactory2(ppvObject[0]);
                    // Create Com Object
                    ppvObject = new long[1];
                    result = classFactory.CreateInstanceLic(0, 0, COM.IIDIUnknown, licinfo, ppvObject);
                    classFactory.Release();
                    if (result != COM.S_OK)
                        OLE.error(OLE.ERROR_CANNOT_CREATE_LICENSED_OBJECT, result);
                } finally {
                    COM.SysFreeString(licinfo);
                }

                objIUnknown = new IUnknown(ppvObject[0]);

                // Prepare a storage medium
                ppvObject = new long[1];
                if (objIUnknown.QueryInterface(COM.IIDIPersistStorage, ppvObject) == COM.S_OK) {
                    IPersistStorage persist = new IPersistStorage(ppvObject[0]);
                    tempStorage = createTempStorage();
                    persist.InitNew(tempStorage.getAddress());
                    persist.Release();
                }
            }

            // Init sinks
            addObjectReferences();

            // Init site properties
            setSiteProperty(COM.DISPID_AMBIENT_USERMODE, new Variant(true));
            setSiteProperty(COM.DISPID_AMBIENT_UIDEAD, new Variant(false));

            if (COM.OleRun(objIUnknown.getAddress()) == OLE.S_OK)
                state = STATE_RUNNING;

        } catch (SWTError e) {
            dispose();
            disposeCOMInterfaces();
            throw e;
        }
    }

    /**
     * Create an OleClientSite child widget to edit the specified file using the specified OLE Document
     * application.  Use style bits to select a particular look or set of properties.
     * <p>
     * <b>IMPORTANT:</b> This method is <em>not</em> part of the public
     * API for <code>OleClientSite</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 parent a composite widget; must be an OleFrame
     * @param style the bitwise OR'ing of widget styles
     * @param progId the unique program identifier of am OLE Document application;
     *               the value of the ProgID key or the value of the VersionIndependentProgID key specified
     *               in the registry for the desired OLE Document (for example, the VersionIndependentProgID
     *               for Word is Word.Document)
     * @param file the file that is to be opened in this OLE Document
     *
     * @exception IllegalArgumentException
     * <ul><li>ERROR_NULL_ARGUMENT when the parent is null
     *     <li>ERROR_INVALID_ARGUMENT when the parent is not an OleFrame</ul>
     * @exception SWTException
     * <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
     *     <li>ERROR_INVALID_CLASSID when the progId does not map to a registered CLSID
     *     <li>ERROR_CANNOT_CREATE_OBJECT when failed to create OLE Object
     *     <li>ERROR_CANNOT_OPEN_FILE when failed to open file
     * </ul>
     *
     * @noreference This method is not intended to be referenced by clients.
     *
     * @since 3.5
     */
    public OleControlSite(Composite parent, int style, String progId, File file) {
        super(parent, style, progId, file);

        // Init site properties
        setSiteProperty(COM.DISPID_AMBIENT_USERMODE, new Variant(true));
        setSiteProperty(COM.DISPID_AMBIENT_UIDEAD, new Variant(false));
    }

    /**
     * Adds the listener to receive events.
     *
     * @param eventID the id of the event
     *
     * @param listener the listener
     *
     * @exception IllegalArgumentException <ul>
     *       <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    public void addEventListener(int eventID, OleListener listener) {
        if (listener == null)
            OLE.error(SWT.ERROR_NULL_ARGUMENT);
        GUID riid = getDefaultEventSinkGUID(objIUnknown);
        if (riid != null) {
            addEventListener(objIUnknown.getAddress(), riid, eventID, listener);
        }

    }

    static GUID getDefaultEventSinkGUID(IUnknown unknown) {
        // get Event Sink I/F from IProvideClassInfo2
        long[] ppvObject = new long[1];
        if (unknown.QueryInterface(COM.IIDIProvideClassInfo2, ppvObject) == COM.S_OK) {
            IProvideClassInfo2 pci2 = new IProvideClassInfo2(ppvObject[0]);
            GUID riid = new GUID();
            int result = pci2.GetGUID(COM.GUIDKIND_DEFAULT_SOURCE_DISP_IID, riid);
            pci2.Release();
            if (result == COM.S_OK)
                return riid;
        }

        // get Event Sink I/F from IProvideClassInfo
        if (unknown.QueryInterface(COM.IIDIProvideClassInfo, ppvObject) == COM.S_OK) {
            IProvideClassInfo pci = new IProvideClassInfo(ppvObject[0]);
            long[] ppTI = new long[1];
            long[] ppEI = new long[1];
            int result = pci.GetClassInfo(ppTI);
            pci.Release();

            if (result == COM.S_OK && ppTI[0] != 0) {
                ITypeInfo classInfo = new ITypeInfo(ppTI[0]);
                long[] ppTypeAttr = new long[1];
                result = classInfo.GetTypeAttr(ppTypeAttr);
                if (result == COM.S_OK && ppTypeAttr[0] != 0) {
                    TYPEATTR typeAttribute = new TYPEATTR();
                    COM.MoveMemory(typeAttribute, ppTypeAttr[0], TYPEATTR.sizeof);
                    classInfo.ReleaseTypeAttr(ppTypeAttr[0]);
                    int implMask = COM.IMPLTYPEFLAG_FDEFAULT | COM.IMPLTYPEFLAG_FSOURCE
                            | COM.IMPLTYPEFLAG_FRESTRICTED;
                    int implBits = COM.IMPLTYPEFLAG_FDEFAULT | COM.IMPLTYPEFLAG_FSOURCE;

                    for (int i = 0; i < typeAttribute.cImplTypes; i++) {
                        int[] pImplTypeFlags = new int[1];
                        if (classInfo.GetImplTypeFlags(i, pImplTypeFlags) == COM.S_OK) {
                            if ((pImplTypeFlags[0] & implMask) == implBits) {
                                int[] pRefType = new int[1];
                                if (classInfo.GetRefTypeOfImplType(i, pRefType) == COM.S_OK) {
                                    classInfo.GetRefTypeInfo(pRefType[0], ppEI);
                                }
                            }
                        }
                    }
                }
                classInfo.Release();

                if (ppEI[0] != 0) {
                    ITypeInfo eventInfo = new ITypeInfo(ppEI[0]);
                    ppTypeAttr = new long[1];
                    result = eventInfo.GetTypeAttr(ppTypeAttr);
                    GUID riid = null;
                    if (result == COM.S_OK && ppTypeAttr[0] != 0) {
                        riid = new GUID();
                        COM.MoveMemory(riid, ppTypeAttr[0], GUID.sizeof);
                        eventInfo.ReleaseTypeAttr(ppTypeAttr[0]);
                    }
                    eventInfo.Release();
                    return riid;
                }
            }
        }
        return null;
    }

    /**
     * Adds the listener to receive events.
     *
     * @since 2.0
     *
     * @param automation the automation object that provides the event notification
     * @param eventID the id of the event
     * @param listener the listener
     *
     * @exception IllegalArgumentException <ul>
     *      <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    public void addEventListener(OleAutomation automation, int eventID, OleListener listener) {
        if (listener == null || automation == null)
            OLE.error(SWT.ERROR_NULL_ARGUMENT);
        long address = automation.getAddress();
        IUnknown unknown = new IUnknown(address);
        GUID riid = getDefaultEventSinkGUID(unknown);
        if (riid != null) {
            addEventListener(address, riid, eventID, listener);
        }

    }

    /**
     * Adds the listener to receive events.
     *
     * @since 3.2
     *
     * @param automation the automation object that provides the event notification
     * @param eventSinkId the GUID of the event sink
     * @param eventID the id of the event
     * @param listener the listener
     *
     * @exception IllegalArgumentException <ul>
     *      <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    public void addEventListener(OleAutomation automation, String eventSinkId, int eventID, OleListener listener) {
        if (listener == null || automation == null || eventSinkId == null)
            OLE.error(SWT.ERROR_NULL_ARGUMENT);
        long address = automation.getAddress();
        if (address == 0)
            return;
        char[] buffer = (eventSinkId + "\0").toCharArray();
        GUID guid = new GUID();
        if (COM.IIDFromString(buffer, guid) != COM.S_OK)
            return;
        addEventListener(address, guid, eventID, listener);
    }

    void addEventListener(long iunknown, GUID guid, int eventID, OleListener listener) {
        if (listener == null || iunknown == 0 || guid == null)
            OLE.error(SWT.ERROR_NULL_ARGUMENT);
        // have we connected to this kind of event sink before?
        int index = -1;
        for (int i = 0; i < oleEventSinkGUID.length; i++) {
            if (COM.IsEqualGUID(oleEventSinkGUID[i], guid)) {
                if (iunknown == oleEventSinkIUnknown[i]) {
                    index = i;
                    break;
                }
            }
        }
        if (index != -1) {
            oleEventSink[index].addListener(eventID, listener);
        } else {
            int oldLength = oleEventSink.length;
            OleEventSink[] newOleEventSink = new OleEventSink[oldLength + 1];
            GUID[] newOleEventSinkGUID = new GUID[oldLength + 1];
            long[] newOleEventSinkIUnknown = new long[oldLength + 1];
            System.arraycopy(oleEventSink, 0, newOleEventSink, 0, oldLength);
            System.arraycopy(oleEventSinkGUID, 0, newOleEventSinkGUID, 0, oldLength);
            System.arraycopy(oleEventSinkIUnknown, 0, newOleEventSinkIUnknown, 0, oldLength);
            oleEventSink = newOleEventSink;
            oleEventSinkGUID = newOleEventSinkGUID;
            oleEventSinkIUnknown = newOleEventSinkIUnknown;

            oleEventSink[oldLength] = new OleEventSink(this, iunknown, guid);
            oleEventSinkGUID[oldLength] = guid;
            oleEventSinkIUnknown[oldLength] = iunknown;
            oleEventSink[oldLength].AddRef();
            oleEventSink[oldLength].connect();
            oleEventSink[oldLength].addListener(eventID, listener);

        }
    }

    @Override
    protected void addObjectReferences() {

        super.addObjectReferences();

        // Get property change notification from control
        connectPropertyChangeSink();

        // Get access to the Control object
        long[] ppvObject = new long[1];
        if (objIUnknown.QueryInterface(COM.IIDIOleControl, ppvObject) == COM.S_OK) {
            IOleControl objIOleControl = new IOleControl(ppvObject[0]);
            // ask the control for its info in case users
            // need to act on it
            currentControlInfo = new CONTROLINFO();
            objIOleControl.GetControlInfo(currentControlInfo);
            objIOleControl.Release();
        }
    }

    /**
     * Adds the listener to receive events.
     *
     * @param propertyID the identifier of the property
     * @param listener the listener
     *
     * @exception IllegalArgumentException <ul>
     *       <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    public void addPropertyListener(int propertyID, OleListener listener) {
        if (listener == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        olePropertyChangeSink.addListener(propertyID, listener);
    }

    private void connectPropertyChangeSink() {
        olePropertyChangeSink = new OlePropertyChangeSink(this);
        olePropertyChangeSink.AddRef();
        olePropertyChangeSink.connect(objIUnknown);
    }

    @Override
    protected void createCOMInterfaces() {
        super.createCOMInterfaces();

        // register each of the interfaces that this object implements
        iOleControlSite = new COMObject(new int[] { 2, 0, 0, 0, 1, 1, 3, 2, 1, 0 }) {
            @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 OnControlInfoChanged();
            }

            // method4 LockInPlaceActive - not implemented
            // method5 GetExtendedControl - not implemented
            // method6 TransformCoords - not implemented
            // method7 Translate Accelerator - not implemented
            @Override
            public long method8(long[] args) {
                return OnFocus((int) args[0]);
            }
            // method9 ShowPropertyFrame - not implemented
        };

        iDispatch = new COMObject(new int[] { 2, 0, 0, 1, 3, 5, 8 }) {
            @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
            @Override
            public long method6(long[] args) {
                return Invoke((int) args[0], args[1], (int) args[2], (int) args[3], args[4], args[5], args[6],
                        args[7]);
            }
        };
    }

    private void disconnectEventSinks() {

        for (int i = 0; i < oleEventSink.length; i++) {
            OleEventSink sink = oleEventSink[i];
            sink.disconnect();
            sink.Release();
        }
        oleEventSink = new OleEventSink[0];
        oleEventSinkGUID = new GUID[0];
        oleEventSinkIUnknown = new long[0];
    }

    private void disconnectPropertyChangeSink() {

        if (olePropertyChangeSink != null) {
            olePropertyChangeSink.disconnect(objIUnknown);
            olePropertyChangeSink.Release();
        }
        olePropertyChangeSink = null;
    }

    @Override
    protected void disposeCOMInterfaces() {
        super.disposeCOMInterfaces();

        if (iOleControlSite != null)
            iOleControlSite.dispose();
        iOleControlSite = null;

        if (iDispatch != null)
            iDispatch.dispose();
        iDispatch = null;
    }

    @Override
    public Color getBackground() {

        if (objIUnknown != null) {
            // !! We are getting the OLE_COLOR - should we change this to the COLORREF value?
            OleAutomation oleObject = new OleAutomation(this);
            Variant varBackColor = oleObject.getProperty(COM.DISPID_BACKCOLOR);
            oleObject.dispose();

            if (varBackColor != null) {
                int[] colorRef = new int[1];
                if (COM.OleTranslateColor(varBackColor.getInt(), 0, colorRef) == COM.S_OK)
                    return Color.win32_new(getDisplay(), colorRef[0]);
            }
        }

        return super.getBackground();
    }

    @Override
    public Font getFont() {
        if (font != null && !font.isDisposed())
            return font;
        if (objIUnknown != null) {
            OleAutomation oleObject = new OleAutomation(this);
            Variant varDispFont = oleObject.getProperty(COM.DISPID_FONT);
            oleObject.dispose();

            if (varDispFont != null) {
                OleAutomation iDispFont = varDispFont.getAutomation();
                Variant lfFaceName = iDispFont.getProperty(COM.DISPID_FONT_NAME);
                Variant lfHeight = iDispFont.getProperty(COM.DISPID_FONT_SIZE);
                Variant lfItalic = iDispFont.getProperty(COM.DISPID_FONT_ITALIC);
                //Variant lfCharSet  = iDispFont.getProperty(COM.DISPID_FONT_CHARSET);
                Variant lfBold = iDispFont.getProperty(COM.DISPID_FONT_BOLD);
                iDispFont.dispose();

                if (lfFaceName != null && lfHeight != null && lfItalic != null && lfBold != null) {
                    int style = 3 * lfBold.getInt() + 2 * lfItalic.getInt();
                    font = new Font(getShell().getDisplay(), lfFaceName.getString(), lfHeight.getInt(), style);
                    return font;
                }
            }
        }

        return super.getFont();
    }

    @Override
    public Color getForeground() {

        if (objIUnknown != null) {
            // !! We are getting the OLE_COLOR - should we change this to the COLORREF value?
            OleAutomation oleObject = new OleAutomation(this);
            Variant varForeColor = oleObject.getProperty(COM.DISPID_FORECOLOR);
            oleObject.dispose();

            if (varForeColor != null) {
                int[] colorRef = new int[1];
                if (COM.OleTranslateColor(varForeColor.getInt(), 0, colorRef) == COM.S_OK)
                    return Color.win32_new(getDisplay(), colorRef[0]);
            }
        }

        return super.getForeground();
    }

    protected long getLicenseInfo(GUID clsid) {
        long[] ppvObject = new long[1];
        if (COM.CoGetClassObject(clsid, COM.CLSCTX_INPROC_HANDLER | COM.CLSCTX_INPROC_SERVER, 0,
                COM.IIDIClassFactory, ppvObject) != COM.S_OK) {
            return 0;
        }
        long result = 0;
        IUnknown unknown = new IUnknown(ppvObject[0]);
        if (unknown.QueryInterface(COM.IIDIClassFactory2, ppvObject) == COM.S_OK) {
            IClassFactory2 classFactory = new IClassFactory2(ppvObject[0]);
            LICINFO licinfo = new LICINFO();
            if (classFactory.GetLicInfo(licinfo) == COM.S_OK) {
                long[] pBstrKey = new long[1];
                if (licinfo != null && licinfo.fRuntimeKeyAvail) {
                    if (classFactory.RequestLicKey(0, pBstrKey) == COM.S_OK) {
                        result = pBstrKey[0];
                    }
                }
            }
            classFactory.Release();
        }
        unknown.Release();
        return result;
    }

    /**
     *
     * Get the control site property specified by the dispIdMember, or
     * <code>null</code> if the dispId is not recognised.
     *
     * @param dispId the dispId
     *
     * @return the property value or <code>null</code>
     *
     * @since 2.1
     */
    public Variant getSiteProperty(int dispId) {
        for (int i = 0; i < sitePropertyIds.length; i++) {
            if (sitePropertyIds[i] == dispId) {
                return sitePropertyValues[i];
            }
        }
        return null;
    }

    @Override
    protected int GetWindow(long phwnd) {

        if (phwnd == 0)
            return COM.E_INVALIDARG;
        if (frame == null) {
            OS.MoveMemory(phwnd, new long[] { 0 }, C.PTR_SIZEOF);
            return COM.E_NOTIMPL;
        }

        // Copy the Window's handle into the memory passed in
        OS.MoveMemory(phwnd, new long[] { handle }, C.PTR_SIZEOF);
        return COM.S_OK;
    }

    private int Invoke(int dispIdMember, long riid, int lcid, int dwFlags, long pDispParams, long pVarResult,
            long pExcepInfo, long pArgErr) {
        if (pVarResult == 0 || dwFlags != COM.DISPATCH_PROPERTYGET) {
            if (pExcepInfo != 0)
                OS.MoveMemory(pExcepInfo, new long[] { 0 }, C.PTR_SIZEOF);
            if (pArgErr != 0)
                OS.MoveMemory(pArgErr, new int[] { 0 }, 4);
            return COM.DISP_E_MEMBERNOTFOUND;
        }
        Variant result = getSiteProperty(dispIdMember);
        if (result != null) {
            if (pVarResult != 0)
                result.getData(pVarResult);
            return COM.S_OK;
        }
        switch (dispIdMember) {
        // indicate a false result
        case COM.DISPID_AMBIENT_SUPPORTSMNEMONICS:
        case COM.DISPID_AMBIENT_SHOWGRABHANDLES:
        case COM.DISPID_AMBIENT_SHOWHATCHING:
            if (pVarResult != 0)
                OS.MoveMemory(pVarResult, new long[] { 0 }, C.PTR_SIZEOF);
            if (pExcepInfo != 0)
                OS.MoveMemory(pExcepInfo, new long[] { 0 }, C.PTR_SIZEOF);
            if (pArgErr != 0)
                OS.MoveMemory(pArgErr, new int[] { 0 }, 4);
            return COM.S_FALSE;

        // not implemented
        case COM.DISPID_AMBIENT_OFFLINEIFNOTCONNECTED:
        case COM.DISPID_AMBIENT_BACKCOLOR:
        case COM.DISPID_AMBIENT_FORECOLOR:
        case COM.DISPID_AMBIENT_FONT:
        case COM.DISPID_AMBIENT_LOCALEID:
        case COM.DISPID_AMBIENT_SILENT:
        case COM.DISPID_AMBIENT_MESSAGEREFLECT:
            if (pVarResult != 0)
                OS.MoveMemory(pVarResult, new long[] { 0 }, C.PTR_SIZEOF);
            if (pExcepInfo != 0)
                OS.MoveMemory(pExcepInfo, new long[] { 0 }, C.PTR_SIZEOF);
            if (pArgErr != 0)
                OS.MoveMemory(pArgErr, new int[] { 0 }, 4);
            return COM.E_NOTIMPL;

        default:
            if (pVarResult != 0)
                OS.MoveMemory(pVarResult, new long[] { 0 }, C.PTR_SIZEOF);
            if (pExcepInfo != 0)
                OS.MoveMemory(pExcepInfo, new long[] { 0 }, C.PTR_SIZEOF);
            if (pArgErr != 0)
                OS.MoveMemory(pArgErr, new int[] { 0 }, 4);
            return COM.DISP_E_MEMBERNOTFOUND;
        }
    }

    private int OnControlInfoChanged() {
        long[] ppvObject = new long[1];
        if (objIUnknown.QueryInterface(COM.IIDIOleControl, ppvObject) == COM.S_OK) {
            IOleControl objIOleControl = new IOleControl(ppvObject[0]);
            // ask the control for its info in case users
            // need to act on it
            currentControlInfo = new CONTROLINFO();
            objIOleControl.GetControlInfo(currentControlInfo);
            objIOleControl.Release();
        }
        return COM.S_OK;
    }

    @Override
    protected int OnUIDeactivate(int fUndoable) {
        return super.OnUIDeactivate(fUndoable);
    }

    @Override
    void onFocusIn(Event e) {
        String progID = getProgramID();
        if (progID == null)
            return;
        if (!progID.startsWith(SHELL_PROG_ID)) {
            super.onFocusIn(e);
            return;
        }
        if (objIOleInPlaceObject == null)
            return;
        if (!isActivated)
            doVerb(OLE.OLEIVERB_UIACTIVATE);
        if (isFocusControl())
            return;
        long[] phwnd = new long[1];
        objIOleInPlaceObject.GetWindow(phwnd);
        if (phwnd[0] == 0)
            return;
        OS.SetFocus(phwnd[0]);
    }

    @Override
    void onFocusOut(Event e) {
        if (objIOleInPlaceObject == null)
            return;
        String progID = getProgramID();
        if (progID == null)
            return;
        if (!progID.startsWith(SHELL_PROG_ID)) {
            super.onFocusOut(e);
            return;
        }

        /*
        * FocusOut is received when focus is reassigned between handles within
        * our site.  In such cases the site should not be UIDeactivated.
        */
        if (isFocusControl())
            return;

        /*
        * Bug in Windows.  When IE7 loses focus and UIDeactivate()
        * is called, IE destroys the caret even though it is
        * no longer owned by IE.  If focus has moved to a control
        * that shows a caret then the caret disappears.  The fix
        * is to detect this case and restore the caret.
        */
        int threadId = OS.GetCurrentThreadId();
        GUITHREADINFO lpgui1 = new GUITHREADINFO();
        lpgui1.cbSize = GUITHREADINFO.sizeof;
        OS.GetGUIThreadInfo(threadId, lpgui1);
        objIOleInPlaceObject.UIDeactivate();
        if (SWT_RESTORECARET == 0) {
            SWT_RESTORECARET = OS.RegisterWindowMessage(new TCHAR(0, "SWT_RESTORECARET", true));
        }
        if (lpgui1.hwndCaret != 0) {
            GUITHREADINFO lpgui2 = new GUITHREADINFO();
            lpgui2.cbSize = GUITHREADINFO.sizeof;
            OS.GetGUIThreadInfo(threadId, lpgui2);
            if (lpgui2.hwndCaret == 0 && lpgui1.hwndCaret == OS.GetFocus()) {
                /*
                * If the caret was not restored by SWT, put it back using
                * the information from GUITHREADINFO.  Note that this will
                * not be correct when the caret has a bitmap.  There is no
                * API to query the bitmap that the caret is using.
                */
                if (OS.SendMessage(lpgui1.hwndCaret, SWT_RESTORECARET, 0, 0) == 0) {
                    int width = lpgui1.right - lpgui1.left;
                    int height = lpgui1.bottom - lpgui1.top;
                    OS.CreateCaret(lpgui1.hwndCaret, 0, width, height);
                    OS.SetCaretPos(lpgui1.left, lpgui1.top);
                    OS.ShowCaret(lpgui1.hwndCaret);
                }
            }
        } else if (lpgui1.hwndFocus != 0 && lpgui1.hwndFocus == OS.GetFocus()) {
            OS.SendMessage(lpgui1.hwndFocus, SWT_RESTORECARET, 0, 0);
        }
    }

    private int OnFocus(int fGotFocus) {
        return COM.S_OK;
    }

    @Override
    protected int QueryInterface(long riid, long ppvObject) {
        int result = super.QueryInterface(riid, ppvObject);
        if (result == COM.S_OK)
            return result;
        if (riid == 0 || ppvObject == 0)
            return COM.E_INVALIDARG;
        GUID guid = new GUID();
        COM.MoveMemory(guid, riid, GUID.sizeof);
        if (COM.IsEqualGUID(guid, COM.IIDIOleControlSite)) {
            OS.MoveMemory(ppvObject, new long[] { iOleControlSite.getAddress() }, C.PTR_SIZEOF);
            AddRef();
            return COM.S_OK;
        }
        if (COM.IsEqualGUID(guid, COM.IIDIDispatch)) {
            OS.MoveMemory(ppvObject, new long[] { iDispatch.getAddress() }, C.PTR_SIZEOF);
            AddRef();
            return COM.S_OK;
        }
        OS.MoveMemory(ppvObject, new long[] { 0 }, C.PTR_SIZEOF);
        return COM.E_NOINTERFACE;
    }

    @Override
    protected int Release() {
        int result = super.Release();
        if (result == 0) {
            for (int i = 0; i < sitePropertyIds.length; i++) {
                sitePropertyValues[i].dispose();
            }
            sitePropertyIds = new int[0];
            sitePropertyValues = new Variant[0];
        }
        return result;
    }

    @Override
    protected void releaseObjectInterfaces() {

        disconnectEventSinks();

        disconnectPropertyChangeSink();

        super.releaseObjectInterfaces();
    }

    /**
     * Removes the listener.
     *
     * @param eventID the event identifier
     *
     * @param listener the listener which should no longer be notified
     *
     * @exception IllegalArgumentException <ul>
     *       <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    public void removeEventListener(int eventID, OleListener listener) {
        checkWidget();
        if (listener == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);

        GUID riid = getDefaultEventSinkGUID(objIUnknown);
        if (riid != null) {
            removeEventListener(objIUnknown.getAddress(), riid, eventID, listener);
        }
    }

    /**
     * Removes the listener.
     *
     * @since 2.0
     * @deprecated - use OleControlSite.removeEventListener(OleAutomation, int, OleListener)
     *
     * @param automation the automation object that provides the event notification
     *
     * @param guid the identifier of the events COM interface
     *
     * @param eventID the event identifier
     *
     * @param listener the listener
     *
     * @exception IllegalArgumentException <ul>
     *       <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    @Deprecated
    public void removeEventListener(OleAutomation automation, GUID guid, int eventID, OleListener listener) {
        checkWidget();
        if (automation == null || listener == null || guid == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        removeEventListener(automation.getAddress(), guid, eventID, listener);
    }

    /**
     * Removes the listener.
     *
     * @param automation the automation object that provides the event notification
     * @param eventID the event identifier
     * @param listener the listener which should no longer be notified
     *
     * @exception IllegalArgumentException <ul>
     *       <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     *
     * @since 2.0
     */
    public void removeEventListener(OleAutomation automation, int eventID, OleListener listener) {
        checkWidget();
        if (automation == null || listener == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        long address = automation.getAddress();
        IUnknown unknown = new IUnknown(address);
        GUID riid = getDefaultEventSinkGUID(unknown);
        if (riid != null) {
            removeEventListener(address, riid, eventID, listener);
        }
    }

    void removeEventListener(long iunknown, GUID guid, int eventID, OleListener listener) {
        if (listener == null || guid == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        for (int i = 0; i < oleEventSink.length; i++) {
            if (COM.IsEqualGUID(oleEventSinkGUID[i], guid)) {
                if (iunknown == oleEventSinkIUnknown[i]) {
                    oleEventSink[i].removeListener(eventID, listener);
                    if (!oleEventSink[i].hasListeners()) {
                        //free resources associated with event sink
                        oleEventSink[i].disconnect();
                        oleEventSink[i].Release();
                        int oldLength = oleEventSink.length;
                        if (oldLength == 1) {
                            oleEventSink = new OleEventSink[0];
                            oleEventSinkGUID = new GUID[0];
                            oleEventSinkIUnknown = new long[0];
                        } else {
                            OleEventSink[] newOleEventSink = new OleEventSink[oldLength - 1];
                            System.arraycopy(oleEventSink, 0, newOleEventSink, 0, i);
                            System.arraycopy(oleEventSink, i + 1, newOleEventSink, i, oldLength - i - 1);
                            oleEventSink = newOleEventSink;

                            GUID[] newOleEventSinkGUID = new GUID[oldLength - 1];
                            System.arraycopy(oleEventSinkGUID, 0, newOleEventSinkGUID, 0, i);
                            System.arraycopy(oleEventSinkGUID, i + 1, newOleEventSinkGUID, i, oldLength - i - 1);
                            oleEventSinkGUID = newOleEventSinkGUID;

                            long[] newOleEventSinkIUnknown = new long[oldLength - 1];
                            System.arraycopy(oleEventSinkIUnknown, 0, newOleEventSinkIUnknown, 0, i);
                            System.arraycopy(oleEventSinkIUnknown, i + 1, newOleEventSinkIUnknown, i,
                                    oldLength - i - 1);
                            oleEventSinkIUnknown = newOleEventSinkIUnknown;
                        }
                    }
                    return;
                }
            }
        }
    }

    /**
     * Removes the listener.
     *
     * @param propertyID the identifier of the property
     * @param listener the listener which should no longer be notified
     *
     * @exception IllegalArgumentException <ul>
     *       <li>ERROR_NULL_ARGUMENT when listener is null</li>
     * </ul>
     */
    public void removePropertyListener(int propertyID, OleListener listener) {
        if (listener == null)
            SWT.error(SWT.ERROR_NULL_ARGUMENT);
        olePropertyChangeSink.removeListener(propertyID, listener);
    }

    @Override
    public void setBackground(Color color) {

        super.setBackground(color);

        //set the background of the ActiveX Control
        if (objIUnknown != null) {
            OleAutomation oleObject = new OleAutomation(this);
            oleObject.setProperty(COM.DISPID_BACKCOLOR, new Variant(color.handle));
            oleObject.dispose();
        }
    }

    @Override
    public void setFont(Font font) {

        super.setFont(font);

        //set the font of the ActiveX Control
        if (objIUnknown != null) {

            OleAutomation oleObject = new OleAutomation(this);
            Variant varDispFont = oleObject.getProperty(COM.DISPID_FONT);
            oleObject.dispose();

            if (varDispFont != null) {
                OleAutomation iDispFont = varDispFont.getAutomation();
                FontData[] fdata = font.getFontData();
                iDispFont.setProperty(COM.DISPID_FONT_NAME, new Variant(fdata[0].getName()));
                iDispFont.setProperty(COM.DISPID_FONT_SIZE, new Variant(fdata[0].getHeight()));
                iDispFont.setProperty(COM.DISPID_FONT_ITALIC, new Variant(fdata[0].getStyle() & SWT.ITALIC));
                //iDispFont.setProperty(COM.DISPID_FONT_CHARSET, new Variant(fdata[0].getCharset));
                iDispFont.setProperty(COM.DISPID_FONT_BOLD, new Variant((fdata[0].getStyle() & SWT.BOLD)));
                iDispFont.dispose();
            }
        }
        this.font = font;
        return;
    }

    @Override
    public void setForeground(Color color) {

        super.setForeground(color);

        //set the foreground of the ActiveX Control
        if (objIUnknown != null) {
            OleAutomation oleObject = new OleAutomation(this);
            oleObject.setProperty(COM.DISPID_FORECOLOR, new Variant(color.handle));
            oleObject.dispose();
        }
    }

    /**
     * Sets the control site property specified by the dispIdMember to a new value.
     * The value will be disposed by the control site when it is no longer required
     * using Variant.dispose.  Passing a value of null will clear the dispId value.
     *
     * @param dispId the ID of the property as specified by the IDL of the ActiveX Control
     * @param value The new value for the property as expressed in a Variant.
     *
     * @since 2.1
     */
    public void setSiteProperty(int dispId, Variant value) {
        for (int i = 0; i < sitePropertyIds.length; i++) {
            if (sitePropertyIds[i] == dispId) {
                if (sitePropertyValues[i] != null) {
                    sitePropertyValues[i].dispose();
                }
                if (value != null) {
                    sitePropertyValues[i] = value;
                } else {
                    int oldLength = sitePropertyIds.length;
                    int[] newSitePropertyIds = new int[oldLength - 1];
                    Variant[] newSitePropertyValues = new Variant[oldLength - 1];
                    System.arraycopy(sitePropertyIds, 0, newSitePropertyIds, 0, i);
                    System.arraycopy(sitePropertyIds, i + 1, newSitePropertyIds, i, oldLength - i - 1);
                    System.arraycopy(sitePropertyValues, 0, newSitePropertyValues, 0, i);
                    System.arraycopy(sitePropertyValues, i + 1, newSitePropertyValues, i, oldLength - i - 1);
                    sitePropertyIds = newSitePropertyIds;
                    sitePropertyValues = newSitePropertyValues;
                }
                return;
            }
        }
        int oldLength = sitePropertyIds.length;
        int[] newSitePropertyIds = new int[oldLength + 1];
        Variant[] newSitePropertyValues = new Variant[oldLength + 1];
        System.arraycopy(sitePropertyIds, 0, newSitePropertyIds, 0, oldLength);
        System.arraycopy(sitePropertyValues, 0, newSitePropertyValues, 0, oldLength);
        newSitePropertyIds[oldLength] = dispId;
        newSitePropertyValues[oldLength] = value;
        sitePropertyIds = newSitePropertyIds;
        sitePropertyValues = newSitePropertyValues;
    }
}