Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Simulate VB's Sendkeys

Posted on 1997-04-20
6
927 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
ID: 1335549
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
ID: 1335550
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
ID: 1335551
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
Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 3

Accepted Solution

by:
sperling earned 200 total points
ID: 1335552
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
ID: 1335553
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
ID: 1335554

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 Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

840 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