aboutsummaryrefslogtreecommitdiff
path: root/src/windows/native/sun/bridge/AccessBridgeJavaVMInstance.cpp
blob: 2647db6d0dcce7057ac88a90314520906e26d916 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/*
 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * A class to track key JVM instance info from the AT WinAccessBridge
 */

#include "AccessBridgeDebug.h"
#include "AccessBridgeJavaVMInstance.h"
#include "AccessBridgeMessages.h"
#include "AccessBridgePackages.h"
#include "accessBridgeResource.h"       // for debugging messages

#include <winbase.h>
#include <jni.h>

// The initialization must only be done one time and to provide for that the initialization
// is now done in WinAccessBridge and the CRITICAL_SECTION memory has been moved to there.
// send memory lock
//CRITICAL_SECTION sendMemoryIPCLock;
extern CRITICAL_SECTION sendMemoryIPCLock;

// protects the javaVMs chain while in use
extern bool isVMInstanceChainInUse;

DEBUG_CODE(extern HWND theDialogWindow);
extern "C" {
    DEBUG_CODE(void AppendToCallInfo(char *s));
}


/**
 *
 *
 */
AccessBridgeJavaVMInstance::AccessBridgeJavaVMInstance(HWND ourABWindow,
                                                       HWND javaABWindow,
                                                       long javaVMID,
                                                       AccessBridgeJavaVMInstance *next) {
    goingAway = FALSE;
    // This should be called once.  Moved to WinAccessBridge c'tor
    //InitializeCriticalSection(&sendMemoryIPCLock);
    ourAccessBridgeWindow = ourABWindow;
    javaAccessBridgeWindow = javaABWindow;
    vmID = javaVMID;
    nextJVMInstance = next;
    memoryMappedFileMapHandle = (HANDLE) 0;
    memoryMappedView = (char *) 0;
    sprintf(memoryMappedFileName, "AccessBridge-%p-%p.mmf",
            ourAccessBridgeWindow, javaAccessBridgeWindow);
}

/**
 *
 *
 */
AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance() {
    DEBUG_CODE(char buffer[256]);

    DEBUG_CODE(AppendToCallInfo("***** in AccessBridgeJavaVMInstance::~AccessBridgeJavaVMInstance\r\n"));
    EnterCriticalSection(&sendMemoryIPCLock);

    // if IPC memory mapped file view is valid, unmap it
    goingAway = TRUE;
    if (memoryMappedView != (char *) 0) {
        DEBUG_CODE(sprintf(buffer, "  unmapping memoryMappedView; view = %p\r\n", memoryMappedView));
        DEBUG_CODE(AppendToCallInfo(buffer));
        UnmapViewOfFile(memoryMappedView);
        memoryMappedView = (char *) 0;
    }
    // if IPC memory mapped file handle map is open, close it
    if (memoryMappedFileMapHandle != (HANDLE) 0) {
        DEBUG_CODE(sprintf(buffer, "  closing memoryMappedFileMapHandle; handle = %p\r\n", memoryMappedFileMapHandle));
        DEBUG_CODE(AppendToCallInfo(buffer));
        CloseHandle(memoryMappedFileMapHandle);
        memoryMappedFileMapHandle = (HANDLE) 0;
    }
    LeaveCriticalSection(&sendMemoryIPCLock);

}

/**
 * initiateIPC - sets up the memory-mapped file to do IPC messaging
 *               1 file is created: to handle requests for information
 *               initiated from Windows AT.  The package is placed into
 *               the memory-mapped file (char *memoryMappedView),
 *               and then a special SendMessage() is sent.  When the
 *               JavaDLL returns from SendMessage() processing, the
 *               data will be in memoryMappedView.  The SendMessage()
 *               return value tells us if all is right with the world.
 *
 *               The set-up proces involves creating the memory-mapped
 *               file, and handshaking with the JavaDLL so it knows
 *               about it as well.
 *
 */
LRESULT
AccessBridgeJavaVMInstance::initiateIPC() {
    DEBUG_CODE(char debugBuf[256]);
    DWORD errorCode;

    DEBUG_CODE(AppendToCallInfo(" in AccessBridgeJavaVMInstance::initiateIPC()\r\n"));

    // create Windows-initiated IPC file & map it to a ptr
    memoryMappedFileMapHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
                                                  PAGE_READWRITE, 0,
                                                  // 8 bytes for return code
                                                  sizeof(WindowsInitiatedPackages) + 8,
                                                  memoryMappedFileName);
    if (memoryMappedFileMapHandle == NULL) {
        errorCode = GetLastError();
        DEBUG_CODE(sprintf(debugBuf, "  Failed to CreateFileMapping for %s, error: %X", memoryMappedFileName, errorCode));
        DEBUG_CODE(AppendToCallInfo(debugBuf));
        return errorCode;
    } else {
        DEBUG_CODE(sprintf(debugBuf, "  CreateFileMapping worked - filename: %s\r\n", memoryMappedFileName));
        DEBUG_CODE(AppendToCallInfo(debugBuf));
    }

    memoryMappedView = (char *) MapViewOfFile(memoryMappedFileMapHandle,
                                              FILE_MAP_READ | FILE_MAP_WRITE,
                                              0, 0, 0);
    if (memoryMappedView == NULL) {
        errorCode = GetLastError();
        DEBUG_CODE(sprintf(debugBuf, "  Failed to MapViewOfFile for %s, error: %X", memoryMappedFileName, errorCode));
        DEBUG_CODE(AppendToCallInfo(debugBuf));
        return errorCode;
    } else {
        DEBUG_CODE(sprintf(debugBuf, "  MapViewOfFile worked - view: %p\r\n", memoryMappedView));
        DEBUG_CODE(AppendToCallInfo(debugBuf));
    }


    // write some data to the memory mapped file
    strcpy(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_QUERY);


    // inform the JavaDLL that we've a memory mapped file ready for it
    char buffer[sizeof(PackageType) + sizeof(MemoryMappedFileCreatedPackage)];
    PackageType *type = (PackageType *) buffer;
    MemoryMappedFileCreatedPackage *pkg = (MemoryMappedFileCreatedPackage *) (buffer + sizeof(PackageType));
    *type = cMemoryMappedFileCreatedPackage;
    pkg->bridgeWindow = ABHandleToLong(ourAccessBridgeWindow);
    strncpy(pkg->filename, memoryMappedFileName, cMemoryMappedNameSize);
    sendPackage(buffer, sizeof(buffer));


    // look for the JavaDLL's answer to see if it could read the file
    if (strcmp(memoryMappedView, AB_MEMORY_MAPPED_FILE_OK_ANSWER) != 0) {
        DEBUG_CODE(sprintf(debugBuf, "  JavaVM failed to deal with memory mapped file %s\r\n",
                      memoryMappedFileName));
        DEBUG_CODE(AppendToCallInfo(debugBuf));
        return -1;
    } else {
        DEBUG_CODE(sprintf(debugBuf, "  Success!  JavaVM accpeted our file\r\n"));
        DEBUG_CODE(AppendToCallInfo(debugBuf));
    }

    return 0;
}

// -----------------------

/**
 * sendPackage - uses SendMessage(WM_COPYDATA) to do IPC messaging
 *               with the Java AccessBridge DLL
 *
 *               NOTE: WM_COPYDATA is only for one-way IPC; there
 *               is now way to return parameters (especially big ones)
 *               Use sendMemoryPackage() to do that!
 */
LRESULT
AccessBridgeJavaVMInstance::sendPackage(char *buffer, long bufsize) {
    COPYDATASTRUCT toCopy;
    toCopy.dwData = 0;          // 32-bits we could use for something...
    toCopy.cbData = bufsize;
    toCopy.lpData = buffer;

    PrintDebugString("In AccessBridgeVMInstance::sendPackage");
    PrintDebugString("    javaAccessBridgeWindow: %p", javaAccessBridgeWindow);
    /* This was SendMessage.  Normally that is a blocking call.  However, if
     * SendMessage is sent to another process, e.g. another JVM and an incoming
     * SendMessage is pending, control will be passed to the DialogProc to handle
     * the incoming message.  A bug occurred where this allowed an AB_DLL_GOING_AWAY
     * message to be processed deleting an AccessBridgeJavaVMInstance object in
     * the javaVMs chain.  SendMessageTimeout with SMTO_BLOCK set will prevent the
     * calling thread from processing other requests while waiting, i.e control
     * will not be passed to the DialogProc.  Also note that PostMessage or
     * SendNotifyMessage can't be used.  Although they don't allow transfer to
     * the DialogProc they can't be used in cases where pointers are passed.  This
     * is because the referenced memory needs to be available when the other thread
     * gets control.
     */
    UINT flags = SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG;
    DWORD_PTR out; // not used
    LRESULT lr = SendMessageTimeout( javaAccessBridgeWindow, WM_COPYDATA,
                                     (WPARAM)ourAccessBridgeWindow, (LPARAM)&toCopy,
                                     flags, 4000, &out );
    return lr;
}


/**
 * sendMemoryPackage - uses Memory-Mapped files to do IPC messaging
 *                     with the Java AccessBridge DLL, informing the
 *                     Java AccessBridge DLL via SendMessage that something
 *                     is waiting for it in the shared file...
 *
 *                     In the SendMessage call, the third param (WPARAM) is
 *                     the source HWND (ourAccessBridgeWindow in this case),
 *                     and the fourth param (LPARAM) is the size in bytes of
 *                     the package put into shared memory.
 *
 */
BOOL
AccessBridgeJavaVMInstance::sendMemoryPackage(char *buffer, long bufsize) {

    // Protect against race condition where the memory mapped file is
    // deallocated before the memory package is being sent
    if (goingAway) {
        return FALSE;
    }
    BOOL retval = FALSE;

    DEBUG_CODE(char outputBuf[256]);
    DEBUG_CODE(sprintf(outputBuf, "AccessBridgeJavaVMInstance::sendMemoryPackage(, %d)", bufsize));
    DEBUG_CODE(AppendToCallInfo(outputBuf));

    DEBUG_CODE(PackageType *type = (PackageType *) buffer);
    DEBUG_CODE(if (*type == cGetAccessibleTextRangePackage) {)
        DEBUG_CODE(AppendToCallInfo("  'buffer' contains:"));
        DEBUG_CODE(GetAccessibleTextRangePackage *pkg = (GetAccessibleTextRangePackage *) (buffer + sizeof(PackageType)));
        DEBUG_CODE(sprintf(outputBuf, "    PackageType = %X", *type));
        DEBUG_CODE(AppendToCallInfo(outputBuf));
        DEBUG_CODE(sprintf(outputBuf, "    GetAccessibleTextRange: start = %d, end = %d, rText = %ls",
            pkg->start, pkg->end, pkg->rText));
        DEBUG_CODE(AppendToCallInfo(outputBuf));
    DEBUG_CODE(})

    EnterCriticalSection(&sendMemoryIPCLock);
    {
        // copy the package into shared memory
        if (!goingAway) {
            memcpy(memoryMappedView, buffer, bufsize);

            DEBUG_CODE(PackageType *type = (PackageType *) memoryMappedView);
            DEBUG_CODE(if (*type == cGetAccessibleTextItemsPackage) {)
                DEBUG_CODE(AppendToCallInfo("  'memoryMappedView' now contains:"));
                DEBUG_CODE(GetAccessibleTextItemsPackage *pkg = (GetAccessibleTextItemsPackage *) (buffer + sizeof(PackageType)));
                DEBUG_CODE(sprintf(outputBuf, "    PackageType = %X", *type));
                DEBUG_CODE(AppendToCallInfo(outputBuf));
            DEBUG_CODE(})
        }

        if (!goingAway) {
            // Let the recipient know there is a package waiting for them. The unset byte
            // at end of buffer which will only be set if message is properly received
            char *done = &memoryMappedView[bufsize];
            *done = 0;

            PrintDebugString("    javaAccessBridgeWindow: %p", javaAccessBridgeWindow);
            // See the comment above the call to SendMessageTimeout in SendPackage method above.
            UINT flags = SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG;
            DWORD_PTR out; // not used
            SendMessageTimeout( javaAccessBridgeWindow, AB_MESSAGE_WAITING, (WPARAM)ourAccessBridgeWindow, (LPARAM)bufsize,
                                flags, 4000, &out );

            // only succeed if message has been properly received
            if(!goingAway) retval = (*done == 1);
        }

        // copy the package back from shared memory
        if (!goingAway) {
            memcpy(buffer, memoryMappedView, bufsize);
        }
    }
    LeaveCriticalSection(&sendMemoryIPCLock);
    return retval;
}


/**
 * findAccessBridgeWindow - walk through linked list from where we are,
 *                          return the HWND of the ABJavaVMInstance that
 *                          matches the passed in vmID; no match: return 0
 *
 */
HWND
AccessBridgeJavaVMInstance::findAccessBridgeWindow(long javaVMID) {
    PrintDebugString("In findAccessBridgeWindow");
    // no need to recurse really
    if (vmID == javaVMID) {
        return javaAccessBridgeWindow;
    } else {
        isVMInstanceChainInUse = true;
        AccessBridgeJavaVMInstance *current = nextJVMInstance;
        while (current != (AccessBridgeJavaVMInstance *) 0) {
            if (current->vmID == javaVMID) {
                isVMInstanceChainInUse = false;
                return current->javaAccessBridgeWindow;
            }
            current = current->nextJVMInstance;
        }
        isVMInstanceChainInUse = false;
    }
    return 0;
}

/**
 * findABJavaVMInstanceFromJavaHWND - walk through linked list from
 *                                    where we are.  Return the
 *                                    AccessBridgeJavaVMInstance
 *                                    of the ABJavaVMInstance that
 *                                    matches the passed in vmID;
 *                                    no match: return 0
 */
AccessBridgeJavaVMInstance *
AccessBridgeJavaVMInstance::findABJavaVMInstanceFromJavaHWND(HWND window) {
    PrintDebugString("In findABJavaInstanceFromJavaHWND");
    // no need to recurse really
    if (javaAccessBridgeWindow == window) {
        return this;
    } else {
        isVMInstanceChainInUse = true;
        AccessBridgeJavaVMInstance *current = nextJVMInstance;
        while (current != (AccessBridgeJavaVMInstance *) 0) {
            if (current->javaAccessBridgeWindow == window) {
                isVMInstanceChainInUse = false;
                return current;
            }
            current = current->nextJVMInstance;
        }
    }
    isVMInstanceChainInUse = false;
    return (AccessBridgeJavaVMInstance *) 0;
}