001 package org.bridj.cpp.com;
002
003 import java.lang.reflect.Type;
004 import java.lang.reflect.Method;
005
006 import org.bridj.ValuedEnum;
007 import org.bridj.FlagSet;
008 import org.bridj.BridJ;
009 import org.bridj.Pointer;
010 import org.bridj.CRuntime;
011 import org.bridj.Platform;
012 import org.bridj.Pointer.StringType;
013 import org.bridj.ann.Convention;
014 import org.bridj.ann.Library;
015 import org.bridj.ann.Ptr;
016 import org.bridj.ann.Runtime;
017 import org.bridj.cpp.CPPRuntime;
018 import static org.bridj.cpp.com.VARENUM.*;
019 import org.bridj.cpp.com.VARIANT.__VARIANT_NAME_1_union;
020 import org.bridj.cpp.com.VARIANT.__VARIANT_NAME_1_union.__tagVARIANT;
021 import org.bridj.cpp.com.VARIANT.__VARIANT_NAME_1_union.__tagVARIANT.__VARIANT_NAME_3_union;
022 import org.bridj.util.Utils;
023 import static org.bridj.Pointer.*;
024 import static org.bridj.cpp.com.OLEAutomationLibrary.*;
025
026 /*
027 * Adding Icons, Previews and Shortcut Menus :
028 * http://msdn.microsoft.com/en-us/library/bb266530(VS.85).aspx
029 *
030 * TODO CoCreateInstanceEx
031 * TODO CoRegisterClassObject
032 * TODO CoRevokeClassObject
033 * TODO CoCreateGuid
034 *
035 * IDL syntax :
036 * http://caml.inria.fr/pub/old_caml_site/camlidl/htmlman/main002.html
037 *
038 * Registering a Running EXE Server :
039 * http://msdn.microsoft.com/en-us/library/ms680076(VS.85).aspx
040 */
041 /**
042 * Microsoft COM runtime, along with useful constants and methods.<br>
043 * All COM classes must extends {@link org.bridj.cpp.com.IUnknown} and hence inherit from it the correct {@link org.bridj.ann.Runtime} annotation that references {@link org.bridj.cpp.com.COMRuntime}.
044 */
045 @Library("Ole32")
046 @Runtime(CRuntime.class)
047 @Convention(Convention.Style.StdCall)
048 public class COMRuntime extends CPPRuntime {
049 static {
050 if (Platform.isWindows())
051 BridJ.register();
052 }
053 public static final int
054 CLSCTX_INPROC_SERVER = 0x1,
055 CLSCTX_INPROC_HANDLER = 0x2,
056 CLSCTX_LOCAL_SERVER = 0x4,
057 CLSCTX_INPROC_SERVER16 = 0x8,
058 CLSCTX_REMOTE_SERVER = 0x10,
059 CLSCTX_INPROC_HANDLER16 = 0x20,
060 CLSCTX_RESERVED1 = 0x40,
061 CLSCTX_RESERVED2 = 0x80,
062 CLSCTX_RESERVED3 = 0x100,
063 CLSCTX_RESERVED4 = 0x200,
064 CLSCTX_NO_CODE_DOWNLOAD = 0x400,
065 CLSCTX_RESERVED5 = 0x800,
066 CLSCTX_NO_CUSTOM_MARSHAL = 0x1000,
067 CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000,
068 CLSCTX_NO_FAILURE_LOG = 0x4000,
069 CLSCTX_DISABLE_AAA = 0x8000,
070 CLSCTX_ENABLE_AAA = 0x10000,
071 CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000,
072 CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000,
073 CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000,
074 CLSCTX_ENABLE_CLOAKING = 0x100000,
075 CLSCTX_PS_DLL = 0x80000000;
076
077 public static final int
078 CLSCTX_INPROC = CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
079 CLSCTX_ALL =(CLSCTX_INPROC_SERVER|
080 CLSCTX_INPROC_HANDLER|
081 CLSCTX_LOCAL_SERVER|
082 CLSCTX_REMOTE_SERVER),
083 CLSCTX_SERVER = (CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER);
084
085 public static final int S_OK = 0,
086 S_FALSE = 1,
087 REGDB_E_CLASSNOTREG = 0x80040154,
088 CLASS_E_NOAGGREGATION = 0x80040110,
089 CO_E_NOTINITIALIZED = 0x800401F0;
090
091 public static final int E_UNEXPECTED = 0x8000FFFF;
092 public static final int E_NOTIMPL = 0x80004001;
093 public static final int E_OUTOFMEMORY = 0x8007000E;
094 public static final int E_INVALIDARG = 0x80070057;
095 public static final int E_NOINTERFACE = 0x80004002;
096 public static final int E_POINTER = 0x80004003;
097 public static final int E_HANDLE = 0x80070006;
098 public static final int E_ABORT = 0x80004004;
099 public static final int E_FAIL = 0x80004005;
100 public static final int E_ACCESSDENIED = 0x80070005;
101
102
103 public static final int DISP_E_BADVARTYPE = -2147352568;
104 public static final int DISP_E_NOTACOLLECTION = -2147352559;
105 public static final int DISP_E_MEMBERNOTFOUND = -2147352573;
106 public static final int DISP_E_ARRAYISLOCKED = -2147352563;
107 public static final int DISP_E_EXCEPTION = -2147352567;
108 public static final int DISP_E_TYPEMISMATCH = -2147352571;
109 public static final int DISP_E_BADINDEX = -2147352565;
110 public static final int DISP_E_BADCALLEE = -2147352560;
111 public static final int DISP_E_OVERFLOW = -2147352566;
112 public static final int DISP_E_UNKNOWNINTERFACE = -2147352575;
113 public static final int DISP_E_DIVBYZERO = -2147352558;
114 public static final int DISP_E_UNKNOWNLCID = -2147352564;
115 public static final int DISP_E_PARAMNOTOPTIONAL = -2147352561;
116 public static final int DISP_E_PARAMNOTFOUND = -2147352572;
117 public static final int DISP_E_BADPARAMCOUNT = -2147352562;
118 public static final int DISP_E_BUFFERTOOSMALL = -2147352557;
119 public static final int DISP_E_UNKNOWNNAME = -2147352570;
120 public static final int DISP_E_NONAMEDARGS = -2147352569;
121
122 public static interface COINIT {
123 public final int
124 COINIT_APARTMENTTHREADED = 0x2, // Apartment model
125 COINIT_MULTITHREADED = 0x0, // OLE calls objects on any thread.
126 COINIT_DISABLE_OLE1DDE = 0x4, // Don't use DDE for Ole1 support.
127 COINIT_SPEED_OVER_MEMORY = 0x8;
128 }
129
130 @Deprecated
131 public static native int CoCreateInstance(
132 Pointer<Byte> rclsid,
133 Pointer<IUnknown> pUnkOuter,
134 int dwClsContext,
135 Pointer<Byte> riid,
136 Pointer<Pointer<?>> ppv
137 );
138
139 static native int CoInitializeEx(@Ptr long pvReserved, int dwCoInit);
140 static native int CoInitialize(@Ptr long pvReserved);
141 static native void CoUninitialize();
142
143 static void error(int err) {
144 switch (err) {
145 case E_INVALIDARG:
146 case E_OUTOFMEMORY:
147 case E_UNEXPECTED:
148 throw new RuntimeException("Error " + Integer.toHexString(err));
149 case S_OK:
150 return;
151 case CO_E_NOTINITIALIZED:
152 throw new RuntimeException("CoInitialized wasn't called !!");
153 case E_NOINTERFACE:
154 throw new RuntimeException("Interface does not inherit from class");
155 case E_POINTER:
156 throw new RuntimeException("Allocated pointer pointer is null !!");
157 default:
158 throw new RuntimeException("Unexpected COM error code : " + err);
159 }
160 }
161 /**
162 * Get the IID declared for a class using the {@link IID} annotation.
163 * @throws RuntimeException if the class isn't annotated with IID
164 */
165 public static <I extends IUnknown> Pointer<Byte> getIID(Class<I> type) {
166 IID id = type.getAnnotation(IID.class);
167 if (id == null)
168 throw new RuntimeException("No " + IID.class.getName() + " annotation set on type " + type.getName() + " !");
169
170 return (Pointer)parseGUID(id.value());
171 }
172
173 /**
174 * Get the CLSID declared for a class using the {@link CLSID} annotation.
175 * @throws RuntimeException if the class isn't annotated with CLSID
176 */
177 public static <I extends IUnknown> Pointer<Byte> getCLSID(Class<I> type) {
178 CLSID id = type.getAnnotation(CLSID.class);
179 if (id == null)
180 throw new RuntimeException("No " + CLSID.class.getName() + " annotation set on type " + type.getName() + " !");
181
182 return (Pointer)parseGUID(id.value());
183 }
184 static ThreadLocal<Object> comInitializer = new ThreadLocal<Object>() {
185 @Override
186 protected Object initialValue() {
187 error(CoInitializeEx(0, COINIT.COINIT_MULTITHREADED));
188 return new Object() {
189 @Override
190 protected void finalize() throws Throwable {
191 CoUninitialize();
192 }
193 };
194 }
195 };
196
197 @Override
198 protected boolean isSymbolOptional(Method method) {
199 return true;
200 }
201
202 /**
203 * Initialize COM the current thread (uninitialization is done automatically upon thread death).<br>
204 * Calls CoInitialize with COINIT_MULTITHREADED max once per thread.<br>
205 * This is called automatically in {@link COMRuntime#newInstance(Class)}, so you'll typically never need to call this method by hand.
206 */
207 public static void initialize() {
208 comInitializer.get();
209 }
210 public static <I extends IUnknown> I newInstance(Class<I> type) throws ClassNotFoundException {
211 return newInstance(type, type);
212 }
213 public static <T extends IUnknown, I extends IUnknown> I newInstance(Class<T> instanceClass, Class<I> instanceInterface) throws ClassNotFoundException {
214 initialize();
215
216 Pointer<Pointer<?>> p = Pointer.allocatePointer();
217 Pointer<Byte> clsid = getCLSID(instanceClass), uuid = getIID(instanceInterface);
218 try {
219 int ret = CoCreateInstance(clsid, null, CLSCTX_ALL, uuid, p);
220 if (ret == REGDB_E_CLASSNOTREG)
221 throw new ClassNotFoundException("COM class is not registered : " + instanceClass.getSimpleName() + " (clsid = " + clsid.getCString() + ")");
222 error(ret);
223
224 Pointer<?> inst = p.getPointer();
225 if (inst == null)
226 throw new RuntimeException("Serious low-level issue : CoCreateInstance executed fine but we only retrieved a null pointer !");
227
228 I instance = inst.getNativeObject(instanceInterface);
229 return instance;
230 } finally {
231 Pointer.release(p, clsid, uuid);
232 }
233 }
234
235 private static final String model = "00000000-0000-0000-0000-000000000000";
236
237 // Need to parse as (int, short, short, char[8])
238 public static Pointer<?> parseGUID(String descriptor) {
239 Pointer<?> out = Pointer.allocateBytes(16 + 4);
240 descriptor = descriptor.replaceAll("-", "");
241 if (descriptor.length() != 32)
242 throw new RuntimeException("Expected something like :\n" + model + "\nBut got instead :\n" +descriptor);
243
244 out.setIntAtOffset(0, (int)Long.parseLong(descriptor.substring(0, 8), 16));
245 out.setShortAtOffset(4, (short)Long.parseLong(descriptor.substring(8, 12), 16));
246 out.setShortAtOffset(6, (short)Long.parseLong(descriptor.substring(12, 16), 16));
247 for (int i = 0; i < 8; i++)
248 out.setByteAtOffset(8 + i, (byte)Long.parseLong(descriptor.substring(16 + i * 2, 16 + i * 2 + 2), 16));
249
250 return out;
251 }
252
253 static ValuedEnum<VARENUM> getType(VARIANT v) {
254 __VARIANT_NAME_1_union v1 = v.__VARIANT_NAME_1();
255 __tagVARIANT v2 = v1.__VARIANT_NAME_2();
256 short vt = v2.vt();
257 return FlagSet.fromValue(vt, VARENUM.class);
258 }
259 static VARIANT setType(VARIANT v, ValuedEnum<VARENUM> vt) {
260 __VARIANT_NAME_1_union v1 = v.__VARIANT_NAME_1();
261 __tagVARIANT v2 = v1.__VARIANT_NAME_2();
262 v2.vt((short)vt.value());
263 return v;
264 }
265 static VARIANT.__VARIANT_NAME_1_union.__tagVARIANT.__VARIANT_NAME_3_union getValues(VARIANT v) {
266 __VARIANT_NAME_1_union v1 = v.__VARIANT_NAME_1();
267 __tagVARIANT v2 = v1.__VARIANT_NAME_2();
268 __VARIANT_NAME_3_union v3 = v2.__VARIANT_NAME_3();
269 return v3;
270 }
271
272 /**
273 * Convert the VARIANT value to an equivalent Java value.
274 * @throws UnsupportedOperationException if the VARIANT type is not handled yet
275 * @throws RuntimeException if the VARIANT is invalid
276 */
277 public static Object getValue(VARIANT v) {
278
279 FlagSet<VARENUM> vt = FlagSet.fromValue(getType(v));
280 __VARIANT_NAME_3_union values = getValues(v);
281 if (vt.has(VT_BYREF)) {
282 switch (vt.without(VT_BYREF).toEnum()) {
283 case VT_DISPATCH:
284 return values.ppdispVal();
285 case VT_UNKNOWN:
286 return values.ppunkVal();
287 case VT_VARIANT:
288 return values.pvarVal();
289 case VT_I1 :
290 case VT_UI1 :
291 return values.pbVal();
292 /* UINT16 */
293 case VT_I2 :
294 case VT_UI2 :
295 return values.piVal();
296 /* UINT32 */
297 case VT_I4 :
298 case VT_UI4 :
299 return values.plVal();
300 case VT_R4:
301 return values.pfltVal();
302 case VT_R8:
303 return values.pdblVal();
304 /* UINT64 */
305 case VT_I8 :
306 case VT_UI8 :
307 return values.pllVal();
308 /* BOOL */
309 case VT_BOOL :
310 return values.pbVal().as(Boolean.class);
311
312 case VT_BSTR:
313 return values.pbstrVal();
314 case VT_LPSTR:
315 return values.byref().getCString();
316 case VT_LPWSTR:
317 return values.byref().getWideCString();
318 case VT_PTR:
319 default:
320 return values.byref();
321 }
322 }
323 switch (vt.toEnum()) {
324 /* UINT8 */
325 case VT_I1 :
326 case VT_UI1 :
327 return values.bVal();
328 /* UINT16 */
329 case VT_I2 :
330 case VT_UI2 :
331 return values.uiVal();
332 /* UINT32 */
333 case VT_I4 :
334 case VT_UI4 :
335 return values.ulVal();
336 /* UINT64 */
337 case VT_I8 :
338 case VT_UI8 :
339 return values.ullVal();
340 /* BOOL */
341 case VT_BOOL :
342 return values.bVal() != 0;
343 case VT_R4:
344 return values.fltVal();
345 case VT_R8:
346 return values.dblVal();
347 case VT_BSTR:
348 return values.bstrVal().getString(StringType.BSTR);
349 case VT_EMPTY:
350 return null;
351 default:
352 throw new UnsupportedOperationException("Conversion not implemented yet from VARIANT type " + vt + " to Java !");
353 }
354 }
355
356 static void change(VARIANT v, ValuedEnum<VARENUM> vt) {
357 Pointer<VARIANT> pv = pointerTo(v);
358 int res = VariantChangeType(pv, pv, (short)0, (short)vt.value());
359 assert res == S_OK;
360 }
361 public static VARIANT setValue(VARIANT v, Object value) {
362 //ValuedEnum<VARENUM> vt;
363 __VARIANT_NAME_3_union values = getValues(v);
364 if (value == null) {
365 change(v, VT_EMPTY);
366 //values.byref(null);
367 } else if (value instanceof Integer) {
368 change(v, VT_I4);
369 values.lVal((Integer)value);
370 } else if (value instanceof Long) {
371 change(v, VT_I8);
372 values.llval((Long)value);
373 } else if (value instanceof Short) {
374 change(v, VT_I2);
375 values.iVal((Short)value);
376 } else if (value instanceof Byte) {
377 change(v, VT_I1);
378 values.bVal((Byte)value);
379 } else if (value instanceof Float) {
380 change(v, VT_R4);
381 values.fltVal((Float)value);
382 } else if (value instanceof Double) {
383 change(v, VT_I8);
384 values.dblVal((Double)value);
385 } else if (value instanceof Character) {
386 change(v, VT_I2);
387 values.iVal((short)((Character)value).charValue());
388 } else if (value instanceof String) {
389 change(v, VT_BSTR);
390 /*String str = (String)value;
391 int len = str.length();
392 int capacity = SysStringLen(values.bstrVal());
393 /Pointer<Character> chars =
394 if (len > capacity)
395 SysReAllocStringLen values.bstrVal()
396 SysReAllocString(values.bstrVal().getReference(),
397 */
398 values.bstrVal().setString((String)value, StringType.BSTR);
399 } else if (value instanceof Pointer) {
400 Pointer ptr = (Pointer)value;
401 Type targetType = ptr.getTargetType();
402 Class targetClass = Utils.getClass(targetType);
403 if (targetClass == null)
404 change(v, VT_PTR);
405 else {
406 VARENUM ve;
407 if (targetClass == Integer.class || targetClass == int.class)
408 ve = VT_I4;
409 else if(targetClass == Long.class || targetClass == long.class)
410 ve = VT_I8;
411 else if(targetClass == Short.class || targetClass == short.class)
412 ve = VT_I2;
413 else if(targetClass == Byte.class || targetClass == byte.class)
414 ve = VT_I1;
415 else if(targetClass == Character.class || targetClass == char.class)
416 ve = VT_LPWSTR; // TODO
417 else if(targetClass == Boolean.class || targetClass == boolean.class)
418 ve = VT_BOOL;
419 else if(targetClass == Float.class || targetClass == float.class)
420 ve = VT_R4;
421 else if(targetClass == Double.class || targetClass == double.class)
422 ve = VT_R8;
423 else if(Pointer.class.isAssignableFrom(targetClass))
424 ve = VT_PTR;
425 else
426 ve = null; // TODO
427
428 change(v, FlagSet.fromValues(VT_BYREF, ve));
429 }
430 } else
431 throw new UnsupportedOperationException("Unable to convert an object of type " + value.getClass().getName() + " to a COM VARIANT object !");
432
433 //setType(v, vt);
434 return v;
435 }
436
437 public static String toString(VARIANT v) {
438 StringBuilder b = new StringBuilder("Variant(value = ");
439 try {
440 b.append(getValue(v));
441 } catch (Throwable th) {
442 b.append("?");
443 }
444 b.append(", type = ").append(getType(v)).append(")");
445
446 return b.toString();
447 }
448 public static VARIANT clone(VARIANT v) {
449 VARIANT c = new VARIANT();
450 int res = VariantCopy(pointerTo(v), pointerTo(c));
451 switch (res) {
452 case S_OK:
453 break;
454 case DISP_E_ARRAYISLOCKED:
455 throw new RuntimeException("The variant contains an array that is locked.");
456 case DISP_E_BADVARTYPE:
457 throw new RuntimeException("The source and destination have an invalid variant type (usually uninitialized).");
458 case E_OUTOFMEMORY:
459 throw new RuntimeException("Memory could not be allocated for the copy.");
460 case E_INVALIDARG:
461 throw new RuntimeException("One of the arguments is invalid.");
462 default:
463 throw new RuntimeException("Grave error : unexpected error code for VariantCopy : " + res);
464 }
465 return c;
466 }
467 }