home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / rwl025.zip / RWLX.C < prev    next >
C/C++ Source or Header  |  2000-06-10  |  35KB  |  1,214 lines

  1. /**********************************************************************/
  2. /*                                                                    */
  3. /*  RWL - A demonstration program placed in the Public Domain         */
  4. /*        by it author, R L Walsh, June 10, 2000                      */
  5. /*                                                                    */
  6. /**********************************************************************/
  7. #if 0
  8.  
  9.     RWL lets the user view and change the BeginLIBPATH and EndLIBPATH
  10.     for any running PM-based process.  Changing these for the first
  11.     instance of pmshell.exe has much the same effect on newly-launched
  12.     programs as updating your global LIBPATH would.  This may enable
  13.     you to begin using newly installed programs without having to
  14.     reboot to actually update your LIBPATH.
  15.  
  16.     This program is intended to demonstrate that a PM-based process
  17.     can selectively enter any other PM process at will by hooking
  18.     one of the target`s message queues.  Once its code is operating
  19.     in the target process, the program can do whatever its author
  20.     deems prudent.  It also demonstrates a method for identifying
  21.     threads that have message queues.
  22.  
  23.     Because RWL is a demonstration of a previously undocumented
  24.     "wormhole" in Warp, the author makes no claims that RWL or the
  25.     technique it demonstrates are ever suitable for use in any way.
  26.     Nonetheless, it should be noted that this method was used by
  27.     IBM in their implementation of Netscape v4.61 to support its
  28.     proprietary but WPS-aware drag and drop.
  29.  
  30. /**********************************************************************/
  31.  
  32. Overview
  33. ==========
  34.  
  35.     RWL consists of two modules:
  36.      o  rwl.exe -> rwlx.c   (most functionality)
  37.      o  rwl.dll -> rwld.c   (hook code, get/set LIBPATH)
  38.  
  39.     rwlx starts by identifying one window for each PM process. If a
  40.     process has several threads with message queues, it chooses a
  41.     window belonging to the lowest-numbered thread (usually TID 1).
  42.     It then inserts the PID and its corresponding HWND in a listbox.
  43.  
  44.     When the user selects a new PID, rwlx releases any hook currently
  45.     in place, then hooks the queue associated with that PID`s hwnd.
  46.     The hook code itself is in rwl.dll.
  47.  
  48.     rwlx conducts its transactions with the target process by posting
  49.     a unique message to the target queue.  The msg ID is an atom
  50.     derived from the string "RWLM_X2D".  Its params are a pointer to
  51.     an RWL structure in gettable shared memory, and the hwnd to which
  52.     it should post a reply message.  The hook intercepts this message,
  53.     accesses the memory, and executes what ever command is indicated.
  54.     It then frees the memory and posts its reply.  When the dialog
  55.     receives the response, it either displays the returned string or
  56.     posts another message requesting additional data.
  57.  
  58. Implementation
  59. ================
  60.  
  61.     While RWL currently uses a single dialog for all processes,
  62.     it was designed with multiple windows in mind, one per process.
  63.     In that environment, each process accessed would need its own
  64.     i/o buffer in shared memory.  Rather than have mutiple 64k
  65.     allocations when only 1k was needed, I opted for an array of
  66.     "RWL" structures containing process info and a 1k string buffer,
  67.     all stored in a single gettable segment.  This allows simultaneous
  68.     access to 62 processes while maintaining undo info for each.
  69.     Thereafter, RWL will replace the least-recently-used entry.
  70.  
  71.     This code is acceptable to my antique C-only compiler with
  72.     most warnings on, but it may be nearly unuseable with yours.
  73.     Similarly, I haven`t included any makefiles because I suspect
  74.     they`d be useless too.  My apologies...
  75.  
  76.     IMPORTANT(?):  I always staticly link dlls like rwl.dll (rwld.c)
  77.     with my compiler`s "subsystem" library.  Eliminating the runtime
  78.     environment eliminates a lot of headaches (and functionality).
  79.  
  80.     rwld.c offers some additional comments.
  81.  
  82. Style
  83. =======
  84.  
  85.     Most of my functions are structured like this:
  86.  
  87.     do {
  88.         resource allocation & init;
  89.         if (error)
  90.             break;
  91.         processing;
  92.         if (done)
  93.             break;
  94.         more processing;
  95.     } while (FALSE);
  96.         error reporting;
  97.         resource deallocation;
  98.         return;
  99.  
  100.     Use of this structure, with its dummy do-while loop,
  101.     ensures that:
  102.      o  resources will always be deallocated
  103.      o  error conditions are easy to identify and report
  104.      o  code is more readable, with minimal nesting
  105.  
  106.     Whenever an error occurs or processing is complete, a "break"
  107.     statement will skip over the remaining processing and goto
  108.     finalization.  The only demands this style makes is that key
  109.     variables be initialized to a known value (usually zero) so
  110.     resources can be deallocated accordingly.
  111.  
  112. #endif
  113. /**********************************************************************/
  114.  
  115. //  RWLX.C
  116.  
  117. /**********************************************************************/
  118.  
  119. #include <stdlib.h>
  120. #include <stdio.h>
  121. #include <string.h>
  122.  
  123. #include "rwl.h"        // most stuff (shared with rwld.c)
  124. #include "rwlx.h"       // dialog IDs
  125.  
  126. /****************************************************************************/
  127.  
  128. // init
  129. int     main( void);
  130. BOOL    RWLXInit( void);
  131.  
  132. // primary dialog functions
  133. MRESULT _System MainWndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
  134. BOOL    RWLXGetLibpath( HWND hwnd);
  135. BOOL    RWLXSetLibpath( HWND hwnd);
  136. BOOL    RWLXUndo( HWND hwnd);
  137. BOOL    RWLXReply( HWND hwnd, PRWL prwl, ULONG rc);
  138. void    RWLXEnableBtns( HWND hwnd, BOOL fEnable);
  139.  
  140. // process selection & tracking
  141. BOOL    RWLXSelectPID( HWND hwnd);
  142. PRWL    RWLXGetPRWL( HWND hHook);
  143. BOOL    RWLXSwitchPIDs( HWND hwnd, PRWL prwlNew);
  144. PRWL    RWLXValidateRWL( PRWL prwl);
  145. PRWL    RWLXNextRWL( PRWL prwl);
  146. PRWL    RWLXAllocRWL( void);
  147. void    RWLXFreeRWL( PRWL prwl);
  148.  
  149. // process identification
  150. BOOL    RWLXUpdatePIDs( HWND hwnd, PID pidSelect);
  151. LONG    RWLXEnumPIDs( PAPP paApp, ULONG cntApp);
  152. void    RWLXListPIDs( HWND hLB, PID pidSelect, PAPP paApp);
  153.  
  154. // errors
  155. BOOL    ErrBox( PSZ pTtl, PSZ pErr);
  156. BOOL    RcErrBox( PSZ pTtl, PSZ pErr, ULONG rc);
  157.  
  158. /****************************************************************************/
  159.  
  160. // miscellanea
  161. BOOL        fFalse = FALSE;     // my compiler can't handle "while(FALSE)"
  162. USHORT      usUsed = 1;         // LRU counter
  163. HAB         habMain = 0;        // public because WinSetHook() needs it
  164. HMODULE     hmodRWLD = 0;       // the handle of rwl.dll
  165. PID         pidRWL = 0;         // our PID
  166. char        szRWLD[] = "RWL";   // unqualified name of our dll
  167.  
  168. // ptr to an array of up to 62 RWL structures stored in
  169. // 64k of shared gettable memory
  170. PRWL        prwlBase = NULL;    // ptr to base address of the allocation
  171. PRWL        prwlNext = NULL;    // ptr to last element + 1 in array
  172.  
  173. // messages - the initial msg ID values will be replaced by
  174. // atoms generated from the strings below
  175. ULONG       ulRWLM_X2D = RWLM_X2D;  // exe to dll msg
  176. ULONG       ulRWLM_D2X = RWLM_D2X;  // dll to exe msg
  177. char        szRWLM_X2D[] = "RWLM_X2D";
  178. char        szRWLM_D2X[] = "RWLM_D2X";
  179.  
  180. /**********************************************************************/
  181.  
  182. // just your basic Init / Msg Loop / Shutdown code
  183.  
  184. int     main( void)
  185.  
  186. {
  187.     HMQ     hmq = 0;
  188.     HWND    hwnd = 0;
  189.     int     nRtn = 8;
  190.     QMSG    qmsg;
  191.  
  192. do
  193. {
  194.     habMain = WinInitialize( 0);
  195.     if (!habMain)
  196.         break;
  197.  
  198.     hmq = WinCreateMsgQueue( habMain, 0);
  199.     if (!hmq)
  200.         break;
  201.  
  202.     if (!RWLXInit())
  203.         break;
  204.  
  205.     hwnd = WinLoadDlg(
  206.                 HWND_DESKTOP,               //  parent-window
  207.                 NULLHANDLE,                 //  owner-window
  208.                 MainWndProc,                //  dialog proc
  209.                 NULLHANDLE,                 //  EXE module handle
  210.                 IDD_MAIN,                   //  dialog id
  211.                 NULL);                      //  pointer to create params
  212. // in a multi-windowed version, you'd pass the desired PID
  213. // as a create param
  214.  
  215.     if (!hwnd)
  216.         break;
  217.  
  218.     if (!WinRestoreWindowPos( "RWL", "WNDPOS", hwnd))
  219.         WinShowWindow( hwnd, TRUE);
  220.  
  221.     while (WinGetMsg( habMain, &qmsg, NULLHANDLE, 0, 0))
  222.         WinDispatchMsg( habMain, &qmsg);
  223.  
  224.     nRtn = 0;
  225.  
  226. } while (fFalse);
  227.  
  228.     if (nRtn)
  229.         DosBeep( 440, 150);
  230.  
  231.     if (hwnd)
  232.     {
  233.         WinStoreWindowPos( "RWL", "WNDPOS", hwnd);
  234.         WinDestroyWindow( hwnd);
  235.     }
  236.     if (hmq)
  237.         WinDestroyMsgQueue( hmq);
  238.     if (habMain)
  239.         WinTerminate( habMain);
  240.     if (prwlBase)
  241.         DosFreeMem( (PVOID)prwlBase);
  242.  
  243.     exit( nRtn);
  244.     return (0);
  245. }
  246.  
  247. /****************************************************************************/
  248.  
  249. //  init global data, alloc mem to be passed among processes,
  250. //  create unique msg IDs
  251.  
  252. BOOL    RWLXInit( void)
  253.  
  254. {
  255.     PPIB        ppib;
  256.     APIRET      rc = 0;
  257.     HATOMTBL    haTbl;
  258.     ATOM        atom;
  259.     char *      pErr;
  260.  
  261. do
  262. {
  263. // save our current PID
  264.     DosGetInfoBlocks( NULL, &ppib);
  265.     pidRWL = ppib->pib_ulpid;
  266.  
  267. // get RWL.DLL's hmod (needed by WinSetHook() and the dll itself)
  268.     pErr = "DosQueryModuleHandle";
  269.     rc = DosQueryModuleHandle( szRWLD, &hmodRWLD);
  270.     if (rc)
  271.         break;
  272.  
  273. // alloc 64k of gettable shared memory to be used as an
  274. // array of RWL structures
  275.     pErr = "DosAllocSharedMem";
  276.     rc = DosAllocSharedMem( (PVOID*)(PVOID)&prwlBase, NULL, 0x10000,
  277.                        PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_GETTABLE);
  278.     if (rc)
  279.         break;
  280.  
  281.     prwlNext = prwlBase;
  282.  
  283. // create 2 atoms to be used as unique msg IDs,
  284.     haTbl = WinQuerySystemAtomTable();
  285.  
  286.     atom = WinAddAtom( haTbl, szRWLM_X2D);
  287.     if (atom)
  288.         ulRWLM_X2D = atom;
  289.  
  290.     atom = WinAddAtom( haTbl, szRWLM_D2X);
  291.     if (atom)
  292.         ulRWLM_D2X = atom;
  293.  
  294. // pass the atoms and the dll's hmod to the dll
  295.     RWLDInitDll( hmodRWLD, ulRWLM_X2D, ulRWLM_D2X);
  296.  
  297. } while (fFalse);
  298.  
  299.     if (rc)
  300.         RcErrBox( __FUNCTION__, pErr, rc);
  301.  
  302.     return (BOOL)(rc ? FALSE : TRUE);
  303. }
  304.  
  305. /**********************************************************************/
  306.  
  307. // The dialog's QWL_USER points to the RWL structure for a
  308. // particular PID.  In this single-window version, QWL_USER is
  309. // changed whenever the user selects a new PIDs.  A multi-
  310. // window version would put the new PID in the CREATEPARAM
  311. // struct so it could be passed to RWLXUpdatePIDs()
  312.  
  313. MRESULT _System MainWndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  314.  
  315. {
  316.     switch (msg)
  317.     {
  318.         case WM_INITDLG:
  319.             WinSetWindowPtr( hwnd, QWL_USER, NULL);
  320.             WinSendDlgItemMsg( hwnd, IDC_MLE, MLM_SETTEXTLIMIT,
  321.                                (MP)1023, 0);
  322.             WinSendDlgItemMsg( hwnd, IDC_BEG, BM_SETCHECK, (MP)1, 0);
  323.             RWLXUpdatePIDs( hwnd, 0);
  324.             WinPostMsg( hwnd, WM_COMMAND, (MP)IDC_PIDSET, 0);
  325.             break;
  326.  
  327.         case WM_COMMAND:
  328.             if ((USHORT)mp1 == IDC_UNDO)
  329.                 RWLXUndo( hwnd);
  330.             else
  331.             if ((USHORT)mp1 == IDC_SET)
  332.                 RWLXSetLibpath( hwnd);
  333.             else
  334.             if ((USHORT)mp1 == IDC_PIDSET)
  335.                 RWLXSelectPID( hwnd);
  336.             else
  337.             if ((USHORT)mp1 == IDC_PIDUPDT)
  338.                 RWLXUpdatePIDs( hwnd, 0);
  339.             break;
  340.  
  341.         case WM_CONTROL:
  342.             if (mp1 == MPFROM2SHORT( IDC_BEG, BN_CLICKED) ||
  343.                 mp1 == MPFROM2SHORT( IDC_END, BN_CLICKED))
  344.                 RWLXGetLibpath( hwnd);
  345.             else
  346.             if (mp1 == MPFROM2SHORT( IDC_PIDLB, LN_ENTER))
  347.                 WinPostMsg( hwnd, WM_COMMAND, (MP)IDC_PIDSET, 0);
  348.             break;
  349.  
  350.         case WM_CHAR:
  351.         // defeat default button action
  352.             if (((CHARMSG(&msg)->fs & (USHORT)KC_VIRTUALKEY) &&
  353.                    CHARMSG(&msg)->vkey == VK_ENTER) ||
  354.                 ((CHARMSG(&msg)->fs & (USHORT)KC_CHAR) &&
  355.                    CHARMSG(&msg)->chr == '\r'))
  356.                 break;
  357.  
  358.             return (WinDefDlgProc( hwnd, msg, mp1, mp2));
  359.  
  360.         case WM_CLOSE:
  361.             WinPostMsg( hwnd, WM_QUIT, NULL, NULL);
  362.             break;
  363.  
  364.         case WM_DESTROY:
  365.         // unhook the current process without hooking another
  366.             RWLXSwitchPIDs( hwnd, NULL);
  367.             return (WinDefDlgProc( hwnd, msg, mp1, mp2));
  368.  
  369.         default:
  370.         // this is the msg the dll posts in response to the exe's msg
  371.             if (msg == ulRWLM_D2X)
  372.             {
  373.                 RWLXReply( hwnd, (PRWL)mp1, (ULONG)mp2);
  374.                 break;
  375.             }
  376.             return (WinDefDlgProc( hwnd, msg, mp1, mp2));
  377.  
  378.     } //end switch (msg)
  379.  
  380.     return (0);
  381. }
  382.  
  383. /****************************************************************************/
  384. /****************************************************************************/
  385.  
  386. // query the hooked process's Begin- or EndLIBPATH
  387.  
  388. BOOL    RWLXGetLibpath( HWND hwnd)
  389.  
  390. {
  391.     BOOL        fRtn = FALSE;
  392.     BOOL        fDisable = TRUE;
  393.     PRWL        prwl;
  394.     char *      pErr;
  395.  
  396. do
  397. {
  398. // ensure our PID info is still valid
  399.     pErr = "invalid PID";
  400.     prwl = RWLXValidateRWL( WinQueryWindowPtr( hwnd, QWL_USER));
  401.     if (prwl == NULL)
  402.         break;
  403.  
  404. // set the Begin or End flags based on the radio buttons
  405.     if (WinSendDlgItemMsg( hwnd, IDC_END, BM_QUERYCHECK, 0, 0))
  406.         prwl->flags = RWLF_GET | RWLF_END;
  407.     else
  408.         prwl->flags = RWLF_GET | RWLF_BEG;
  409.  
  410. // if we're querying ourself, tell the hook not to free its prwl
  411.     if (prwl->pid == pidRWL)
  412.         prwl->flags |= RWLF_NOFREE;
  413.  
  414. // post our query to the target's queue
  415.     pErr = "WinPostQueueMsg";
  416.     fDisable = WinPostQueueMsg( prwl->hmq, ulRWLM_X2D, (MP)prwl, (MP)hwnd);
  417.     if (fDisable == FALSE)
  418.         break;
  419.  
  420.     fRtn = TRUE;
  421.  
  422. } while (fFalse);
  423.  
  424.     if (fDisable)
  425.         RWLXEnableBtns( hwnd, FALSE);
  426.  
  427. // if anything went wrong, refresh the PID listbox
  428.     if (fRtn == FALSE)
  429.     {
  430.         ErrBox( __FUNCTION__, pErr);
  431.         RWLXUpdatePIDs( hwnd, 0);
  432.     }
  433.  
  434.     return (fRtn);
  435. }
  436.  
  437. /****************************************************************************/
  438.  
  439. // set the hooked process's Begin- or EndLIBPATH
  440. // using the edited contents of the MLE
  441.  
  442. BOOL    RWLXSetLibpath( HWND hwnd)
  443.  
  444. {
  445.     BOOL        fRtn = FALSE;
  446.     BOOL        fDisable = TRUE;
  447.     PRWL        prwl;
  448.     char *      pSrc;
  449.     char *      pDst;
  450.     char *      pErr;
  451.  
  452. do
  453. {
  454.     pErr = "invalid PID";
  455.     prwl = RWLXValidateRWL( WinQueryWindowPtr( hwnd, QWL_USER));
  456.     if (prwl == NULL)
  457.         break;
  458.  
  459. // get the text, then remove any CRs or LFs
  460.     WinQueryDlgItemText( hwnd, IDC_MLE, sizeof( prwl->szPath), prwl->szPath);
  461.     for (pSrc=pDst=prwl->szPath; *pSrc; pSrc++)
  462.         if (*pSrc != '\r' && *pSrc != '\n')
  463.             *pDst++ = *pSrc;
  464.     *pDst = '\0';
  465.  
  466. // set the Begin or End flags based on the radio buttons
  467.     if (WinSendDlgItemMsg( hwnd, IDC_END, BM_QUERYCHECK, 0, 0))
  468.         prwl->flags = RWLF_SET | RWLF_END;
  469.     else
  470.         prwl->flags = RWLF_SET | RWLF_BEG;
  471.  
  472. // if we're setting ourself, tell the hook not to free its prwl
  473.     if (prwl->pid == pidRWL)
  474.         prwl->flags |= RWLF_NOFREE;
  475.  
  476. // post our update to the target's queue
  477.     pErr = "WinPostQueueMsg";
  478.     fDisable = WinPostQueueMsg( prwl->hmq, ulRWLM_X2D, (MP)prwl, (MP)hwnd);
  479.     if (fDisable == FALSE)
  480.         break;
  481.  
  482.     fRtn = TRUE;
  483.  
  484. } while (fFalse);
  485.  
  486.     if (fDisable)
  487.         RWLXEnableBtns( hwnd, FALSE);
  488.  
  489. // if anything went wrong, refresh the PID listbox
  490.     if (fRtn == FALSE)
  491.     {
  492.         ErrBox( __FUNCTION__, pErr);
  493.         RWLXUpdatePIDs( hwnd, 0);
  494.     }
  495.  
  496.     return (fRtn);
  497. }
  498.  
  499. /****************************************************************************/
  500.  
  501. // restore the target's Begin- or EndLIBPATH to its state
  502. // when RWL first queried it
  503.  
  504. BOOL    RWLXUndo( HWND hwnd)
  505.  
  506. {
  507.     BOOL        fRtn = FALSE;
  508.     BOOL        fDisable = FALSE;
  509.     PRWL        prwl;
  510.     char *      pErr;
  511.  
  512. do
  513. {
  514. // validate the dialog's current RWL ptr
  515.     pErr = "invalid PID";
  516.     prwl = RWLXValidateRWL( WinQueryWindowPtr( hwnd, QWL_USER));
  517.     if (prwl == NULL)
  518.     {
  519.         fDisable = TRUE;
  520.         break;
  521.     }
  522.  
  523. // make sure we saved something, then copy it to the buffer
  524. // and set the flags
  525.     pErr = "Undo buffer not initialized";
  526.     if (WinSendDlgItemMsg( hwnd, IDC_END, BM_QUERYCHECK, 0, 0))
  527.     {
  528.         if (prwl->pszEnd == NULL)
  529.             break;
  530.         strcpy( prwl->szPath, prwl->pszEnd);
  531.         prwl->flags = RWLF_SET | RWLF_END;
  532.     }
  533.     else
  534.     {
  535.         if (prwl->pszBeg == NULL)
  536.             break;
  537.         strcpy( prwl->szPath, prwl->pszBeg);
  538.         prwl->flags = RWLF_SET | RWLF_BEG;
  539.     }
  540.  
  541.     if (prwl->pid == pidRWL)
  542.         prwl->flags |= RWLF_NOFREE;
  543.  
  544. // post our update request to the target queue
  545.     pErr = "WinPostQueueMsg";
  546.     fDisable = WinPostQueueMsg( prwl->hmq, ulRWLM_X2D, (MP)prwl, (MP)hwnd);
  547.     if (fDisable == FALSE)
  548.         break;
  549.  
  550.     fRtn = TRUE;
  551.  
  552. } while (fFalse);
  553.  
  554.     if (fDisable)
  555.         RWLXEnableBtns( hwnd, FALSE);
  556.  
  557. // if anything went wrong, refresh the PID listbox
  558.     if (fRtn == FALSE)
  559.     {
  560.         ErrBox( __FUNCTION__, pErr);
  561.         RWLXUpdatePIDs( hwnd, 0);
  562.     }
  563.  
  564.     return (fRtn);
  565. }
  566.  
  567. /****************************************************************************/
  568.  
  569. // this proc handles the hook's response to our previous msg
  570.  
  571. BOOL    RWLXReply( HWND hwnd, PRWL prwl, ULONG rc)
  572.  
  573. {
  574.     BOOL        fRtn = TRUE;
  575.     BOOL        fEnable = TRUE;
  576.     char *      pErr;
  577.     char        szText[128];
  578.  
  579. do
  580. {
  581. // the msg refers to a different RWL than this window's
  582.     pErr = "misdirected reply";
  583.     if (WinQueryWindowPtr( hwnd, QWL_USER) != prwl)
  584.         break;
  585.  
  586. // the hook had a catastrophic failure (i.e. it couldn't
  587. // access RWL's shared memory)
  588.     pErr = "operation failed";
  589.     if (rc)
  590.         break;
  591.  
  592. // lotsa possibilities...
  593.  
  594.     switch (prwl->flags)
  595.     {
  596.  
  597. // we queried the target's BeginLIBPATH
  598.  
  599.         case (RWLF_GET | RWLF_BEG | RWLF_OK):
  600.         // if this is our first query, save for Undo;
  601.         // update the MLE and caption;
  602.  
  603.             if (prwl->pszBeg == NULL)
  604.                 prwl->pszBeg = strdup( prwl->szPath);
  605.             WinSetDlgItemText( hwnd, IDC_MLE, prwl->szPath);
  606.             sprintf( szText, "BeginLIBPATH for PID %d <%s>", prwl->pid,
  607.                      (prwl->pszProc ? prwl->pszProc : "unidentified process"));
  608.             WinSetDlgItemText( hwnd, IDC_CAP, szText);
  609.             break;
  610.  
  611. // we queried the target's EndLIBPATH
  612.  
  613.         case (RWLF_GET | RWLF_END | RWLF_OK):
  614.         // if this is our first query, save for Undo;
  615.         // update the MLE and caption;
  616.  
  617.             if (prwl->pszEnd == NULL)
  618.                 prwl->pszEnd = strdup( prwl->szPath);
  619.             WinSetDlgItemText( hwnd, IDC_MLE, prwl->szPath);
  620.             sprintf( szText, "EndLIBPATH for PID %d <%s>", prwl->pid,
  621.                      (prwl->pszProc ? prwl->pszProc : "unidentified process"));
  622.             WinSetDlgItemText( hwnd, IDC_CAP, szText);
  623.             break;
  624.  
  625. // we queried the f/q name of the target's exe
  626.  
  627.         case (RWLF_GET | RWLF_PROC | RWLF_OK):
  628.         // if this is our first query, save for Undo;
  629.         // query the target's Begin- or EndLIBPATH;
  630.         // leave the buttons disabled
  631.  
  632.             if (prwl->pszProc == NULL)
  633.                 prwl->pszProc = strdup( prwl->szPath);
  634.  
  635.             if (WinSendDlgItemMsg( hwnd, IDC_END, BM_QUERYCHECK, 0, 0))
  636.                 prwl->flags = RWLF_GET | RWLF_END;
  637.             else
  638.                 prwl->flags = RWLF_GET | RWLF_BEG;
  639.             if (prwl->pid == pidRWL)
  640.                 prwl->flags |= RWLF_NOFREE;
  641.  
  642.             pErr = "WinPostQueueMsg";
  643.             fRtn = WinPostQueueMsg( prwl->hmq, ulRWLM_X2D, (MP)prwl, (MP)hwnd);
  644.             fEnable = (BOOL)(fRtn ? FALSE : TRUE);
  645.             break;
  646.  
  647. // we set the target's Begin- or EndLIBPATH
  648.  
  649.         case (RWLF_SET | RWLF_BEG | RWLF_OK):
  650.         case (RWLF_SET | RWLF_END | RWLF_OK):
  651.         // our update was accepted, now query the
  652.         // target to see what the result looks like;
  653.         // leave the buttons disabled
  654.  
  655.             prwl->flags &= RWLF_BEGEND;
  656.             prwl->flags |= RWLF_GET;
  657.             if (prwl->pid == pidRWL)
  658.                 prwl->flags |= RWLF_NOFREE;
  659.  
  660.             pErr = "WinPostQueueMsg";
  661.             fRtn = WinPostQueueMsg( prwl->hmq, ulRWLM_X2D, (MP)prwl, (MP)hwnd);
  662.             fEnable = (BOOL)(fRtn ? FALSE : TRUE);
  663.             break;
  664.  
  665. // our command failed
  666.  
  667.         case (RWLF_GET | RWLF_BEG | RWLF_BAD):
  668.         case (RWLF_GET | RWLF_END | RWLF_BAD):
  669.         case (RWLF_GET | RWLF_PROC | RWLF_BAD):
  670.         case (RWLF_SET | RWLF_BEG | RWLF_BAD):
  671.         case (RWLF_SET | RWLF_END | RWLF_BAD):
  672.             pErr = "operation failed";
  673.             fRtn = FALSE;
  674.             break;
  675.  
  676. // no other combination of flags should occur
  677.  
  678.         default:
  679.             pErr = "invalid response";
  680.             rc = prwl->flags;
  681.             fRtn = FALSE;
  682.             break;
  683.     }
  684.  
  685. } while (fFalse);
  686.  
  687.     if (fRtn == FALSE)
  688.         RcErrBox( __FUNCTION__, pErr, rc);
  689.  
  690. // enable/disable buttons per flag set above
  691.     RWLXEnableBtns( hwnd, fEnable);
  692.  
  693.     return (fRtn);
  694. }
  695.  
  696. /****************************************************************************/
  697.  
  698. // prevent user changes while a command is in progress
  699.  
  700. void    RWLXEnableBtns( HWND hwnd, BOOL fEnable)
  701.  
  702. {
  703.     WinEnableWindow( WinWindowFromID( hwnd, IDC_SET), fEnable);
  704.     WinEnableWindow( WinWindowFromID( hwnd, IDC_UNDO), fEnable);
  705.     WinEnableWindow( WinWindowFromID( hwnd, IDC_BEG), fEnable);
  706.     WinEnableWindow( WinWindowFromID( hwnd, IDC_END), fEnable);
  707.     return;
  708. }
  709.  
  710. /****************************************************************************/
  711. /****************************************************************************/
  712.  
  713. //  switch to whichever PID is selected in the listbox
  714.  
  715. BOOL    RWLXSelectPID( HWND hwnd)
  716.  
  717. {
  718.     HWND        hHook;
  719.     BOOL        fRtn = FALSE;
  720.     PRWL        prwl;
  721.     SHORT       ndx;
  722.  
  723. do
  724. {
  725. // get the item handle for the selected LB item;  this is a window
  726. // belonging to the selected process
  727.  
  728.     ndx = (SHORT)WinSendDlgItemMsg( hwnd, IDC_PIDLB, LM_QUERYSELECTION,
  729.                                     (MP)LIT_FIRST, 0);
  730.     if (ndx == LIT_NONE)
  731.         ndx = 0;
  732.  
  733.     hHook = (HWND)WinSendDlgItemMsg( hwnd, IDC_PIDLB, LM_QUERYITEMHANDLE,
  734.                                      (MP)ndx, 0);
  735.     if (hHook == 0)
  736.     {
  737.         ErrBox( __FUNCTION__, "invalid entry in PID list");
  738.         break;
  739.     }
  740.  
  741. // get an RWL struct for this window cum process;
  742. // it may already exist if we've examined this PID before,
  743. // otherwise it will be created
  744.     prwl = RWLXGetPRWL( hHook);
  745.     if (prwl == NULL)
  746.         break;
  747.  
  748. // switch to the target PID;  RWLXSwitchPIDs() will unhook the
  749. // current process, hook the new one, then reinit the dialog
  750. // for the new PID and update its QWL_USER
  751.     fRtn = RWLXSwitchPIDs( hwnd, prwl);
  752.  
  753. } while (fFalse);
  754.  
  755. // if anything went wrong, refresh the PID listbox
  756.     if (fRtn == FALSE)
  757.         RWLXUpdatePIDs( hwnd, 0);
  758.  
  759.     return (fRtn);
  760. }
  761.  
  762. /****************************************************************************/
  763.  
  764. // return a ptr to an RWL struct for the specified window;
  765. // it may already exist or it may need to be created
  766.  
  767. PRWL    RWLXGetPRWL( HWND hHook)
  768.  
  769. {
  770.     ULONG       ul;
  771.     PID         pid;
  772.     HMQ         hmq;
  773.     PRWL        pRtn = NULL;
  774.     PRWL        ptr;
  775.     char *      pErr;
  776.  
  777. do
  778. {
  779. // look for an existing entry
  780.     ptr = NULL;
  781.     while ( (ptr = RWLXNextRWL( ptr)) != NULL)
  782.         if (ptr->hwnd == hHook)
  783.             break;
  784.  
  785. // if we found something, validate it then exit
  786.     pErr = "invalid PID";
  787.     if (ptr)
  788.     {
  789.         pRtn = RWLXValidateRWL( ptr);
  790.         break;
  791.     }
  792.  
  793. // get the target window's PID and HMQ
  794.     if (!WinQueryWindowProcess( hHook, &pid, &ul))
  795.         break;
  796.  
  797.     pErr = "WinQueryWindowULong";
  798.     hmq = WinQueryWindowULong( hHook, QWL_HMQ);
  799.     if (hmq == NULLHANDLE)
  800.         break;
  801.  
  802. // get a ptr to a new RWL struct
  803.     pErr = "too many processes being accessed";
  804.     pRtn = RWLXAllocRWL();
  805.     if (pRtn == NULL)
  806.         break;
  807.  
  808. // init it;
  809.     pRtn->pid = pid;
  810.     pRtn->hmq = hmq;
  811.     pRtn->hwnd = hHook;
  812.     pRtn->flags = (ULONG)-1;
  813.  
  814. } while (fFalse);
  815.  
  816. // if we're returning a valid ptr, update its
  817. // least-recently-used indicator
  818.  
  819.     if (pRtn)
  820.         pRtn->used = usUsed++;
  821.     else
  822.         ErrBox( __FUNCTION__, pErr);
  823.  
  824.     return (pRtn);
  825. }
  826.  
  827. /****************************************************************************/
  828.  
  829. // unhook the current PID's queue, hook the new queue,
  830. // then post a message that queries the process's name;
  831. // if prwlNew is NULL, just unhook (used at shutdown)
  832.  
  833.  
  834. BOOL    RWLXSwitchPIDs( HWND hwnd, PRWL prwlNew)
  835.  
  836. {
  837.     BOOL        fRtn = FALSE;
  838.     PRWL        prwl;
  839.     char *      pErr;
  840.  
  841. do
  842. {
  843. // if the window's current RWL is still valid, unhook from
  844. // that process's msg queue
  845.     prwl = RWLXValidateRWL( WinQueryWindowPtr( hwnd, QWL_USER));
  846.     if (prwl)
  847.     {
  848.         if (WinReleaseHook( habMain, prwl->hmq, HK_INPUT,
  849.                             (PFN)RWLDInputHook, hmodRWLD) == FALSE)
  850.             ErrBox( __FUNCTION__, "WinReleaseHook failed - continuing");
  851.         else
  852.             prwl->inuse = FALSE;
  853.         prwl = NULL;
  854.     }
  855.  
  856. // prevent user updates 
  857.     RWLXEnableBtns( hwnd, FALSE);
  858.     WinSetDlgItemText( hwnd, IDC_CAP, "Waiting...");
  859.  
  860. // if there's nothing to switch to, exit OK
  861.     if (prwlNew == NULL)
  862.     {
  863.         pErr = NULL;
  864.         fRtn = TRUE;
  865.         break;
  866.     }
  867.  
  868. // hook the designated msg queue in the target process;
  869. // exit if this fails
  870.     if (prwlNew->inuse == FALSE)
  871.     {
  872.         pErr = "WinSetHook";
  873.         prwlNew->inuse = (USHORT)WinSetHook( habMain, prwlNew->hmq, HK_INPUT,
  874.                                              (PFN)RWLDInputHook, hmodRWLD);
  875.         if (prwlNew->inuse == FALSE)
  876.             break;
  877.     }
  878.     prwl = prwlNew;
  879.  
  880. // if we don't already know the name of the target PID's exe,
  881. // set the flags accordingly;  otherwise set the Begin or End
  882. // flags based on the radio buttons
  883.     if (prwl->pszProc == NULL)
  884.         prwl->flags = RWLF_GET | RWLF_PROC;
  885.     else
  886.         if (WinSendDlgItemMsg( hwnd, IDC_END, BM_QUERYCHECK, 0, 0))
  887.             prwl->flags = RWLF_GET | RWLF_END;
  888.         else
  889.             prwl->flags = RWLF_GET | RWLF_BEG;
  890.  
  891. // if we've hooked our own process's queue, warn the hook
  892. // not to get or free its RWL
  893.     if (prwl->pid == pidRWL)
  894.         prwl->flags |= RWLF_NOFREE;
  895.  
  896. // post a msg to the queue we've hooked;
  897. // pass the address of the current RWL struct (which is in
  898. // shared gettable memory), and the hwnd to which it should
  899. // post its reply
  900.     pErr = "WinPostQueueMsg";
  901.     fRtn = WinPostQueueMsg( prwl->hmq, ulRWLM_X2D, (MP)prwl, (MP)hwnd);
  902.  
  903. } while (fFalse);
  904.  
  905. // update QWL_USER with a ptr to the current RWL (this could be null)
  906.     WinSetWindowPtr( hwnd, QWL_USER, prwl);
  907.  
  908.     if (fRtn == FALSE)
  909.         ErrBox( __FUNCTION__, pErr);
  910.  
  911.     return (fRtn);
  912. }
  913.  
  914. /****************************************************************************/
  915.  
  916. // this confirms that the target window still exists
  917. // and belongs to the expected PID.  If so, it returns
  918. // the input;  if not, it frees the invalid RWL and
  919. // returns NULL.
  920.  
  921. PRWL    RWLXValidateRWL( PRWL prwl)
  922.  
  923. {
  924.     PID         pid;
  925.     TID         tid;
  926.     PRWL        pRtn = NULL;
  927.  
  928.     if (prwl)
  929.     {
  930.         if (WinQueryWindowProcess( prwl->hwnd, &pid, &tid) &&
  931.             pid == prwl->pid)
  932.             pRtn = prwl;
  933.         else
  934.             RWLXFreeRWL( prwl);
  935.     }
  936.     return (pRtn);
  937. }
  938.  
  939. /****************************************************************************/
  940.  
  941. // step through the array from the beginning or from a designated
  942. // starting point, skipping over empty entries
  943.  
  944. PRWL    RWLXNextRWL( PRWL prwl)
  945.  
  946. {
  947.     PRWL    pRtn = NULL;
  948.  
  949.     if (prwl == NULL)
  950.         prwl = &prwlBase[-1];
  951.  
  952.     if (prwl >= &prwlBase[-1])
  953.         while (++prwl < prwlNext)
  954.             if (prwl->hwnd)
  955.             {
  956.                 pRtn = prwl;
  957.                 break;
  958.             }
  959.  
  960.     return (pRtn);
  961. }
  962.  
  963. /****************************************************************************/
  964.  
  965. // Return the first available entry in our RWL array.
  966. // (this proc was designed with a multi-window version
  967. // in mind, so it doesn't compact the array and checks
  968. // whether an entry is in use)
  969.  
  970. PRWL    RWLXAllocRWL( void)
  971.  
  972. {
  973.     PRWL    pRtn = NULL;
  974.     PRWL    pLRU;
  975.     PRWL    ptr;
  976.  
  977. // step through currently used entries looking for an empty one;
  978. // if found, exit;  if not, save a ptr to the oldest RWL not
  979. // currently in use (this last helps support a multi-windowed
  980. // version, as does not compacting the array).
  981.  
  982.     for (ptr=pLRU=prwlBase; ptr < prwlNext; ptr++)
  983.     {
  984.         if (ptr->hwnd == 0)
  985.         {
  986.             pRtn = ptr;
  987.             break;
  988.         }
  989.         if (ptr->used < pLRU->used && ptr->inuse == FALSE)
  990.             pLRU = ptr;
  991.     }
  992.  
  993. // if nothing was found, see if there's any more room in
  994. // the array.  if so, use it;  otherwise, recycle the
  995. // least recently used entry
  996.     if (pRtn == NULL)
  997.         if (&prwlNext[1] < &prwlBase[ 0x10000/sizeof( RWL) ])
  998.             pRtn = prwlNext++;
  999.         else
  1000.             if (pLRU->inuse == FALSE)
  1001.             {
  1002.                 RWLXFreeRWL( pLRU);
  1003.                 pRtn = pLRU;
  1004.             }
  1005.  
  1006.     return (pRtn);
  1007. }
  1008.  
  1009. /****************************************************************************/
  1010.  
  1011. // free the storage for the PID's name and the Undo buffers;
  1012. // then clear the entry
  1013.  
  1014. void    RWLXFreeRWL( PRWL prwl)
  1015.  
  1016. {
  1017.     free( prwl->pszProc);
  1018.     free( prwl->pszBeg);
  1019.     free( prwl->pszEnd);
  1020.     memset( prwl, '\0', sizeof( RWL));
  1021.     return;
  1022. }
  1023.  
  1024. /**********************************************************************/
  1025. /**********************************************************************/
  1026.  
  1027. // identifies all PM processes, then fills a listbox with their PIDs
  1028.  
  1029. BOOL    RWLXUpdatePIDs( HWND hwnd, PID pidSelect)
  1030.  
  1031. {
  1032.     PAPP    paApp = NULL;
  1033.     PRWL    prwl;
  1034.     BOOL    fRtn = FALSE;
  1035.  
  1036. do
  1037. {
  1038. // alloc space for an arbitrary number of APP structures;
  1039. // this corresponds to the maximum number of PIDs we can handle
  1040.     paApp = calloc( MAXAPP, sizeof( APP));
  1041.     if (paApp == NULL)
  1042.     {
  1043.         ErrBox( __FUNCTION__, "calloc");
  1044.         break;
  1045.     }
  1046.  
  1047. // fill the array, one entry per PID
  1048.     if (RWLXEnumPIDs( paApp, MAXAPP-1) == 0)
  1049.         break;
  1050.  
  1051. // if the PID to select in the listbox wasn't specified,
  1052. // use the dialog's current PID if possible
  1053.     if (pidSelect == 0 && 
  1054.         (prwl = RWLXValidateRWL( WinQueryWindowPtr( hwnd, QWL_USER))) != NULL)
  1055.         pidSelect = prwl->pid;
  1056.  
  1057. // fill the listbox and select an item
  1058.     RWLXListPIDs( WinWindowFromID( hwnd, IDC_PIDLB), pidSelect, paApp);
  1059.  
  1060.     fRtn = TRUE;
  1061.  
  1062. } while (fFalse);
  1063.  
  1064. // free our APP structs
  1065.     free( paApp);
  1066.  
  1067.     return (fRtn);
  1068. }
  1069.  
  1070. /**********************************************************************/
  1071.  
  1072. // for each PID, identify a window associated with the lowest
  1073. // numbered thread that has a msg queue
  1074.  
  1075. LONG    RWLXEnumPIDs( PAPP paApp, ULONG cntApp)
  1076.  
  1077. {
  1078.     HENUM   hEnum;
  1079.     HWND    hTmp;
  1080.     LONG    ctr = 0;
  1081.     PID     pid;
  1082.     TID     tid;
  1083.     PAPP    ptr;
  1084.     char    szText[16];
  1085.  
  1086. do
  1087. {
  1088.     hEnum = WinBeginEnumWindows( HWND_OBJECT);
  1089.     if (hEnum == NULLHANDLE)
  1090.     {
  1091.         ErrBox( __FUNCTION__, "WinBeginEnumWindows");
  1092.         break;
  1093.     }
  1094.  
  1095.     while (ctr < cntApp &&
  1096.            (hTmp = WinGetNextWindow( hEnum)) != 0)
  1097.     {
  1098.         WinQueryClassName( hTmp, sizeof( szText), szText);
  1099.         if (strcmp( szText, "#32767"))
  1100.             continue;
  1101.  
  1102.         WinQueryWindowProcess( hTmp, &pid, &tid);
  1103.  
  1104.         for (ptr=paApp; ptr < &paApp[cntApp]; ptr++)
  1105.         {
  1106.             if (ptr->pid == 0)
  1107.             {
  1108.                 ptr->hwnd = hTmp;
  1109.                 ptr->pid  = pid;
  1110.                 ptr->tid  = tid;
  1111.                 ctr++;
  1112.                 break;
  1113.             }
  1114.  
  1115.             if (ptr->pid == pid)
  1116.             {
  1117.                 if (tid < ptr->tid)
  1118.                 {                
  1119.                     ptr->hwnd = hTmp;
  1120.                     ptr->tid  = tid;
  1121.                 }
  1122.                 break;
  1123.             }
  1124.  
  1125.         } // end for()
  1126.     } // end while()
  1127.  
  1128.     WinEndEnumWindows( hEnum);
  1129.  
  1130. } while (fFalse);
  1131.  
  1132.     return (ctr);
  1133. }
  1134.  
  1135. /**********************************************************************/
  1136.  
  1137. // fills the listbox with PIDs and their corresponding hwnds,
  1138. // then selects an item
  1139.  
  1140. void    RWLXListPIDs( HWND hLB, PID pidSelect, PAPP paApp)
  1141.  
  1142. {
  1143.     SHORT   ndx;
  1144.     char    szText[16];
  1145.  
  1146. // clear the listbox
  1147.     WinSendMsg( hLB, LM_DELETEALL, 0, 0);
  1148.  
  1149. // for each entry in the APP array, format its PID,
  1150. // then insert the item sorted;  if this succeeds,
  1151. // set the item's handle with the hwnd
  1152.     while (paApp->pid)
  1153.     {
  1154.         sprintf( szText, "%4d", paApp->pid);
  1155.         ndx = (SHORT)WinSendMsg( hLB, LM_INSERTITEM,
  1156.                                  (MP)LIT_SORTASCENDING, szText);
  1157.         if (ndx >= 0)
  1158.             WinSendMsg( hLB, LM_SETITEMHANDLE,
  1159.                         (MP)ndx, (MP)paApp->hwnd);
  1160.  
  1161.         paApp++;
  1162.     }
  1163.  
  1164. // since the list was sorted during insertion, we now have
  1165. // to search for the PID we want to select;  if this fails,
  1166. // we'll select the first item (which should be pmshell)
  1167.  
  1168.     sprintf( szText, "%4d", pidSelect);
  1169.     ndx = (SHORT)WinSendMsg( hLB, LM_SEARCHSTRING,
  1170.                         MPFROM2SHORT( LSS_CASESENSITIVE, LIT_FIRST),
  1171.                         (MP)szText);
  1172.     if (ndx < 0)
  1173.         ndx = 0;
  1174.  
  1175.     WinSendMsg( hLB, LM_SELECTITEM, (MP)ndx, (MP)TRUE);
  1176.  
  1177.     return;
  1178. }
  1179.  
  1180. /**********************************************************************/
  1181. /**********************************************************************/
  1182.  
  1183. // invokes the main errorbox routine with a rtn code of zero
  1184.  
  1185. BOOL    ErrBox( PSZ pTtl, PSZ pErr)
  1186.  
  1187. {
  1188.     return (RcErrBox( pTtl, pErr, 0));
  1189. }
  1190.  
  1191. /**********************************************************************/
  1192.  
  1193. // a dumb little msg box
  1194.  
  1195. BOOL    RcErrBox( PSZ pTtl, PSZ pErr, ULONG rc)
  1196.  
  1197. {
  1198.     char    szText[256];
  1199.  
  1200.     if (rc)
  1201.         sprintf( szText, "%s:  %s  rc=%x", pTtl, pErr, rc);
  1202.     else
  1203.         sprintf( szText, "%s:  %s", pTtl, pErr);
  1204.  
  1205.     WinMessageBox( HWND_DESKTOP, NULLHANDLE, szText, "RWL", IDD_ERRBOX,
  1206.                    (ULONG)MB_OK | (ULONG)MB_ICONEXCLAMATION | (ULONG)MB_MOVEABLE);
  1207.  
  1208.     return (FALSE);
  1209. }
  1210.  
  1211. /**********************************************************************/
  1212. /**********************************************************************/
  1213.  
  1214.