Skip to content

Commit

Permalink
Implement named callback parameters for COM Callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasblaesing committed May 6, 2016
1 parent 0296115 commit 62b6061
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,12 @@ protected HRESULT oleMethod(int nType, VARIANT.ByReference pvResult,

// Handle special-case for property-puts!
if (nType == OleAuto.DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = new UINT(_argsLen);
dp.rgdispidNamedArgs = new DISPIDByReference(
OaIdl.DISPID_PROPERTYPUT);
dp.setRgdispidNamedArgs(new DISPID[] {OaIdl.DISPID_PROPERTYPUT});
}

// Build DISPPARAMS
if (_argsLen > 0) {
dp.cArgs = new UINT(_args.length);
// make pointer of variant array
dp.rgvarg = new VariantArg.ByReference(_args);
dp.setArgs(_args);

// write 'DISPPARAMS' structure to memory
dp.write();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Guid;
import com.sun.jna.platform.win32.Guid.IID;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.OaIdl.DISPID;
Expand All @@ -29,7 +28,6 @@
import com.sun.jna.platform.win32.OleAuto.DISPPARAMS;
import com.sun.jna.platform.win32.Variant;
import com.sun.jna.platform.win32.Variant.VARIANT;
import com.sun.jna.platform.win32.Variant.VariantArg;
import com.sun.jna.platform.win32.WinDef.LCID;
import com.sun.jna.platform.win32.WinDef.UINT;
import com.sun.jna.platform.win32.WinDef.UINTByReference;
Expand All @@ -40,7 +38,6 @@
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.platform.win32.COM.Dispatch;
import com.sun.jna.platform.win32.COM.DispatchListener;
import com.sun.jna.platform.win32.COM.IDispatch;
import com.sun.jna.platform.win32.COM.IDispatchCallback;
import com.sun.jna.platform.win32.COM.Unknown;
import com.sun.jna.platform.win32.COM.util.annotation.ComEventCallback;
Expand All @@ -50,6 +47,14 @@

public class CallbackProxy implements IDispatchCallback {

private static boolean DEFAULT_BOOLEAN;
private static byte DEFAULT_BYTE;
private static short DEFAULT_SHORT;
private static int DEFAULT_INT;
private static long DEFAULT_LONG;
private static float DEFAULT_FLOAT;
private static double DEFAULT_DOUBLE;

public CallbackProxy(Factory factory, Class<?> comEventCallbackInterface,
IComEventCallbackListener comEventCallbackListener) {
this.factory = factory;
Expand Down Expand Up @@ -90,6 +95,11 @@ Map<DISPID, Method> createDispIdMap(Class<?> comEventCallbackInterface) {
if (-1 == dispId) {
dispId = this.fetchDispIdFromName(annotation);
}
if(dispId == -1) {
CallbackProxy.this.comEventCallbackListener.errorReceivingCallbackEvent(
"DISPID for " + meth.getName() + " not found",
null);
}
map.put(new DISPID(dispId), meth);
}
}
Expand All @@ -105,50 +115,101 @@ int fetchDispIdFromName(ComEventCallback annotation) {
void invokeOnThread(final DISPID dispIdMember, final REFIID riid, LCID lcid, WORD wFlags,
final DISPPARAMS.ByReference pDispParams) {

final Method eventMethod;
if (CallbackProxy.this.dsipIdMap.containsKey(dispIdMember)) {
eventMethod = CallbackProxy.this.dsipIdMap.get(dispIdMember);
if (eventMethod.getParameterTypes().length != pDispParams.cArgs.intValue()) {
CallbackProxy.this.comEventCallbackListener.errorReceivingCallbackEvent(
"Trying to invoke method " + eventMethod + " with " + pDispParams.cArgs.intValue() + " arguments",
null);
return;
}
} else {
VARIANT[] arguments = pDispParams.getArgs();

final Method eventMethod = CallbackProxy.this.dsipIdMap.get(dispIdMember);
if (eventMethod == null) {
CallbackProxy.this.comEventCallbackListener.errorReceivingCallbackEvent(
"No method found with dispId = " + dispIdMember, null);
return;
}

// DISPPARAMs can use two different ways to pass arguments
// Arguments can be passed as a linear list with all arguments
// specified to a certain position (positional) or the position of
// an argument can be passed via the rgdispidNamedArgs array (named).
//
// pDispParams.rgvarg (length in pDispParams.cArgs) contains all
// arguments (named + position based)
//
// pDispParams.rgdispidNamedArgs (length in pDispParams.cNamedArgs) contains
// the named parameters as DISPIDs - the DISPIDs are the target index
// in the method signature (zero based).
//
// Each entry in pDispParams.rgvarg is either position based or name
// based and the position bases arguments are passed in reverse order,
// so getting this:
//
// rgvarg = ["arg1", "arg2", "arg3", "arg4", "arg5"]
// rgdispidNamedArgs = [3, 4]
//
// Would lead to this paramater array in the handler:
//
// ["arg5", "arg4", "arg3", "arg1", "arg2"]
//
// See also: https://msdn.microsoft.com/de-de/library/windows/desktop/ms221653%28v=vs.85%29.aspx


// Arguments are converted to the JAVA side and IDispatch Interfaces
// are wrapped into an ProxyObject if so requested.
//
// Out-Parameter need to be specified as VARIANT, VARIANT args are
// not converted, so COM memory allocation rules apply.
final Class<?>[] params = eventMethod.getParameterTypes();
List<Object> rjargs = new ArrayList<Object>();
if (pDispParams.cArgs.intValue() > 0) {
VariantArg vargs = pDispParams.rgvarg;
vargs.setArraySize(pDispParams.cArgs.intValue());
for ( int i = 0; i < vargs.variantArg.length; i++) {
Class targetClass = params[vargs.variantArg.length - 1 - i];
Variant.VARIANT varg = vargs.variantArg[i];
Object jarg = Convert.toJavaObject(varg, targetClass, factory, true, false);
rjargs.add(jarg);

DISPID[] positionMap = pDispParams.getRgdispidNamedArgs();

final Class<?>[] paramTypes = eventMethod.getParameterTypes();
final Object[] params = new Object[paramTypes.length];

// Handle position based parameters first
for ( int i = 0; i < params.length && (arguments.length - positionMap.length - i) > 0; i++) {
Class targetClass = paramTypes[i];
Variant.VARIANT varg = arguments[arguments.length - i - 1];
params[i] = Convert.toJavaObject(varg, targetClass, factory, true, false);
}

for ( int i = 0; i < positionMap.length; i++) {
int targetPosition = positionMap[i].intValue();
if(targetPosition >= params.length) {
// If less parameters are mapped then supplied, ignore
continue;
}
Class targetClass = paramTypes[targetPosition];
Variant.VARIANT varg = arguments[i];
params[targetPosition] = Convert.toJavaObject(varg, targetClass, factory, true, false);
}


// Make sure the parameters are correctly initialized -- primitives
// should not throw a nullpointer exception, but
for(int i = 0; i < params.length; i++) {
if(params[i] == null && paramTypes[i].isPrimitive()) {
if (paramTypes[i].equals(boolean.class)) {
params[i] = DEFAULT_BOOLEAN;
} else if (paramTypes[i].equals(byte.class)) {
params[i] = DEFAULT_BYTE;
} else if (paramTypes[i].equals(short.class)) {
params[i] = DEFAULT_SHORT;
} else if (paramTypes[i].equals(int.class)) {
params[i] = DEFAULT_INT;
} else if (paramTypes[i].equals(long.class)) {
params[i] = DEFAULT_LONG;
} else if (paramTypes[i].equals(float.class)) {
params[i] = DEFAULT_FLOAT;
} else if (paramTypes[i].equals(double.class)) {
params[i] = DEFAULT_DOUBLE;
} else {
throw new IllegalArgumentException("Class type " + paramTypes[i].getName() + " not mapped to primitive default value.");
}
}
}

List<Object> margs = new ArrayList<Object>();
try {
// Reverse order from calling convention
int lastParamIdx = eventMethod.getParameterTypes().length - 1;
for (int i = lastParamIdx; i >= 0; i--) {
margs.add(rjargs.get(i));
}
eventMethod.invoke(comEventCallbackListener, margs.toArray());
eventMethod.invoke(comEventCallbackListener, params);
} catch (Exception e) {
List<String> decodedClassNames = new ArrayList<String>(margs.size());
for(Object o: margs) {
List<String> decodedClassNames = new ArrayList<String>(params.length);
for(Object o: params) {
if(o == null) {
decodedClassNames.add("NULL");
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,7 @@ protected HRESULT oleMethod(final int nType, final VARIANT.ByReference pvResult,

// Handle special-case for property-puts!
if (nType == OleAuto.DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = new UINT(_argsLen);
dp.rgdispidNamedArgs = new DISPIDByReference(OaIdl.DISPID_PROPERTYPUT);
dp.setRgdispidNamedArgs(new DISPID[] {OaIdl.DISPID_PROPERTYPUT});
}

// Apply "fix" according to
Expand Down Expand Up @@ -631,9 +630,7 @@ protected HRESULT oleMethod(final int nType, final VARIANT.ByReference pvResult,

// Build DISPPARAMS
if (_argsLen > 0) {
dp.cArgs = new UINT(_args.length);
// make pointer of variant array
dp.rgvarg = new VariantArg.ByReference(_args);
dp.setArgs(_args);

// write 'DISPPARAMS' structure to memory
dp.write();
Expand Down
67 changes: 57 additions & 10 deletions contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
*/
package com.sun.jna.platform.win32;

import com.sun.jna.Memory;
import java.util.List;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Guid.GUID;
import com.sun.jna.platform.win32.OaIdl.DISPIDByReference;
import com.sun.jna.platform.win32.OaIdl.DISPID;
import com.sun.jna.platform.win32.OaIdl.SAFEARRAY;
import com.sun.jna.platform.win32.OaIdl.SAFEARRAYBOUND;
import com.sun.jna.platform.win32.Variant.VARIANT;
Expand Down Expand Up @@ -563,15 +564,61 @@ public static class ByReference extends DISPPARAMS implements
/** The rgvarg. */
public VariantArg.ByReference rgvarg;

/** The rgdispid named args. */
public DISPIDByReference rgdispidNamedArgs;

/** The c args. */
public UINT cArgs;

/** The c named args. */
public UINT cNamedArgs;

/** The rgdispid named args. */
public Pointer rgdispidNamedArgs = Pointer.NULL;

/** The c args. - use setArgs to update arguments */
public UINT cArgs = new UINT(0);

/** The c named args. - use setRgdispidNamedArgs to update named arguments map */
public UINT cNamedArgs = new UINT(0);

public DISPID[] getRgdispidNamedArgs() {
DISPID[] namedArgs = null;
int count = cNamedArgs.intValue();
if(rgdispidNamedArgs != null && count > 0) {
int[] rawData = rgdispidNamedArgs.getIntArray(0, count);
namedArgs = new DISPID[count];
for(int i = 0; i < count; i++) {
namedArgs[i] = new DISPID(rawData[i]);
}
} else {
namedArgs = new DISPID[0];
}
return namedArgs;
}

public void setRgdispidNamedArgs(DISPID[] namedArgs) {
if(namedArgs == null) {
namedArgs = new DISPID[0];
}
cNamedArgs = new UINT(namedArgs.length);
rgdispidNamedArgs = new Memory(DISPID.SIZE * namedArgs.length);
int[] rawData = new int[namedArgs.length];
for(int i = 0; i < rawData.length; i++) {
rawData[i] = namedArgs[i].intValue();
}
rgdispidNamedArgs.write(0, rawData, 0, namedArgs.length);
}

public VARIANT[] getArgs() {
if(this.rgvarg != null) {
this.rgvarg.setArraySize(cArgs.intValue());
return this.rgvarg.variantArg;
} else {
return new VARIANT[0];
}
}

public void setArgs(VARIANT[] arguments) {
if(arguments == null) {
arguments = new VARIANT[0];
}

rgvarg = new VariantArg.ByReference(arguments);
cArgs = new UINT(arguments.length);
}

/**
* Instantiates a new dispparams.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ public void before() {
public void after() {
// Shutdown Internet Explorer
DISPPARAMS.ByReference pDispParams = new DISPPARAMS.ByReference();
pDispParams.cArgs = new UINT(0);
pDispParams.cNamedArgs = new UINT(0);
pDispParams.rgvarg = null;
VARIANT.ByReference pVarResult = new VARIANT.ByReference();
IntByReference puArgErr = new IntByReference();
EXCEPINFO.ByReference pExcepInfo = new EXCEPINFO.ByReference();
Expand Down Expand Up @@ -196,27 +193,24 @@ public HRESULT Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
VARIANT.ByReference pVarResult, EXCEPINFO.ByReference pExcepInfo,
IntByReference puArgErr) {

// @toDo: Move setArraySize into invoke method
if (pDispParams.rgvarg != null && pDispParams.cArgs.intValue() > 0) {
pDispParams.rgvarg.setArraySize(pDispParams.cArgs.intValue());
}
VARIANT[] arguments = pDispParams.getArgs();

try {
switch (dispIdMember.intValue()) {
case DISPID_NavigateComplete2:
navigateComplete2Called = true;
// URL ist passed as VARIANT$ByReference
VARIANT urlByRef = pDispParams.rgvarg.variantArg[0];
VARIANT urlByRef = arguments[0];
navigateComplete2String = ((VARIANT) urlByRef.getValue()).stringValue();
break;
case DISPID_BeforeNavigate2:
VARIANT Cancel = pDispParams.rgvarg.variantArg[0];
VARIANT Headers = pDispParams.rgvarg.variantArg[1];
VARIANT PostData = pDispParams.rgvarg.variantArg[2];
VARIANT TargetFrameName = pDispParams.rgvarg.variantArg[3];
VARIANT Flags = pDispParams.rgvarg.variantArg[4];
VARIANT URL = pDispParams.rgvarg.variantArg[5];
VARIANT pDisp = pDispParams.rgvarg.variantArg[6];
VARIANT Cancel = arguments[0];
VARIANT Headers = arguments[1];
VARIANT PostData = arguments[2];
VARIANT TargetFrameName = arguments[3];
VARIANT Flags = arguments[4];
VARIANT URL = arguments[5];
VARIANT pDisp = arguments[6];
VARIANT_BOOLByReference cancelValue = ((VARIANT_BOOLByReference) Cancel.getValue());
if (blockNavigation) {
cancelValue.setValue(Variant.VARIANT_TRUE);
Expand Down Expand Up @@ -280,11 +274,8 @@ public void testComEventCallback() throws InterruptedException {
DISPPARAMS.ByReference pDispParams;

pDispParams = new DISPPARAMS.ByReference();
pDispParams.cArgs = new UINT(1);
pDispParams.cNamedArgs = new UINT(1);
pDispParams.rgvarg = new Variant.VariantArg.ByReference(new VARIANT[1]);
pDispParams.rgvarg.variantArg[0] = new VARIANT(true);
pDispParams.rgdispidNamedArgs = new DISPIDByReference(new DISPID(OaIdl.DISPID_PROPERTYPUT.intValue()));
pDispParams.setArgs(new VARIANT[] {new VARIANT(true)});
pDispParams.setRgdispidNamedArgs(new DISPID[] {OaIdl.DISPID_PROPERTYPUT});
// Visible-Prioperty
hr = ieDispatch.Invoke(dispIdVisible.getValue(), niid, lcid, propertyPutFlags, pDispParams, null, null, null);
COMUtils.checkRC(hr);
Expand Down Expand Up @@ -319,11 +310,9 @@ public void testComEventCallback() throws InterruptedException {
String navigateURL = "https://github.com/java-native-access/jna";
String blockedURL = "http://www.google.de";

VARIANT[] arguments = new VARIANT[] {new VARIANT(navigateURL)};
pDispParams = new DISPPARAMS.ByReference();
pDispParams.cArgs = new UINT(1);
pDispParams.cNamedArgs = new UINT(0);
pDispParams.rgvarg = new Variant.VariantArg.ByReference(new VARIANT[1]);
pDispParams.rgvarg.variantArg[0] = new VARIANT(navigateURL);
pDispParams.setArgs(arguments);
hr = ieDispatch.Invoke(dispIdNavigate.getValue(), niid, lcid, methodFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
COMUtils.checkRC(hr, pExcepInfo, puArgErr);

Expand All @@ -343,11 +332,9 @@ public void testComEventCallback() throws InterruptedException {
listener.navigateComplete2String = null;
listener.blockNavigation = true;

arguments = new VARIANT[]{new VARIANT(blockedURL)};
pDispParams = new DISPPARAMS.ByReference();
pDispParams.cArgs = new UINT(1);
pDispParams.cNamedArgs = new UINT(0);
pDispParams.rgvarg = new Variant.VariantArg.ByReference(new VARIANT[1]);
pDispParams.rgvarg.variantArg[0] = new VARIANT(blockedURL);
pDispParams.setArgs(arguments);
hr = ieDispatch.Invoke(dispIdNavigate.getValue(), niid, lcid, methodFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
COMUtils.checkRC(hr, pExcepInfo, puArgErr);

Expand Down
Loading

0 comments on commit 62b6061

Please sign in to comment.