home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / msysjour / vol07 / 08 / sendkey / sendkeys.c < prev    next >
Text File  |  1992-12-01  |  17KB  |  493 lines

  1. /****************************************************************************
  2. Module name: SendKeys.C
  3. Programmer : Jeffrey M. Richter.
  4. Description: Simulation of the VB/Word/Excel SendKeys statement.
  5. *****************************************************************************/
  6.  
  7. #include <Windows.h>
  8. #include <String.h>
  9. #include <Stdlib.h>   // For atoi().
  10. #include "SendKeys.h"
  11.  
  12. #define MAKEWORD(low, high) \
  13.    ((WORD)(((BYTE)(low)) | (((WORD)((BYTE)(high))) << 8)))
  14.  
  15. #define MAX_VIRT_KEY_CODES  1024
  16.  
  17. #define CHECKFORSTRINGTOOLONG() \
  18.    if (g_uMaxVirtKeys == sizeof(g_uVirtKeys)) return(SK_STRINGTOOLONG);
  19.  
  20. // Application global variables (all start with "g_").
  21. static HINSTANCE      g_hInstance = NULL;
  22.  
  23. static UINT           g_uVirtKeys[MAX_VIRT_KEY_CODES];
  24. static UINT           g_uVirtKeyNum = 0; // Index into g_uVirtKeys.
  25. static UINT           g_uMaxVirtKeys = 0; // Max entries in g_uVirtKeys.
  26.  
  27. static BOOL           g_fNoTokensRetrievedYet = TRUE;
  28. static LPCSTR         g_szKeys;
  29. static volatile HHOOK g_hHook = NULL;
  30.  
  31. // Used to defeat compiler warning.
  32. #define UNREFERENCED_PARAMETER(P)          ((void)(P))
  33.  
  34.  
  35. BOOL WINAPI LibMain (HINSTANCE hInstance, WORD wDataSeg,
  36.    WORD cbHeapSize, LPSTR lpCmdLine) {
  37.    g_hInstance = hInstance;
  38.  
  39.    UNREFERENCED_PARAMETER(wDataSeg);
  40.    UNREFERENCED_PARAMETER(cbHeapSize);
  41.    UNREFERENCED_PARAMETER(lpCmdLine);
  42.    return(TRUE);   // Initialization is successful
  43. }
  44.  
  45.  
  46. // ****************************************************************************
  47. // The table of special key codes.
  48. struct {
  49.    UINT uVirtKey;
  50.    const char *szKeyName;
  51. } g_SpecialKeys[] = {
  52.    { VK_CAPITAL,   "CAPSLOCK"  },  { VK_NUMLOCK,   "NUMLOCK"  },
  53.    { VK_SCROLL,    "SCROLLOCK" },  { VK_ESCAPE,    "ESCAPE"  },
  54.    { VK_ESCAPE,    "ESC"   },      { VK_RETURN,    "ENTER"   },
  55.    { VK_HELP,      "HELP"        },  { VK_SNAPSHOT,  "PRTSC"       },
  56.    { VK_TAB,       "TAB"         },  { VK_CONTROL,   "BREAK"       },
  57.    { VK_CLEAR,     "CLEAR"       },  { VK_BACK,      "BACKSPACE"   },
  58.    { VK_BACK,      "BS"          },  { VK_BACK,      "BKSP"        },
  59.    { VK_DELETE,    "DELETE"      },  { VK_DELETE,    "DEL"         },
  60.    { VK_INSERT,    "INSERT"      },  { VK_LEFT,      "LEFT"        },
  61.    { VK_RIGHT,     "RIGHT"       },  { VK_UP,        "UP"          },
  62.    { VK_DOWN,      "DOWN"        },  { VK_PRIOR,     "PGUP"        },
  63.    { VK_NEXT,      "PGDN"        },  { VK_HOME,      "HOME"        },
  64.    { VK_END,       "END"         },  { VK_F1,        "F1"          },
  65.    { VK_F2,        "F2"          },  { VK_F3,        "F3"          },
  66.    { VK_F4,        "F4"          },  { VK_F5,        "F5"          },
  67.    { VK_F6,        "F6"          },  { VK_F7,        "F7"          },
  68.    { VK_F8,        "F8"          },  { VK_F9,        "F9"          },
  69.    { VK_F10,       "F10"         },  { VK_F11,       "F11"         },
  70.    { VK_F12,       "F12"         },  { VK_F13,       "F13"         },
  71.    { VK_F14,       "F14"         },  { VK_F15,       "F15"         },
  72.    { VK_F16,       "F16"         }
  73. };
  74.  
  75.  
  76. // Function to convert a special key string into its
  77. // equivalent virtual-key code.
  78. static UINT NEAR SpecialKeyToVirtKey (LPCSTR lpszSpecialKey) {
  79.    UINT x;
  80.  
  81.    // Scan the array and compare each of the possible strings.
  82.    for (x = 0; x < ARRAY_LEN(g_SpecialKeys); x++) {
  83.       if (lstrcmpi(lpszSpecialKey, g_SpecialKeys[x].szKeyName) == 0) {
  84.          // Found the special key in the list, return its virtual-key code.
  85.          return(MAKEWORD(g_SpecialKeys[x].uVirtKey, 0));
  86.       }
  87.    }
  88.  
  89.    // The special key was not found in the list.
  90.    if (lstrlen(lpszSpecialKey) == 1) {
  91.       // If the special key is a single character, convert that
  92.       // character to its virtual-key code equivalent.
  93.       // Note 1: This must come after the loop above so that special single
  94.       //         characters are checked first (i.e.-"~" (tilde)).
  95.       // Note 2: This check is necessary for other special single characters
  96.       //         (i.e.-"+^%{}()).
  97.       return(VkKeyScan(*lpszSpecialKey));
  98.    }
  99.    return(0);      // Error, special key not found.
  100. }
  101.  
  102.  
  103.  
  104.  
  105. // Function to preprocess the next token in the input string.
  106. static SENDKEYSERR NEAR _PreprocessKeys (void) {
  107.    char cChar, szSpecialKey[15]; LPCSTR lpEndOfToken; UINT uVirtKey, uCount;
  108.    SENDKEYSERR SendKeysErr = SK_NOERROR;
  109.  
  110.    // Get the next character from the input string.
  111.    switch (cChar = *g_szKeys++) {
  112.  
  113.       case 0:     // Reached the end of the input string.
  114.          g_szKeys--;    // Point back to the zero-byte.
  115.          return(SK_NOERROR); // End of string - no errors.
  116.  
  117.  
  118.  
  119.       case '{':   // Beginning of special key text.
  120.          if (*g_szKeys == '}') {
  121.             // The special character is a close brace.
  122.             uVirtKey = VkKeyScan('}');
  123.             lpEndOfToken = ++g_szKeys; // Point past the virtual key.
  124.          } else {
  125.             // Assume that we don't know what virtual key we are looking at.
  126.             uVirtKey = 0;
  127.             lpEndOfToken = g_szKeys;
  128.          }
  129.  
  130.          // Locate the end of the first token in the braced expression.
  131.          // This is either:
  132.          //    1. End of string,
  133.          //    2. a special word/symbol followed by a close brace, or
  134.          //    3. a special word/symbol followed by a space and a number
  135.          //       indicating a repeat count.
  136.          while ((*lpEndOfToken != 0) && (*lpEndOfToken != ' ') &&
  137.                 (*lpEndOfToken != '}'))
  138.             lpEndOfToken++;
  139.  
  140.          if (*lpEndOfToken == 0)
  141.             return(SK_MISSINGCLOSEBRACE); // Error, missing end brace.
  142.  
  143.  
  144.          if (uVirtKey == 0) {
  145.             // The key must not be the close brace.  We must determine
  146.             // the virtual key corresponding to the this special key.
  147.             lstrcpyn(szSpecialKey, g_szKeys,
  148.                      (UINT) (lpEndOfToken - g_szKeys + 1));
  149.             uVirtKey = SpecialKeyToVirtKey(szSpecialKey);
  150.          }
  151.          if (uVirtKey == 0)
  152.             return(SK_INVALIDKEY); // Error, special key not found
  153.  
  154.  
  155.  
  156.          // Just a special word/character without a repeat count.
  157.          if (*lpEndOfToken == '}') uCount = 1;
  158.  
  159.          // Calculate the repeat count for this special character.
  160.          if (*lpEndOfToken == ' ')
  161.             uCount = (UINT) atoi(++lpEndOfToken);
  162.  
  163.          if (uCount == 0)
  164.             return(SK_INVALIDCOUNT); // Error, invalid count value
  165.  
  166.  
  167.  
  168.          // Add this special key to the virtual key list.
  169.          if (HIBYTE(uVirtKey) & 1) {
  170.             // This key is a shifted key.
  171.             CHECKFORSTRINGTOOLONG();
  172.             g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Press the SHIFT key.
  173.          }
  174.  
  175.  
  176.          while (uCount--) {
  177.             CHECKFORSTRINGTOOLONG();
  178.             g_uVirtKeys[g_uMaxVirtKeys++] = LOBYTE(uVirtKey);
  179.          }
  180.  
  181.          if (HIBYTE(uVirtKey) & 1) {
  182.             // This key is a shifted key.
  183.             CHECKFORSTRINGTOOLONG();
  184.             g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Release the SHIFT key.
  185.          }
  186.  
  187.          // Find the character after the closing brace
  188.          while (*lpEndOfToken++ != '}') ;
  189.  
  190.          // Point to character after the closing brace so that
  191.          // parsing may continue
  192.          g_szKeys = lpEndOfToken;
  193.          break;
  194.  
  195.  
  196.       case '(':   // Beginning of sub-group.
  197.          // While not at the end of the input string and not at a close paren.
  198.          while ((*g_szKeys != 0) && (*g_szKeys != ')')) {
  199.  
  200.             // Add the next character to the array.
  201.             SendKeysErr = _PreprocessKeys();
  202.             if (SendKeysErr != SK_NOERROR) return(SendKeysErr);
  203.          }
  204.  
  205.          // If terminating because of end of string and not because
  206.          // of right paren, there is an error.
  207.          if (*g_szKeys == 0) return(SK_MISSINGCLOSEPAREN);
  208.  
  209.          g_szKeys++; // Skip past the close paren.
  210.          break;
  211.  
  212.  
  213.  
  214.       case '~':   // Press the ENTER key.
  215.          CHECKFORSTRINGTOOLONG();
  216.          g_uVirtKeys[g_uMaxVirtKeys++] = VK_RETURN; // Press the (ENTER) key.
  217.          break;
  218.  
  219.  
  220.       case '+':       // Press the SHIFT key.
  221.       case '^':       // Press the CONTROL key.
  222.       case '%':       // Press the ALT key.
  223.          if      (cChar == '+')  cChar = VK_SHIFT;
  224.          else if (cChar == '^')  cChar = VK_CONTROL;
  225.          else                    cChar = VK_MENU;
  226.  
  227.          // Press the (SHIFT/CONTROL/ALT) key.
  228.          CHECKFORSTRINGTOOLONG();
  229.          g_uVirtKeys[g_uMaxVirtKeys++] = cChar;
  230.  
  231.          SendKeysErr = _PreprocessKeys();
  232.          if (SendKeysErr != SK_NOERROR) return(SendKeysErr);
  233.  
  234.          // Release the (SHIFT/CONTROL/ALT) key
  235.          CHECKFORSTRINGTOOLONG();
  236.          g_uVirtKeys[g_uMaxVirtKeys++] = cChar;
  237.          break;
  238.  
  239.  
  240.       default:       // Just a normal character.
  241.          uVirtKey = VkKeyScan(cChar);
  242.          if (HIBYTE(uVirtKey) & 1) {
  243.             CHECKFORSTRINGTOOLONG();
  244.             g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Press the SHIFT key.
  245.          }
  246.  
  247.          CHECKFORSTRINGTOOLONG();
  248.          g_uVirtKeys[g_uMaxVirtKeys++] = LOBYTE(uVirtKey);
  249.  
  250.          if (HIBYTE(uVirtKey) & 1) {
  251.             CHECKFORSTRINGTOOLONG();
  252.             g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Release the SHIFT key.
  253.          }
  254.          break;
  255.  
  256.    }  // switch
  257.    return(SK_NOERROR);
  258. }  // function
  259.  
  260.  
  261.  
  262. SENDKEYSERR WINAPI PreprocessKeys (LPCSTR lpszPassedKeys) {
  263.    SENDKEYSERR SKError;
  264.  
  265.    // Clear out the array and the index into the array.
  266.    g_uMaxVirtKeys = g_uVirtKeyNum = 0;
  267.  
  268.    // Use an internal variable for input string parsing.
  269.    g_szKeys = lpszPassedKeys;
  270.  
  271.    // While there are more more characters in the string to be parsed,
  272.    // call the preprocessor to evaluate the next token.
  273.    while (*g_szKeys != 0)
  274.       if ((SKError = _PreprocessKeys()) != SK_NOERROR) break;
  275.    return(SKError);
  276. }
  277.  
  278.  
  279.  
  280. // This function returns the next key event from the array.
  281. // Returns True if a token was retrieved from the list.
  282. // Returns False if there were no more tokens to retrieve.
  283. // This function is also used to initialize playback.  This is done by
  284. // calling it and passing zero in the lpuVirtKey parameter.
  285. static BOOL NEAR GetToken (UINT FAR *lpuVirtKey, BOOL FAR *lpfPressDown,
  286.    BOOL FAR *lpfSysKey) {
  287.  
  288.    static UINT uVirtKey = 0;
  289.    static BOOL fKeyIsDown = FALSE;
  290.    static BOOL fKeyPressWhileAltDown = FALSE;
  291.    static BOOL fShiftDown = FALSE, fControlDown = FALSE, fAltDown = FALSE;
  292.  
  293.    if (lpuVirtKey == NULL) {
  294.       // Priming the token engine.
  295.       uVirtKey = 0;
  296.       fKeyIsDown = FALSE;
  297.  
  298.       // Assume that none of the shift state keys are down.
  299.       fShiftDown = fControlDown = fAltDown = FALSE;
  300.  
  301.       // Assume that a key has not been pressed in between ALT keys.
  302.       fKeyPressWhileAltDown = FALSE;
  303.  
  304.       // Start playback from the first (0th) entry in the array.
  305.       g_uVirtKeyNum = 0;
  306.  
  307.       return(TRUE);
  308.    }
  309.  
  310.    // Make sure that the key is not a shift state key.
  311.    if ((uVirtKey != VK_SHIFT) && (uVirtKey != VK_CONTROL) &&
  312.        (uVirtKey != VK_MENU)) {
  313.  
  314.       // If we last played back a key and said that it was down, we now
  315.       // have to play back the same key and say that it is up.
  316.       if (uVirtKey != 0 && fKeyIsDown) {
  317.          *lpuVirtKey = uVirtKey; // Same key as last time.
  318.          uVirtKey = 0;    // Next time in, use a new key.
  319.          *lpfPressDown = FALSE;  // Release the key.
  320.          *lpfSysKey = fAltDown && !fControlDown; // Is it a SYS key?
  321.          fKeyPressWhileAltDown = TRUE;
  322.          return(TRUE);
  323.       }
  324.    }
  325.  
  326.    // Have all of the key events in the array been played back?
  327.    if (g_uVirtKeyNum == g_uMaxVirtKeys)
  328.       return(FALSE); // No more tokens to playback.
  329.  
  330.  
  331.  
  332.    // Do special processing if we are playing back a shift state key.
  333.    switch (*lpuVirtKey = uVirtKey = g_uVirtKeys[g_uVirtKeyNum++]) {
  334.  
  335.       case VK_SHIFT:
  336.          // Toggle the state of the SHIFT key.
  337.          *lpfPressDown = (fShiftDown = !fShiftDown);
  338.          break;
  339.  
  340.       case VK_CONTROL:
  341.          // Toggle the state of the CONTROL key.
  342.          *lpfPressDown = (fControlDown = !fControlDown);
  343.          break;
  344.  
  345.       case VK_MENU:
  346.          // Toggle the state of the ALT key.
  347.          *lpfPressDown = (fAltDown = !fAltDown);
  348.  
  349.          // If the ALT key is going down, reset this flag.
  350.          if (fAltDown) fKeyPressWhileAltDown = FALSE;
  351.          break;
  352.  
  353.       default:
  354.          // For any other key, make the event a key down.
  355.          *lpfPressDown = TRUE;
  356.          fKeyIsDown = TRUE;
  357.          fKeyPressWhileAltDown = TRUE;
  358.          break;
  359.    }
  360.  
  361.    // Do some special checking to determine if the key is a SYS key or not.
  362.    if ((uVirtKey == VK_MENU) && (!fAltDown))
  363.       *lpfSysKey = !fKeyPressWhileAltDown && !fControlDown;
  364.    else
  365.       *lpfSysKey = fAltDown && !fControlDown;
  366.  
  367.    return(TRUE);
  368. }
  369.  
  370.  
  371. // This is the Journal Playback hook callback function.  Every time it is
  372. // called, Windows is requesting the next keyboard event to be played back.
  373. // This function determines what the next event is by calling GetToken, fills
  374. // an EVENTMSG structure with the correct information about the keyboard event
  375. // and playback time and returns to let Windows process it.  After all events
  376. // have been played back, the function uninstalls itself and sets the  global
  377. // flag "g_hHook" to NULL so that the SendKeys function knows to terminate.
  378. LRESULT _export CALLBACK JrnlPlayBackHook (int nCode, WPARAM wParam,
  379.                                            LPARAM lParam) {
  380.    BOOL fCallNextHookProc = FALSE;
  381.    LRESULT lResult = 0;
  382.    LPEVENTMSG lpEvent;
  383.    static UINT uVirtKey = 0;
  384.    static BOOL fKeyDown = FALSE;
  385.    static BOOL fSysKey = FALSE;
  386.  
  387.  
  388.    if (g_fNoTokensRetrievedYet) {
  389.       // If no tokens have ever been retrieved from the list, we must
  390.       // force ourselves to get the first one in case Windows sends
  391.       // us a HC_GETNEXT notification BEFORE a HC_SKIP notification.
  392.       GetToken(&uVirtKey, &fKeyDown, &fSysKey);
  393.       g_fNoTokensRetrievedYet = FALSE;
  394.    }
  395.  
  396.    switch (nCode) {
  397.       case HC_SKIP:
  398.          // Prepare to return the next event the next time the
  399.          // hook code is HC_GETNEXT.  If all events have been
  400.          // played, stop playing.
  401.          if (!GetToken(&uVirtKey, &fKeyDown, &fSysKey)) {
  402.             // There were no more tokens left to get.
  403.             UnhookWindowsHookEx(g_hHook);
  404.             g_hHook = NULL; // Reset flag so SendKeys function terminates.
  405.          }
  406.          break;
  407.  
  408.  
  409.       case HC_GETNEXT:
  410.          // Copy the current event to the EVENTMSG
  411.          // structure pointed to by lParam.
  412.          lpEvent = (LPEVENTMSG) lParam;
  413.  
  414.          if (!fSysKey)
  415.             lpEvent->message = fKeyDown ? WM_KEYDOWN : WM_KEYUP;
  416.          else
  417.             lpEvent->message = fKeyDown ? WM_SYSKEYDOWN : WM_SYSKEYUP;
  418.  
  419.          // Scan code & Virtual key
  420.          lpEvent->paramL = MAKEWORD(uVirtKey, MapVirtualKey(uVirtKey, 0));
  421.  
  422.          lpEvent->paramH = 1;  // Repeat count, bit 15 is extended key.
  423.          lpEvent->time = GetTickCount();
  424.  
  425.          // Return the number of milliseconds Windows should wait
  426.          // before processing the event.
  427.          lResult = 0; // Process event immediately.
  428.          break;
  429.  
  430.  
  431.       case HC_SYSMODALOFF:
  432.          // When the system-modal dialog box is removed, stop
  433.          // playing the events and notify the application.
  434.          UnhookWindowsHookEx(g_hHook);
  435.          g_hHook = NULL; // Reset flag so SendKeys function terminates.
  436.          fCallNextHookProc = TRUE;
  437.          break;
  438.  
  439.  
  440.       case HC_SYSMODALON:
  441.       default:
  442.          fCallNextHookProc = TRUE;
  443.          break;
  444.    }
  445.  
  446.    if (fCallNextHookProc)
  447.       lResult = CallNextHookEx(g_hHook, nCode, wParam, lParam);
  448.  
  449.    return(lResult);
  450. }
  451.  
  452.  
  453.  
  454. SENDKEYSERR WINAPI SendKeys (LPCSTR lpszKeys) {
  455.    SENDKEYSERR SKError;
  456.  
  457.    SKError = PreprocessKeys(lpszKeys);
  458.    if (SKError != SK_NOERROR) return(SKError);
  459.  
  460.    GetToken(NULL, NULL, NULL);     // Prime the token pump.
  461.    g_fNoTokensRetrievedYet = TRUE;
  462.  
  463.    // Install the Journal Playback hook.
  464.    g_hHook = SetWindowsHookEx(WH_JOURNALPLAYBACK,
  465.                              (HOOKPROC) JrnlPlayBackHook, g_hInstance, NULL);
  466.    if (g_hHook == NULL) return(SK_CANTINSTALLHOOK);
  467.  
  468.    // Wait here until all of the keyboard events have been processed.
  469.    // PeekMessage allows other application to process the keystrokes.
  470.    while (g_hHook != NULL) {
  471.       MSG msg;
  472.       if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
  473.          TranslateMessage(&msg);
  474.          DispatchMessage(&msg);
  475.       }
  476.    }
  477.    return(SK_NOERROR);
  478. }
  479.  
  480.  
  481.  
  482.  
  483. void WINAPI Keybd_Event (void);
  484. void WINAPI PostVirtualKeyEvent (BYTE bVirtKey, BOOL fUp) {
  485.    WORD AXReg, BXReg;
  486.  
  487.    BXReg = (BYTE) MapVirtualKey(bVirtKey, 0);   // Scan code from VirtKey
  488.    AXReg = MAKEWORD(bVirtKey, (fUp ? 0x80 : 0x00));
  489.    _asm mov bx, BXReg;   // BH = 0 (No prefix)
  490.    _asm mov ax, AXReg;
  491.    Keybd_Event();
  492. }
  493.