home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / rwl025.zip / goodidea.txt next >
Text File  |  2000-06-10  |  11KB  |  215 lines

  1. __________________________________________________________________________
  2. __________________________________________________________________________
  3.  
  4. But Is It a Good Idea?
  5.  
  6. (C) Copyright RL Walsh 2000 - All Rights Reserved
  7. __________________________________________________________________________
  8. __________________________________________________________________________
  9.  
  10. RWL v0.25 is a utility program which demonstrates that any PM process
  11. can use a well-known API to enter any other PM process's execution
  12. environment at will and alter it as desired.
  13.  
  14. But is it a good idea to do this sort of thing?  Conventional wisdom
  15. says "no".  However, that hasn't stopped any number of programmers from
  16. doing so via other means.  Indeed, some may already be using the
  17. technique described below.  To my mind, the possibility of creative new
  18. ways to integrate the operations of disparate programs outweighs the
  19. equal certainty that most uses of this technique will be ill-conceived,
  20. at best.  If having this method more widely known inspires new ideas
  21. for OS/2 programs and causes some old ideas to be revived, then
  22. documenting it - at least - may prove to be a good idea.
  23.  
  24. __________________________________________________________________________
  25.  
  26. Input Hooks
  27. __________________________________________________________________________
  28.  
  29. Most PM programmers are familiar with the input hook as a way to
  30. intercept posted messages before they're dispatched to the target
  31. window.  Intended for monitoring and altering the flow of events,
  32. input hooks typically have two uses.
  33.  
  34. Programs that are deemed applications may occassionally hook their own
  35. message queues to provide app-level processing for certain messages,
  36. or simply as a way to avoid subclassing numerous windows.  The scope
  37. of their activities is intentionally limited to the thread that set
  38. the hook.
  39.  
  40. In contrast, many utility programs have been built around hooks in
  41. the system message queue.  Starting with keyboard and mouse button
  42. modifiers, many authors soon realized that their code was being
  43. pulled into nearly every process.  Once there, it could do whatever
  44. they wished whenever a message passed its way.  From this came the
  45. ubiquitous cut-and-paste menus, titlebar buttons, customized window
  46. animations, and more.
  47.  
  48. While some authors have added app-specific features for a few well-
  49. known processes (e.g. the WPS), these utils tend to replicate a fixed
  50. feature-set across every PM process with little need to differentiate
  51. amongst them.  For them, a hook in the system message queue functions
  52. as a series of single-thread hooks, differing from those used by
  53. applications more in scope than intent.
  54.  
  55. __________________________________________________________________________
  56.  
  57. RWL
  58. __________________________________________________________________________
  59.  
  60. RWL demonstrates a different way to employ the input queue hook.  It
  61. capitalizes on the little-known fact that any PM thread can hook any
  62. other thread's message queue (provided the hook is in a dll).  Setting
  63. such a hook lets an application establish an ad-hoc, runtime linkage
  64. with whatever target it chooses.  Its code enjoys the same liberties
  65. and faces the same perils as the code in a system queue hook.
  66.  
  67. In short, it entails:
  68.  
  69.  o  obtaining one or more unique message IDs from the system atom table
  70.  o  determining the thread on which you want your code to execute
  71.     (presumably, in another process)
  72.  o  identifying a window belonging to this thread (AFAIK, every
  73.     thread with a message queue has at least one window)
  74.  o  retrieving the message queue handle from the window
  75.  o  hooking that particular message queue
  76.  o  posting your message(s) to the target queue to activate your code.
  77.  
  78. __________________________________________________________________________
  79.  
  80. Identifying the Target Queue
  81. __________________________________________________________________________
  82.  
  83. Depending on your circumstances, you may begin your search equipped
  84. either with a way to uniquely identify the target process, or with
  85. the target's PID and TID.  In either case, the goal is a window
  86. belonging to the target thread.
  87.  
  88. Using a unique identifier may involve searching for a switchlist
  89. entry or window title, or for an instance of an app-specific window
  90. class.  Other methods can be used for well-known processes.  To hook
  91. pmshell.exe's primary message queue, use WinQueryDesktopWindow().
  92. For the WPS, locate the bottom-most child of HWND_DESKTOP and confirm
  93. that it is an instance of the wpFolder window class.
  94.  
  95. You might start with a PID if, for example, your app launched a child
  96. process whose queue you wish to hook.  Or, you might wish to make
  97. every process available for manipulation by listing all current PIDs,
  98. as RWL does.  I'm not aware of any way to directly correlate a thread
  99. with its queue, but there is a surrogate method.  Enumerate all the
  100. children of HWND_OBJECT, looking for instances of class "#32767".
  101. There should be exactly one such window for each message queue;
  102. WinQueryWindowProcess() will identify the PID/TID to which it belongs.
  103.  
  104. Unless you have specific knowledge of a program's design , it is
  105. generally safest and easiest to enter a process on its primary UI
  106. thread, typically TID 1.  The primary thread is likely to be per-
  107. sistent while a secondary thread may terminate at any time, taking
  108. your hook with it.
  109.  
  110. Once you have a window associated with the target queue, you can
  111. retrieve the queue's handle using WinQueryWindowULong(QWL_HMQ).
  112. Pass it to WinSetHook(HK_INPUT) to install your hook in the target
  113. queue.  Its next WinGetMsg() or WinPeekMsg() should cause PM to
  114. load your dll in the target process and to invoke its DLL_InitTerm.
  115.  
  116. __________________________________________________________________________
  117.  
  118. Operating In Another Process
  119. __________________________________________________________________________
  120.  
  121. Although the method described here lets you intercept and handle
  122. a specific process's messages, this is a fairly trivial use for
  123. it.  Far more powerful is its ability to let one process establish
  124. a "presence" in another.  This might be done on an ad-hoc basis:
  125. enter a process, get/set some datum, then exit.  Or, it might be
  126. used to create a persistent agent in the target with which your
  127. app periodically communicates.  The hook should be viewed as your
  128. entree into the process and not necessarily the context in which
  129. most of your new functionality should operate.
  130.  
  131. Given the invasive and opportunistic nature of this method, it seems
  132. prudent to decouple your program's activities from those of the host
  133. process.  Functions that are going to be performed by the hook code
  134. itself (e.g. initialization) should be handled as commands in the
  135. form of messages with unique IDs posted to the target queue.  This
  136. lets your processing occur during its own timeslice and makes clear
  137. to any observer that your messages are not directed to a particular
  138. window.  (Remember, a queue hook installed after yours will see the
  139. messages before yours does, and might divert all messages for a
  140. given window.)
  141.  
  142. If you need to establish an interface between your process and the
  143. target, a conventional mechanism such as an object window or a pipe
  144. is suggested.  This can be created on the current thread if you plan
  145. to interact with the target's own windows, or spun off to another
  146. thread if its activities are largely independent of the host's.
  147. Using this method, your hook code is invoked only once:  to kickoff
  148. the creation of these independent structures.
  149.  
  150. As examples of independent interfaces, both Netscape 4.61 and the
  151. author's DragText v3.3 use hooks to create object windows on the WPS's
  152. primary thread.  NS does so to identify the object under the mouse
  153. pointer during its proprietary drag and drop.  DT's enables any process
  154. to command the WPS to popup an object's menu or initiate a drag.
  155.  
  156. __________________________________________________________________________
  157.  
  158. Staying Afloat
  159. __________________________________________________________________________
  160.  
  161. Initially, the hook keeps your dll loaded in the target process.
  162. Once you release the hook, PM unloads the dll.  If your exe fails
  163. to release it before termination, PM will do so during exitlist
  164. processing.  Thus, your hook and the dll that supports it persist
  165. in the target process only for the duration of your application -
  166. unless you take counter-measures.
  167.  
  168. If all your transactions with the target are conducted using the
  169. hook, neither your app nor the target should be adversely affected
  170. when the other ends.  If your app terminates first, references to
  171. your code will be removed before the code itself is, so segment-
  172. not-present faults shouldn't occur.  If the target terminates, your
  173. app's attempts to post msgs to the target queue will fail.
  174.  
  175. The situation is quite different if your code operates independently
  176. of the hook, e.g. an object window.  At any time, its supporting dll
  177. could be unloaded and its address space made invalid.  There are
  178. several complementary ways to deal with this.
  179.  
  180. One way dances around problems by designing away from their likelihood.
  181. If you limit the scope of your activities to the immediate needs of
  182. your app and keep your interface proprietary, your exposure to serious
  183. failure is vastly reduced.  If there's nothing currently in the system
  184. to invoke your code in the target, then concerns about whether it's
  185. actually present may be moot.
  186.  
  187. Another way uses brute force:  have the dll load itself after it enters
  188. the target process.  This should keep it in place until you explicitly
  189. unload it or the target process ends.  Unless your dll lies along the
  190. LIBPATH, you'll have to use its fully-qualified name.  Retrieve this
  191. in the source process and make it available for use in the target.
  192.  
  193. __________________________________________________________________________
  194.  
  195. Acknowledgements
  196. __________________________________________________________________________
  197.  
  198. I doubt that I can take credit for "discovering" this technique, nor
  199. even for first publishing a description of it.  I recall encountering
  200. a PM process killer by an employee of IBM-Israel that used message
  201. queue hooks.  If his util set individual hooks into other processes
  202. rather than a single system queue hook, then he certainly deserves
  203. full credit for originating this powerful technique.  Others are
  204. welcome to vie for the honor as well...
  205.  
  206. __________________________________________________________________________
  207. __________________________________________________________________________
  208.  
  209. Rich Walsh  (rlwalsh@packet.net)
  210. Ft Myers, Florida
  211.  
  212. June 10, 2000
  213. __________________________________________________________________________
  214. __________________________________________________________________________
  215.