Question

How to use SendInput API

Asked by: YousefEisa

I wish to use SendInput API to allow me to insert characters into any Active window.  I cannot find any Delphi exmaple to illustrate how to use SendInput.

I  have downloaded the latest converted win32 API from delphi-jedi.org/APLILIBRARY  to allow me to use  SendInput calls, but I cannot find the appropraite API in the variuous pas units supplied.  Can anyone tell me where to look or perhaps where to get the appropriate converted APIs. An erxample of how to use these calls would be much appreciated.

What can I put in the uses caluse, to make Delphi accept SenDInput?

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2003-06-02 at 07:37:41ID20633689
Tags

sendinput

,

delphi

Topic

Delphi Programming

Participating Experts
7
Points
500
Comments
44

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. Using .lib API in Delphi?
    I'm using a PC card in my software, which has an API. However it is only available as a VC++ .lib file. How can I use it in Delphi 4??
  2. icq api
    i got a copy of the icq api off the icq website. But how do you use it, source examples would be appreciated - for how to send messages etc and how to put user info into fields of a program
  3. Crypto API - how to use them in Delphi
    How to use Crypto API functions from Crypt32.dll for encrypting/decrypting text from the ASCII file, fro example ?
  4. Delphi & API
    How can i use API function in Delphi? thank in advance
  5. How can I use DerectX Api in Delphi 5.0?
    How can I use DerectX Api in Delphi 5.0?
  6. delphi API
    can anyone please help me to find a site that teaches API using API or there are enumeration of functions of API

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: Slick812Posted on 2003-06-02 at 09:58:10ID: 8630956

hello YousefEisa,  What makes you think that there is an API function called  "SendInput", I have never heard of this, so I did  a search at  www.msdn.com (the MS developer information site) and they do NOT have it there.
What is it you want to do? You say that " allow me to insert characters into any Active window". . .  Do you think that all windows are the same? The method you use will depend on the type (Class) of window, and some other factors.

 

by: EvarestPosted on 2003-06-02 at 10:39:51ID: 8631268

I wouldn't know what's that SendInput about but

function SendInput; external user32 name 'SendInput';

states that it's in the user32.dll. That Microsoft claims never to have heard of it, is not very surprising... Their API "manuals" contain more than one flaw :)

Of course this doesn't solve your problem. I too would like to know what you're planning,
Evarest

 

by: rllibbyPosted on 2003-06-02 at 11:29:54ID: 8631609


How about something like this

// In case you don't have the declarations

const
  {$EXTERNALSYM INPUT_MOUSE}
  INPUT_MOUSE = 0;
  {$EXTERNALSYM INPUT_KEYBOARD}
  INPUT_KEYBOARD = 1;
  {$EXTERNALSYM INPUT_HARDWARE}
  INPUT_HARDWARE = 2;

type
  PInput = ^TInput;
  {$EXTERNALSYM tagINPUT}
  tagINPUT = packed record
    Itype: DWORD;
    case Integer of
      0: (mi: TMouseInput);
      1: (ki: TKeybdInput);
      2: (hi: THardwareInput);
  end;
  TInput      =  tagINPUT;

  // DO NOT CREATE AN INSTANCE OF TInputArray - It is used for typecasting
  // PInputArray, which will be user allocated to desired size
  TInputArray =  Array [0..Pred(MaxInt div SizeOf(tagInput))] of TInput;
  PInputArray =  ^TInputArray;

// Redlared for easier calling using PInputArray
function SendInput(cInputs: UINT; lpInputs: PInputArray; cbSize: Integer): UINT; stdcall; external 'user32.dll';


// Can handle both upper and lowercase text strings
function InsertTextInput(S: String): Boolean;
var  lpiaKeys:   PInputArray;
     lpszText:   PChar;
     nInputs:    LongWord;
     key:        Short;
     i:          Integer;
begin

  // Set default result
  result:=False;

  // Exit if nothing to send
  if (S = '') then exit;

  // Allocate memory for keys strokes (3x the number of chars, due to shift injection)
  nInputs:=Length(S) * 3;
  lpiaKeys:=AllocMem(Length(S) * nInputs);

  // Convert the text stream into keystrokes
  i:=0;
  lpszText:=PChar(S);
  while (lpszText^ > #0) do
  begin

     // Convert to virtual key
     key:=VkKeyScan(lpszText^);
     Inc(lpszText);

     // Is the shift set?
     if (Hi(key) > 0) then
     begin
        // Shift key down
        lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
        lpiaKeys^[i].ki.wVk:=VK_SHIFT;
        Inc(i);
     end;

     // Actual key
     lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
     lpiaKeys^[i].ki.wVk:=Lo(key);
     Inc(i);

     // Shift key set?
     if (Hi(key) > 0) then
     begin
        // Shift key up
        lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
        lpiaKeys^[i].ki.wVk:=VK_SHIFT;
        lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
        Inc(i);
     end;

  end;

  // Send the keys
  result:=(SendInput(nInputs, lpiaKeys, SizeOf(TInput)) = nInputs);

end;


Hope this helps,
Russell

 

by: MunimPosted on 2003-06-02 at 11:31:45ID: 8631622

Hello! YousefEisa,
   There is nothing called SendInput function in Win32 API system. There maybe  some of customized functions supplied by other libraries.
   I think I've understood what you want to mean. Do you want to send key presses to active window automatically? If YES then, use the API function keybd_event (Take a look at Platform SDK or use search in http://msdn.microsoft.com). I cannot include an example, if you need it, please do tell me...

Munim.VIP

 

by: YousefEisaPosted on 2003-06-02 at 11:56:45ID: 8631817

Perhaps I should have my request clearer.  I have built a cursive  handwriting recogniser.  The user uses the mouse or atblet to enter the handwriting and the output of the recogniser is supposed to send its output to any active window that accepts keyborad input.  In other words my handwriting recogniser mimics a keyboard and provides an laternative for dta input.  its input is handwriting and its output is a stream of ASCII chacarters.  Since it needs to mimick the keyboard it should be able to send its results to any active window.

An Expert from this site previouslytold me that I should use SendKey or SendInput (and to find the API in converted libraries).   There is even a few references to SendInput within Experts-Exchange (if you were to use the search function).

Anyway I am happy to use whatever can do the job, as long as I am given clear instruction of how to do.

I wonder if this comment would encourage more input.
Thanks for all the interest shown from the experts and I hope you would respond further to the above.

 

by: YousefEisaPosted on 2003-06-02 at 13:17:36ID: 8632432

Response to Rllibby,
Thanks very much for all the effort in providing your code. I have a bulit a dummy program with one RichEdit component and a button which send the contents of my RichEdit.text to your InsertTextInput function.

When I put some text into the RichEdit object and click on the button, your process appears to add a crraige return into my RichEdit text area.

Perhaps I need to remove the focus from my application first (any idea how to do that would be welcome) so that you program can send the text to another active window.

But teh main issue is that the string I pass to your function does not appear anywhere.  Am I doing something wrong.
Regards

 

by: rllibbyPosted on 2003-06-02 at 14:07:50ID: 8632852


Well, first make sure to add the following after the SendInput call

FreeMem(lpiaKeys);

Missed that when i put the code together. Don't want any memory leaks <g>. Also noticed the nInputs is the MAX number of inputs that will get sent (re-did this code twice to handle uppercasing). The ACTUAL number being sent is in "i". So the code should look like.

  <body of code>
  ...
  result:=(SendInput(i, lpiaKeys, SizeOf(TInput)) = i);
  FreeMem(lpiaKeys);

end;

Also added code to correctly handle CRLF and LF combinations. They should have been sent over as VK_RETURN. Also noticed that some keys REQUIRED a "key up" event to work correctly, so have coded accordingly. Probably best to use the routine as posted below. (with all changes)

You are also correct that the focus needs to be on the window control that should recieve the keyboard messages. In my testing, i used 2 edit boxes and in my button click code added "Edit2.SetFocus" before calling the code. For cross application injecting, you need to be able to get the window handle that you plan on sending the text to, and then perform a SetForegroundWindow() using that handle. (the keyboard input is sent to the active window, which is YOUR window if your clicking a button on your form)

Here is an example of a form, one button, and one richedit contol that redirects output to notepad (if running).

---------------------------------------
unit inj1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls;

type
  TForm1         =  class(TForm)
     Button1:    TButton;
     RichEdit1:  TRichEdit;
     procedure   Button1Click(Sender: TObject);
  private
     // Private declarations
  public
     // Public declarations
  end;

var
  Form1:         TForm1;

const
  {$EXTERNALSYM INPUT_MOUSE}
  INPUT_MOUSE = 0;
  {$EXTERNALSYM INPUT_KEYBOARD}
  INPUT_KEYBOARD = 1;
  {$EXTERNALSYM INPUT_HARDWARE}
  INPUT_HARDWARE = 2;

type
  PInput = ^TInput;
  {$EXTERNALSYM tagINPUT}
  tagINPUT = packed record
    Itype: DWORD;
    case Integer of
      0: (mi: TMouseInput);
      1: (ki: TKeybdInput);
      2: (hi: THardwareInput);
  end;
  TInput      =  tagINPUT;
  TInputArray =  Array [0..Pred(MaxInt div SizeOf(tagInput))] of TInput;
  PInputArray =  ^TInputArray;

function SendInput(cInputs: UINT; lpInputs: PInputArray; cbSize: Integer): UINT; stdcall; external 'user32.dll';

implementation
{$R *.DFM}

function InsertTextInput(S: String): Boolean;
var  lpiaKeys:   PInputArray;
     lpszText:   PChar;
     nInputs:    LongWord;
     key:        Short;
     i:          Integer;
begin

  // Set default result
  result:=False;

  // Exit if nothing to send
  if (S = '') then exit;

  // Allocate memory for keys strokes (4x the number of chars, due to shift
  // injection and key down/up handling)
  nInputs:=Length(S) * 4;
  lpiaKeys:=AllocMem(Length(S) * nInputs);

  // Convert the text stream into keystrokes
  i:=0;
  lpszText:=PChar(S);
  while (lpszText^ > #0) do
  begin

     // Check for CR or LF
     if (lpszText^ = #13) or (lpszText^ = #10) then
     begin
        // Push past CR
        if (lpszText^ = #13) then Inc(lpszText);
        // Check for LF (this will handle single LF with no CR)
        if (lpszText^ = #10) then
        begin
           // Push past LF
           Inc(lpszText);
           // Inject the VK_RETURN
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           lpiaKeys^[i].ki.wVk:=VK_RETURN;
           Inc(i);
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           lpiaKeys^[i].ki.wVk:=VK_RETURN;
           lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
           Inc(i);
        end;
        // Continue with the main loop
        Continue;
     end;

     // Convert to virtual key
     key:=VkKeyScan(lpszText^);
     Inc(lpszText);

     // Is the shift set?
     if (Hi(key) > 0) then
     begin
        // Shift key down
        lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
        lpiaKeys^[i].ki.wVk:=VK_SHIFT;
        Inc(i);
     end;

     // Key down
     lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
     lpiaKeys^[i].ki.wVk:=Lo(key);
     Inc(i);

     // Key up
     lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
     lpiaKeys^[i].ki.wVk:=Lo(key);
     lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
     Inc(i);

     // Shift key set?
     if (Hi(key) > 0) then
     begin
        // Shift key up
        lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
        lpiaKeys^[i].ki.wVk:=VK_SHIFT;
        lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
        Inc(i);
     end;

  end;

  // Send the keys
  result:=(SendInput(i, lpiaKeys, SizeOf(TInput)) = i);

  // Free the memory
  FreeMem(lpiaKeys);

end;

procedure TForm1.Button1Click(Sender: TObject);
var  hwndNotepad:      THandle;
begin

  hwndNotepad:=FindWindow('Notepad', nil);
  if (hwndNotepad = 0) then
     MessageBox(Handle, 'Unable to locate an instance of notepad!', nil, MB_OK)
  else
  begin
     SetForegroundWindow(hwndNotepad);
     InsertTextInput(RichEdit1.Text);
     MessageBox(Handle, 'Text injected to notepad', nil, MB_OK)
  end;

end;

end.


 

by: YousefEisaPosted on 2003-06-02 at 16:45:58ID: 8633935

Message to Rllibby,
Wow, I am impressed.  At last I can send something to another application without having to use COM or automation server stuff.  How would I get the handle of any other active window though, since I cannot predict which application may be running when my recognizer is in use.  Is there an easy way of finding the active window (assuming that I can hide the focus from my own recognizer application as i do not want text to my own application). If you appreciate your recommendations.

I really should increase the points allocated to this question.

 

by: DragonSlayerPosted on 2003-06-02 at 23:44:39ID: 8635900

hi yousef,

remember my post to your previous Q about making a window always being not on top (the WS_EX_NOACTIVATE in the Params ExStyle)?

well, if that is done, then your window will not get the focus and GetForegroundWindow() or GetTopWindow(0) should get your the active window.

 

by: rllibbyPosted on 2003-06-03 at 07:38:13ID: 8639078


Yousef,

The issue you are faced with now is determining "which" window is active. Because there can be only ONE window with "input focus", if your window takes the focus, then there is no other active window, just yours. I would suggest trying DragonSlayer's suggestion and see if that works correctly for you. Keep in mind that the window style of WS_EX_NOACTIVATE is only available for Win2000 and later. If this does not satisfy your requirements, then you may be able to use GetNextWindow to work for what you need

<GetNextWindow---
The GetNextWindow function retrieves the handle of the next or previous window in the Z order. The next window is below the specified window; the previous window is above. If the specified window is a topmost window, the function retrieves the handle of the next (or previous) topmost window. If the specified window is a top-level window, the function retrieves the handle of the next (or previous)
>

Hope this helps,
Russell

 

by: YousefEisaPosted on 2003-06-03 at 12:18:38ID: 8641753

To  DragonSlayer: Thanks for your comments and for the reminder.  You help and interest is much appreciated.  I shall certainly go back to the your previous advice in finding the active window and removing the focus from my application.  For some reason The option to increase the points has disappeared. otherwise I would have gladly allocated at aleat 100 points to your reminder.


To Rllibby: Thanks for you latest contribution about the last window.  The solution is interesting but may lead to interesting results.  It gave me an idea tht may be the user should be give a list of avaible windows so that the user decides where the output should go.  I do not know if that creates a nigtmare in practice.

Anyway I am very garteful for your detailed solution which has saved me much anguish. you solution has put book I resorted to (such as Win32 Shell APi - the Tomes of Delphi to shame).
I am happy to award you the full points.

To the other experts, Thanks to munim for pointing me to the Microsoft site, which I found danuting but useful, and thanks to Slick812 and Everaset for their interest, and investigations into what is published.
You all have made my experience very rewarding and enjoyable.

Yousef


 

by: rllibbyPosted on 2003-06-03 at 12:32:09ID: 8641865


Thank you as well Yousef... let us know if you need further assistance with the "active" window issue

Russell

 

by: YousefEisaPosted on 2003-06-03 at 12:58:26ID: 8642104

Message to Rllibby,
Your code works fine with Latni characters and numbers but seems to ignore Arabic characters (depicted by higher Ascii numbers!!) . Any suggestions?

Regards,


Youseg

 

by: YousefEisaPosted on 2003-06-03 at 12:58:26ID: 8642105

Message to Rllibby,
Your code works fine with Latni characters and numbers but seems to ignore Arabic characters (depicted by higher Ascii numbers!!) . Any suggestions?

Regards,


Youseg

 

by: YousefEisaPosted on 2003-06-03 at 13:13:05ID: 8642239

Hi Rllibby,
I also get  'Invalid pointer operations' crash message  at the source line: FreeMem(lpiaKeys); it could be when my RichEdit1.text contains Arabic characters.  but it is not consistent as when it happens. Any thoughts would be welcome.

Regards



Yousef

 

by: rllibbyPosted on 2003-06-03 at 13:16:06ID: 8642272


Let me look at it, and will get back with you in a short bit. (btw, which code page are you using, just for my reference)

Russell

 

by: rllibbyPosted on 2003-06-03 at 13:48:58ID: 8642556


I caught the memory issue; record size was never figured/calculate in. Am correcting the routine to fix this, as well as to try and handle the upper ascii, which don't really map to a single key stroke (there lies the problem)

Russell


 

by: rllibbyPosted on 2003-06-03 at 14:50:17ID: 8643067

Updated routine, still looking at the handling of upper ascii chars (doesnt seem to want to take the ALT.NUM.NUM.NUM keystokes....)

function InsertTextInput(S: String): Boolean;
var  lpHead:     PChar;
     lpCurr:     PChar;
     lpiaKeys:   PInputArray;
     szHighAsc:  String;
     key:        Short;
     alloc:      Integer;
     i:          Integer;
     j:          Integer;
begin

  // Set default result
  result:=False;

  // Exit if nothing to send
  if (S = '') then exit;

  // Allocate null string and copy the string
  lpHead:=AllocMem(Succ(Length(S)));
  StrPLCopy(lpHead, S, Length(S));

  // Set starting iterator
  lpCurr:=lpHead;

  // Iterate the string to calcuate the key stroke array size
  alloc:=0;
  while (lpCurr^ > #0) do
  begin
     // Check for CRLF combinations
     if (lpCurr^ = #13) or (lpCurr^ = #10) then
     begin
        // Push past CR
        if (lpCurr^ = #13) then Inc(lpCurr);
        // Check for LF (this will handle single LF with no CR)
        if (lpCurr^ = #10) then
        begin
           Inc(alloc, 2);
           Inc(lpCurr);
        end;
        // Continue with the main loop
        Continue;
     end;
     // Convert to virtual key
     key:=VkKeyScan(lpCurr^);
     // If key is (-1) then we will need to translate the key as
     // ALT.NUM.NUM.NUM which is a total of 8 keystrokes
     if (key = -1) then
     begin
        Inc(alloc, 8);
        Inc(lpCurr);
        Continue;
     end;
     Inc(lpCurr);
     // Will be at least a key down and key up
     Inc(alloc, 2);
     // Check for shift
     if ((Hi(Key) and 1) > 0) then Inc(alloc, 2);
     // Check for ctrl
     if ((Hi(Key) and 2) > 0) then Inc(alloc, 2);
     // Check for alt
     if ((Hi(Key) and 4) > 0) then Inc(alloc, 2);
  end;

  // Allocate memory for key strokes array
  lpiaKeys:=AllocMem(alloc * SizeOf(TInput));

  // Now build the array of keystrokes
  i:=0;
  lpCurr:=lpHead;
  while (lpCurr^ > #0) do
  begin
     // Check for CR or LF
     if (lpCurr^ = #13) or (lpCurr^ = #10) then
     begin
        // Push past CR
        if (lpCurr^ = #13) then Inc(lpCurr);
        // Check for LF (this will handle single LF with no CR)
        if (lpCurr^ = #10) then
        begin
           // Push past LF
           Inc(lpCurr);
           // Inject the VK_RETURN
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           lpiaKeys^[i].ki.wVk:=VK_RETURN;
           Inc(i);
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           lpiaKeys^[i].ki.wVk:=VK_RETURN;
           lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
           Inc(i);
        end;
        // Continue with the main loop
        Continue;
     end;
     // Convert to virtual key
     key:=VkKeyScan(lpCurr^);
     // If we failed to convert then we must use the ALT.NUM.NUM.NUM
     if (key = -1) then
     begin
        // Convert the key to an a string of numbers
        szHighAsc:=Format('%.3d', [Ord(lpCurr^)]);
        Inc(lpCurr);
        // Alt down
        lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
        lpiaKeys^[i].ki.wVk:=VK_MENU;
        Inc(i);
        // Iterate the string of numerics
        for j:=1 to Length(szHighAsc) do
        begin
           // Calculate the numpad key
           key:=48+Ord(szHighAsc[j]);
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           lpiaKeys^[i].ki.wVk:=key;
           Inc(i);
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           lpiaKeys^[i].ki.wVk:=key;
           lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
           Inc(i);
        end;
        // Alt up
        lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
        lpiaKeys^[i].ki.wVk:=VK_MENU;
        lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
        Inc(i);
        // Continue with main loop
        Continue;
     end;
     Inc(lpCurr);
     // Check for shift, ctrl and alt
     for j:=0 to 2 do
     begin
        if (Hi(key) and (1 shl j) > 0) then
        begin
           // Special key handling
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           case j of
              0  :  lpiaKeys^[i].ki.wVk:=VK_SHIFT;
              1  :  lpiaKeys^[i].ki.wVk:=VK_CONTROL;
              2  :  lpiaKeys^[i].ki.wVk:=VK_MENU;
           end;
           Inc(i);
        end;
     end;
     // The actual key down/up
     lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
     lpiaKeys^[i].ki.wVk:=Lo(key);
     Inc(i);
     lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
     lpiaKeys^[i].ki.wVk:=Lo(key);
     lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
     Inc(i);
     // Check for shift, ctrl and alt
     for j:=0 to 2 do
     begin
        if (Hi(key) and (1 shl j) > 0) then
        begin
           // Special key handling
           lpiaKeys^[i].Itype:=INPUT_KEYBOARD;
           case j of
              0  :  lpiaKeys^[i].ki.wVk:=VK_SHIFT;
              1  :  lpiaKeys^[i].ki.wVk:=VK_CONTROL;
              2  :  lpiaKeys^[i].ki.wVk:=VK_MENU;
           end;
           lpiaKeys^[i].ki.dwFlags:=KEYEVENTF_KEYUP;
           Inc(i);
        end;
     end;
  end;

  // Sanity check, alloc should now equal i
  Assert(alloc = i, 'Array entry calculation error');

  // Send the keys
  result:=(SendInput(i, lpiaKeys, SizeOf(TInput)) = i);

  // Free the string buffer
  FreeMem(lpHead);

  // Free the key stroke array
  FreeMem(lpiaKeys);

end;

 

by: YousefEisaPosted on 2003-06-04 at 04:40:35ID: 8647412

Hi rllibby,
Thansk for the new modification. It certainly solves the problem of errors and crashing.  You would be interested to know that if I use the language bar to change the language  for Notepad to 'Arabic', your software works!!.  May be that is the solution: to ensure that the application receiving the input from InsertKey is configured before hand by the lanaguage bar.

I have tried to send the output to Word., and changed the
hwndNotepad:=FindWindow('Notepad', nil); to   hwndNotepad:=FindWindow('Word', nil);

but it does not find Word.  I have tried 'Microsoft Word'  and 'Word.basic' without any success.  the handle retuned is always zero.  I wonder what Word is  called!!

Regards

Yousef

 

by: rllibbyPosted on 2003-06-04 at 07:38:46ID: 8648938


Yousef,
Glad to see that this is now working for you; am still looking at the extended ascii chars, but SendInput is really not designed for injecting those chars. If the language setting corrects this issue, i would go with that.

Regarding Word and FindWindow, the FindWindow usage i gave you was for finding Notepad using its class name, which by the way is "Notepad". This is not always the case, as my version of Word uses "OpusApp" as the class name

ie: FindWindow('OpusApp', nil);

Perhaps for debugging purposes, you might use EnumWindows to iterate the windows, then get the window caption and add the caption and hwnd to a list box. Then you could select from the list which window was to recieve the key injection after performing a SetForegroundWindow() on it.

Russell

 

by: YousefEisaPosted on 2003-06-04 at 08:11:58ID: 8649280

Russell,
thanks for clarifying the FindWindow proble.  I'll follow you advice and try to find out what word is called in my system.

Although I am happy now using the language setting, I would be interested to know if you solve the problem another way.

one thought about your solution. rather than have the array (that holds the string) being created every time the procedure is called, why not make it a global object rather than a local varaibel.  Then you would need to free it only once - when the program closes.  This assumes that you set its size large enough for any string and the SendInput will only need to the number of characters to be inserted.

I am sure you are right that SendInput was not designed for this sort of thing.  How about SendKey? any pointers to where information about SendKey may be found?

Once again I appreciate your genorous help.  Thanks very much.

Yousef

 

by: rllibbyPosted on 2003-06-04 at 10:21:58ID: 8650508


Yousef,

WinSight, WinSpy, or other similar tool can be used to find the classname of the window you wish to find. You can also iteratate (through EnumWindows) the windows and then call GetClassName to explore the different classes for the windows

Have spent a few hours at this point trying to get the emulation down for extended ascii chars, to no avail. I don't feel too bad though, tried using WScript's SendKey() method with "%(231)" (tried 128..255 as well in place of the 231) and it does nothing more than my code, which is to flicker the underline in the menu items. Sorry can't help further there.

Regarding the array: You can handle that as best you see fit. Your question started off asking for an "example" on how SendInput is used, and my code was meant to be just that, an example. If you do use it as a global, just make sure to allocate using

Size = (MaxKeyStokesPerChar(which is 8) * MaxLengthOfString) * SizeOf(TInput);

This also means that you will need to limit the max string you pass in to be MaxLengthOfString chars or less. You can also get rid of the code for the extended key handling, as it does not work (thus is dead weight).

Russell

 

by: YousefEisaPosted on 2003-06-04 at 12:41:37ID: 8651623

You have been a star.  I was very fortunate that you responded to my question.

thanks a million for all the efforts expended. I am sure that you have been hampered by the limitations of Microsoft's own designs and limitations.  Iam consident that you can offer better insight and solutions than the stuff piblsihed in many books.

I would like to wish you you the best.  you deserve the hight of success.

Best regards

 

by: rllibbyPosted on 2003-06-04 at 12:59:13ID: 8651764


Thank you for the kind comments Yousef.
Best regards and success with your Delphi endeavors,

Russell

 

by: PoeticAudioPosted on 2003-06-05 at 02:36:52ID: 8655489

I have made a window spy utility that gets all the info from the window where you cursor is
i'll give you all my source and everything. You simply place your mouse over a window and
press "S" (it doesnt matter if my app is in focus I use a system wide hook) once you have the window you can view all the class info, you can view details showing the owner of that window, it extracts the icon from the window, gets handle, class, parent, owner, caption, coordinates...etc.

I just finished the app, lemme know if you wanna check it out (will include all pas files...etc, exe and source)

Best Regards,
Steven

 

by: YousefEisaPosted on 2003-06-05 at 05:45:04ID: 8656615

To PoeticAudio:
What a brilliant solution! And it will give the user some control and certainty about where the output shoud be going.  It is a much better idea than showing the user a list of available windows, since a lot of the names on the list might not make sense and it is difficult to associate names with actual applications.

I would be delighted if you would could share this novel and samrt solutions with me. It would certainly make my day.

Thank you for such unexpected kindess and generosity.


best regards,


Yousef

 

by: PoeticAudioPosted on 2003-06-05 at 07:36:42ID: 8657615

Yousef, thank you very much for the kind words, however I can't take full credit for this project.
Although the code is purely my own code I have seen a similar program before, I just overhauled
this tiny program with some useful features such as autocopying to clipboard, the ability
to select a window using the S key (even if the app is out of focus), a transparecy feature
that allows you to work with windows behind the app and a few other tidbits. I would be
more then happy to send you my whole source and exe file. I am doing a little bit of tidying
up on it so give me about 2 hours to finish this project off.

Best Regards,
Steven

 

by: PoeticAudioPosted on 2003-06-05 at 08:35:42ID: 8658183

Okay, whenever you are ready just provide me with your email address and i'll send =)

Regards,
Steven

 

by: YousefEisaPosted on 2003-06-05 at 09:31:49ID: 8658771

Steven,
I look forward to the code; here is my email address: yousef@btinternet.com

once again thanks for your efforts and generosity.  Even if you have seen the idea before, the fact you have got it to work despite the mysteries of Windows shell programing is a great feat.

Best regards,


Yousef

 

by: Slick812Posted on 2003-06-05 at 12:05:49ID: 8660018

-- I beleive the system uses a WM_CHAR message to the control with keyboard focus to get key board typing into the text of the control (controls displaying text input, Edits). Reading this EE page confuses me very much, but in an effort to help, here is some code that I might use to do what I think you want to do.
If you want to get a child windows's handle to send the WM_CHAR message to then there is some code for that. I use the Get window at Cursor Position way of doing that. However, I beleive I would just use the Key board event keybd_event function to get the char to the window that has the key board focus. I have shown both ways.


  private
    { Private declarations }
    SendHandle, CharPos: Integer;
    SendChar: Array[0..10] of Char;
    procedure HotKey(var msg : TMessage); message WM_HOTKEY;


procedure TForm1.FormCreate(Sender: TObject);
begin
SendHandle := 0;
SendChar := 'A Send Char';
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
UnregisterHotKey(Handle, 55);
end;


procedure TForm1.HotKey(var msg : TMessage);
var
CurPnt: TPoint;
Whandle, Chandle: Integer;
begin
{this is the WM_HOTKEY message, your program does NOT need
to be the Focus Window, no matter when the user presses the Ctrl+F10
key combo, your program will get this message}
if msg.WParam = 55 then
  begin
  GetCursorPos(CurPnt);
  Whandle := WindowFromPoint(CurPnt);
  if WHandle <> 0 then
    begin
    windows.ScreenToClient(Whandle, CurPnt);
    Chandle := ChildWindowFromPointEx(Whandle, CurPnt, CWP_SKIPINVISIBLE);
    if CHandle <> 0 then
    SendHandle := CHandle else
    SendHandle := 0;
    ShowMessage(IntToStr(Chandle));
    end else
    ShowMessage('ERROR, - Failed to get window under cursor');
  end;
end;


procedure TForm1.sbut_GetHandleClick(Sender: TObject);
begin
{a button click to start the get Get child window Handle process}
if not RegisterHotKey(Handle, 55 {ID number from 0 to 49150}, MOD_CONTROL, VK_F10{Ord('E')}) then
  begin
  ShowMessage('ERROR - ERROR - Could not register the Hot Key, It is already in use by another Program'#10'Pick another Key');
  Exit;
  end;
ShowMessage('Place your Mouse Arrow Pointer DIRECTLY over the place you want the text to go into, '+
            'DO NOT CLICK, when your Cursor is over the area, press Ctrl+F10.'#10'Then look at the '+
            'ShowMessage to see if there is a Number above zero there.');
end;


procedure TForm1.sbut_SendCharTimerClick(Sender: TObject);
begin
{this is a button click to start Timer1 and send the WM_CHAR to the
SendHandle  window}
if SendHandle = 0 then
  begin
  ShowMessage('NO WAY, , SendHandle is Zero');
  Exit;
  end;
Timer1.Interval := 4000;
{I set 4 seconds here so you can change the current foreground window
minimize this program or whatever}
Timer1.Enabled := True;
CharPos := 0;
end;


procedure TForm1.Timer1Timer(Sender: TObject);
begin
{Timer1 OnTimer event}
Timer1.Interval := 600;
if Sendhandle = 0 then
  begin
  Timer1.Enabled := False;
  Exit;
  end;
{the WM_CHAR message is what the System sends to a control for
keyboard text charaters, keys like Enter and Esc are not sent as WM_CHAR}
PostMessage(SendHandle, WM_CHAR, Ord(SendChar[CharPos]), 0);
{the Ord value should be the Char of the current language code page for this program}
if CharPos > 9 then Timer1.Enabled := False
else Inc(CharPos);
end;


{***  the next code is for the keyboard event stuff}

procedure TForm1.sbut_TimerKEventClick(Sender: TObject);
begin
{a button Ckick to start Timer2}
Timer2.Interval := 4000;
Timer2.Enabled := True;
CharPos := 0;
end;


procedure TForm1.Timer2Timer(Sender: TObject);
var
VkScan1: Word;
begin
{Timer2 OnTimer event}
Timer2.Interval := 600;
{get the virtual key and shift, ctrl, alt for
the Char in the language code page for your app}
VkScan1 := VkKeyScan(SendChar[CharPos]);

{test for the Shift, Ctrl and Alt key press}
if HiByte(VkScan1) and 1 <> 0 then
keybd_event(VK_SHIFT,0,0,0);
if HiByte(VkScan1) and 2 <> 0 then
keybd_event(VK_CONTROL,0,0,0);
if HiByte(VkScan1) and 4 <> 0 then
keybd_event(VK_MENU,0,0,0);
//do the key board event down and up for that language char Virtual key
keybd_event(LoByte(VkScan1),0,0,0);
keybd_event(LoByte(VkScan1),0,KEYEVENTF_KEYUP,0);

if HiByte(VkScan1) and 1 <> 0 then
keybd_event(VK_SHIFT,0,KEYEVENTF_KEYUP,0);
if HiByte(VkScan1) and 2 <> 0 then
keybd_event(VK_CONTROL,0,KEYEVENTF_KEYUP,0);
if HiByte(VkScan1) and 4 <> 0 then
keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);

if CharPos > 9 then Timer2.Enabled := False
else Inc(CharPos);
end;

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
I feel like the keybd_event would be the best way to go, if you can keep your writing translator program from being the Focus Window

 

by: PoeticAudioPosted on 2003-06-05 at 18:42:09ID: 8662285

Yousef, I went ahead and sent the EXE file and the code. The code wasnt totally cleaned up or anything (havent had much free time lately) so there is probably a few bugs and a few undeclared vars, I still have a lot of organizing to do but this will give you my general idea. Sorry if I included files you dont need I was in a rush before work (at work now) and I just zipped everything in that dir, i'll send you the final version probably after this weekend.

Have a great day!
Steven

 

by: PoeticAudioPosted on 2003-06-05 at 18:49:06ID: 8662327

oh yeah I also wanted to mention that I have this terrible habbit of not commenting my code until i'm pretty much finished! so some of the code is heavily commented and some isn't... sorry bout that. Didnt think I was actually gonna send the program to anyone

 

by: YousefEisaPosted on 2003-06-06 at 06:07:59ID: 8665807

To Slick812
Thansk for your thoughtful contribution. There is certainly a lot of merit in your approcah of using kebd_event. I have just been learning about that function and it may ultimately be the most practical route.  Although rllibby has heroically provided a concise and eleagnt solution using SendInput, SindInut itself has shortcomings when it comes to dealing with Unicode.  And although Microsoft pages now say that there is a falg to cater for that situation, the new flag will only work for Win2000 and XP.  If my software is to run on various machines it needs to have a solution that works on the various windows OS versions.

Many experts have suggested clever ways of identifying the active window so that its handle could be used for employing SendInput.  PeotocAudio has generously provided a novel approach were users themsleves select the window where the output should be sent.  Such solution will be treasured but your argument that keybd_event can automatically send the output to any window (that has focus) points to a more elegant approach.

The most viable solution then rests on:
1. using keybd_event to send text to the active window (assuming keybd-event can handle unicode characters), and
2. removing the focus from my own program.

I have found a solution for removing the focus, timely provided by DragonSlayer. Thus the only problem left is to test kebd_event with unicode characters.

I shall now study your code and test keybd_event within my program.  If it works with unicode I would  be a happy man.

I have learnt a lot from this all the contributions and I have gained an insight in difficulties that were not even apparent when I started.  I am grateful to all the experts for their convictions ofnthought, analysis beyond expectations, persevrance, contuned interest and above all their genrousity of time and effort.


 

by: rllibbyPosted on 2003-06-06 at 06:28:23ID: 8665982


Yousef,

I think you will end up finding that keybd_event has the same shortcomings (even more so) as SendInput. It might be easier to think of SendInput as a function that can be used to generate an array of key strokes (or mouse, hardware input), vs the single WM_KEYUP/KEYDOWN that is generated by keybd_event. If you take a look at the keybd_event description in the MSDN, you will see that it does not handle unicode either (whereas sendinput will on 2000 and up).

Regards,
Russell

 

by: Slick812Posted on 2003-06-06 at 10:08:02ID: 8667949

??????
again I am confused, maybe there are unicode or wideCharater keyboards that have more than 256 keys on them, maybe they have thousands of keys on them, seems like slow typing to me. But if the keyboard has the 102 to 116 keys then it will use the Virtual key number as the Key, and then use the Shift, Ctrl and Alt keys to get more "Characters", thus the code -


VkScan1 := VkKeyScan(SendChar[CharPos]);

if HiByte(VkScan1) and 1 <> 0 then
keybd_event(VK_SHIFT,0,0,0);
if HiByte(VkScan1) and 2 <> 0 then
keybd_event(VK_CONTROL,0,0,0);
if HiByte(VkScan1) and 4 <> 0 then
keybd_event(VK_MENU,0,0,0);

keybd_event(LoByte(VkScan1),0,0,0);
keybd_event(LoByte(VkScan1),0,KEYEVENTF_KEYUP,0);

if HiByte(VkScan1) and 1 <> 0 then
keybd_event(VK_SHIFT,0,KEYEVENTF_KEYUP,0);
if HiByte(VkScan1) and 2 <> 0 then
keybd_event(VK_CONTROL,0,KEYEVENTF_KEYUP,0);
if HiByte(VkScan1) and 4 <> 0 then
keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

I did not set up the  SendChar  array as a mutibyte charater array, but you would need to do that for WideCharater language code-page systems. I do not have any MutiByte language code-pages instaled on any of my machines, so I can not verify if it would accually work.

 

by: YousefEisaPosted on 2003-06-06 at 14:52:47ID: 8669876

To Slick812,
I may have failed to elaborate on the nature of the problem which may have caused your confusion.  The string that I need to send from my application to another application window is generated by software as a result of reading handwriting input.  The keyboard has nothing to do with it.  I needed to find a solution to enable me to transfer the string (representing the output of my handwriting recognizer) to another application, such as Word or Excel.  Think of my handwriting recogniser as a new kind of keyboard driver, but instead of having a real keyboard I use an application that accepts handwriting as input, and generates unicode characters as output.  This output (just as if had come from a real keyboard) has to go the top level window -  whatever it may happens to be.

Since my software is reading Arabic handwriting, it generates unicode chacarters representing arabic characters.  The objective is that I take this unicode string, one character at a time and use keybd_event to send it to another application.  This would enable users enter text into Word or Excell by handwriting input only and can avoid using the keyboard.

The trouble is that the handwriting recognition software recognises the entered arabic handwriting (in real time) and generate unicode characters to be sent whatever active window the user wishes to enter information into (for example Word).

There are two problems with the code you have sendt me:

1. KKeyScan reports an out of range error if you give it a unicode character ( acharacter whose ord is larger than 127)

2. keybod_event itself also generates the same error if bypass KkeyScan and try to send an arabic character to it.

 This mean I cannot use ketbd_event a s the transport mechansim between the handwriting recognizer and the target application.

I have tried to map the arabic unicode charcters to keystrokes , ie to translate them down to actual keyboard keys.  but then the target window receives English characters - because there is no way to instruct the recipient window that I am sending Arabic characters.


So it appears that I am back to square one.  The only viable solution seems to be identify the active window (after I permanently remove the focus from recognizer) and send the recognizer output via a sendmessage function which accepts unicode strings.


 

by: Slick812Posted on 2003-06-07 at 11:22:48ID: 8673366

OK, thank you for the information, as to the KKeyScan, I had assumed since you were used to using mutiByte Charcters that you would know to use the WIDE charater version of KKeyScan

could you try

KKeyScanW(  )

for your mutibyte wide char

also here is some code to check the return of a KKeyScan or KKeyScanW for error or No Keyboad charater for that  numeric charater value

if (VkScan1 = 0) or (LoByte(VkScan1) = 255) or
   (HiByte(VkScan1) = 255) then DoERROR;

 

by: YousefEisaPosted on 2003-06-08 at 14:03:45ID: 8677900

I have at last arrived at a working solution;

1. I remove focus from my own application
2. I use GetForegroundwindow to find the Window that has focus
3. I use keybd_Event to switch the language setting to Arabic (sending control shift '1'
4. I emply InsertInput to send the whole string to the active Window.

This solves the problem of sending a string to any active window and overcome the problems of characters having values higher than 127 (which always trips the keybd_event function)

Since my recognizer generates Arabic characters (whose keyposition on the keyboard are not known), the SendInput is the best mechanism to send the whole string.

Thanks for all the experts for their valuable contributions.

 

by: _Rob_Posted on 2003-09-15 at 10:11:54ID: 9364453

>This solves the problem of sending a string to any active window and overcome
>the problems of characters having values higher than 127 (which always trips
>the keybd_event function)

Maybe this is a bit late, but what you were missing was KEYEVENTF_EXTENDEDKEY.

/rob

 

by: YousefEisaPosted on 2003-09-18 at 15:57:59ID: 9390018

To _Rob_,
I have just come across your contribution, having been away on business.  I do appreciate your help and your solution is a godsend, elegant and very welcome.  I am still having trouble using othr solutions and was experiencing starnge behaviour of my program.

Thanks for your kind generousity.


Yousef

 

by: _Rob_Posted on 2003-09-18 at 22:46:55ID: 9391341

Maybe this could give you some ideas (just some parts of a program that uses keybd_event).

/rob

//////////////////////////////////////////////////////////////////////////
//
// Structure for data items in the ASCII to keyboard mapping table
//
// NOTE! m_uchScanCode is no longer used but is kept to keep the registry
// format unchanged.
//

#pragma pack( push )
#pragma pack( 1 )
struct ASCII2KeyCode
{
      BYTE m_uchKeyCode;
      BYTE m_uchScanCode;         // NOT USED!
      BYTE m_fValid         : 1;
      BYTE m_fCapsDependent : 1;
      BYTE m_fControl       : 1;
      BYTE m_fAlt           : 1;
      BYTE m_fShift         : 1;
      BYTE m_fDiacritic     : 1;
      BYTE m_fExtended      : 1;
};
#pragma pack( pop )

//////////////////////////////////////////////////////////////////////////
//
// The ASCII to keyboard mapping table used by ::SendKeys()
//

struct ASCII2KeyCode grgWLinqKeyMap[] =
{
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_LBUTTON,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_RBUTTON,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {     VK_CANCEL,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_MBUTTON,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {       VK_BACK,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_TAB,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {      VK_CLEAR,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {     VK_RETURN,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {      VK_SHIFT,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_CONTROL,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {       VK_MENU,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {      VK_PAUSE,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_CAPITAL,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {     VK_ESCAPE,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {            54,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // &
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {      VK_SPACE,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // " "
      {            49,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // !
      {            50,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // "
      {            51,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // #
      {            52,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // $
      {            53,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // %
      {            54,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // &
      {           191,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // '
      {            56,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // (
      {            57,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // )
      {           191,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // *
      {           187,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // +
      {           188,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // ,
      {           189,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // -
      {           190,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // .
      {            55,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // /
      {            48,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 0
      {            49,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 1
      {            50,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 2
      {            51,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 3
      {            52,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 4
      {            53,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 5
      {            54,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 6
      {            55,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 7
      {            56,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 8
      {            57,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // 9
      {           190,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // :
      {           188,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // ;
      {           226,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // <
      {            48,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // =
      {           226,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // >
      {           187,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // ?
      {            50,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // @
      {            65,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // A
      {            66,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // B
      {            67,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // C
      {            68,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // D
      {            69,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // E
      {            70,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // F
      {            71,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // G
      {            72,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // H
      {            73,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // I
      {            74,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // J
      {            75,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // K
      {            76,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // L
      {            77,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // M
      {            78,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // N
      {            79,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // O
      {            80,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // P
      {            81,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // Q
      {            82,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // R
      {            83,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // S
      {            84,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // T
      {            85,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // U
      {            86,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // V
      {            87,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // W
      {            88,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // X
      {            89,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // Y
      {            90,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // Z
      {            56,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // [
      {           187,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // "\"
      {            57,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // ]
      {           186,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE,  TRUE, FALSE  }, // ^
      {           189,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // _
      {           219,   0,  TRUE, FALSE, FALSE, FALSE,  TRUE,  TRUE, FALSE  }, // `
      {            65,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // a
      {            66,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // b
      {            67,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // c
      {            68,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // d
      {            69,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // e
      {            70,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // f
      {            71,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // g
      {            72,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // h
      {            73,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // i
      {            74,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // j
      {            75,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // k
      {            76,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // l
      {            77,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // m
      {            78,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // n
      {            79,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // o
      {            80,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // p
      {            81,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // q
      {            82,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // r
      {            83,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // s
      {            84,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // t
      {            85,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // u
      {            86,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // v
      {            87,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // w
      {            88,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // x
      {            89,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // y
      {            90,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // z
      {            55,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // {
      {           226,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // |
      {            48,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE, FALSE, FALSE  }, // }
      {           186,   0,  TRUE, FALSE,  TRUE,  TRUE, FALSE,  TRUE, FALSE  }, // ~
      {     VK_DELETE,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {      VK_PRIOR,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_NEXT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {        VK_END,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_HOME,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_LEFT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {         VK_UP,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {      VK_RIGHT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_DOWN,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {     VK_SELECT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {      VK_PRINT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_EXECUTE,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {   VK_SNAPSHOT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {     VK_INSERT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {     VK_DELETE,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_HELP,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {       VK_LWIN,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_RWIN,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {       VK_APPS,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD0,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD1,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD2,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD3,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD4,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD5,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_NUMPAD6,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD7,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD8,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {    VK_NUMPAD9,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {   VK_MULTIPLY,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_ADD,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {  VK_SEPARATOR,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {   VK_SUBTRACT,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_DECIMAL,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {     VK_DIVIDE,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE   }, //
      {         VK_F1,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F2,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F3,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F4,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F5,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F6,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F7,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F8,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {         VK_F9,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F10,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F11,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // x
      {        VK_F12,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // x
      {        VK_F13,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F14,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F15,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F16,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F17,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F18,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F19,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F20,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F21,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F22,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F23,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {        VK_F24,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {    VK_NUMLOCK,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // x
      {     VK_SCROLL,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // x
      {     VK_RETURN,   0,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE,  TRUE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {           222,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // Ä
      {           221,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // Å
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {           192,   0,  TRUE,  TRUE, FALSE, FALSE,  TRUE, FALSE, FALSE  }, // Ö
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {           222,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // ä
      {           221,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // å
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {           192,   0,  TRUE,  TRUE, FALSE, FALSE, FALSE, FALSE, FALSE  }, // ö
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
      {             0,   0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE  }, //
};

void sendString( LPCTSTR pszString )
{
      CKeyTranslatorMap ktm;
      CKeyTranslator* pKeyTranslator;

      for( int i = 0; i < (int)strlen( pszString ); i++ )
      {
            if( TRUE == MapASCII2KeyCode( (BYTE)pszString[i], ktm, pKeyTranslator ) )
            {
                  // Check if the CapsLook.dll library has been loaded and we know the address of the GetCapsLockState() function.
                  BOOL fCapsLock = FALSE;
                  /*
                  if( NULL != ::gpfnGetCapsLockState )
                  {
                        // Get the current caps lock state
                        fCapsLock = ::gpfnGetCapsLockState();
                  }
                  */

                  // Check if the Control key modifier should be sent
                  if( TRUE == pKeyTranslator->getControl() )
                  {
                        ::keybd_event( VK_CONTROL, eRawScanCode_LeftControl, (DWORD)0, (DWORD)0 );

                        //strKeyDebug += GetKeyName( eRawScanCode_LeftControl );
                        //strKeyDebug += kszKeySeparator;
                  }

                  // Check if the Alternate key modifier should be sent
                  if( TRUE == pKeyTranslator->getAlt() )
                  {
                        ::keybd_event( VK_MENU,  eRawScanCode_LeftAlt, (DWORD)0, (DWORD)0 );

                        //strKeyDebug += GetKeyName( eRawScanCode_LeftAlt );
                        //strKeyDebug += kszKeySeparator;
                  }

                  //////////

                  BOOL fShiftStatus = FALSE;

                  fShiftStatus = pKeyTranslator->getShift();

                  // Check if key is Caps Lock dependent
                  if( TRUE == pKeyTranslator->getCapsDependent() )
                  {
                        // If caps lock is active the shift status should be inverted for capitals
                        if( TRUE == fCapsLock )
                        {
                              // Reverse value of fShiftStatus
                              if( FALSE == fShiftStatus )
                              {
                                    fShiftStatus = TRUE;
                              }
                              else
                              {
                                    fShiftStatus = FALSE;
                              }
                        }
                  }

                  // Check if shift should be sent
                  if( TRUE == fShiftStatus )
                  {
                        ::keybd_event( VK_SHIFT, eRawScanCode_LeftShift, (DWORD)0, (DWORD)0 );

                        //strKeyDebug += GetKeyName( eRawScanCode_LeftShift );
                        //strKeyDebug += kszKeySeparator;
                  }

                  //////////

                  DWORD dwExtendedStatus;
                  if( TRUE == pKeyTranslator->getExtended() )
                  {
                        dwExtendedStatus = KEYEVENTF_EXTENDEDKEY;
                  }
                  else
                  {
                        dwExtendedStatus = 0;
                  }

                  // The simulated key's down and up event
                  ::keybd_event( (BYTE)pKeyTranslator->getKeyCode(), (BYTE)pKeyTranslator->getScanCode(), (DWORD)dwExtendedStatus, (DWORD)0 );
                  ::keybd_event( (BYTE)pKeyTranslator->getKeyCode(), (BYTE)pKeyTranslator->getScanCode(), (DWORD)KEYEVENTF_KEYUP|dwExtendedStatus, (DWORD)0 );

                  /*
                  if( 0 == dwExtendedStatus )
                  {
                        strKeyDebug += GetKeyName( pKeyTranslator->getScanCode() );
                  }
                  else
                  {
                        strKeyDebug += GetExtendedKeyName( pKeyTranslator->getScanCode() );
                  }
                  */

                  // Below follows the ctrl, alt and shift up simulations
                  if( TRUE == pKeyTranslator->getAlt() )
                  {
                        ::keybd_event( VK_MENU, eRawScanCode_LeftAlt, (DWORD)KEYEVENTF_KEYUP, (DWORD)0 );
                  }

                  if( TRUE == pKeyTranslator->getControl() )
                  {
                        ::keybd_event( VK_CONTROL, eRawScanCode_LeftControl, (DWORD)KEYEVENTF_KEYUP, (DWORD)0 );
                  }

                  if( TRUE == fShiftStatus )
                  {
                        ::keybd_event( VK_SHIFT, eRawScanCode_LeftShift, (DWORD)KEYEVENTF_KEYUP, (DWORD)0 );
                  }

                  // If the ASCII character is a diacritic character (for example ¨^~) then
                  // a space is needed to "release" the character since the key is actually
                  // a combine key that waits for a letter key.
                  if( TRUE == pKeyTranslator->getDiacritic() )
                  {
                        ::keybd_event( VK_SPACE, eRawScanCode_Space, (DWORD)0, (DWORD)0 );
                        ::keybd_event( VK_SPACE, eRawScanCode_Space, (DWORD)KEYEVENTF_KEYUP, (DWORD)0 );
                  }
            }
      }
}

 

by: _Rob_Posted on 2003-09-18 at 22:49:18ID: 9391350

Oh, this was Delphi. But I guess you can read and understand C++ code I hope ;-)

/rob

 

by: YousefEisaPosted on 2003-09-19 at 02:49:14ID: 9392238

To _Rob_,
Thanks very much for the code. There is more to Windows API than I ever imagined,  Although C++ has always appeared cryptic to me and full of pitfalls, I shall gladly go through your code as soon as I can.  
I am mystifed about the double colon before some lines! what do they indicate? And how can you have a plus sign before an equal sign (e.g. "StrKeyDebug += GetExtndedName (...)" ).   Thank God for Delphi to keep us sane.

Your help is gratefully received and appreciated.

Yousef

 

by: _Rob_Posted on 2003-09-20 at 05:16:36ID: 9398523

>I am mystifed about the double colon before some lines!

It makes sure that the name is from the global name space. If you have a class that has a method with the name GetWindowText but in another method in the same class you want to call the Windows API function also called GetWindowText. Then you write

::GetWindowText( <whatever> );

>And how can you have a plus sign before an equal sign (e.g. "StrKeyDebug += GetExtndedName (...)" ).

x = x + 5;

can be abbreviated with

x += 5;

There are also -= &= and other stuff like that.

The code is pasted in my post is in no way complete. I only posted the mapping table (maps most ASCII characters) and the main function responsible for simulating the keyboard.

/rob

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...