home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
msysjour
/
vol07
/
08
/
sendkey
/
sendkeys.c
< prev
next >
Wrap
Text File
|
1992-12-01
|
17KB
|
493 lines
/****************************************************************************
Module name: SendKeys.C
Programmer : Jeffrey M. Richter.
Description: Simulation of the VB/Word/Excel SendKeys statement.
*****************************************************************************/
#include <Windows.h>
#include <String.h>
#include <Stdlib.h> // For atoi().
#include "SendKeys.h"
#define MAKEWORD(low, high) \
((WORD)(((BYTE)(low)) | (((WORD)((BYTE)(high))) << 8)))
#define MAX_VIRT_KEY_CODES 1024
#define CHECKFORSTRINGTOOLONG() \
if (g_uMaxVirtKeys == sizeof(g_uVirtKeys)) return(SK_STRINGTOOLONG);
// Application global variables (all start with "g_").
static HINSTANCE g_hInstance = NULL;
static UINT g_uVirtKeys[MAX_VIRT_KEY_CODES];
static UINT g_uVirtKeyNum = 0; // Index into g_uVirtKeys.
static UINT g_uMaxVirtKeys = 0; // Max entries in g_uVirtKeys.
static BOOL g_fNoTokensRetrievedYet = TRUE;
static LPCSTR g_szKeys;
static volatile HHOOK g_hHook = NULL;
// Used to defeat compiler warning.
#define UNREFERENCED_PARAMETER(P) ((void)(P))
BOOL WINAPI LibMain (HINSTANCE hInstance, WORD wDataSeg,
WORD cbHeapSize, LPSTR lpCmdLine) {
g_hInstance = hInstance;
UNREFERENCED_PARAMETER(wDataSeg);
UNREFERENCED_PARAMETER(cbHeapSize);
UNREFERENCED_PARAMETER(lpCmdLine);
return(TRUE); // Initialization is successful
}
// ****************************************************************************
// The table of special key codes.
struct {
UINT uVirtKey;
const char *szKeyName;
} g_SpecialKeys[] = {
{ VK_CAPITAL, "CAPSLOCK" }, { VK_NUMLOCK, "NUMLOCK" },
{ VK_SCROLL, "SCROLLOCK" }, { VK_ESCAPE, "ESCAPE" },
{ VK_ESCAPE, "ESC" }, { VK_RETURN, "ENTER" },
{ VK_HELP, "HELP" }, { VK_SNAPSHOT, "PRTSC" },
{ VK_TAB, "TAB" }, { VK_CONTROL, "BREAK" },
{ VK_CLEAR, "CLEAR" }, { VK_BACK, "BACKSPACE" },
{ VK_BACK, "BS" }, { VK_BACK, "BKSP" },
{ VK_DELETE, "DELETE" }, { VK_DELETE, "DEL" },
{ VK_INSERT, "INSERT" }, { VK_LEFT, "LEFT" },
{ VK_RIGHT, "RIGHT" }, { VK_UP, "UP" },
{ VK_DOWN, "DOWN" }, { VK_PRIOR, "PGUP" },
{ VK_NEXT, "PGDN" }, { VK_HOME, "HOME" },
{ VK_END, "END" }, { VK_F1, "F1" },
{ VK_F2, "F2" }, { VK_F3, "F3" },
{ VK_F4, "F4" }, { VK_F5, "F5" },
{ VK_F6, "F6" }, { VK_F7, "F7" },
{ VK_F8, "F8" }, { VK_F9, "F9" },
{ VK_F10, "F10" }, { VK_F11, "F11" },
{ VK_F12, "F12" }, { VK_F13, "F13" },
{ VK_F14, "F14" }, { VK_F15, "F15" },
{ VK_F16, "F16" }
};
// Function to convert a special key string into its
// equivalent virtual-key code.
static UINT NEAR SpecialKeyToVirtKey (LPCSTR lpszSpecialKey) {
UINT x;
// Scan the array and compare each of the possible strings.
for (x = 0; x < ARRAY_LEN(g_SpecialKeys); x++) {
if (lstrcmpi(lpszSpecialKey, g_SpecialKeys[x].szKeyName) == 0) {
// Found the special key in the list, return its virtual-key code.
return(MAKEWORD(g_SpecialKeys[x].uVirtKey, 0));
}
}
// The special key was not found in the list.
if (lstrlen(lpszSpecialKey) == 1) {
// If the special key is a single character, convert that
// character to its virtual-key code equivalent.
// Note 1: This must come after the loop above so that special single
// characters are checked first (i.e.-"~" (tilde)).
// Note 2: This check is necessary for other special single characters
// (i.e.-"+^%{}()).
return(VkKeyScan(*lpszSpecialKey));
}
return(0); // Error, special key not found.
}
// Function to preprocess the next token in the input string.
static SENDKEYSERR NEAR _PreprocessKeys (void) {
char cChar, szSpecialKey[15]; LPCSTR lpEndOfToken; UINT uVirtKey, uCount;
SENDKEYSERR SendKeysErr = SK_NOERROR;
// Get the next character from the input string.
switch (cChar = *g_szKeys++) {
case 0: // Reached the end of the input string.
g_szKeys--; // Point back to the zero-byte.
return(SK_NOERROR); // End of string - no errors.
case '{': // Beginning of special key text.
if (*g_szKeys == '}') {
// The special character is a close brace.
uVirtKey = VkKeyScan('}');
lpEndOfToken = ++g_szKeys; // Point past the virtual key.
} else {
// Assume that we don't know what virtual key we are looking at.
uVirtKey = 0;
lpEndOfToken = g_szKeys;
}
// Locate the end of the first token in the braced expression.
// This is either:
// 1. End of string,
// 2. a special word/symbol followed by a close brace, or
// 3. a special word/symbol followed by a space and a number
// indicating a repeat count.
while ((*lpEndOfToken != 0) && (*lpEndOfToken != ' ') &&
(*lpEndOfToken != '}'))
lpEndOfToken++;
if (*lpEndOfToken == 0)
return(SK_MISSINGCLOSEBRACE); // Error, missing end brace.
if (uVirtKey == 0) {
// The key must not be the close brace. We must determine
// the virtual key corresponding to the this special key.
lstrcpyn(szSpecialKey, g_szKeys,
(UINT) (lpEndOfToken - g_szKeys + 1));
uVirtKey = SpecialKeyToVirtKey(szSpecialKey);
}
if (uVirtKey == 0)
return(SK_INVALIDKEY); // Error, special key not found
// Just a special word/character without a repeat count.
if (*lpEndOfToken == '}') uCount = 1;
// Calculate the repeat count for this special character.
if (*lpEndOfToken == ' ')
uCount = (UINT) atoi(++lpEndOfToken);
if (uCount == 0)
return(SK_INVALIDCOUNT); // Error, invalid count value
// Add this special key to the virtual key list.
if (HIBYTE(uVirtKey) & 1) {
// This key is a shifted key.
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Press the SHIFT key.
}
while (uCount--) {
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = LOBYTE(uVirtKey);
}
if (HIBYTE(uVirtKey) & 1) {
// This key is a shifted key.
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Release the SHIFT key.
}
// Find the character after the closing brace
while (*lpEndOfToken++ != '}') ;
// Point to character after the closing brace so that
// parsing may continue
g_szKeys = lpEndOfToken;
break;
case '(': // Beginning of sub-group.
// While not at the end of the input string and not at a close paren.
while ((*g_szKeys != 0) && (*g_szKeys != ')')) {
// Add the next character to the array.
SendKeysErr = _PreprocessKeys();
if (SendKeysErr != SK_NOERROR) return(SendKeysErr);
}
// If terminating because of end of string and not because
// of right paren, there is an error.
if (*g_szKeys == 0) return(SK_MISSINGCLOSEPAREN);
g_szKeys++; // Skip past the close paren.
break;
case '~': // Press the ENTER key.
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = VK_RETURN; // Press the (ENTER) key.
break;
case '+': // Press the SHIFT key.
case '^': // Press the CONTROL key.
case '%': // Press the ALT key.
if (cChar == '+') cChar = VK_SHIFT;
else if (cChar == '^') cChar = VK_CONTROL;
else cChar = VK_MENU;
// Press the (SHIFT/CONTROL/ALT) key.
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = cChar;
SendKeysErr = _PreprocessKeys();
if (SendKeysErr != SK_NOERROR) return(SendKeysErr);
// Release the (SHIFT/CONTROL/ALT) key
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = cChar;
break;
default: // Just a normal character.
uVirtKey = VkKeyScan(cChar);
if (HIBYTE(uVirtKey) & 1) {
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Press the SHIFT key.
}
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = LOBYTE(uVirtKey);
if (HIBYTE(uVirtKey) & 1) {
CHECKFORSTRINGTOOLONG();
g_uVirtKeys[g_uMaxVirtKeys++] = VK_SHIFT; // Release the SHIFT key.
}
break;
} // switch
return(SK_NOERROR);
} // function
SENDKEYSERR WINAPI PreprocessKeys (LPCSTR lpszPassedKeys) {
SENDKEYSERR SKError;
// Clear out the array and the index into the array.
g_uMaxVirtKeys = g_uVirtKeyNum = 0;
// Use an internal variable for input string parsing.
g_szKeys = lpszPassedKeys;
// While there are more more characters in the string to be parsed,
// call the preprocessor to evaluate the next token.
while (*g_szKeys != 0)
if ((SKError = _PreprocessKeys()) != SK_NOERROR) break;
return(SKError);
}
// This function returns the next key event from the array.
// Returns True if a token was retrieved from the list.
// Returns False if there were no more tokens to retrieve.
// This function is also used to initialize playback. This is done by
// calling it and passing zero in the lpuVirtKey parameter.
static BOOL NEAR GetToken (UINT FAR *lpuVirtKey, BOOL FAR *lpfPressDown,
BOOL FAR *lpfSysKey) {
static UINT uVirtKey = 0;
static BOOL fKeyIsDown = FALSE;
static BOOL fKeyPressWhileAltDown = FALSE;
static BOOL fShiftDown = FALSE, fControlDown = FALSE, fAltDown = FALSE;
if (lpuVirtKey == NULL) {
// Priming the token engine.
uVirtKey = 0;
fKeyIsDown = FALSE;
// Assume that none of the shift state keys are down.
fShiftDown = fControlDown = fAltDown = FALSE;
// Assume that a key has not been pressed in between ALT keys.
fKeyPressWhileAltDown = FALSE;
// Start playback from the first (0th) entry in the array.
g_uVirtKeyNum = 0;
return(TRUE);
}
// Make sure that the key is not a shift state key.
if ((uVirtKey != VK_SHIFT) && (uVirtKey != VK_CONTROL) &&
(uVirtKey != VK_MENU)) {
// If we last played back a key and said that it was down, we now
// have to play back the same key and say that it is up.
if (uVirtKey != 0 && fKeyIsDown) {
*lpuVirtKey = uVirtKey; // Same key as last time.
uVirtKey = 0; // Next time in, use a new key.
*lpfPressDown = FALSE; // Release the key.
*lpfSysKey = fAltDown && !fControlDown; // Is it a SYS key?
fKeyPressWhileAltDown = TRUE;
return(TRUE);
}
}
// Have all of the key events in the array been played back?
if (g_uVirtKeyNum == g_uMaxVirtKeys)
return(FALSE); // No more tokens to playback.
// Do special processing if we are playing back a shift state key.
switch (*lpuVirtKey = uVirtKey = g_uVirtKeys[g_uVirtKeyNum++]) {
case VK_SHIFT:
// Toggle the state of the SHIFT key.
*lpfPressDown = (fShiftDown = !fShiftDown);
break;
case VK_CONTROL:
// Toggle the state of the CONTROL key.
*lpfPressDown = (fControlDown = !fControlDown);
break;
case VK_MENU:
// Toggle the state of the ALT key.
*lpfPressDown = (fAltDown = !fAltDown);
// If the ALT key is going down, reset this flag.
if (fAltDown) fKeyPressWhileAltDown = FALSE;
break;
default:
// For any other key, make the event a key down.
*lpfPressDown = TRUE;
fKeyIsDown = TRUE;
fKeyPressWhileAltDown = TRUE;
break;
}
// Do some special checking to determine if the key is a SYS key or not.
if ((uVirtKey == VK_MENU) && (!fAltDown))
*lpfSysKey = !fKeyPressWhileAltDown && !fControlDown;
else
*lpfSysKey = fAltDown && !fControlDown;
return(TRUE);
}
// This is the Journal Playback hook callback function. Every time it is
// called, Windows is requesting the next keyboard event to be played back.
// This function determines what the next event is by calling GetToken, fills
// an EVENTMSG structure with the correct information about the keyboard event
// and playback time and returns to let Windows process it. After all events
// have been played back, the function uninstalls itself and sets the global
// flag "g_hHook" to NULL so that the SendKeys function knows to terminate.
LRESULT _export CALLBACK JrnlPlayBackHook (int nCode, WPARAM wParam,
LPARAM lParam) {
BOOL fCallNextHookProc = FALSE;
LRESULT lResult = 0;
LPEVENTMSG lpEvent;
static UINT uVirtKey = 0;
static BOOL fKeyDown = FALSE;
static BOOL fSysKey = FALSE;
if (g_fNoTokensRetrievedYet) {
// If no tokens have ever been retrieved from the list, we must
// force ourselves to get the first one in case Windows sends
// us a HC_GETNEXT notification BEFORE a HC_SKIP notification.
GetToken(&uVirtKey, &fKeyDown, &fSysKey);
g_fNoTokensRetrievedYet = FALSE;
}
switch (nCode) {
case HC_SKIP:
// Prepare to return the next event the next time the
// hook code is HC_GETNEXT. If all events have been
// played, stop playing.
if (!GetToken(&uVirtKey, &fKeyDown, &fSysKey)) {
// There were no more tokens left to get.
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL; // Reset flag so SendKeys function terminates.
}
break;
case HC_GETNEXT:
// Copy the current event to the EVENTMSG
// structure pointed to by lParam.
lpEvent = (LPEVENTMSG) lParam;
if (!fSysKey)
lpEvent->message = fKeyDown ? WM_KEYDOWN : WM_KEYUP;
else
lpEvent->message = fKeyDown ? WM_SYSKEYDOWN : WM_SYSKEYUP;
// Scan code & Virtual key
lpEvent->paramL = MAKEWORD(uVirtKey, MapVirtualKey(uVirtKey, 0));
lpEvent->paramH = 1; // Repeat count, bit 15 is extended key.
lpEvent->time = GetTickCount();
// Return the number of milliseconds Windows should wait
// before processing the event.
lResult = 0; // Process event immediately.
break;
case HC_SYSMODALOFF:
// When the system-modal dialog box is removed, stop
// playing the events and notify the application.
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL; // Reset flag so SendKeys function terminates.
fCallNextHookProc = TRUE;
break;
case HC_SYSMODALON:
default:
fCallNextHookProc = TRUE;
break;
}
if (fCallNextHookProc)
lResult = CallNextHookEx(g_hHook, nCode, wParam, lParam);
return(lResult);
}
SENDKEYSERR WINAPI SendKeys (LPCSTR lpszKeys) {
SENDKEYSERR SKError;
SKError = PreprocessKeys(lpszKeys);
if (SKError != SK_NOERROR) return(SKError);
GetToken(NULL, NULL, NULL); // Prime the token pump.
g_fNoTokensRetrievedYet = TRUE;
// Install the Journal Playback hook.
g_hHook = SetWindowsHookEx(WH_JOURNALPLAYBACK,
(HOOKPROC) JrnlPlayBackHook, g_hInstance, NULL);
if (g_hHook == NULL) return(SK_CANTINSTALLHOOK);
// Wait here until all of the keyboard events have been processed.
// PeekMessage allows other application to process the keystrokes.
while (g_hHook != NULL) {
MSG msg;
if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return(SK_NOERROR);
}
void WINAPI Keybd_Event (void);
void WINAPI PostVirtualKeyEvent (BYTE bVirtKey, BOOL fUp) {
WORD AXReg, BXReg;
BXReg = (BYTE) MapVirtualKey(bVirtKey, 0); // Scan code from VirtKey
AXReg = MAKEWORD(bVirtKey, (fUp ? 0x80 : 0x00));
_asm mov bx, BXReg; // BH = 0 (No prefix)
_asm mov ax, AXReg;
Keybd_Event();
}