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    }