Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

836 lines
24 KiB

  1. //
  2. // key.c
  3. //
  4. // Handles all keyboard input messages sent to the server.
  5. //
  6. // Copyright (C) 2001 Microsoft Corporation
  7. //
  8. // Author: a-devjen (Devin Jenson)
  9. //
  10. #include "apihandl.h"
  11. #include "tclient2.h"
  12. // Average number of characters per word
  13. #define AVG_CHARS_PER_WORD 6
  14. // Max WPM before setting delay to 0
  15. #define MAX_WORDS_PER_MIN 1000
  16. // This macro calculates the delay between each character
  17. // (in milliseconds) according to the specified WPM
  18. #define CALC_DELAY_BETWEEN_CHARS(WPM) \
  19. (60000 / (WPM * AVG_CHARS_PER_WORD))
  20. // This constant simply defines the default delay
  21. // between each character according to the default
  22. // words per minute specified in TCLIENT2.H
  23. static const UINT DEFAULT_DELAY_BETWEEN_CHARS =
  24. CALC_DELAY_BETWEEN_CHARS(T2_DEFAULT_WORDS_PER_MIN);
  25. //
  26. // The Foucher Formula
  27. // (Defining MS. Per Char)
  28. //
  29. // 60000
  30. // ---------
  31. // WPM * CPW
  32. //
  33. // Internal Helper Function Prototypes
  34. static LPARAM KeyGenParam(UINT Message, BOOL IsAltDown, int vKeyCode);
  35. static BOOL KeyCharToVirt(WCHAR KeyChar, int *vKeyCode,
  36. BOOL *RequiresShift);
  37. static BOOL KeyVirtToChar(int vKeyCode, WCHAR *KeyChar);
  38. static LPCSTR KeySendVirtMessage(HANDLE Connection, UINT Message,
  39. int vKeyCode);
  40. static LPCSTR KeySendCharMessage(HANDLE Connection, UINT Message,
  41. WCHAR KeyChar);
  42. // Macros to quick return the specified keystate
  43. #define ISALTDOWN ((TSAPIHANDLE *)Connection)->IsAltDown
  44. #define ISSHIFTDOWN ((TSAPIHANDLE *)Connection)->IsShiftDown
  45. #define ISCTRLDOWN ((TSAPIHANDLE *)Connection)->IsCtrlDown
  46. /*
  47. R - Specifies the repeat count for the current message. The value is
  48. the number of times the keystroke is autorepeated as a result
  49. of the user holding down the key. If the keystroke is held long
  50. enough, multiple messages are sent. However, the repeat count
  51. is not cumulative. This value is always 1 for WM_SYSKEYUP and
  52. WM_KEYUP.
  53. S - Specifies the scan code. The value depends on the original
  54. equipment manufacturer (OEM).
  55. E - Specifies whether the key is an extended key, such as the
  56. right-hand ALT and CTRL keys that appear on an enhanced 101- or
  57. 102-key keyboard. The value is 1 if it is an extended key;
  58. otherwise, it is 0.
  59. X - Reserved; do not use.
  60. C - Specifies the context code. The value is always 0 for a
  61. WM_KEYDOWN and WM_KEYUP message or if the message is posted to
  62. the active window because no window has the keyboard focus.
  63. For WM_SYSKEYDOWN and WM_SYSKEYUP, the value is 1 if the ALT
  64. key is down while the message is activated.
  65. P - Specifies the previous key state. The value is 1 if the key is down
  66. before the message is sent, or it is zero if the key is up.
  67. The value is always 1 for a WM_KEYUP or WM_SYSKEYUP message.
  68. T - Specifies the transition state. The value is always 0 for a
  69. WM_KEYDOWN or WM_SYSKEYDOWN message, and 1 for a
  70. WM_KEYUP or a WM_SYSKEYUP message.
  71. TPCXXXXESSSSSSSSRRRRRRRRRRRRRRRR
  72. 10987654321098765432109876543210
  73. | | | |
  74. 30 20 10 0
  75. */
  76. // KeyGenParam
  77. //
  78. // Based on the specified information, this generates
  79. // a valid LPARAM as documented by Windows keyboard
  80. // messages. Quick documentation is provided above.
  81. //
  82. // Returns the generated LPARAM, no failure code.
  83. static LPARAM KeyGenParam(
  84. UINT Message,
  85. BOOL IsAltDown,
  86. int vKeyCode)
  87. {
  88. // Set the repeat count for all key messages (1)
  89. DWORD Param = 0x00000001; // Set zBit 0-15 (WORD) as value 1
  90. // Next set the OEM Scan code for the virtual key code
  91. DWORD ScanCode = (DWORD)((BYTE)MapVirtualKey((UINT)vKeyCode, 0));
  92. if (ScanCode != 0)
  93. Param |= (ScanCode << 16); // Set zBits 16-23
  94. // Set the extended flag for extended keys
  95. switch (vKeyCode) {
  96. // Add more keys here
  97. case VK_DELETE:
  98. case VK_LWIN:
  99. case VK_RWIN:
  100. Param |= 0x01000000;
  101. break;
  102. }
  103. // Is the ALT Key down for SYS_ messages?
  104. if (IsAltDown)
  105. Param |= 0x20000000; // Enable zBit 29
  106. // Set the previous key and transition state
  107. if (Message == WM_SYSKEYUP || Message == WM_KEYUP)
  108. Param |= 0xC0000000; // Enable zBit 30 and 31
  109. // Convert the DWORD to an LPARAM and return
  110. return (LPARAM)Param;
  111. }
  112. // KeyCharToVirt
  113. //
  114. // Maps the specified character to a virtual key code.
  115. // This will also specify whether or not shift is required for
  116. // the virtual key code have the same effect. For example:
  117. // the letter 'K'. Sending VK_K is not enough shift must be
  118. // down to get the capital 'K'.
  119. //
  120. // Returns TRUE of the data has been successfully
  121. // translated, FALSE otherwise. If FALSE, pointers have
  122. // been unmodified.
  123. static BOOL KeyCharToVirt(
  124. WCHAR KeyChar,
  125. int *vKeyCode,
  126. BOOL *RequiresShift)
  127. {
  128. // Get the key data
  129. SHORT ScanData = VkKeyScanW(KeyChar);
  130. // Check Success/Failure of API call
  131. if (LOBYTE(ScanData) == -1 && HIBYTE(ScanData) == -1)
  132. return FALSE;
  133. // Set the data
  134. if (vKeyCode != NULL)
  135. *vKeyCode = LOBYTE(ScanData);
  136. if (RequiresShift != NULL)
  137. *RequiresShift = (HIBYTE(ScanData) & 0x01);
  138. return TRUE;
  139. }
  140. // KeyVirtToChar
  141. //
  142. // Translates the specified virtual key code into a character.
  143. // There are some virtual key codes that do not have
  144. // character representations, such as the arrow keys. In this
  145. // case, the function fails.
  146. //
  147. // Returns TRUE if the virtual key code was successfully
  148. // translated, FALSE otherwise. If FALSE, the KeyChar
  149. // pointer has not been modified.
  150. static BOOL KeyVirtToChar(
  151. int vKeyCode,
  152. WCHAR *KeyChar)
  153. {
  154. // Use Win32 API to map, two is the value used for
  155. // vKey->Char, no type is defined for easier
  156. // readability there :-( [as of Jan 2001]
  157. UINT Result = MapVirtualKeyW((UINT)vKeyCode, 2);
  158. if (Result == 0)
  159. return FALSE;
  160. // Set the data
  161. if (KeyChar != NULL)
  162. *KeyChar = (WCHAR)Result;
  163. return TRUE;
  164. }
  165. // KeySendVirtMessage
  166. //
  167. // This routine corrects and sends a key message to the server.
  168. // WM_SYSKEY messages are converted over to WM_KEY messages so
  169. // that they can be transferred over the wire correctly - they
  170. // still work though. An LPARAM is automatically generated.
  171. // Support is only implemented for the messages:
  172. // WM_KEYDOWN and WM_KEYUP (and the SYS versions).
  173. //
  174. // The return value is a string specifying an error if one
  175. // occured, otherwise the process was successful and NULL
  176. // is returned.
  177. static LPCSTR KeySendVirtMessage(
  178. HANDLE Connection,
  179. UINT Message,
  180. int vKeyCode)
  181. {
  182. // Wait until the user unpauses the script (if paused)
  183. T2WaitForPauseInput(Connection);
  184. // Filter out invalid messages, and convert SYS messages
  185. switch (Message) {
  186. case WM_SYSKEYDOWN:
  187. case WM_KEYDOWN:
  188. Message = WM_KEYDOWN;
  189. break;
  190. case WM_SYSKEYUP:
  191. case WM_KEYUP:
  192. Message = WM_KEYUP;
  193. break;
  194. default:
  195. return "Unsupported keyboard message";
  196. }
  197. // Trigger specific actions for special virtual key codes
  198. switch (vKeyCode) {
  199. // CTRL Key message
  200. case VK_CONTROL:
  201. case VK_LCONTROL:
  202. case VK_RCONTROL: {
  203. switch (Message) {
  204. case WM_KEYDOWN:
  205. // Flag CTRL as down
  206. if (ISCTRLDOWN == TRUE)
  207. return NULL;
  208. ISCTRLDOWN = TRUE;
  209. break;
  210. case WM_KEYUP:
  211. // Flag CTRL as up
  212. if (ISCTRLDOWN == FALSE)
  213. return NULL;
  214. ISCTRLDOWN = FALSE;
  215. break;
  216. }
  217. }
  218. // SHIFT Key message
  219. case VK_SHIFT:
  220. case VK_LSHIFT:
  221. case VK_RSHIFT: {
  222. switch (Message) {
  223. case WM_KEYDOWN:
  224. // Flag SHIFT as down
  225. if (ISSHIFTDOWN == TRUE)
  226. return NULL;
  227. ISSHIFTDOWN = TRUE;
  228. break;
  229. case WM_KEYUP:
  230. // Flag SHIFT as up
  231. if (ISSHIFTDOWN == FALSE)
  232. return NULL;
  233. ISSHIFTDOWN = FALSE;
  234. break;
  235. }
  236. break;
  237. }
  238. // ALT Key message
  239. case VK_MENU:
  240. case VK_LMENU:
  241. case VK_RMENU: {
  242. switch (Message) {
  243. case WM_KEYDOWN:
  244. // Flag ALT as down
  245. if (ISALTDOWN == TRUE)
  246. return NULL;
  247. ISALTDOWN = TRUE;
  248. break;
  249. case WM_KEYUP:
  250. // Flag ALT as up
  251. if (ISALTDOWN == FALSE)
  252. return NULL;
  253. ISALTDOWN = FALSE;
  254. break;
  255. }
  256. break;
  257. }
  258. }
  259. // Send the message over the wire
  260. return T2SendData(Connection, Message, (WPARAM)vKeyCode,
  261. KeyGenParam(Message, ISALTDOWN, vKeyCode));
  262. }
  263. // KeySendCharMessage
  264. //
  265. // This routine automatically translates a charater into its
  266. // virtual key code counterpart and passes the code onto the
  267. // KeySendVirtMessage. Shift reliant characters will NOT
  268. // have the SHIFT key automatically be sent over as well,
  269. // because this would be more difficult to manage the shift key
  270. // locally.
  271. //
  272. // Only WM_KEYDOWN and WM_KEYUP messages (and their SYS versions)
  273. // are supported. Unsupported keyboard messages will cause the
  274. // routine to fail.
  275. //
  276. // The return value is a string specifying an error if one
  277. // occured, otherwise the process was successful and NULL
  278. // is returned.
  279. static LPCSTR KeySendCharMessage(HANDLE Connection, UINT Message,
  280. WCHAR KeyChar)
  281. {
  282. // Wait until the user unpauses the script (if paused)
  283. T2WaitForPauseInput(Connection);
  284. // Only the following messages are supported
  285. if (Message == WM_KEYDOWN || Message == WM_KEYUP ||
  286. Message == WM_SYSKEYDOWN || Message == WM_SYSKEYUP) {
  287. int vKeyCode = 0;
  288. // Translate the character to a virtual key code
  289. if (KeyCharToVirt(KeyChar, &vKeyCode, NULL) == FALSE)
  290. return "Failed to map character to a virtual key code";
  291. // Submit the virtual key code
  292. return KeySendVirtMessage(Connection, Message, vKeyCode);
  293. }
  294. // WM_CHAR and WM_SYSCHAR is not used over this tclient connection
  295. return "Unsupported keyboard message";
  296. }
  297. // T2SetDefaultWPM
  298. //
  299. // Every TCLIENT connection has it's own special properties, and this
  300. // includes the "Words Per Minute" property. The property represents
  301. // the default speed in which TCLIENT is to type text to the server.
  302. // The scripter can override this by using the optional parameter
  303. // after TypeText() to indicate the temporary words per minute.
  304. //
  305. // This routine sets the default words per minute as described above.
  306. //
  307. // The return value is NULL if successful, otherwise a string
  308. // describing the error.
  309. TSAPI LPCSTR T2SetDefaultWPM(HANDLE Connection, DWORD WordsPerMinute)
  310. {
  311. // Validate the handle
  312. if (T2IsHandle(Connection) == FALSE)
  313. return "Not a valid connection";
  314. // If WPM is set to 0, use the default
  315. if (WordsPerMinute == 0) {
  316. WordsPerMinute = T2_DEFAULT_WORDS_PER_MIN;
  317. ((TSAPIHANDLE *)Connection)->DelayPerChar =
  318. DEFAULT_DELAY_BETWEEN_CHARS;
  319. }
  320. // If WPM is suggested to be insanely high, set the delay to
  321. // 0 instead of trying to calculate it
  322. else if (WordsPerMinute > MAX_WORDS_PER_MIN)
  323. ((TSAPIHANDLE *)Connection)->DelayPerChar = 0;
  324. // Otherwise, calculate the words per minute into
  325. // the delay value used in Sleep() calls
  326. else
  327. ((TSAPIHANDLE *)Connection)->DelayPerChar =
  328. CALC_DELAY_BETWEEN_CHARS(WordsPerMinute);
  329. // Record the words per minute if the user wants this value back
  330. ((TSAPIHANDLE *)Connection)->WordsPerMinute = WordsPerMinute;
  331. return NULL;
  332. }
  333. // T2GetDefaultWPM
  334. //
  335. // Every TCLIENT connection has it's own special properties, and this
  336. // includes the "Words Per Minute" property. The property represents
  337. // the default speed in which TCLIENT is to type text to the server.
  338. // The scripter can override this by using the optional parameter
  339. // after TypeText() to indicate the temporary words per minute.
  340. //
  341. // This routine retrieves the current default words per minute
  342. // as described above.
  343. //
  344. // The return value is NULL if successful, otherwise a string
  345. // describing the error.
  346. TSAPI LPCSTR T2GetDefaultWPM(HANDLE Connection, DWORD *WordsPerMinute)
  347. {
  348. // Validate the handle
  349. if (T2IsHandle(Connection) == FALSE)
  350. return "Not a valid connection";
  351. __try {
  352. // Attempt to set a value at the specified pointer
  353. *WordsPerMinute = ((TSAPIHANDLE *)Connection)->WordsPerMinute;
  354. }
  355. __except (EXCEPTION_EXECUTE_HANDLER) {
  356. _ASSERT(FALSE);
  357. // Nope, it failed.
  358. return "Invalid WordsPerMinute pointer";
  359. }
  360. return NULL;
  361. }
  362. //
  363. // Keyboard Routines
  364. //
  365. // These functions are used to make keyboard handling easier.
  366. // They come in two flavors, character version, and
  367. // virtual keycode version. The virtual keycode functions
  368. // are prefixed with a "V" after the T2 to indicate so.
  369. //
  370. // T2KeyAlt
  371. //
  372. // Allows for easy ability to do an ALT + Key combonation.
  373. // For example, ALT-F in a typical Windows application will
  374. // open up the File menu.
  375. //
  376. // Returns NULL if successful, otherwise a string describing
  377. // the failure.
  378. // Character Version
  379. TSAPI LPCSTR T2KeyAlt(HANDLE Connection, WCHAR KeyChar)
  380. {
  381. // Don't validate the connection handle here,
  382. // it is done within T2KeyDown, and no reason
  383. // two double check it.
  384. // First press the ALT key
  385. LPCSTR Result = T2VKeyDown(Connection, VK_MENU);
  386. if (Result != NULL)
  387. return Result;
  388. // Be realistic
  389. T2WaitForLatency(Connection);
  390. // Next press and release the specified custom key
  391. Result = T2KeyPress(Connection, KeyChar);
  392. // Be realistic
  393. T2WaitForLatency(Connection);
  394. // Finally, let up on the ALT key
  395. T2VKeyUp(Connection, VK_MENU);
  396. return Result;
  397. }
  398. // Virtual Key Code Version
  399. TSAPI LPCSTR T2VKeyAlt(HANDLE Connection, INT vKeyCode)
  400. {
  401. // Don't validate the connection handle here,
  402. // it is done within T2VKeyDown, and no reason
  403. // two double check it.
  404. // First press the ALT key
  405. LPCSTR Result = T2VKeyDown(Connection, VK_MENU);
  406. if (Result != NULL)
  407. return Result;
  408. // Be realistic
  409. T2WaitForLatency(Connection);
  410. // Next press and release the specified custom key
  411. Result = T2VKeyPress(Connection, vKeyCode);
  412. // Be realistic
  413. T2WaitForLatency(Connection);
  414. // Finally, let up on the ALT key
  415. T2VKeyUp(Connection, VK_MENU);
  416. return Result;
  417. }
  418. // T2KeyCtrl
  419. //
  420. // Allows for easy ability to do a CTRL + Key combonation.
  421. // For example, CTRL-C in a typical Windows application will
  422. // copy a selected item to the clipboard.
  423. //
  424. // Returns NULL if successful, otherwise a string describing
  425. // the failure.
  426. // Character Version
  427. TSAPI LPCSTR T2KeyCtrl(HANDLE Connection, WCHAR KeyChar)
  428. {
  429. // Don't validate the connection handle here,
  430. // it is done within T2KeyDown, and no reason
  431. // two double check it.
  432. // First press the CTRL key
  433. LPCSTR Result = T2VKeyDown(Connection, VK_CONTROL);
  434. if (Result != NULL)
  435. return Result;
  436. // Be realistic
  437. T2WaitForLatency(Connection);
  438. // Next press and release the specified custom key
  439. Result = T2KeyPress(Connection, KeyChar);
  440. // Be realistic
  441. T2WaitForLatency(Connection);
  442. // Finally, let up on the CTRL key
  443. T2VKeyUp(Connection, VK_CONTROL);
  444. return Result;
  445. }
  446. // Virtual Key Code Version
  447. TSAPI LPCSTR T2VKeyCtrl(HANDLE Connection, INT vKeyCode)
  448. {
  449. // Don't validate the connection handle here,
  450. // it is done within T2VKeyDown, and no reason
  451. // two double check it.
  452. // First press the CTRL key
  453. LPCSTR Result = T2VKeyDown(Connection, VK_CONTROL);
  454. if (Result != NULL)
  455. return Result;
  456. // Be realistic
  457. T2WaitForLatency(Connection);
  458. // Next press and release the specified custom key
  459. Result = T2VKeyPress(Connection, vKeyCode);
  460. // Be realistic
  461. T2WaitForLatency(Connection);
  462. // Finally, let up on the CTRL key
  463. T2VKeyUp(Connection, VK_CONTROL);
  464. return Result;
  465. }
  466. // T2KeyDown
  467. //
  468. // Presses and a key down, and holds it down until
  469. // T2KeyUp is called. This is useful for holding down SHIFT
  470. // to type several letters in caps, etc. NOTE: For character
  471. // versions of this function, SHIFT will NOT automatically be
  472. // pressed. If you do a T2KeyDown(hCon, L'T'), a lowercase
  473. // key may be the output! You will need to manually press the
  474. // SHIFT key using T2VKeyDown(hCon, VK_SHIFT). Remember,
  475. // these are low level calls. TypeText() on the other hand,
  476. // will automatically press/release SHIFT as needed.
  477. //
  478. // Returns NULL if successful, otherwise a string describing
  479. // the failure.
  480. // Character Version
  481. TSAPI LPCSTR T2KeyDown(HANDLE Connection, WCHAR KeyChar)
  482. {
  483. // Validate the handle
  484. if (T2IsHandle(Connection) == FALSE)
  485. return "Not a valid connection";
  486. // Simply send the WM_KEYDOWN message over the wire
  487. return KeySendCharMessage(Connection, WM_KEYDOWN, KeyChar);
  488. }
  489. // Virtual Key Code Version
  490. TSAPI LPCSTR T2VKeyDown(HANDLE Connection, INT vKeyCode)
  491. {
  492. // Validate the handle
  493. if (T2IsHandle(Connection) == FALSE)
  494. return "Not a valid connection";
  495. // Simply send the WM_KEYDOWN message over the wire
  496. return KeySendVirtMessage(Connection, WM_KEYDOWN, vKeyCode);
  497. }
  498. // T2KeyPress
  499. //
  500. // Presses and releases a key. NOTE: For character
  501. // versions of this function, SHIFT will NOT automatically be
  502. // pressed. If you do a T2KeyDown(hCon, L'T'), a lowercase
  503. // key may be the output! You will need to manually press the
  504. // SHIFT key using T2VKeyDown(hCon, VK_SHIFT). Remember,
  505. // these are low level calls. TypeText() on the other hand,
  506. // will automatically press/release SHIFT as needed.
  507. //
  508. // Returns NULL if successful, otherwise a string describing
  509. // the failure.
  510. // Character Version
  511. TSAPI LPCSTR T2KeyPress(HANDLE Connection, WCHAR KeyChar)
  512. {
  513. // Don't validate the connection handle here,
  514. // it is done within T2VKeyDown, and no reason
  515. // two double check it.
  516. // First press the key
  517. LPCSTR Result = T2KeyDown(Connection, KeyChar);
  518. if (Result != NULL)
  519. return Result;
  520. // Be realistic
  521. T2WaitForLatency(Connection);
  522. // Then release it
  523. return T2KeyUp(Connection, KeyChar);
  524. }
  525. // Virtual Key Code Version
  526. TSAPI LPCSTR T2VKeyPress(HANDLE Connection, INT vKeyCode)
  527. {
  528. // Don't validate the connection handle here,
  529. // it is done within T2VKeyDown, and no reason
  530. // two double check it.
  531. // First press the key
  532. LPCSTR Result = T2VKeyDown(Connection, vKeyCode);
  533. if (Result != NULL)
  534. return Result;
  535. // Be realistic
  536. T2WaitForLatency(Connection);
  537. // Then release it
  538. return T2VKeyUp(Connection, vKeyCode);
  539. }
  540. // T2KeyUp
  541. //
  542. // Releases a key that has been pressed by the T2KeyDown
  543. // function. If the key is not down, behavior is undefined.
  544. //
  545. // Returns NULL if successful, otherwise a string describing
  546. // the failure.
  547. // Character Version
  548. TSAPI LPCSTR T2KeyUp(HANDLE Connection, WCHAR KeyChar)
  549. {
  550. // Simply send the WM_KEYUP message over the wire
  551. return KeySendCharMessage(Connection, WM_KEYUP, KeyChar);
  552. }
  553. // Virtual Key Code Version
  554. TSAPI LPCSTR T2VKeyUp(HANDLE Connection, INT vKeyCode)
  555. {
  556. // Simply send the WM_KEYUP message over the wire
  557. return KeySendVirtMessage(Connection, WM_KEYUP, vKeyCode);
  558. }
  559. // T2TypeText
  560. //
  561. // This handy function enumerates each character specified in the text
  562. // and sends the required key messages over the wire to end up with
  563. // the proper result. The shift key is automatically pressed/depressed
  564. // as needed for capital letters and acts as real user action.
  565. // Additionally, the speed of typed text is indicated by the
  566. // (optional) WordsPerMinute parameter. If WordsPerMinute is 0 (zero),
  567. // the default WordsPerMinute for the handle is used.
  568. //
  569. // Returns NULL if successful, otherwise a string describing
  570. // the failure.
  571. TSAPI LPCSTR T2TypeText(HANDLE Connection, LPCWSTR Text, UINT WordsPerMin)
  572. {
  573. LPCSTR Result = NULL;
  574. int vKeyCode = 0;
  575. BOOL RequiresShift = FALSE;
  576. UINT DelayBetweenChars;
  577. BOOL ShiftToggler;
  578. // Validate the handle
  579. if (T2IsHandle(Connection) == FALSE)
  580. return "Not a valid connection";
  581. // First get the default delay between each character
  582. DelayBetweenChars = ((TSAPIHANDLE *)Connection)->DelayPerChar;
  583. // Get the current state of the shift key
  584. ShiftToggler = ISSHIFTDOWN;
  585. // If specified, use the custom WordsPerMinute
  586. if (WordsPerMin > 0)
  587. DelayBetweenChars = CALC_DELAY_BETWEEN_CHARS(WordsPerMin);
  588. // Enter the exception clause in case we have some bad
  589. // Text pointer, and we attempt to continue forever...
  590. __try {
  591. // Loop between each character until a null character is hit
  592. for (; *Text != 0; ++Text) {
  593. // First get the key code associated with the current character
  594. if (KeyCharToVirt(*Text, &vKeyCode, &RequiresShift) == FALSE) {
  595. // This should never happen, but Roseanne is still being
  596. // aired on channel 11, so you never know...
  597. _ASSERT(FALSE);
  598. return "Failed to map a character to a virtual key code";
  599. }
  600. // Press shift if we need
  601. if (RequiresShift == TRUE && ShiftToggler == FALSE)
  602. Result = KeySendVirtMessage(Connection, WM_KEYDOWN, VK_SHIFT);
  603. // Release shift if we need
  604. else if (RequiresShift == FALSE && ShiftToggler == TRUE)
  605. Result = KeySendVirtMessage(Connection, WM_KEYUP, VK_SHIFT);
  606. // Set the current shift state now
  607. ShiftToggler = RequiresShift;
  608. // We are not using T2VKeyPress() here because we need want
  609. // to prevent as many performance hits as possible, and in this
  610. // case we would be checking the Connection handle over and
  611. // over, in addition to making and new stacks etc,
  612. // which is quite pointless.
  613. // Press the current key down
  614. Result = KeySendVirtMessage(Connection, WM_KEYDOWN, vKeyCode);
  615. if (Result != NULL)
  616. return Result;
  617. // Release the current key
  618. Result = KeySendVirtMessage(Connection, WM_KEYUP, vKeyCode);
  619. if (Result != NULL)
  620. return Result;
  621. // Note we are not releasing shift if it is down, this is because
  622. // the next key may be caps as well, and normal users don't
  623. // press and release shift for each character (assuming they
  624. // didn't use the caps key...)
  625. // Delay for the specified amount of time to get accurate
  626. // count for WordsPerMinute
  627. Sleep(DelayBetweenChars);
  628. }
  629. // We are done going through all the text. If we still have
  630. // shift down, release it at this point.
  631. if (ShiftToggler == TRUE)
  632. return KeySendVirtMessage(Connection, WM_KEYUP, VK_SHIFT);
  633. }
  634. __except(EXCEPTION_EXECUTE_HANDLER) {
  635. // Uhm, shouldn't happen?
  636. _ASSERT(FALSE);
  637. return "Exception occured";
  638. }
  639. return NULL;
  640. }