Skip to content

Commit 071e2af

Browse files
Merge pull request #654 from matthiasblaesing/com_callback_named_params
Implement named callback parameters for COM Callbacks
2 parents 9c63dc4 + 68c5978 commit 071e2af

File tree

8 files changed

+464
-88
lines changed

8 files changed

+464
-88
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Features
5252
* [#642](https://github.com/java-native-access/jna/pull/642): COM calls with variable number of arguments (varargs) are now supported - [@SevenOf9Sleeper](https://github.com/SevenOf9Sleeper).
5353
* [#644](https://github.com/java-native-access/jna/pull/644): New ant target 'install' for installing JNA artifacts in local m2-repository - [@SevenOf9Sleeper](https://github.com/SevenOf9Sleeper).
5454
* [#649](https://github.com/java-native-access/jna/pull/649): Bugfix msoffice sample and add two samples taken from MSDN and translated from VisualBasic to Java - [@matthiasblaesing](https://github.com/matthiasblaesing).
55+
* [#654](https://github.com/java-native-access/jna/pull/654): Support named arguments for `com.sun.jna.platform.win32.COM.util.CallbackProxy` based callbacks - [@matthiasblaesing](https://github.com/matthiasblaesing).
5556

5657
Bug Fixes
5758
---------

contrib/platform/src/com/sun/jna/platform/win32/COM/COMBindingBaseObject.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -210,16 +210,12 @@ protected HRESULT oleMethod(int nType, VARIANT.ByReference pvResult,
210210

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

218216
// Build DISPPARAMS
219217
if (_argsLen > 0) {
220-
dp.cArgs = new UINT(_args.length);
221-
// make pointer of variant array
222-
dp.rgvarg = new VariantArg.ByReference(_args);
218+
dp.setArgs(_args);
223219

224220
// write 'DISPPARAMS' structure to memory
225221
dp.write();

contrib/platform/src/com/sun/jna/platform/win32/COM/util/CallbackProxy.java

+98-33
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import com.sun.jna.Pointer;
2222
import com.sun.jna.WString;
23-
import com.sun.jna.platform.win32.Guid;
2423
import com.sun.jna.platform.win32.Guid.IID;
2524
import com.sun.jna.platform.win32.Guid.REFIID;
2625
import com.sun.jna.platform.win32.OaIdl.DISPID;
@@ -29,7 +28,6 @@
2928
import com.sun.jna.platform.win32.OleAuto.DISPPARAMS;
3029
import com.sun.jna.platform.win32.Variant;
3130
import com.sun.jna.platform.win32.Variant.VARIANT;
32-
import com.sun.jna.platform.win32.Variant.VariantArg;
3331
import com.sun.jna.platform.win32.WinDef.LCID;
3432
import com.sun.jna.platform.win32.WinDef.UINT;
3533
import com.sun.jna.platform.win32.WinDef.UINTByReference;
@@ -40,7 +38,6 @@
4038
import com.sun.jna.platform.win32.COM.COMUtils;
4139
import com.sun.jna.platform.win32.COM.Dispatch;
4240
import com.sun.jna.platform.win32.COM.DispatchListener;
43-
import com.sun.jna.platform.win32.COM.IDispatch;
4441
import com.sun.jna.platform.win32.COM.IDispatchCallback;
4542
import com.sun.jna.platform.win32.COM.Unknown;
4643
import com.sun.jna.platform.win32.COM.util.annotation.ComEventCallback;
@@ -49,7 +46,15 @@
4946
import com.sun.jna.ptr.PointerByReference;
5047

5148
public class CallbackProxy implements IDispatchCallback {
52-
49+
// Helper declarations, initialized to default values by jvm
50+
private static boolean DEFAULT_BOOLEAN;
51+
private static byte DEFAULT_BYTE;
52+
private static short DEFAULT_SHORT;
53+
private static int DEFAULT_INT;
54+
private static long DEFAULT_LONG;
55+
private static float DEFAULT_FLOAT;
56+
private static double DEFAULT_DOUBLE;
57+
5358
public CallbackProxy(Factory factory, Class<?> comEventCallbackInterface,
5459
IComEventCallbackListener comEventCallbackListener) {
5560
this.factory = factory;
@@ -90,6 +95,11 @@ Map<DISPID, Method> createDispIdMap(Class<?> comEventCallbackInterface) {
9095
if (-1 == dispId) {
9196
dispId = this.fetchDispIdFromName(annotation);
9297
}
98+
if(dispId == -1) {
99+
CallbackProxy.this.comEventCallbackListener.errorReceivingCallbackEvent(
100+
"DISPID for " + meth.getName() + " not found",
101+
null);
102+
}
93103
map.put(new DISPID(dispId), meth);
94104
}
95105
}
@@ -105,50 +115,105 @@ int fetchDispIdFromName(ComEventCallback annotation) {
105115
void invokeOnThread(final DISPID dispIdMember, final REFIID riid, LCID lcid, WORD wFlags,
106116
final DISPPARAMS.ByReference pDispParams) {
107117

108-
final Method eventMethod;
109-
if (CallbackProxy.this.dsipIdMap.containsKey(dispIdMember)) {
110-
eventMethod = CallbackProxy.this.dsipIdMap.get(dispIdMember);
111-
if (eventMethod.getParameterTypes().length != pDispParams.cArgs.intValue()) {
112-
CallbackProxy.this.comEventCallbackListener.errorReceivingCallbackEvent(
113-
"Trying to invoke method " + eventMethod + " with " + pDispParams.cArgs.intValue() + " arguments",
114-
null);
115-
return;
116-
}
117-
} else {
118+
VARIANT[] arguments = pDispParams.getArgs();
119+
120+
final Method eventMethod = CallbackProxy.this.dsipIdMap.get(dispIdMember);
121+
if (eventMethod == null) {
118122
CallbackProxy.this.comEventCallbackListener.errorReceivingCallbackEvent(
119123
"No method found with dispId = " + dispIdMember, null);
120124
return;
121125
}
122126

127+
/**
128+
* DISPPARAMs provides two different ways to pass arguments.
129+
*
130+
* Arguments can be passed as a linear list with all arguments
131+
* specified to a certain position (positional) or the position of
132+
* an argument can be passed via the rgdispidNamedArgs array
133+
* (named).
134+
*
135+
* pDispParams.rgvarg (length in pDispParams.cArgs) contains all
136+
* arguments (named + position based)
137+
*
138+
* pDispParams.rgdispidNamedArgs (length in pDispParams.cNamedArgs)
139+
* contains the named parameters as DISPIDs - the DISPIDs are the
140+
* target index in the method signature (zero based).
141+
*
142+
* Each entry in pDispParams.rgvarg is either position based or name
143+
* based and the position bases arguments are passed in reverse
144+
* order, so getting this:
145+
*
146+
* rgvarg = ["arg1", "arg2", "arg3", "arg4", "arg5"]
147+
* rgdispidNamedArgs = [3, 4]
148+
*
149+
* Would lead to this paramater array in the handler:
150+
*
151+
* ["arg5", "arg4", "arg3", "arg1", "arg2"]
152+
*
153+
* See also:
154+
* https://msdn.microsoft.com/de-de/library/windows/desktop/ms221653%28v=vs.85%29.aspx
155+
*/
156+
123157
// Arguments are converted to the JAVA side and IDispatch Interfaces
124158
// are wrapped into an ProxyObject if so requested.
125159
//
126160
// Out-Parameter need to be specified as VARIANT, VARIANT args are
127161
// not converted, so COM memory allocation rules apply.
128-
final Class<?>[] params = eventMethod.getParameterTypes();
129-
List<Object> rjargs = new ArrayList<Object>();
130-
if (pDispParams.cArgs.intValue() > 0) {
131-
VariantArg vargs = pDispParams.rgvarg;
132-
vargs.setArraySize(pDispParams.cArgs.intValue());
133-
for ( int i = 0; i < vargs.variantArg.length; i++) {
134-
Class targetClass = params[vargs.variantArg.length - 1 - i];
135-
Variant.VARIANT varg = vargs.variantArg[i];
136-
Object jarg = Convert.toJavaObject(varg, targetClass, factory, true, false);
137-
rjargs.add(jarg);
162+
163+
DISPID[] positionMap = pDispParams.getRgdispidNamedArgs();
164+
165+
final Class<?>[] paramTypes = eventMethod.getParameterTypes();
166+
final Object[] params = new Object[paramTypes.length];
167+
168+
// Handle position based parameters first
169+
for ( int i = 0; i < params.length && (arguments.length - positionMap.length - i) > 0; i++) {
170+
Class targetClass = paramTypes[i];
171+
Variant.VARIANT varg = arguments[arguments.length - i - 1];
172+
params[i] = Convert.toJavaObject(varg, targetClass, factory, true, false);
173+
}
174+
175+
for ( int i = 0; i < positionMap.length; i++) {
176+
int targetPosition = positionMap[i].intValue();
177+
if(targetPosition >= params.length) {
178+
// If less parameters are mapped then supplied, ignore
179+
continue;
138180
}
181+
Class targetClass = paramTypes[targetPosition];
182+
Variant.VARIANT varg = arguments[i];
183+
params[targetPosition] = Convert.toJavaObject(varg, targetClass, factory, true, false);
139184
}
140185

141-
List<Object> margs = new ArrayList<Object>();
142-
try {
143-
// Reverse order from calling convention
144-
int lastParamIdx = eventMethod.getParameterTypes().length - 1;
145-
for (int i = lastParamIdx; i >= 0; i--) {
146-
margs.add(rjargs.get(i));
186+
187+
// Make sure the parameters are correctly initialized -- primitives
188+
// are initialized to their default value, else a NullPointer
189+
// exception occurs while doing the call into the target method
190+
for(int i = 0; i < params.length; i++) {
191+
if(params[i] == null && paramTypes[i].isPrimitive()) {
192+
if (paramTypes[i].equals(boolean.class)) {
193+
params[i] = DEFAULT_BOOLEAN;
194+
} else if (paramTypes[i].equals(byte.class)) {
195+
params[i] = DEFAULT_BYTE;
196+
} else if (paramTypes[i].equals(short.class)) {
197+
params[i] = DEFAULT_SHORT;
198+
} else if (paramTypes[i].equals(int.class)) {
199+
params[i] = DEFAULT_INT;
200+
} else if (paramTypes[i].equals(long.class)) {
201+
params[i] = DEFAULT_LONG;
202+
} else if (paramTypes[i].equals(float.class)) {
203+
params[i] = DEFAULT_FLOAT;
204+
} else if (paramTypes[i].equals(double.class)) {
205+
params[i] = DEFAULT_DOUBLE;
206+
} else {
207+
throw new IllegalArgumentException("Class type " + paramTypes[i].getName() + " not mapped to primitive default value.");
208+
}
147209
}
148-
eventMethod.invoke(comEventCallbackListener, margs.toArray());
210+
}
211+
212+
try {
213+
eventMethod.invoke(comEventCallbackListener, params);
149214
} catch (Exception e) {
150-
List<String> decodedClassNames = new ArrayList<String>(margs.size());
151-
for(Object o: margs) {
215+
List<String> decodedClassNames = new ArrayList<String>(params.length);
216+
for(Object o: params) {
152217
if(o == null) {
153218
decodedClassNames.add("NULL");
154219
} else {

contrib/platform/src/com/sun/jna/platform/win32/COM/util/ProxyObject.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,7 @@ protected HRESULT oleMethod(final int nType, final VARIANT.ByReference pvResult,
595595

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

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

632631
// Build DISPPARAMS
633632
if (_argsLen > 0) {
634-
dp.cArgs = new UINT(_args.length);
635-
// make pointer of variant array
636-
dp.rgvarg = new VariantArg.ByReference(_args);
633+
dp.setArgs(_args);
637634

638635
// write 'DISPPARAMS' structure to memory
639636
dp.write();

contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java

+57-10
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
*/
1313
package com.sun.jna.platform.win32;
1414

15+
import com.sun.jna.Memory;
1516
import java.util.List;
1617

1718
import com.sun.jna.Native;
1819
import com.sun.jna.Pointer;
1920
import com.sun.jna.Structure;
2021
import com.sun.jna.WString;
2122
import com.sun.jna.platform.win32.Guid.GUID;
22-
import com.sun.jna.platform.win32.OaIdl.DISPIDByReference;
23+
import com.sun.jna.platform.win32.OaIdl.DISPID;
2324
import com.sun.jna.platform.win32.OaIdl.SAFEARRAY;
2425
import com.sun.jna.platform.win32.OaIdl.SAFEARRAYBOUND;
2526
import com.sun.jna.platform.win32.Variant.VARIANT;
@@ -563,15 +564,61 @@ public static class ByReference extends DISPPARAMS implements
563564
/** The rgvarg. */
564565
public VariantArg.ByReference rgvarg;
565566

566-
/** The rgdispid named args. */
567-
public DISPIDByReference rgdispidNamedArgs;
568-
569-
/** The c args. */
570-
public UINT cArgs;
571-
572-
/** The c named args. */
573-
public UINT cNamedArgs;
574-
567+
/** The rgdispid named args. */
568+
public Pointer rgdispidNamedArgs = Pointer.NULL;
569+
570+
/** The c args. - use setArgs to update arguments */
571+
public UINT cArgs = new UINT(0);
572+
573+
/** The c named args. - use setRgdispidNamedArgs to update named arguments map */
574+
public UINT cNamedArgs = new UINT(0);
575+
576+
public DISPID[] getRgdispidNamedArgs() {
577+
DISPID[] namedArgs = null;
578+
int count = cNamedArgs.intValue();
579+
if(rgdispidNamedArgs != null && count > 0) {
580+
int[] rawData = rgdispidNamedArgs.getIntArray(0, count);
581+
namedArgs = new DISPID[count];
582+
for(int i = 0; i < count; i++) {
583+
namedArgs[i] = new DISPID(rawData[i]);
584+
}
585+
} else {
586+
namedArgs = new DISPID[0];
587+
}
588+
return namedArgs;
589+
}
590+
591+
public void setRgdispidNamedArgs(DISPID[] namedArgs) {
592+
if(namedArgs == null) {
593+
namedArgs = new DISPID[0];
594+
}
595+
cNamedArgs = new UINT(namedArgs.length);
596+
rgdispidNamedArgs = new Memory(DISPID.SIZE * namedArgs.length);
597+
int[] rawData = new int[namedArgs.length];
598+
for(int i = 0; i < rawData.length; i++) {
599+
rawData[i] = namedArgs[i].intValue();
600+
}
601+
rgdispidNamedArgs.write(0, rawData, 0, namedArgs.length);
602+
}
603+
604+
public VARIANT[] getArgs() {
605+
if(this.rgvarg != null) {
606+
this.rgvarg.setArraySize(cArgs.intValue());
607+
return this.rgvarg.variantArg;
608+
} else {
609+
return new VARIANT[0];
610+
}
611+
}
612+
613+
public void setArgs(VARIANT[] arguments) {
614+
if(arguments == null) {
615+
arguments = new VARIANT[0];
616+
}
617+
618+
rgvarg = new VariantArg.ByReference(arguments);
619+
cArgs = new UINT(arguments.length);
620+
}
621+
575622
/**
576623
* Instantiates a new dispparams.
577624
*/

0 commit comments

Comments
 (0)