问题
I successfully injected my own Java code in a running Oracle Forms application, using DLL Injection and some jni trickery. (Windows 7, 32 bits, Oracle Forms 11, JRE Java 8)
I am able to traverse the tree of Components and to query and set values in some basic Java objects, such as those from class oracle.forms.ui.VTextField
I am stuck when trying to simulate user click on a oracle.apps.fnd.ui.Button
I tried 2 things :
- call the
simulatePush
method of theAbstractButton
class - call the
activate
method of thePushButton
class
(the 2 classes are in the class hierarchy for Button
)
Results were identical:
1. At first, it works fine: when the button is a "Search" button, the search is done and the results are displayed.
2. Then, it immediately breaks the application, with a pop-up saying FRM-92100 Your connection to the Server was interrupted
.
From there, the Application is hung.
Update: It seems that the error which cause a disconnection from the Server is:
java.lang.SecurityException: this KeyboardFocusManager is not installed in the current thread's context at java.awt.KeyboardFocusManager.checkCurrentKFMSecurity(Unknown Source) at java.awt.KeyboardFocusManager.getGlobalFocusOwner(Unknown Source) at java.awt.KeyboardFocusManager.processSynchronousLightweightTransfer(Unknown Source) at sun.awt.windows.WComponentPeer.processSynchronousLightweightTransfer(Native Method) at sun.awt.windows.WComponentPeer.requestFocus(Unknown Source) at java.awt.Component.requestFocusHelper(Unknown Source) at java.awt.Component.requestFocusHelper(Unknown Source) at java.awt.Component.requestFocus(Unknown Source) at oracle.forms.handler.UICommon.updateFocus(Unknown Source) at oracle.forms.handler.UICommon.setFVP(Unknown Source) at oracle.forms.handler.UICommon.setFVP(Unknown Source) at oracle.forms.handler.UICommon.onUpdate(Unknown Source) at oracle.forms.handler.ComponentItem.onUpdate(Unknown Source) at oracle.forms.handler.JavaContainer.onUpdate(Unknown Source) at oracle.forms.handler.UICommon.onUpdate(Unknown Source) at oracle.forms.engine.Runform.onUpdateHandler(Unknown Source) at oracle.forms.engine.Runform.processMessage(Unknown Source) at oracle.forms.engine.Runform.processSet(Unknown Source) at oracle.forms.engine.Runform.onMessageReal(Unknown Source) at oracle.forms.engine.Runform.onMessage(Unknown Source) at oracle.forms.engine.Runform.processEventEnd(Unknown Source) at oracle.ewt.lwAWT.LWComponent.redispatchEvent(Unknown Source) at oracle.ewt.lwAWT.LWComponent.processEvent(Unknown Source) at oracle.ewt.button.PushButton.activate(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at CustomAWT.run(CustomAWT.java:34) at java.awt.event.InvocationEvent.dispatch(Unknown Source) at java.awt.EventQueue.dispatchEventImpl(Unknown Source) at java.awt.EventQueue.access$400(Unknown Source) at java.awt.EventQueue$2.run(Unknown Source) at java.awt.EventQueue$2.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source)
My code is here: CustomAWT.run(CustomAWT.java:34)
and is called with invokeLater
. Problem is probably: when calling the oracle.ewt.button.PushButton.activate
method, I am NOT in the right EDT.
Using "List Threads" in the Java Console, I got:
Dump thread list ...
Group main,ac=30,agc=2,pri=10
main,5,alive
traceMsgQueueThread,5,alive,daemon
Timer-0,5,alive
Java Plug-In Pipe Worker Thread (Client-Side),5,alive,daemon
AWT-Shutdown,5,alive
AWT-Windows,6,alive,daemon
AWT-EventQueue-0,6,alive
SysExecutionTheadCreator,5,alive,daemon
CacheMemoryCleanUpThread,5,alive,daemon
CacheCleanUpThread,5,alive,daemon
Browser Side Object Cleanup Thread,5,alive
JVM[id=0]-Heartbeat,5,alive,daemon
Windows Tray Icon Thread,5,alive
Thread-13,5,alive
Group Plugin Thread Group,ac=3,agc=0,pri=10
AWT-EventQueue-1,6,alive
TimerQueue,5,alive,daemon
ConsoleWriterThread,6,alive,daemon
Group http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup,ac=13,agc=0,pri=4
Applet 1 LiveConnect Worker Thread,4,alive
AWT-EventQueue-2,4,alive
thread applet-oracle/apps/fnd/formsClient/FormsLauncher.class-1,4,alive
Applet 2 LiveConnect Worker Thread,4,alive
thread applet-oracle.forms.engine.Main-2,4,alive
Forms-StreamMessageReader,4,alive
Forms-StreamMessageWriter,4,alive
HeartBeat,4,alive
Busy indicator,1,alive,daemon
TaskScheduler timer,4,alive
CursorIdler,4,alive
Thread-14,4,alive
Flush Queue,4,alive
Done.
So, there is THREE AWT-EventQueue
threads... Question is now: How to query/retrieve the right one, and how to make the Runnable
passed to invokeLater
to run in the "Good Thread" (I guess that the good one is the last one (AWT-EventQueue-2
)
回答1:
After a lot of experimentation and google searches with keywords like EventQueue
and ThreadGroup
I have finally found a solution (in the Works For Me category, mind you).
I use the sun.awt.AppContext
class. Some documentation and sources here (grepcode.com)
- Get a Collection of the running
AppContext
's using thegetAppContexts
method. - For each retrieved
AppContext
, get hisThreadGroup
using thegetThreadGroup
method. - With the
ThreadGroup
object, Use thegetName
method. - When the name of the Thread Group starts with the http: address of your Forms Application, retrieve the
Object
property with key namesun.awt.AppContext.EVENT_QUEUE_KEY
, using theget
method ofAppContext
. - The retrieved object is an
EventQueue
. Create anjava.awt.event.InvocationEvent
object, passing yourRunnable
to the CTOR, and use thepostEvent
method ofEventQueue
. - Your
run
method will be executed in the right thread.
Remarks:
- This answer is a specific, works for me, solution for an Oracle Forms Application launched via an Internet Explorer link, and running in a java.exe process. In that situation, the 3 Thread Groups are as shown in the question:
main
,Plugin Thread Group
, andhttp://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup
Your mileage may vary. - If you don't use full reflection, but instead do import
sun.awt.AppContext
, the compiler may emit warnings in the formwarning: sun.awt.AppContext is Sun proprietary API and may be removed in a future release
That's not very cool, but I will live with that, for the time being. - In the
run
method, I tested OK with thesimulatePush
method oforacle.ewt.lwAWT.AbstractButton
. - The method emulated here is
invokeLater
. ForinvokeAndWait
, more code is needed around thepostEvent
call. See some sources for theEventQueue
class, as a starting point.
回答2:
To get the correct EDT thread regardless of your thread group, you can use SunToolkit.targetToAppContext(Object target)
, and for the parameter you can feed it the AWT component you intend to act on. Example source.
Then get the EventQueue using EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);
Finally, create a new InvocationEvent with your runnable and call postEvent on the EQ.
回答3:
You should be able to extend VButton class Your class definition should be something like:
public class AmazingButton extends VButton implements FocusListener
Then you need an init
class like:
public void init(IHandler handler)
{
m_handler = handler;
super.init(handler);
addMouseListener(new ButtonMouseAdapter());
addFocusListener(this);
}
And then afterwards you need to implement the listeners and do some stuff in it:
public void focusGained(FocusEvent e)
{
if (e.getComponent() == this)
{
// put the focus on the component
e.getComponent().requestFocus();
bFocus = true ;
}
}
public void focusLost(FocusEvent e)
{
bFocus = false ;
}
/**
* Private class to handle user mouse actions
*/
class ButtonMouseAdapter extends MouseAdapter
{
/**
* User moved the mouse over the button
*/
public void mouseEntered(MouseEvent me)
{
bFocus=true ;
mouseON();
}
/**
* User moved the mouse out of the button
*/
public void mouseExited(MouseEvent me)
{
bFocus=false ;
mouseOFF();
}
/**
* User moved the mouse out of the button
*/
public void mousePressed(MouseEvent me)
{
bPressed = true ;
}
/**
* User moved the mouse out of the button
*/
public void mouseReleased(MouseEvent me)
{
bPressed = false ;
}
}
I hope this code works for you.
Regards
回答4:
I successfully injected my own Java code in a running Oracle Forms application, using DLL Injection and some jni trickery.
That is the real problem here, IMO.
You are suffering from target fixation, which means that you, the programmer, has a fixed mental idea of what kind of solution they want and this blinds you to everything else. Target fixation has resulted in plane crashes, as even highly experienced and intelligent pilots ( in fact whole cockpits ! ) have become so fixated on one issue in one mindset that they let other disasters slip right by.
Get out of this frame of mind.
Your desired solution is not working out, so move on and try something else. Like the sensible option already presented to you by @nightfox79 and variations on that.
You are trying to circumvent a complex object class, when you should probably simply be extending the existing class you are trying to hack your way around. That's the whole basis of OOPs development.
DLL/JNI Trickery has no place in a sensible solution, IMO.
And I pity the person who has to maintain and repair any code solution based on a DLL/JNI hack. That way madness lies.
Your theory that invokeLater()
is not running under the right EDT is probably wrong. invokeLater()
will, according to the documentation, always queue the code you request onto the pending code list for the AWT Event handler, which is precisely where it should be. Trying to bypass that is almost certain to cause ghastly problems. The entire purpose of invokeLater()
is to defer heavyweight processing in the EDT you invoke it from, and run it later on the exact same thread. It's a bug in invokeLater()
if it does not, IMO.
If, however, you wish to check what thread code is running in then the only test I know of is to use this in your code ;
if (SwingUtilities.isEventDispatchThread())
{
System.err.println("Is running on EDT");
}
else
{
System.err.println("Is not running on EDT");
}
来源:https://stackoverflow.com/questions/34227178/how-to-choose-an-awt-eventqueue-thread-when-there-are-several-of-them