blob: 3d66374c1fe30d2f6dc5d784f2a91f2f48fcd072 [file] [log] [blame]
/*
* 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 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(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("[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;
}