Solved

Simulate VB's Sendkeys

Posted on 1997-04-20
6
907 Views
Last Modified: 2008-02-26
I'm rewriting a VB program in Delphi. The VB Program uses a SendKeys 'Alt-FOTEST.TXT-Enter' to open a File Open Dialog, and to introduce the Filename 'TEST.TXT', followed by a ENTER.
I'm using PostMessage Functions to get the FIle Open Dialog, but if i then Use PostMessage, with the handle of the currently active window (the file open dialog), the messages (characters) are not sent to the active Edit Box of this window. I can, however, via GetNextWindow get hold of the handle of this edit box, and then send these Postmessage's. This works fine, but it makes my function not general. How does VB know what the handle is of the active object on the active window, or what technique does VB use to send these keystrokes to the active object of the active window ?
0
Comment
Question by:jvh042097
  • 4
  • 2
6 Comments
 
LVL 3

Expert Comment

by:sperling
Comment Utility
If you need to know how VB does it, this ain't the right group...

If you need similar functionality in Delphi, I could give it a try. But I don't know how VB implements this, and therefore I cannot guarantee that I'll do it the same way. Please leave a comment if you'd like me to try writing some generic sendkeys function.

Regards,

Erik.
0
 

Author Comment

by:jvh042097
Comment Utility
In fact, i'm not really interested how VB does it. The only thing i need is a generic solution for the sendkeys problem.

My problem is that using Postmessage to the File Open Dialog box, doesn't send the keys right to the active edit box. I can - in this particular case - get hold of it's handle and send the keys i want, but that's becuase i know it's specific order on this form. What if i didn't. Please give it a try !
txs
0
 
LVL 3

Expert Comment

by:sperling
Comment Utility
Just a questions on VB Sendkeys

Can you specify a window to receive the keys, or does the foreground window receive them?

I've written a function for sending keys to the foreground window. I'll have to add parsing for ALT, CTRL and so on before posting it.

Regards,

Erik.
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 3

Accepted Solution

by:
sperling earned 200 total points
Comment Utility
Here's a unit...

Use e.g.

SendString ('[ALT-F]OTEST.TXT[ENTER]');

SendString ('[ALT-F4][ENTER]');


I have tested it, but not very thoroughly. Leave me a comment if you find any bugs...


Regards,

Erik.

---
unit Sendkeys;

interface
uses
  Windows;

type
  TSpecialSequence =
    record
      Name    : STRING[10];
      Code    : INTEGER;
    end;

const
  SpecialSequences : ARRAY [0..20] of TSpecialSequence =
  (
    (Name : 'TAB';           Code : VK_TAB),
    (Name : 'ENTER';         Code : VK_RETURN),
    (Name : 'ESC';           Code : VK_ESCAPE),
    (Name : 'LEFT';          Code : VK_LEFT),
    (Name : 'RIGHT';         Code : VK_RIGHT),
    (Name : 'UP';            Code : VK_UP),
    (Name : 'DOWN';          Code : VK_DOWN),
    (Name : 'INSERT';        Code : VK_INSERT),
    (Name : 'DELETE';        Code : VK_DELETE),
    (Name : 'PAGEUP';        Code : VK_PRIOR),
    (Name : 'PAGEDOWN';      Code : VK_NEXT),
    (Name : 'F1';            Code : VK_F1),
    (Name : 'F2';            Code : VK_F2),
    (Name : 'F3';            Code : VK_F3),
    (Name : 'F4';            Code : VK_F4),
    (Name : 'F5';            Code : VK_F5),
    (Name : 'F6';            Code : VK_F6),
    (Name : 'F7';            Code : VK_F7),
    (Name : 'F8';            Code : VK_F8),
    (Name : 'F9';            Code : VK_F9),
    (Name : 'F10';           Code : VK_F10)
  );

  SpecialSequenceStart     = '[';
  SpecialSequenceEnd       = ']';
  SpecialSequenceSeparator = '-';

  KeyNameAlt               = 'ALT';
  KeyNameCtrl              = 'CTRL';
  KeyNameShift             = 'SHIFT';

function SendString (s : STRING) : BOOLEAN;

implementation
uses
  SysUtils, Messages, Forms, Classes;

type
  PEventMsgLink = ^TEventMsgLink;
  TEventMsgLink =
    record
      EventMsg  : TEventMsg;
      Next      : PEventMsgLink;
    end;


var
  PlaybackHook : INTEGER;
  ShouldSleep  : BOOLEAN;
  FirstListEntry,
  CurrentListEntry      : PEventMsgLink;

function ParseString (s : STRING; IgnoreCase : BOOLEAN) : BOOLEAN; forward;


function PlaybackProc (code : INTEGER; wParam : WORD; eMsg : PEventMsg) : INTEGER; stdcall;
begin
  case code of
    HC_GETNEXT :
      begin
        Move (CurrentListEntry^.EventMsg, eMsg^, SizeOf(TEventMsg));
        Result := 0;
        if ShouldSleep then begin
          Result := 10;
          ShouldSleep := FALSE
        end;
      end;
    HC_NOREMOVE :
      begin
        Result := 0;
      end;
    HC_SKIP :
      begin
        CurrentListEntry := CurrentListEntry^.Next;
        Result := 0;
        ShouldSleep := TRUE;
      end;
    HC_SYSMODALON :
      begin
        Result := 0;
      end;
    HC_SYSMODALOFF :
      begin
        Result := 0;
      end;
  else
    if code<0 then Result := CallnextHookEx(PlaybackHook, code, wParam, INTEGER(eMsg));
  end;
  if CurrentListEntry=nil then begin
    UnhookWindowsHookEx(PlaybackHook);
    PlaybackHook := 0;
  end;
end;

function AddEventMsg (ACode : INTEGER; AMsg : INTEGER) : BOOLEAN;
var
  pLink   : PEventMsgLink;
begin
  if FirstListEntry = nil then begin
    new (FirstListEntry);
    pLink := FirstListEntry;
  end else begin
    pLink := FirstListEntry;
    while pLink^.Next<>nil do pLink := pLink^.Next;
    new (pLink^.next);
    pLink := pLink^.Next;
  end;
  FillChar (pLink^, SizeOf(TEventMsgLink), 0);
  pLink^.EventMsg.message := AMsg;
  pLink^.EventMsg.ParamH := MapVirtualKey(ACode, 0);
  pLink^.EventMsg.ParamL := ACode + $100 * MapVirtualKey(ACode, 0);
  Result := TRUE;
end;

function AddChar (Source : STRING; var SourcePos : INTEGER; IgnoreCase : BOOLEAN) : BOOLEAN;
var
  AChar  : CHAR;
  IsChar : BOOLEAN;
begin
  AChar := Source[SourcePos];
  Result := TRUE;
  IsChar := ANSILowerCase(AChar) <> ANSIUpperCase(AChar);
  if (AChar=UpCase(AChar)) and IsChar and (not IgnoreCase) then Result := Result AND AddEventMsg (VK_SHIFT, WM_KEYDOWN);
  Result := Result AND AddEventMsg (ORD(UpCase(AChar)), WM_KEYDOWN);
  Result := Result AND AddEventMsg (ORD(UpCase(AChar)), WM_KEYUP);
  if (AChar=UpCase(AChar)) and IsChar and (not IgnoreCase) then Result := Result AND AddEventMsg (VK_SHIFT, WM_KEYUP);
  if Result then inc (SourcePos);
end;

function AddSpecial (Source : STRING; var SourcePos : INTEGER) : BOOLEAN;
var
  ndx     : INTEGER;
  Shift,
  Special : STRING;
  ShiftState : TShiftState;
begin
  Result := FALSE;
  Special := Copy (Source, SourcePos, Length(Source));
  ndx := 2;
  while (ndx<=Length(Special)) and (Special[ndx]<>SpecialSequenceEnd) do inc(ndx);
  if ndx>Length(Special) then exit;
  Delete(Special, ndx+1, Length(Special));
  INC(SourcePos, Length(Special));
  Delete(Special, 1, 1);
  Delete(Special, Length(Special), 1);

  // Extract wanted shift keys
  ShiftState := [];
  while Pos(SpecialSequenceSeparator, Special)>0 do begin
    Shift := Copy(Special, 1, Pos(SpecialSequenceSeparator, Special)-1);
    Delete (Special, 1, Pos(SpecialSequenceSeparator, Special));
    if UpperCase(Shift)=KeyNameAlt then Include (ShiftState, ssAlt)
    else if UpperCase(Shift)=KeyNameCtrl then Include (ShiftState, ssCtrl)
    else if UpperCase(Shift)=KeyNameShift then Include (ShiftState, ssShift)
    else exit;
  end;

  if ssAlt in ShiftState then begin
    AddEventMsg (VK_MENU, WM_SYSKEYDOWN);
    if ssCtrl in ShiftState then AddEventMsg (VK_CONTROL, WM_SYSKEYDOWN);
    if ssShift in ShiftState then AddEventMsg (VK_SHIFT, WM_SYSKEYDOWN);
  end else begin
    if ssCtrl in ShiftState then AddEventMsg (VK_CONTROL, WM_KEYDOWN);
    if ssShift in ShiftState then AddEventMsg (VK_SHIFT, WM_KEYDOWN);
  end;
  if Length(Special)=1 then begin
    if ssAlt in ShiftState then begin
      AddEventMsg (ORD(Special[1]), WM_SYSKEYDOWN);
      AddEventMsg (ORD(Special[1]), WM_SYSKEYUP);
    end else begin
      AddEventMsg (ORD(Special[1]), WM_KEYDOWN);
      AddEventMsg (ORD(Special[1]), WM_KEYUP);
    end;
    Result := TRUE;
  end else begin
    for ndx := LOW (SpecialSequences) to HIGH (SpecialSequences) do begin
      if CompareText (SpecialSequences[ndx].Name, Special) = 0 then begin
        if ssAlt in ShiftState then begin
          AddEventMsg (SpecialSequences[ndx].Code, WM_SYSKEYDOWN);
          AddEventMsg (SpecialSequences[ndx].Code, WM_SYSKEYUP);
        end else begin
          AddEventMsg (SpecialSequences[ndx].Code, WM_KEYDOWN);
          AddEventMsg (SpecialSequences[ndx].Code, WM_KEYUP);
        end;
        Result := TRUE;
        break;
      end;
    end;
  end;

  if not Result then exit;

  if ssAlt in ShiftState then begin
    if ssShift in ShiftState then AddEventMsg (VK_SHIFT, WM_SYSKEYUP);
    if ssCtrl in ShiftState then AddEventMsg (VK_CONTROL, WM_SYSKEYUP);
    AddEventMsg (VK_MENU, WM_SYSKEYUP);
  end else begin
    if ssShift in ShiftState then AddEventMsg (VK_SHIFT, WM_KEYUP);
    if ssCtrl in ShiftState then AddEventMsg (VK_CONTROL, WM_KEYUP);
  end;
end;

function ParseString (s : STRING; IgnoreCase : BOOLEAN) : BOOLEAN;
var
  ndx     : INTEGER;
begin
  Result := FALSE;
  ndx := 1;
  while ndx<=Length(s) do begin
    if s[ndx]=SpecialSequenceStart then begin
      if ndx=Length(s) then exit;
      if s[ndx+1]=SpecialSequenceStart then begin
        INC(ndx);
        if not AddChar (s, ndx, IgnoreCase) then exit;
      end else begin
        if not AddSpecial (s, ndx) then exit;
      end;
    end else begin
      if not AddChar (s, ndx, IgnoreCase) then exit;
    end;
  end;
  Result := TRUE;
end;


function SendString (s : STRING) : BOOLEAN;
begin
  Result := ParseString(s, FALSE);

  if Result then begin
    ShouldSleep := TRUE;
    CurrentListEntry := FirstListEntry;
    PlaybackHook := SetWindowsHookEx (WH_JOURNALPLAYBACK, @PlaybackProc, hInstance, 0);
    while PlaybackHook <> 0 do Application.ProcessMessages;
  end;

  while FirstListEntry <> nil do begin
    CurrentListEntry := FirstListEntry;
    FirstListEntry := CurrentListEntry^.Next;
    Dispose(CurrentListEntry);
  end;
  CurrentListEntry := nil;
end;

initialization
  CurrentListEntry := nil;
  FirstListEntry := nil;
end.



0
 

Author Comment

by:jvh042097
Comment Utility
Your program seems to work fine, except for the '.' : This character is sent as VK_Delete, rather than the '.' character. I have temporary added a filter which remaps those VK_delete into the right '.'

TXS !
0
 
LVL 3

Expert Comment

by:sperling
Comment Utility

Result := Result AND AddEventMsg (ORD(UpCase(AChar)),WM_KEYDOWN);
Result := Result AND AddEventMsg (ORD(UpCase(AChar)),WM_KEYUP);


Oops...

The ORD(UpCase(AChar)) here only gives correct values for A..Z and 0..9

You'll probably have to check the char here, and then map to the correct virtual key code.

Regards,

Erik.
 
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now