All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.jna.platform.win32.COM.util.Factory Maven / Gradle / Ivy

/* Copyright (c) 2014 Dr David H. Akehurst (itemis), All Rights Reserved
 *
 * The contents of this file is dual-licensed under 2
 * alternative Open Source/Free licenses: LGPL 2.1 or later and
 * Apache License 2.0. (starting with JNA version 4.0.0).
 *
 * You can freely decide which license you want to apply to
 * the project.
 *
 * You may obtain a copy of the LGPL License at:
 *
 * http://www.gnu.org/licenses/licenses.html
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "LGPL2.1".
 *
 * You may obtain a copy of the Apache License at:
 *
 * http://www.apache.org/licenses/
 *
 * A copy is also included in the downloadable source code package
 * containing JNA, in file "AL2.0".
 */

package com.sun.jna.platform.win32.COM.util;

import com.sun.jna.platform.win32.COM.COMException;
import com.sun.jna.platform.win32.COM.IDispatch;
import com.sun.jna.platform.win32.COM.IDispatchCallback;
import com.sun.jna.platform.win32.COM.util.annotation.ComObject;
import com.sun.jna.platform.win32.Guid;
import com.sun.jna.platform.win32.OaIdl;
import com.sun.jna.platform.win32.OleAuto;
import com.sun.jna.platform.win32.Variant;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

/**
 * Factory is intended as a simpler to use version of ObjectFactory.
 *
 * 

The Factory abstracts the necessity to handle COM threading by introducing * a dispatching thread, that is correctly COM initialized and is used to handle * all outgoing calls.

* *

NOTE: Remember to call factory.getComThread().terminate() at some * appropriate point, when the factory is not used anymore

*/ public class Factory extends ObjectFactory { private ComThread comThread; public Factory() { this(new ComThread("Default Factory COM Thread", 5000, new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { //ignore } })); } public Factory(ComThread comThread) { this.comThread = comThread; } private class ProxyObject2 implements InvocationHandler { private final Object delegate; public ProxyObject2(Object delegate) { this.delegate = delegate; } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { if (args != null) { for (int i = 0; i < args.length; i++) { if (args[i] != null && Proxy.isProxyClass(args[i].getClass())) { InvocationHandler ih = Proxy.getInvocationHandler(args[i]); if (ih instanceof ProxyObject2) { args[i] = ((ProxyObject2) ih).delegate; } } } } return runInComThread(new Callable() { @Override public Object call() throws Exception { return method.invoke(delegate, args); } }); } } private class CallbackProxy2 extends CallbackProxy { public CallbackProxy2(ObjectFactory factory, Class comEventCallbackInterface, IComEventCallbackListener comEventCallbackListener) { super(factory, comEventCallbackInterface, comEventCallbackListener); } @Override public WinNT.HRESULT Invoke(OaIdl.DISPID dispIdMember, Guid.REFIID riid, WinDef.LCID lcid, WinDef.WORD wFlags, OleAuto.DISPPARAMS.ByReference pDispParams, Variant.VARIANT.ByReference pVarResult, OaIdl.EXCEPINFO.ByReference pExcepInfo, IntByReference puArgErr) { // Mark callbacks as COM initialized - so normal inline call // invocation can be used -- see ComThread# ComThread.setComThread(true); try { return super.Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } finally { ComThread.setComThread(false); } } } @Override public T createProxy(Class comInterface, IDispatch dispatch) { T result = super.createProxy(comInterface, dispatch); ProxyObject2 po2 = new ProxyObject2(result); Object proxy = Proxy.newProxyInstance(comInterface.getClassLoader(), new Class[]{comInterface}, po2); return (T) proxy; } @Override Guid.GUID discoverClsId(final ComObject annotation) { return runInComThread(new Callable() { public Guid.GUID call() throws Exception { return Factory.super.discoverClsId(annotation); } }); } @Override public T fetchObject(final Class comInterface) throws COMException { // Proxy2 is added by createProxy inside fetch Object return runInComThread(new Callable() { public T call() throws Exception { return Factory.super.fetchObject(comInterface); } }); } @Override public T createObject(final Class comInterface) { // Proxy2 is added by createProxy inside fetch Object return runInComThread(new Callable() { public T call() throws Exception { return Factory.super.createObject(comInterface); } }); } @Override IDispatchCallback createDispatchCallback(Class comEventCallbackInterface, IComEventCallbackListener comEventCallbackListener) { return new CallbackProxy2(this, comEventCallbackInterface, comEventCallbackListener); } @Override public IRunningObjectTable getRunningObjectTable() { return super.getRunningObjectTable(); } private T runInComThread(Callable callable) { try { return comThread.execute(callable); } catch (TimeoutException ex) { throw new RuntimeException(ex); } catch (InterruptedException ex) { throw new RuntimeException(ex); } catch (ExecutionException ex) { Throwable cause = ex.getCause(); if (cause instanceof RuntimeException) { appendStacktrace(ex, cause); throw (RuntimeException) cause; } else if (cause instanceof InvocationTargetException) { cause = ((InvocationTargetException) cause).getTargetException(); if (cause instanceof RuntimeException) { appendStacktrace(ex, cause); throw (RuntimeException) cause; } } throw new RuntimeException(ex); } } /** * Append the stack trace available via caughtException to the stack trace * of toBeThrown. The combined stack trace is reassigned to toBeThrown */ private static void appendStacktrace(Exception caughtException, Throwable toBeThrown) { StackTraceElement[] upperTrace = caughtException.getStackTrace(); StackTraceElement[] lowerTrace = toBeThrown.getStackTrace(); StackTraceElement[] trace = new StackTraceElement[upperTrace.length + lowerTrace.length]; System.arraycopy(upperTrace, 0, trace, lowerTrace.length, upperTrace.length); System.arraycopy(lowerTrace, 0, trace, 0, lowerTrace.length); toBeThrown.setStackTrace(trace); } public ComThread getComThread() { return comThread; } }