在SWT中提供了访问OLE的方式,不过相关的例子都是进程内OLE的例子,比如嵌入浏览器、引用ActiveX控件什么的。由于客户的需求,我们需要在程序中通过进程外Automation服务的方式访问IE浏览器。经过网上搜寻,找到一个朋友写的CComObject类可以访问Automation服务。不过经过使用发现一些错误,因此对其进行了修改,为了方便访问IE,我又封装了一个IEAutomation类,这样就暴露给使用者navigate、quit这样的方法。(作者:www.cownew.com 杨中科)
这里例子完全可以用来操控其他的Automation对象,比如操控Work、Excel、AcrobatReader等。
全部代码如下:
CComObject.java
package test;


import org.eclipse.swt.SWT;

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

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

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

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

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

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

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

import org.eclipse.swt.internal.win32.OS;

import org.eclipse.swt.ole.win32.OLE;

import org.eclipse.swt.ole.win32.Variant;


/** *//**

* @author hcw

*/

public class CComObject



{

private GUID guid = new GUID();


private IDispatch Autoface = null;


private ITypeInfo TypeInfo = null;


private void CreateComObject()



{

dispose();


int[] ppv = new int[1];


int ret = COM.CoCreateInstance(guid, 0, COM.CLSCTX_INPROC_SERVER

| COM.CLSCTX_LOCAL_SERVER, COM.IIDIUnknown, ppv);

if (ret != COM.S_OK)

OLE.error(ret);

// throw new RuntimeException("对象创建失败!");


IUnknown objIUnknown = new IUnknown(ppv[0]);

ppv = new int[1];


ret = objIUnknown.QueryInterface(COM.IIDIDispatch, ppv);

objIUnknown.Release();

if (ret != COM.S_OK)

OLE.error(ret);

// throw new RuntimeException("对象不支持Dispatch!");


Autoface = new IDispatch(ppv[0]);


ppv = new int[1];

ret = Autoface.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, ppv);

if (ret == OLE.S_OK)



{

TypeInfo = new ITypeInfo(ppv[0]);

TypeInfo.AddRef();


}


}


private int getIDsOfNames(String name)



{

int[] rgdispid = new int[1];


String[] names = new String[]
{ name };

int ret = Autoface.GetIDsOfNames(guid, names, names.length,

COM.LOCALE_USER_DEFAULT, rgdispid);

if (ret != COM.S_OK)

return -1;


return rgdispid[0];

}


private void getVariantData(Variant aVar, int pData)



{

if (pData == 0)

OLE.error(OLE.ERROR_OUT_OF_MEMORY);


COM.VariantInit(pData);


if ((aVar.getType() & COM.VT_BYREF) == COM.VT_BYREF)



{


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);


COM.MoveMemory(pData + 8, new int[]
{ aVar.getByRef() }, 4);

return;

}


switch (aVar.getType())



{

case COM.VT_EMPTY:

break;

case COM.VT_BOOL:


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);

COM.MoveMemory(pData + 8,


new int[]
{ (aVar.getBoolean()) ? COM.VARIANT_TRUE

: COM.VARIANT_FALSE }, 2);

break;

case COM.VT_R4:


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);


COM.MoveMemory(pData + 8, new float[]
{ aVar.getFloat() }, 4);

break;

case COM.VT_I4:


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);


COM.MoveMemory(pData + 8, new int[]
{ aVar.getInt() }, 4);

break;

case COM.VT_DISPATCH:

aVar.getDispatch().AddRef();


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);


COM.MoveMemory(pData + 8, new int[]
{ aVar.getDispatch()

.getAddress() }, 4);

break;

case COM.VT_UNKNOWN:

aVar.getUnknown().AddRef();


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);


COM.MoveMemory(pData + 8, new int[]
{ aVar.getUnknown()

.getAddress() }, 4);

break;

case COM.VT_I2:


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);


COM.MoveMemory(pData + 8, new short[]
{ aVar.getShort() }, 2);

break;

case COM.VT_BSTR:


COM.MoveMemory(pData, new short[]
{ aVar.getType() }, 2);

char[] data = (aVar.getString() + "\0").toCharArray();

int ptr = COM.SysAllocString(data);


COM.MoveMemory(pData + 8, new int[]
{ ptr }, 4);

break;


default:

OLE.error(SWT.ERROR_NOT_IMPLEMENTED);

}

}


private Variant setVariantData(int pData)



{

if (pData == 0)

OLE.error(OLE.ERROR_INVALID_ARGUMENT);


Variant ret = null;

short[] dataType = new short[1];

COM.MoveMemory(dataType, pData, 2);

int type = dataType[0];


if ((type & COM.VT_BYREF) == COM.VT_BYREF)



{

int[] newByRefPtr = new int[1];

OS.MoveMemory(newByRefPtr, pData + 8, 4);

return new Variant(newByRefPtr[0], COM.VT_BYREF);

}


switch (type)



{

case COM.VT_EMPTY:

break;

case COM.VT_BOOL:

short[] newBooleanData = new short[1];

COM.MoveMemory(newBooleanData, pData + 8, 2);

ret = new Variant(newBooleanData[0] != 0);

break;

case COM.VT_R4:

float[] newFloatData = new float[1];

COM.MoveMemory(newFloatData, pData + 8, 4);

ret = new Variant(newFloatData[0]);

break;

case COM.VT_I4:

int[] newIntData = new int[1];

OS.MoveMemory(newIntData, pData + 8, 4);

ret = new Variant(newIntData[0]);

break;

case COM.VT_DISPATCH:



{

int[] ppvObject = new int[1];

OS.MoveMemory(ppvObject, pData + 8, 4);

if (ppvObject[0] == 0)



{

type = COM.VT_EMPTY;

break;

}

ret = new Variant(new IDispatch(ppvObject[0]));

break;

}

case COM.VT_UNKNOWN:



{

int[] ppvObject = new int[1];

OS.MoveMemory(ppvObject, pData + 8, 4);

if (ppvObject[0] == 0)



{

type = COM.VT_EMPTY;

break;

}

ret = new Variant(new IUnknown(ppvObject[0]));

break;

}

case COM.VT_I2:

short[] newShortData = new short[1];

COM.MoveMemory(newShortData, pData + 8, 2);

ret = new Variant(newShortData[0]);

break;

case COM.VT_BSTR:

// get the address of the memory in which the string resides

int[] hMem = new int[1];

OS.MoveMemory(hMem, pData + 8, 4);

if (hMem[0] == 0)



{

type = COM.VT_EMPTY;

break;

}

int size = COM.SysStringByteLen(hMem[0]);

if (size > 0)



{

char[] buffer = new char[(size + 1) / 2]; // add one to avoid

// rounding errors

COM.MoveMemory(buffer, hMem[0], size);

ret = new Variant(new String(buffer));

} else



{

ret = new Variant(""); //$NON-NLS-1$

}

break;


default:

int newPData = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT,

Variant.sizeof);

if (COM.VariantChangeType(newPData, pData, (short) 0, COM.VT_R4) == COM.S_OK)



{

ret = setVariantData(newPData);

} else if (COM.VariantChangeType(newPData, pData, (short) 0,

COM.VT_I4) == COM.S_OK)



{

setVariantData(newPData);

} else if (COM.VariantChangeType(newPData, pData, (short) 0,

COM.VT_BSTR) == COM.S_OK)



{

ret = setVariantData(newPData);

}

COM.VariantClear(newPData);

OS.GlobalFree(newPData);

break;

}

return ret;

}


private int invoke(int dispIdMember, int wFlags, Variant[] rgvarg,

int[] rgdispidNamedArgs, Variant[] pVarResult)



{


if (Autoface == null)

return COM.E_FAIL;


DISPPARAMS pDispParams = new DISPPARAMS();

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--)



{

getVariantData(rgvarg[i], pDispParams.rgvarg + offset);

offset += Variant.sizeof;

}

}


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;

}

}


EXCEPINFO excepInfo = new EXCEPINFO();

int[] pArgErr = new int[1];

int pVarResultAddress = 0;

if (pVarResult != null)

pVarResultAddress = OS.GlobalAlloc(

OS.GMEM_FIXED | OS.GMEM_ZEROINIT, Variant.sizeof);

int ret = Autoface.Invoke(dispIdMember, new GUID(),

COM.LOCALE_USER_DEFAULT, wFlags, pDispParams,

pVarResultAddress, excepInfo, pArgErr);

if (ret != COM.S_OK)

return ret;


if (pVarResultAddress != 0)



{

pVarResult[0] = setVariantData(pVarResultAddress);

COM.VariantClear(pVarResultAddress);

OS.GlobalFree(pVarResultAddress);

}


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);

}


return ret;

}



/** *//**

* 创建空对象在使用需调用CreateComObject或CreateComObjectProgID创建COM对象。

*

*/

public CComObject()



{


}



/** *//**

* 使用CLSID创建COM对象,建构后可以调用COM对象方法或属性。

*

* @param sCLSID

* COM对象CLSID

* @throws RuntimeException

*/

public CComObject(String sCLSID) throws RuntimeException



{

COM.IIDFromString(sCLSID.toCharArray(), guid);

CreateComObject();

}



/** *//**

* 使用现有自动化对象接口创建COM对象。

*

* @param prgDispatch

*/

public CComObject(IDispatch prgDispatch)



{

Autoface = prgDispatch;

Autoface.AddRef();

}



/** *//**

* @author 杨中科

* @param clientName

* @param guid

*/

protected void getClassID(String clientName, GUID 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)

OLE.error(result);

}

}



/** *//**

* 使用CLSID创建COM对象。可用于创建新的对象(原有对象将释放)

*

* @param sCLSID

* @return

* @throws RuntimeException

*/

public IDispatch CreateComObject(String sCLSID) throws RuntimeException



{

COM.IIDFromString(sCLSID.toCharArray(), guid);

CreateComObject();

return Autoface;

}



/** *//**

* 创建远程COM对象(未实现)。

*

* @param MachineName

* @param sCLSID

* @return

*/

public IDispatch CreateRemoteComObject(String MachineName, String sCLSID)



{

COM.IIDFromString(sCLSID.toCharArray(), guid);

return Autoface;

}



/** *//**

* 使用ProgID创建COM对象。

*

* @param ProgID

* @return

* @throws RuntimeException

*/

public IDispatch CreateComObjectProgID(String ProgID)

throws RuntimeException



{

getClassID(ProgID, guid);

CreateComObject();

return Autoface;

}



/** *//**

* 调用COM对象方法,有返回值(可变参数暂不支持)。

*

* @param FunctionName

* @param rgvarg

* @return

* @throws RuntimeException

*/

public Variant CallFunction(String FunctionName, Variant[] rgvarg)

throws RuntimeException



{

if (Autoface == null)

throw new RuntimeException("对象为空");

int rgdispid = getIDsOfNames(FunctionName);

if (rgdispid == -1)

throw new RuntimeException("方法不支持!");

Variant[] pVarResult = new Variant[1];

int ret = invoke(rgdispid, COM.DISPATCH_METHOD, rgvarg, null,

pVarResult);

if (ret != COM.S_OK)

OLE.error(ret);

// throw new RuntimeException("方法调用失败!");

return pVarResult[0];

}



/** *//**

* 调用COM对象方法,无参数和返回值。

*

* @param FunctionName

* @throws RuntimeException

*/

public void CallFunction(String FunctionName) throws RuntimeException



{


CallFunction(FunctionName, new Variant[]
{});

}



/** *//**

* 获取COM对象属性值。

*

* @param PropertyName

* @param rgvarg

* @return

* @throws RuntimeException

*/

public Variant getProperty(String PropertyName, Variant[] rgvarg)

throws RuntimeException



{

if (Autoface == null)

throw new RuntimeException("对象为空");

int rgdispid = getIDsOfNames(PropertyName);

if (rgdispid == -1)

throw new RuntimeException("属性不支持!");

Variant[] pVarResult = new Variant[1];

int ret = invoke(rgdispid, COM.DISPATCH_PROPERTYGET, rgvarg, null,

pVarResult);

if (ret != COM.S_OK)

OLE.error(ret);

// throw new RuntimeException("属性获取失败");

return pVarResult[0];


}



/** *//**

* 给COM对象属性赋值。

*

* @param PropertyName

* @param rgvarg

* @throws RuntimeException

*/

public void setProperty(String PropertyName, Variant[] rgvarg)

throws RuntimeException



{

if (Autoface == null)

throw new RuntimeException("对象为空");

int rgdispid = getIDsOfNames(PropertyName);

if (rgdispid == -1)

throw new RuntimeException("属性不支持!");


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[1];

int ret = invoke(rgdispid, dwFlags, rgvarg, rgdispidNamedArgs,

pVarResult);

if (ret != COM.S_OK)

OLE.error(ret);

}



/** *//**

* 释放COM对象的连接。在对象不用的时候需调用此函数,以便及时释放对象。

*

*/

public void dispose()



{

if (Autoface != null)

Autoface.Release();

Autoface = null;

if (TypeInfo != null)

TypeInfo.Release();

TypeInfo = null;

}

}


IEAutomation.java
package test;


import org.eclipse.swt.ole.win32.Variant;



/** *//**

* 更多方法参考MSDN“InternetExplorer Object”文档

* @author 杨中科

*

*/

public class IEAutomation



{

static final String progId = "InternetExplorer.Application";


private CComObject ieComObject = new CComObject();


public IEAutomation()



{

ieComObject.CreateComObjectProgID(progId);

}


public void setProperty(String name, int value)



{


ieComObject.setProperty(name, new Variant[]
{ new Variant(value) });

}


public int getPropertyAsInt(String name)



{

Variant value = ieComObject.getProperty(name, new Variant[0]);

return value.getInt();

}


public void setProperty(String name, boolean value)



{


ieComObject.setProperty(name, new Variant[]
{ new Variant(value) });

}


public boolean getPropertyAsBool(String name)



{

Variant value = ieComObject.getProperty(name, new Variant[0]);

return value.getBoolean();

}


public void setProperty(String name, String value)



{


ieComObject.setProperty(name, new Variant[]
{ new Variant(value) });

}


public String getPropertyAsString(String name)



{

Variant value = ieComObject.getProperty(name, new Variant[0]);

return value.getString();

}


public void setVisible(boolean value)



{

setProperty("Visible", value);

}


public boolean isVisible()



{

return getPropertyAsBool("Visible");

}


public void setMenuBar(boolean value)



{

setProperty("MenuBar", value);

}


public boolean isMenuBar()



{

return getPropertyAsBool("MenuBar");

}


public int getHWND()



{

return getPropertyAsInt("HWND");

}


public void navigate(String url)



{


ieComObject.CallFunction("Navigate", new Variant[]
{ new Variant(url) });

}


public void quit()



{

ieComObject.CallFunction("Quit");

}

}


调用代码:
final IEAutomation ie = new IEAutomation();

ie.setVisible(true);

ie.navigate("http://www.baidu.com");

ie.setMenuBar(false);

int hwnd = ie.getHWND();

// composite为承载被启动程序的控件

OS.SetParent(hwnd, composite.handle);

// 窗口最大化

OS.SendMessage(hwnd, OS.WM_SYSCOMMAND, OS.SC_MAXIMIZE, 0);

OS.SendMessage(hwnd, OS.WM_ACTIVATE, 0, 0);



composite.addDisposeListener(new DisposeListener()
{

public void widgetDisposed(DisposeEvent e)



{

//必须手动指定退出,否则会报异常

ie.quit();

}

});


我们高呼,SWT,你丫真牛,比CowNew还牛,呵呵。