20
20
21
21
import com .sun .jna .Pointer ;
22
22
import com .sun .jna .WString ;
23
- import com .sun .jna .platform .win32 .Guid ;
24
23
import com .sun .jna .platform .win32 .Guid .IID ;
25
24
import com .sun .jna .platform .win32 .Guid .REFIID ;
26
25
import com .sun .jna .platform .win32 .OaIdl .DISPID ;
29
28
import com .sun .jna .platform .win32 .OleAuto .DISPPARAMS ;
30
29
import com .sun .jna .platform .win32 .Variant ;
31
30
import com .sun .jna .platform .win32 .Variant .VARIANT ;
32
- import com .sun .jna .platform .win32 .Variant .VariantArg ;
33
31
import com .sun .jna .platform .win32 .WinDef .LCID ;
34
32
import com .sun .jna .platform .win32 .WinDef .UINT ;
35
33
import com .sun .jna .platform .win32 .WinDef .UINTByReference ;
40
38
import com .sun .jna .platform .win32 .COM .COMUtils ;
41
39
import com .sun .jna .platform .win32 .COM .Dispatch ;
42
40
import com .sun .jna .platform .win32 .COM .DispatchListener ;
43
- import com .sun .jna .platform .win32 .COM .IDispatch ;
44
41
import com .sun .jna .platform .win32 .COM .IDispatchCallback ;
45
42
import com .sun .jna .platform .win32 .COM .Unknown ;
46
43
import com .sun .jna .platform .win32 .COM .util .annotation .ComEventCallback ;
49
46
import com .sun .jna .ptr .PointerByReference ;
50
47
51
48
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
+
53
58
public CallbackProxy (Factory factory , Class <?> comEventCallbackInterface ,
54
59
IComEventCallbackListener comEventCallbackListener ) {
55
60
this .factory = factory ;
@@ -90,6 +95,11 @@ Map<DISPID, Method> createDispIdMap(Class<?> comEventCallbackInterface) {
90
95
if (-1 == dispId ) {
91
96
dispId = this .fetchDispIdFromName (annotation );
92
97
}
98
+ if (dispId == -1 ) {
99
+ CallbackProxy .this .comEventCallbackListener .errorReceivingCallbackEvent (
100
+ "DISPID for " + meth .getName () + " not found" ,
101
+ null );
102
+ }
93
103
map .put (new DISPID (dispId ), meth );
94
104
}
95
105
}
@@ -105,50 +115,105 @@ int fetchDispIdFromName(ComEventCallback annotation) {
105
115
void invokeOnThread (final DISPID dispIdMember , final REFIID riid , LCID lcid , WORD wFlags ,
106
116
final DISPPARAMS .ByReference pDispParams ) {
107
117
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 ) {
118
122
CallbackProxy .this .comEventCallbackListener .errorReceivingCallbackEvent (
119
123
"No method found with dispId = " + dispIdMember , null );
120
124
return ;
121
125
}
122
126
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
+
123
157
// Arguments are converted to the JAVA side and IDispatch Interfaces
124
158
// are wrapped into an ProxyObject if so requested.
125
159
//
126
160
// Out-Parameter need to be specified as VARIANT, VARIANT args are
127
161
// 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 ;
138
180
}
181
+ Class targetClass = paramTypes [targetPosition ];
182
+ Variant .VARIANT varg = arguments [i ];
183
+ params [targetPosition ] = Convert .toJavaObject (varg , targetClass , factory , true , false );
139
184
}
140
185
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
+ }
147
209
}
148
- eventMethod .invoke (comEventCallbackListener , margs .toArray ());
210
+ }
211
+
212
+ try {
213
+ eventMethod .invoke (comEventCallbackListener , params );
149
214
} 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 ) {
152
217
if (o == null ) {
153
218
decodedClassNames .add ("NULL" );
154
219
} else {
0 commit comments