| /* | |
| * Copyright (c) 2005, 2023, 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(const 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; | |
| snprintf(memoryMappedFileName, sizeof(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(snprintf(buffer, sizeof(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(snprintf(buffer, sizeof(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(snprintf(debugBuf, sizeof(debugBuf), " Failed to CreateFileMapping for %s, error: %X", memoryMappedFileName, errorCode)); | |
| DEBUG_CODE(AppendToCallInfo(debugBuf)); | |
| return errorCode; | |
| } else { | |
| DEBUG_CODE(snprintf(debugBuf, sizeof(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(snprintf(debugBuf, sizeof(debugBuf), " Failed to MapViewOfFile for %s, error: %X", memoryMappedFileName, errorCode)); | |
| DEBUG_CODE(AppendToCallInfo(debugBuf)); | |
| return errorCode; | |
| } else { | |
| DEBUG_CODE(snprintf(debugBuf, sizeof(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(snprintf(debugBuf, sizeof(debugBuf), " JavaVM failed to deal with memory mapped file %s\r\n", | |
| memoryMappedFileName)); | |
| DEBUG_CODE(AppendToCallInfo(debugBuf)); | |
| return -1; | |
| } else { | |
| DEBUG_CODE(snprintf(debugBuf, sizeof(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 no 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("[INFO]: In AccessBridgeVMInstance::sendPackage"); | |
| PrintDebugString("[INFO]: 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(snprintf(outputBuf, sizeof(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(snprintf(outputBuf, sizeof(outputBuf), " PackageType = %X", *type)); | |
| DEBUG_CODE(AppendToCallInfo(outputBuf)); | |
| DEBUG_CODE(snprintf(outputBuf, sizeof(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(snprintf(outputBuf, sizeof(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("[INFO]: 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("[INFO]: 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("[INFO]: 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; | |
| } |