1
+ /* Copyright (c) 2014 Dr David H. Akehurst (itemis), All Rights Reserved
2
+ *
3
+ * This library is free software; you can redistribute it and/or
4
+ * modify it under the terms of the GNU Lesser General Public
5
+ * License as published by the Free Software Foundation; either
6
+ * version 2.1 of the License, or (at your option) any later version.
7
+ *
8
+ * This library is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ * Lesser General Public License for more details.
12
+ */
13
+ package com .sun .jna .platform .win32 .COM .util ;
14
+
15
+ import java .lang .Thread .UncaughtExceptionHandler ;
16
+ import java .util .concurrent .Callable ;
17
+ import java .util .concurrent .ExecutionException ;
18
+ import java .util .concurrent .ExecutorService ;
19
+ import java .util .concurrent .Executors ;
20
+ import java .util .concurrent .ThreadFactory ;
21
+ import java .util .concurrent .TimeUnit ;
22
+ import java .util .concurrent .TimeoutException ;
23
+
24
+ import com .sun .jna .platform .win32 .Ole32 ;
25
+ import com .sun .jna .platform .win32 .WinNT ;
26
+ import com .sun .jna .platform .win32 .COM .COMUtils ;
27
+
28
+ public class ComThread {
29
+ private static ThreadLocal <Boolean > isCOMThread = new ThreadLocal <Boolean >();
30
+
31
+ ExecutorService executor ;
32
+ Runnable firstTask ;
33
+ boolean requiresInitialisation ;
34
+ long timeoutMilliseconds ;
35
+ UncaughtExceptionHandler uncaughtExceptionHandler ;
36
+
37
+ public ComThread (final String threadName , long timeoutMilliseconds , UncaughtExceptionHandler uncaughtExceptionHandler ) {
38
+ this (threadName , timeoutMilliseconds , uncaughtExceptionHandler , Ole32 .COINIT_MULTITHREADED );
39
+ }
40
+
41
+ public ComThread (final String threadName , long timeoutMilliseconds , UncaughtExceptionHandler uncaughtExceptionHandler , final int coinitialiseExFlag ) {
42
+ this .requiresInitialisation = true ;
43
+ this .timeoutMilliseconds = timeoutMilliseconds ;
44
+ this .uncaughtExceptionHandler = uncaughtExceptionHandler ;
45
+ this .firstTask = new Runnable () {
46
+ @ Override
47
+ public void run () {
48
+ try {
49
+ //If we do not use COINIT_MULTITHREADED, it is necessary to have
50
+ // a message loop see -
51
+ // [http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5529/Understanding-COM-Apartments-Part-I.htm]
52
+ // [http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5533/Understanding-COM-Apartments-Part-II.htm]
53
+ WinNT .HRESULT hr = Ole32 .INSTANCE .CoInitializeEx (null , coinitialiseExFlag );
54
+ isCOMThread .set (true );
55
+ COMUtils .checkRC (hr );
56
+ ComThread .this .requiresInitialisation = false ;
57
+ } catch (Throwable t ) {
58
+ ComThread .this .uncaughtExceptionHandler .uncaughtException (Thread .currentThread (), t );
59
+ }
60
+ }
61
+ };
62
+ executor = Executors .newSingleThreadExecutor (new ThreadFactory () {
63
+
64
+ @ Override
65
+ public Thread newThread (Runnable r ) {
66
+ if (!ComThread .this .requiresInitialisation ) {
67
+ // something has gone wrong!
68
+ throw new RuntimeException ("ComThread executor has a problem." );
69
+ }
70
+ Thread thread = new Thread (r , threadName );
71
+ //make sure this is a daemon thread, or it will stop JVM existing
72
+ // if program does not call terminate();
73
+ thread .setDaemon (true );
74
+
75
+ thread .setUncaughtExceptionHandler (new UncaughtExceptionHandler () {
76
+ @ Override
77
+ public void uncaughtException (Thread t , Throwable e ) {
78
+ ComThread .this .requiresInitialisation = true ;
79
+ ComThread .this .uncaughtExceptionHandler .uncaughtException (t , e );
80
+ }
81
+ });
82
+
83
+ return thread ;
84
+ }
85
+ });
86
+
87
+ }
88
+
89
+ /**
90
+ * Stop the COM Thread.
91
+ *
92
+ * @param timeoutMilliseconds
93
+ * number of milliseconds to wait for a clean shutdown before a
94
+ * forced shutdown is attempted
95
+ */
96
+ public void terminate (long timeoutMilliseconds ) {
97
+ try {
98
+
99
+ executor .submit (new Runnable () {
100
+ @ Override
101
+ public void run () {
102
+ Ole32 .INSTANCE .CoUninitialize ();
103
+ }
104
+ }).get (timeoutMilliseconds , TimeUnit .MILLISECONDS );
105
+
106
+ executor .shutdown ();
107
+
108
+ } catch (InterruptedException e ) {
109
+ e .printStackTrace ();
110
+ } catch (ExecutionException e ) {
111
+ e .printStackTrace ();
112
+ } catch (TimeoutException e ) {
113
+ executor .shutdownNow ();
114
+ }
115
+ }
116
+
117
+ @ Override
118
+ protected void finalize () throws Throwable {
119
+ if (!executor .isShutdown ()) {
120
+ this .terminate (100 );
121
+ }
122
+ }
123
+
124
+ static void setComThread (boolean value ) {
125
+ isCOMThread .set (value );
126
+ }
127
+
128
+ public <T > T execute (Callable <T > task ) throws TimeoutException , InterruptedException , ExecutionException {
129
+ // If the call is done on a COM thread, invoke directly
130
+ // if the call comes from outside the invokation is dispatched
131
+ // into the Dispatch Thread.
132
+ Boolean comThread = isCOMThread .get ();
133
+ if (comThread == null ) {
134
+ comThread = false ;
135
+ }
136
+ if (comThread ) {
137
+ try {
138
+ return task .call ();
139
+ } catch (Exception ex ) {
140
+ throw new ExecutionException (ex );
141
+ }
142
+ } else {
143
+ if (this .requiresInitialisation ) {
144
+ executor .execute (firstTask );
145
+ }
146
+ return executor .submit (task ).get (this .timeoutMilliseconds , TimeUnit .MILLISECONDS );
147
+ }
148
+ }
149
+
150
+ }
0 commit comments