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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.ole.win32;

import org.eclipse.swt.*;
import org.eclipse.swt.internal.ole.win32.*;
import org.eclipse.swt.internal.win32.*;

/**
 * OleAutomation provides a generic mechanism for accessing functionality that is 
 * specific to a particular ActiveX Control or OLE Document.
 *
 * <p>The OLE Document or ActiveX Control must support the IDispatch interface in order to provide
 * OleAutomation support. The additional functionality provided by the OLE Object is specified in 
 * its IDL file.  The additional methods can either be to get property values (<code>getProperty</code>), 
 * to set property values (<code>setProperty</code>) or to invoke a method (<code>invoke</code> or
 * <code>invokeNoReply</code>).  Arguments are passed around in the form of <code>Variant</code> 
 * objects.
 *
 * <p>Here is a sample IDL fragment:
 *
 * <pre>
 *   interface IMyControl : IDispatch
 *   {
 *      [propget, id(0)] HRESULT maxFileCount([retval, out] int *c);
 *      [propput, id(0)] HRESULT maxFileCount([in] int c);
 *      [id(1)]   HRESULT AddFile([in] BSTR fileName);
 *   };
 * </pre>
 *
 * <p>An example of how to interact with this extended functionality is shown below:
 *
 * <code><pre>
 *   OleAutomation automation = new OleAutomation(myControlSite);
 *
 *   // Look up the ID of the maxFileCount parameter
 *   int[] rgdispid = automation.getIDsOfNames(new String[]{"maxFileCount"});
 *   int maxFileCountID = rgdispid[0];
 *
 *   // Set the property maxFileCount to 100:
 *   if (automation.setProperty(maxFileCountID, new Variant(100))) {
 *      System.out.println("Max File Count was successfully set.");
 *   }
 *
 *   // Get the new value of the maxFileCount parameter:
 *   Variant pVarResult = automation.getProperty(maxFileCountID);
 *   if (pVarResult != null) {
 *      System.out.println("Max File Count is "+pVarResult.getInt());
 *   }
 *
 *   // Invoke the AddFile method
 *   // Look up the IDs of the AddFile method and its parameter
 *   rgdispid = automation.getIDsOfNames(new String[]{"AddFile", "fileName"}); 
 *   int dispIdMember = rgdispid[0];
 *   int[] rgdispidNamedArgs = new int[] {rgdispid[1]};
 *
 *   // Convert arguments to Variant objects
 *   Variant[] rgvarg = new Variant[1];
 *   String fileName = "C:\\testfile";
 *    rgvarg[0] = new Variant(fileName);
 *
 *   // Call the method
 *   Variant pVarResult = automation.invoke(dispIdMember, rgvarg, rgdispidNamedArgs);
 *
 *   // Check the return value
 *    if (pVarResult == null || pVarResult.getInt() != OLE.S_OK){
 *       System.out.println("Failed to add file "+fileName);
 *   }
 *
 *   automation.dispose();
 *
 * </pre></code>
 * 
 * @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 final class OleAutomation {
    private IUnknown objIUnknown;
    private IDispatch objIDispatch;
    private String exceptionDescription;
    private ITypeInfo objITypeInfo;

    OleAutomation(IDispatch idispatch) {
        if (idispatch == null)
            OLE.error(OLE.ERROR_INVALID_INTERFACE_ADDRESS);
        objIDispatch = idispatch;
        objIDispatch.AddRef();

        long /*int*/[] ppv = new long /*int*/[1];
        /* GetTypeInfo([in] iTInfo, [in] lcid, [out] ppTInfo) 
         * AddRef has already been called on ppTInfo by the callee and must be released by the caller. 
         */
        int result = objIDispatch.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, ppv);
        if (result == OLE.S_OK) {
            objITypeInfo = new ITypeInfo(ppv[0]);
        }
    }

    /**
     * Creates an OleAutomation object for the specified client.
     *
     * @param clientSite the site for the OLE Document or ActiveX Control whose additional functionality 
     *        you need to access
     *
     * @exception IllegalArgumentException <ul>
     *      <li>ERROR_INVALID_INTERFACE_ADDRESS when called with an invalid client site
     *   </ul>
     */
    public OleAutomation(OleClientSite clientSite) {
        if (clientSite == null)
            OLE.error(OLE.ERROR_INVALID_INTERFACE_ADDRESS);
        objIDispatch = clientSite.getAutomationObject();

        long /*int*/[] ppv = new long /*int*/[1];
        /* GetTypeInfo([in] iTInfo, [in] lcid, [out] ppTInfo) 
         * AddRef has already been called on ppTInfo by the callee and must be released by the caller. 
         */
        int result = objIDispatch.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, ppv);
        if (result == OLE.S_OK) {
            objITypeInfo = new ITypeInfo(ppv[0]);
        }
    }

    /**
     * Creates an OleAutomation object for the specified <code>progID</code>.
     *
     * @param progId the unique program identifier of an 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)
     *               
     * @exception SWTException
     * <ul><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_INTERFACE_NOT_FOUND when the OLE object specified does not implement IDispatch
     * </ul>
     * 
     * @since 3.6
     */
    public OleAutomation(String progId) {
        try {
            OS.OleInitialize(0);
            GUID appClsid = getClassID(progId);
            if (appClsid == null) {
                OS.OleUninitialize();
                OLE.error(OLE.ERROR_INVALID_CLASSID);
            }
            int flags = COM.CLSCTX_INPROC_SERVER;
            if (progId.startsWith("Excel")) //$NON-NLS-1$
                flags |= COM.CLSCTX_LOCAL_SERVER;
            long /*int*/[] ppvObject = new long /*int*/[1];
            int result = COM.CoCreateInstance(appClsid, 0, flags, COM.IIDIUnknown, ppvObject);
            if (result != COM.S_OK) {
                OS.OleUninitialize();
                OLE.error(OLE.ERROR_CANNOT_CREATE_OBJECT, result);
            }
            objIUnknown = new IUnknown(ppvObject[0]);

            ppvObject[0] = 0;
            result = objIUnknown.QueryInterface(COM.IIDIDispatch, ppvObject);
            if (result != COM.S_OK)
                OLE.error(OLE.ERROR_INTERFACE_NOT_FOUND);
            objIDispatch = new IDispatch(ppvObject[0]);

            ppvObject[0] = 0;
            result = objIDispatch.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, ppvObject);
            if (result == OLE.S_OK) {
                objITypeInfo = new ITypeInfo(ppvObject[0]);
            }
        } catch (SWTException e) {
            dispose();
            throw e;
        }
    }

    /**
     * Disposes the automation object.
     * <p>
     * This method releases the IDispatch interface on the OLE Document or ActiveX Control.
     * Do not use the OleAutomation object after it has been disposed.
     */
    public void dispose() {

        if (objIDispatch != null) {
            objIDispatch.Release();
        }
        objIDispatch = null;

        if (objITypeInfo != null) {
            objITypeInfo.Release();
        }
        objITypeInfo = null;

        if (objIUnknown != null) {
            objIUnknown.Release();
            OS.OleUninitialize();
        }
        objIUnknown = null;
    }

    long /*int*/ getAddress() {
        return objIDispatch.getAddress();
    }

    GUID getClassID(String clientName) {
        // create a GUID struct to hold the result
        GUID guid = new GUID();

        // create a null terminated array of char
        char[] buffer = null;
        if (clientName != null) {
            int count = clientName.length();
            buffer = new char[count + 1];
            clientName.getChars(0, count, buffer, 0);
        }
        if (COM.CLSIDFromProgID(buffer, guid) != COM.S_OK) {
            int result = COM.CLSIDFromString(buffer, guid);
            if (result != COM.S_OK)
                return null;
        }
        return guid;
    }

    /**
     * Returns the fully qualified name of the Help file for the given member ID.
     * 
     * @param dispId the member ID whose Help file is being retrieved.
     * @return a string representing the fully qualified name of a Help 
     * file or null.
     */
    public String getHelpFile(int dispId) {
        if (objITypeInfo == null)
            return null;
        String[] file = new String[1];
        int rc = objITypeInfo.GetDocumentation(dispId, null, null, null, file);
        if (rc == OLE.S_OK)
            return file[0];
        return null;
    }

    /**
     * Returns the documentation string for the given member ID.
     * 
     * @param dispId the member ID in which the documentation is being retrieved.
     * @return the documentation string if it exists; otherwise return null.
     */
    public String getDocumentation(int dispId) {
        if (objITypeInfo == null)
            return null;
        String[] doc = new String[1];
        int rc = objITypeInfo.GetDocumentation(dispId, null, doc, null, null);
        if (rc == OLE.S_OK)
            return doc[0];
        return null;
    }

    /**
     * Returns the property description of a variable at the given index.
     * 
     * @param index the index of a variable whose property is being retrieved.
     * @return an OlePropertyDescription for a variable at the given index.
     */
    public OlePropertyDescription getPropertyDescription(int index) {
        if (objITypeInfo == null)
            return null;
        long /*int*/[] ppVarDesc = new long /*int*/[1];
        int rc = objITypeInfo.GetVarDesc(index, ppVarDesc);
        if (rc != OLE.S_OK)
            return null;
        VARDESC vardesc = new VARDESC();
        COM.MoveMemory(vardesc, ppVarDesc[0], VARDESC.sizeof);

        OlePropertyDescription data = new OlePropertyDescription();
        data.id = vardesc.memid;
        data.name = getName(vardesc.memid);
        data.type = vardesc.elemdescVar_tdesc_vt;
        if (data.type == OLE.VT_PTR) {
            short[] vt = new short[1];
            COM.MoveMemory(vt, vardesc.elemdescVar_tdesc_union + OS.PTR_SIZEOF, 2);
            data.type = vt[0];
        }
        data.flags = vardesc.wVarFlags;
        data.kind = vardesc.varkind;
        data.description = getDocumentation(vardesc.memid);
        data.helpFile = getHelpFile(vardesc.memid);

        objITypeInfo.ReleaseVarDesc(ppVarDesc[0]);
        return data;
    }

    /**
     * Returns the description of a function at the given index.
     * 
     * @param index the index of a function whose property is being retrieved.
     * @return an OleFunctionDescription for a function at the given index.
     */
    public OleFunctionDescription getFunctionDescription(int index) {
        if (objITypeInfo == null)
            return null;
        long /*int*/[] ppFuncDesc = new long /*int*/[1];
        int rc = objITypeInfo.GetFuncDesc(index, ppFuncDesc);
        if (rc != OLE.S_OK)
            return null;
        FUNCDESC funcdesc = new FUNCDESC();
        COM.MoveMemory(funcdesc, ppFuncDesc[0], FUNCDESC.sizeof);

        OleFunctionDescription data = new OleFunctionDescription();

        data.id = funcdesc.memid;
        data.optionalArgCount = funcdesc.cParamsOpt;
        data.invokeKind = funcdesc.invkind;
        data.funcKind = funcdesc.funckind;
        data.flags = funcdesc.wFuncFlags;
        data.callingConvention = funcdesc.callconv;
        data.documentation = getDocumentation(funcdesc.memid);
        data.helpFile = getHelpFile(funcdesc.memid);

        String[] names = getNames(funcdesc.memid, funcdesc.cParams + 1);
        if (names.length > 0) {
            data.name = names[0];
        }
        data.args = new OleParameterDescription[funcdesc.cParams];
        for (int i = 0; i < data.args.length; i++) {
            data.args[i] = new OleParameterDescription();
            if (names.length > i + 1) {
                data.args[i].name = names[i + 1];
            }
            //TODO 0- use structures
            short[] vt = new short[1];
            COM.MoveMemory(vt, funcdesc.lprgelemdescParam + i * COM.ELEMDESC_sizeof() + OS.PTR_SIZEOF, 2);
            if (vt[0] == OLE.VT_PTR) {
                long /*int*/ [] pTypedesc = new long /*int*/ [1];
                COM.MoveMemory(pTypedesc, funcdesc.lprgelemdescParam + i * COM.ELEMDESC_sizeof(), OS.PTR_SIZEOF);
                short[] vt2 = new short[1];
                COM.MoveMemory(vt2, pTypedesc[0] + OS.PTR_SIZEOF, 2);
                vt[0] = (short) (vt2[0] | COM.VT_BYREF);
            }
            data.args[i].type = vt[0];
            short[] wParamFlags = new short[1];
            COM.MoveMemory(wParamFlags,
                    funcdesc.lprgelemdescParam + i * COM.ELEMDESC_sizeof() + COM.TYPEDESC_sizeof() + OS.PTR_SIZEOF,
                    2);
            data.args[i].flags = wParamFlags[0];
        }

        data.returnType = funcdesc.elemdescFunc_tdesc_vt;
        if (data.returnType == OLE.VT_PTR) {
            short[] vt = new short[1];
            COM.MoveMemory(vt, funcdesc.elemdescFunc_tdesc_union + OS.PTR_SIZEOF, 2);
            data.returnType = vt[0];
        }

        objITypeInfo.ReleaseFuncDesc(ppFuncDesc[0]);
        return data;
    }

    /**
     * Returns the type info of the current object referenced by the automation.
     * The type info contains information about the object such as the function descriptions,
     * the member descriptions and attributes of the type.
     * 
     * @return the type info of the receiver
     */
    public TYPEATTR getTypeInfoAttributes() {
        if (objITypeInfo == null)
            return null;
        long /*int*/ [] ppTypeAttr = new long /*int*/ [1];
        int rc = objITypeInfo.GetTypeAttr(ppTypeAttr);
        if (rc != OLE.S_OK)
            return null;
        TYPEATTR typeattr = new TYPEATTR();
        COM.MoveMemory(typeattr, ppTypeAttr[0], TYPEATTR.sizeof);
        objITypeInfo.ReleaseTypeAttr(ppTypeAttr[0]);
        return typeattr;
    }

    /**
     * Returns the name of the given member ID.
     * 
     * @param dispId the member ID in which the name is being retrieved.
     * @return the name if it exists; otherwise return null.
     */
    public String getName(int dispId) {
        if (objITypeInfo == null)
            return null;
        String[] name = new String[1];
        int rc = objITypeInfo.GetDocumentation(dispId, name, null, null, null);
        if (rc == OLE.S_OK)
            return name[0];
        return null;
    }

    /**
     * Returns the name of a function and parameter names for the specified function ID.
     * 
     * @param dispId the function ID in which the name and parameters are being retrieved.
     * @param maxSize the maximum number of names to retrieve.
     * @return an array of name containing the function name and the parameter names
     */
    public String[] getNames(int dispId, int maxSize) {
        if (objITypeInfo == null)
            return new String[0];
        String[] names = new String[maxSize];
        int[] count = new int[1];
        int rc = objITypeInfo.GetNames(dispId, names, maxSize, count);
        if (rc == OLE.S_OK) {
            String[] newNames = new String[count[0]];
            System.arraycopy(names, 0, newNames, 0, count[0]);
            return newNames;
        }
        return new String[0];
    }

    /**
     * Returns the positive integer values (IDs) that are associated with the specified names by the
     * IDispatch implementor.  If you are trying to get the names of the parameters in a method, the first 
     * String in the names array must be the name of the method followed by the names of the parameters.
     *
     * @param names an array of names for which you require the identifiers
     *
     * @return positive integer values that are associated with the specified names in the same
     *         order as the names where provided; or null if the names are unknown
     */
    public int[] getIDsOfNames(String[] names) {

        int[] rgdispid = new int[names.length];
        int result = objIDispatch.GetIDsOfNames(new GUID(), names, names.length, COM.LOCALE_USER_DEFAULT, rgdispid);
        if (result != COM.S_OK)
            return null;

        return rgdispid;
    }

    /**
     * Returns a description of the last error encountered.
     *
     * @return a description of the last error encountered
     */
    public String getLastError() {

        return exceptionDescription;

    }

    /**
     * Returns the value of the property specified by the dispIdMember.
     *
     * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @return the value of the property specified by the dispIdMember or null
     */
    public Variant getProperty(int dispIdMember) {
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, COM.DISPATCH_PROPERTYGET, null, null, pVarResult);
        return (result == OLE.S_OK) ? pVarResult : null;
    }

    /**
     * Returns the value of the property specified by the dispIdMember.
     *
     * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *        read only unless the Variant is a By Reference Variant type.
     * 
     * @return the value of the property specified by the dispIdMember or null
     * 
     * @since 2.0
     */
    public Variant getProperty(int dispIdMember, Variant[] rgvarg) {
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, COM.DISPATCH_PROPERTYGET, rgvarg, null, pVarResult);
        return (result == OLE.S_OK) ? pVarResult : null;

    }

    /**
     * Returns the value of the property specified by the dispIdMember.
     *
     * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *        read only unless the Variant is a By Reference Variant type.
     * 
     * @param rgdispidNamedArgs an array of identifiers for the arguments specified in rgvarg; the
     *        parameter IDs must be in the same order as their corresponding values;
     *        all arguments must have an identifier - identifiers can be obtained using 
     *        OleAutomation.getIDsOfNames
     * 
     * @return the value of the property specified by the dispIdMember or null
     * 
     * @since 2.0
     */
    public Variant getProperty(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs) {
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, COM.DISPATCH_PROPERTYGET, rgvarg, rgdispidNamedArgs, pVarResult);
        return (result == OLE.S_OK) ? pVarResult : null;
    }

    public boolean equals(Object object) {
        if (object == this)
            return true;
        if (object instanceof OleAutomation) {
            if (objIDispatch == null)
                return false;
            OleAutomation oleAutomation = ((OleAutomation) object);
            if (oleAutomation.objIDispatch == null)
                return false;
            long /*int*/ address1 = objIDispatch.getAddress();
            long /*int*/ address2 = oleAutomation.objIDispatch.getAddress();
            return address1 == address2;
        }
        return false;
    }

    /** 
     * Invokes a method on the OLE Object; the method has no parameters.
     *
     * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @return the result of the method or null if the method failed to give result information
     */
    public Variant invoke(int dispIdMember) {
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, COM.DISPATCH_METHOD, null, null, pVarResult);
        return (result == COM.S_OK) ? pVarResult : null;
    }

    /** 
     * Invokes a method on the OLE Object; the method has no optional parameters.
     *
     * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *        read only unless the Variant is a By Reference Variant type.
     *
     * @return the result of the method or null if the method failed to give result information
     */
    public Variant invoke(int dispIdMember, Variant[] rgvarg) {
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, null, pVarResult);
        return (result == COM.S_OK) ? pVarResult : null;
    }

    /** 
     * Invokes a method on the OLE Object; the method has optional parameters.  It is not
     * necessary to specify all the optional parameters, only include the parameters for which
     * you are providing values.
     *
     * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *        read only unless the Variant is a By Reference Variant type.
     *
     * @param rgdispidNamedArgs an array of identifiers for the arguments specified in rgvarg; the
     *        parameter IDs must be in the same order as their corresponding values;
     *        all arguments must have an identifier - identifiers can be obtained using 
     *        OleAutomation.getIDsOfNames
     *
     * @return the result of the method or null if the method failed to give result information
     */
    public Variant invoke(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs) {
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, rgdispidNamedArgs, pVarResult);
        return (result == COM.S_OK) ? pVarResult : null;
    }

    private int invoke(int dispIdMember, int wFlags, Variant[] rgvarg, int[] rgdispidNamedArgs,
            Variant pVarResult) {

        // get the IDispatch interface for the control
        if (objIDispatch == null)
            return COM.E_FAIL;

        // create a DISPPARAMS structure for the input parameters
        DISPPARAMS pDispParams = new DISPPARAMS();
        // store arguments in rgvarg
        if (rgvarg != null && rgvarg.length > 0) {
            pDispParams.cArgs = rgvarg.length;
            pDispParams.rgvarg = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, VARIANT.sizeof * rgvarg.length);
            int offset = 0;
            for (int i = rgvarg.length - 1; i >= 0; i--) {
                rgvarg[i].getData(pDispParams.rgvarg + offset);
                offset += VARIANT.sizeof;
            }
        }

        // if arguments have ids, store the ids in rgdispidNamedArgs
        if (rgdispidNamedArgs != null && rgdispidNamedArgs.length > 0) {
            pDispParams.cNamedArgs = rgdispidNamedArgs.length;
            pDispParams.rgdispidNamedArgs = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT,
                    4 * rgdispidNamedArgs.length);
            int offset = 0;
            for (int i = rgdispidNamedArgs.length; i > 0; i--) {
                COM.MoveMemory(pDispParams.rgdispidNamedArgs + offset, new int[] { rgdispidNamedArgs[i - 1] }, 4);
                offset += 4;
            }
        }

        // invoke the method
        EXCEPINFO excepInfo = new EXCEPINFO();
        int[] pArgErr = new int[1];
        long /*int*/ pVarResultAddress = 0;
        if (pVarResult != null)
            pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, VARIANT.sizeof);
        int result = objIDispatch.Invoke(dispIdMember, new GUID(), COM.LOCALE_USER_DEFAULT, wFlags, pDispParams,
                pVarResultAddress, excepInfo, pArgErr);

        if (pVarResultAddress != 0) {
            pVarResult.setData(pVarResultAddress);
            COM.VariantClear(pVarResultAddress);
            OS.GlobalFree(pVarResultAddress);
        }

        // free the Dispparams resources
        if (pDispParams.rgdispidNamedArgs != 0) {
            OS.GlobalFree(pDispParams.rgdispidNamedArgs);
        }
        if (pDispParams.rgvarg != 0) {
            int offset = 0;
            for (int i = 0, length = rgvarg.length; i < length; i++) {
                COM.VariantClear(pDispParams.rgvarg + offset);
                offset += VARIANT.sizeof;
            }
            OS.GlobalFree(pDispParams.rgvarg);
        }

        // save error string and cleanup EXCEPINFO
        manageExcepinfo(result, excepInfo);

        return result;
    }

    /** 
     * Invokes a method on the OLE Object; the method has no parameters.  In the early days of OLE, 
     * the IDispatch interface was not well defined and some applications (mainly Word) did not support 
     * a return value.  For these applications, call this method instead of calling
     * <code>public void invoke(int dispIdMember)</code>.
     *
     * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @exception org.eclipse.swt.SWTException <ul>
     *      <li>ERROR_ACTION_NOT_PERFORMED when method invocation fails
     *   </ul>
     */
    public void invokeNoReply(int dispIdMember) {
        int result = invoke(dispIdMember, COM.DISPATCH_METHOD, null, null, null);
        if (result != COM.S_OK)
            OLE.error(OLE.ERROR_ACTION_NOT_PERFORMED, result);
    }

    /** 
     * Invokes a method on the OLE Object; the method has no optional parameters.  In the early days of OLE, 
     * the IDispatch interface was not well defined and some applications (mainly Word) did not support 
     * a return value.  For these applications, call this method instead of calling
     * <code>public void invoke(int dispIdMember, Variant[] rgvarg)</code>.
     *
     * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *        read only unless the Variant is a By Reference Variant type.
     *
     * @exception org.eclipse.swt.SWTException <ul>
     *      <li>ERROR_ACTION_NOT_PERFORMED when method invocation fails
     *   </ul>
     */
    public void invokeNoReply(int dispIdMember, Variant[] rgvarg) {
        int result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, null, null);
        if (result != COM.S_OK)
            OLE.error(OLE.ERROR_ACTION_NOT_PERFORMED, result);
    }

    /** 
     * Invokes a method on the OLE Object; the method has optional parameters.  It is not
     * necessary to specify all the optional parameters, only include the parameters for which
     * you are providing values.  In the early days of OLE, the IDispatch interface was not well 
     * defined and some applications (mainly Word) did not support a return value.  For these 
     * applications, call this method instead of calling
     * <code>public void invoke(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs)</code>.
     *
     * @param dispIdMember the ID of the method as specified by the IDL of the ActiveX Control; the
     *        value for the ID can be obtained using OleAutomation.getIDsOfNames
     *
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *        read only unless the Variant is a By Reference Variant type.
     *
     * @param rgdispidNamedArgs an array of identifiers for the arguments specified in rgvarg; the
     *        parameter IDs must be in the same order as their corresponding values;
     *        all arguments must have an identifier - identifiers can be obtained using 
     *        OleAutomation.getIDsOfNames
     *
     * @exception org.eclipse.swt.SWTException <ul>
     *      <li>ERROR_ACTION_NOT_PERFORMED when method invocation fails
     *   </ul>
     */
    public void invokeNoReply(int dispIdMember, Variant[] rgvarg, int[] rgdispidNamedArgs) {
        int result = invoke(dispIdMember, COM.DISPATCH_METHOD, rgvarg, rgdispidNamedArgs, null);
        if (result != COM.S_OK)
            OLE.error(OLE.ERROR_ACTION_NOT_PERFORMED, result);
    }

    private void manageExcepinfo(int hResult, EXCEPINFO excepInfo) {

        if (hResult == COM.S_OK) {
            exceptionDescription = "No Error"; //$NON-NLS-1$
            return;
        }

        // extract exception info
        if (hResult == COM.DISP_E_EXCEPTION) {
            if (excepInfo.bstrDescription != 0) {
                int size = COM.SysStringByteLen(excepInfo.bstrDescription);
                char[] buffer = new char[(size + 1) / 2];
                COM.MoveMemory(buffer, excepInfo.bstrDescription, size);
                exceptionDescription = new String(buffer);
            } else {
                exceptionDescription = "OLE Automation Error Exception "; //$NON-NLS-1$
                if (excepInfo.wCode != 0) {
                    exceptionDescription += "code = " + excepInfo.wCode; //$NON-NLS-1$
                } else if (excepInfo.scode != 0) {
                    exceptionDescription += "code = " + excepInfo.scode; //$NON-NLS-1$
                }
            }
        } else {
            exceptionDescription = "OLE Automation Error HResult : " + hResult; //$NON-NLS-1$
        }

        // cleanup EXCEPINFO struct
        if (excepInfo.bstrDescription != 0)
            COM.SysFreeString(excepInfo.bstrDescription);
        if (excepInfo.bstrHelpFile != 0)
            COM.SysFreeString(excepInfo.bstrHelpFile);
        if (excepInfo.bstrSource != 0)
            COM.SysFreeString(excepInfo.bstrSource);
    }

    /**
     * Sets the property specified by the dispIdMember to a new value.
     *
     * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
     *                     value for the ID can be obtained using OleAutomation.getIDsOfNames
     * @param rgvarg the new value of the property
     *
     * @return true if the operation was successful
     */
    public boolean setProperty(int dispIdMember, Variant rgvarg) {
        Variant[] rgvarg2 = new Variant[] { rgvarg };
        int[] rgdispidNamedArgs = new int[] { COM.DISPID_PROPERTYPUT };
        int dwFlags = COM.DISPATCH_PROPERTYPUT;
        if ((rgvarg.getType() & COM.VT_BYREF) == COM.VT_BYREF)
            dwFlags = COM.DISPATCH_PROPERTYPUTREF;
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, dwFlags, rgvarg2, rgdispidNamedArgs, pVarResult);
        return (result == COM.S_OK);
    }

    /**
     * Sets the property specified by the dispIdMember to a new value.
     *
     * @param dispIdMember the ID of the property as specified by the IDL of the ActiveX Control; the
     *                     value for the ID can be obtained using OleAutomation.getIDsOfNames
     * @param rgvarg an array of arguments for the method.  All arguments are considered to be
     *                     read only unless the Variant is a By Reference Variant type.
     *
     * @return true if the operation was successful
     *
     * @since 2.0
     */
    public boolean setProperty(int dispIdMember, Variant[] rgvarg) {
        int[] rgdispidNamedArgs = new int[] { COM.DISPID_PROPERTYPUT };
        int dwFlags = COM.DISPATCH_PROPERTYPUT;
        for (int i = 0; i < rgvarg.length; i++) {
            if ((rgvarg[i].getType() & COM.VT_BYREF) == COM.VT_BYREF)
                dwFlags = COM.DISPATCH_PROPERTYPUTREF;
        }
        Variant pVarResult = new Variant();
        int result = invoke(dispIdMember, dwFlags, rgvarg, rgdispidNamedArgs, pVarResult);
        return (result == COM.S_OK);
    }
}