| /* |
| * 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; |
| } |