1 /*
2 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * A class to track key JVM instance info from the AT WinAccessBridge
28 */
29
30 #include "AccessBridgeDebug.h"
31 #include "AccessBridgeJavaVMInstance.h"
32 #include "AccessBridgeMessages.h"
33 #include "AccessBridgePackages.h"
34 #include "accessBridgeResource.h" // for debugging messages
35
36 #include <winbase.h>
37 #include <jni.h>
38
39 // The initialization must only be done one time and to provide for that the initialization
40 // is now done in WinAccessBridge and the CRITICAL_SECTION memory has been moved to there.
41 // send memory lock
42 //CRITICAL_SECTION sendMemoryIPCLock;
43 extern CRITICAL_SECTION sendMemoryIPCLock;
44
45 // protects the javaVMs chain while in use
46 extern bool isVMInstanceChainInUse;
47
48 DEBUG_CODE(extern HWND theDialogWindow);
49 extern "C" {
50 DEBUG_CODE(void AppendToCallInfo(char *s));
51 }
52
53
54 /**
55 *
56 *
57 */
AccessBridgeJavaVMInstance(HWND ourABWindow,HWND javaABWindow,long javaVMID,AccessBridgeJavaVMInstance * next)58 AccessBridgeJavaVMInstance::AccessBridgeJavaVMInstance(HWND ourABWindow,
59 HWND javaABWindow,
60 long javaVMID,
61 AccessBridgeJavaVMInstance *next) {
62 goingAway = FALSE;
63 // This should be called once. Moved to WinAccessBridge c'tor
64 //InitializeCriticalSection(&sendMemoryIPCLock);
65 ourAccessBridgeWindow = ourABWindow;
66 javaAccessBridgeWindow = javaABWindow;
67 vmID = javaVMID;
68 nextJVMInstance = next;
69 memoryMappedFileMapHandle = (HANDLE) 0;
70 memoryMappedView = (char *) 0;
71 sprintf(memoryMappedFileName, "AccessBridge-%p-%p.mmf",
72 ourAccessBridgeWindow, javaAccessBridgeWindow);
73 }
74
75 /**
76 *
77 *
78 */
~AccessBridgeJavaVMInstance()79 AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance() {
80 DEBUG_CODE(char buffer[256]);
81
82 DEBUG_CODE(AppendToCallInfo("***** in AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance\r\n"));
83 EnterCriticalSection(&sendMemoryIPCLock);
84
85 // if IPC memory mapped file view is valid, unmap it
86 goingAway = TRUE;
87 if (memoryMappedView != (char *) 0) {
88 DEBUG_CODE(sprintf(buffer, " unmapping memoryMappedView; view = %p\r\n", memoryMappedView));
89 DEBUG_CODE(AppendToCallInfo(buffer));
90 UnmapViewOfFile(memoryMappedView);
91 memoryMappedView = (char *) 0;
92 }
93 // if IPC memory mapped file handle map is open, close it
94 if (memoryMappedFileMapHandle != (HANDLE) 0) {
95 DEBUG_CODE(sprintf(buffer, " closing memoryMappedFileMapHandle; handle = %p\r\n", memoryMappedFileMapHandle));
96 DEBUG_CODE(AppendToCallInfo(buffer));
97 CloseHandle(memoryMappedFileMapHandle);
98 memoryMappedFileMapHandle = (HANDLE) 0;
99 }
100 LeaveCriticalSection(&sendMemoryIPCLock);
101
102 }
103
104 /**
105 * initiateIPC - sets up the memory-mapped file to do IPC messaging
106 * 1 file is created: to handle requests for information
107 * initiated from Windows AT. The package is placed into
108 * the memory-mapped file (char *memoryMappedView),
109 * and then a special SendMessage() is sent. When the
110 * JavaDLL returns from SendMessage() processing, the
111 * data will be in memoryMappedView. The SendMessage()
112 * return value tells us if all is right with the world.
113 *
114 * The set-up proces involves creating the memory-mapped
115 * file, and handshaking with the JavaDLL so it knows
116 * about it as well.
117 *
118 */
119 LRESULT
initiateIPC()120 AccessBridgeJavaVMInstance::initiateIPC() {
121 DEBUG_CODE(char debugBuf[256]);
122 DWORD errorCode;
123
124 DEBUG_CODE(AppendToCallInfo(" in AccessBridgeJavaVMInstance::initiateIPC()\r\n"));
125
126 // create Windows-initiated IPC file & map it to a ptr
127 memoryMappedFileMapHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
128 PAGE_READWRITE, 0,
129 // 8 bytes for return code
130 sizeof(WindowsInitiatedPackages) + 8,
131 memoryMappedFileName);
132 if (memoryMappedFileMapHandle == NULL) {
133 errorCode = GetLastError();
134 DEBUG_CODE(sprintf(debugBuf, " Failed to CreateFileMapping for %s, error: %X", memoryMappedFileName, errorCode));
135 DEBUG_CODE(AppendToCallInfo(debugBuf));
136 return errorCode;
137 } else {
138 DEBUG_CODE(sprintf(debugBuf, " CreateFileMapping worked - filename: %s\r\n", memoryMappedFileName));
139 DEBUG_CODE(AppendToCallInfo(debugBuf));
140 }
141
142 memoryMappedView = (char *) MapViewOfFile(memoryMappedFileMapHandle,
143 FILE_MAP_READ | FILE_MAP_WRITE,
144 0, 0, 0);
145 if (memoryMappedView == NULL) {
146 errorCode = GetLastError();
147 DEBUG_CODE(sprintf(debugBuf, " Failed to MapViewOfFile for %s, error: %X", memoryMappedFileName, errorCode));
148 DEBUG_CODE(AppendToCallInfo(debugBuf));
149 return errorCode;
150 } else {
151 DEBUG_CODE(sprintf(debugBuf, " MapViewOfFile worked - view: %p\r\n", memoryMappedView));
152 DEBUG_CODE(AppendToCallInfo(debugBuf));
153 }
154
155
156 // write some data to the memory mapped file
157 strcpy(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_QUERY);
158
159
160 // inform the JavaDLL that we've a memory mapped file ready for it
161 char buffer[sizeof(PackageType) + sizeof(MemoryMappedFileCreatedPackage)];
162 PackageType *type = (PackageType *) buffer;
163 MemoryMappedFileCreatedPackage *pkg = (MemoryMappedFileCreatedPackage *) (buffer + sizeof(PackageType));
164 *type = cMemoryMappedFileCreatedPackage;
165 pkg->bridgeWindow = ABHandleToLong(ourAccessBridgeWindow);
166 strncpy(pkg->filename, memoryMappedFileName, cMemoryMappedNameSize);
167 sendPackage(buffer, sizeof(buffer));
168
169
170 // look for the JavaDLL's answer to see if it could read the file
171 if (strcmp(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_ANSWER) != 0) {
172 DEBUG_CODE(sprintf(debugBuf, " JavaVM failed to deal with memory mapped file %s\r\n",
173 memoryMappedFileName));
174 DEBUG_CODE(AppendToCallInfo(debugBuf));
175 return -1;
176 } else {
177 DEBUG_CODE(sprintf(debugBuf, " Success! JavaVM accpeted our file\r\n"));
178 DEBUG_CODE(AppendToCallInfo(debugBuf));
179 }
180
181 return 0;
182 }
183
184 // -----------------------
185
186 /**
187 * sendPackage - uses SendMessage(WM_COPYDATA) to do IPC messaging
188 * with the Java AccessBridge DLL
189 *
190 * NOTE: WM_COPYDATA is only for one-way IPC; there
191 * is no way to return parameters (especially big ones)
192 * Use sendMemoryPackage() to do that!
193 */
194 LRESULT
sendPackage(char * buffer,long bufsize)195 AccessBridgeJavaVMInstance::sendPackage(char *buffer, long bufsize) {
196 COPYDATASTRUCT toCopy;
197 toCopy.dwData = 0; // 32-bits we could use for something...
198 toCopy.cbData = bufsize;
199 toCopy.lpData = buffer;
200
201 PrintDebugString("[INFO]: In AccessBridgeVMInstance::sendPackage");
202 PrintDebugString("[INFO]: javaAccessBridgeWindow: %p", javaAccessBridgeWindow);
203 /* This was SendMessage. Normally that is a blocking call. However, if
204 * SendMessage is sent to another process, e.g. another JVM and an incoming
205 * SendMessage is pending, control will be passed to the DialogProc to handle
206 * the incoming message. A bug occurred where this allowed an AB_DLL_GOING_AWAY
207 * message to be processed deleting an AccessBridgeJavaVMInstance object in
208 * the javaVMs chain. SendMessageTimeout with SMTO_BLOCK set will prevent the
209 * calling thread from processing other requests while waiting, i.e control
210 * will not be passed to the DialogProc. Also note that PostMessage or
211 * SendNotifyMessage can't be used. Although they don't allow transfer to
212 * the DialogProc they can't be used in cases where pointers are passed. This
213 * is because the referenced memory needs to be available when the other thread
214 * gets control.
215 */
216 UINT flags = SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG;
217 DWORD_PTR out; // not used
218 LRESULT lr = SendMessageTimeout( javaAccessBridgeWindow, WM_COPYDATA,
219 (WPARAM)ourAccessBridgeWindow, (LPARAM)&toCopy,
220 flags, 4000, &out );
221 return lr;
222 }
223
224
225 /**
226 * sendMemoryPackage - uses Memory-Mapped files to do IPC messaging
227 * with the Java AccessBridge DLL, informing the
228 * Java AccessBridge DLL via SendMessage that something
229 * is waiting for it in the shared file...
230 *
231 * In the SendMessage call, the third param (WPARAM) is
232 * the source HWND (ourAccessBridgeWindow in this case),
233 * and the fourth param (LPARAM) is the size in bytes of
234 * the package put into shared memory.
235 *
236 */
237 BOOL
sendMemoryPackage(char * buffer,long bufsize)238 AccessBridgeJavaVMInstance::sendMemoryPackage(char *buffer, long bufsize) {
239
240 // Protect against race condition where the memory mapped file is
241 // deallocated before the memory package is being sent
242 if (goingAway) {
243 return FALSE;
244 }
245 BOOL retval = FALSE;
246
247 DEBUG_CODE(char outputBuf[256]);
248 DEBUG_CODE(sprintf(outputBuf, "AccessBridgeJavaVMInstance::sendMemoryPackage(, %d)", bufsize));
249 DEBUG_CODE(AppendToCallInfo(outputBuf));
250
251 DEBUG_CODE(PackageType *type = (PackageType *) buffer);
252 DEBUG_CODE(if (*type == cGetAccessibleTextRangePackage) {)
253 DEBUG_CODE(AppendToCallInfo(" 'buffer' contains:"));
254 DEBUG_CODE(GetAccessibleTextRangePackage *pkg = (GetAccessibleTextRangePackage *) (buffer + sizeof(PackageType)));
255 DEBUG_CODE(sprintf(outputBuf, " PackageType = %X", *type));
256 DEBUG_CODE(AppendToCallInfo(outputBuf));
257 DEBUG_CODE(sprintf(outputBuf, " GetAccessibleTextRange: start = %d, end = %d, rText = %ls",
258 pkg->start, pkg->end, pkg->rText));
259 DEBUG_CODE(AppendToCallInfo(outputBuf));
260 DEBUG_CODE(})
261
262 EnterCriticalSection(&sendMemoryIPCLock);
263 {
264 // copy the package into shared memory
265 if (!goingAway) {
266 memcpy(memoryMappedView, buffer, bufsize);
267
268 DEBUG_CODE(PackageType *type = (PackageType *) memoryMappedView);
269 DEBUG_CODE(if (*type == cGetAccessibleTextItemsPackage) {)
270 DEBUG_CODE(AppendToCallInfo(" 'memoryMappedView' now contains:"));
271 DEBUG_CODE(GetAccessibleTextItemsPackage *pkg = (GetAccessibleTextItemsPackage *) (buffer + sizeof(PackageType)));
272 DEBUG_CODE(sprintf(outputBuf, " PackageType = %X", *type));
273 DEBUG_CODE(AppendToCallInfo(outputBuf));
274 DEBUG_CODE(})
275 }
276
277 if (!goingAway) {
278 // Let the recipient know there is a package waiting for them. The unset byte
279 // at end of buffer which will only be set if message is properly received
280 char *done = &memoryMappedView[bufsize];
281 *done = 0;
282
283 PrintDebugString("[INFO]: javaAccessBridgeWindow: %p", javaAccessBridgeWindow);
284 // See the comment above the call to SendMessageTimeout in SendPackage method above.
285 UINT flags = SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG;
286 DWORD_PTR out; // not used
287 SendMessageTimeout( javaAccessBridgeWindow, AB_MESSAGE_WAITING, (WPARAM)ourAccessBridgeWindow, (LPARAM)bufsize,
288 flags, 4000, &out );
289
290 // only succeed if message has been properly received
291 if(!goingAway) retval = (*done == 1);
292 }
293
294 // copy the package back from shared memory
295 if (!goingAway) {
296 memcpy(buffer, memoryMappedView, bufsize);
297 }
298 }
299 LeaveCriticalSection(&sendMemoryIPCLock);
300 return retval;
301 }
302
303
304 /**
305 * findAccessBridgeWindow - walk through linked list from where we are,
306 * return the HWND of the ABJavaVMInstance that
307 * matches the passed in vmID; no match: return 0
308 *
309 */
310 HWND
311 AccessBridgeJavaVMInstance::findAccessBridgeWindow(long javaVMID) {
312 PrintDebugString("[INFO]: In findAccessBridgeWindow");
313 // no need to recurse really
314 if (vmID == javaVMID) {
315 return javaAccessBridgeWindow;
316 } else {
317 isVMInstanceChainInUse = true;
318 AccessBridgeJavaVMInstance *current = nextJVMInstance;
319 while (current != (AccessBridgeJavaVMInstance *) 0) {
320 if (current->vmID == javaVMID) {
321 isVMInstanceChainInUse = false;
322 return current->javaAccessBridgeWindow;
323 }
324 current = current->nextJVMInstance;
325 }
326 isVMInstanceChainInUse = false;
327 }
328 return 0;
329 }
330
331 /**
332 * findABJavaVMInstanceFromJavaHWND - walk through linked list from
333 * where we are. Return the
334 * AccessBridgeJavaVMInstance
335 * of the ABJavaVMInstance that
336 * matches the passed in vmID;
337 * no match: return 0
338 */
339 AccessBridgeJavaVMInstance *
340 AccessBridgeJavaVMInstance::findABJavaVMInstanceFromJavaHWND(HWND window) {
341 PrintDebugString("[INFO]: In findABJavaInstanceFromJavaHWND");
342 // no need to recurse really
343 if (javaAccessBridgeWindow == window) {
344 return this;
345 } else {
346 isVMInstanceChainInUse = true;
347 AccessBridgeJavaVMInstance *current = nextJVMInstance;
348 while (current != (AccessBridgeJavaVMInstance *) 0) {
349 if (current->javaAccessBridgeWindow == window) {
350 isVMInstanceChainInUse = false;
351 return current;
352 }
353 current = current->nextJVMInstance;
354 }
355 }
356 isVMInstanceChainInUse = false;
357 return (AccessBridgeJavaVMInstance *) 0;
358 }
359