Solved

Remembering and sending keystrokes to a spawned app

Posted on 2000-03-01
35
287 Views
Last Modified: 2010-04-04
This is a two part question. How do you spawn another app from within a delphi app and send keystrokes to it? Example, spawn a paint program and send it the key strokes to convert a 24bit picture to an 8bit picture.
0
Comment
Question by:evansj
  • 19
  • 5
  • 4
  • +3
35 Comments
 
LVL 1

Expert Comment

by:wmckie
ID: 2575716
Is the example of changing the number of bits per pixcel what your actualy wanting to do, if so would it not be better to load the picture (e.g. with an instance of TBitmap) change its bits/pixel and save it again?

Walter McKie
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2575752
Generally, spawning another application and control it then from within the starter application is very unreliable and therefor only recommendable for old software which doesn't know about COM or at least DDE. I recommend to use automation to control another application if it is necessary or (in the case your true problem is really only to convert a picture color resolution) to use Delphi's features.

Ciao, Mike
0
 
LVL 1

Expert Comment

by:runebj
ID: 2575788
If you still want to record and play back keystrokes you can experiment with the JournalRecordProc and JournalPlaybackProc API calls.

Alternatively you can use something the keybd_event function to send keystrokes directly (you'll need the handle of the window to send them to). If you specify your problem in more detail I'll be happy to give you a code example of how to use keybd_event.

runebj
0
 
LVL 5

Expert Comment

by:TheNeil
ID: 2575817
If you want to send keystrokes to an app, try this lot:

PROCEDURE SendKeys(sText : STRING);
VAR
  iCount    : INTEGER;
  bShift    : BOOLEAN;
  wVK       : WORD;
  wScancode : WORD;
  ch        : CHAR;
  byC       : BYTE;
  byS       : BYTE;
CONST
  vk_keys : ARRAY[0..9] OF BYTE = (VK_HOME, VK_END, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_PRIOR, VK_NEXT, VK_INSERT, VK_DELETE);
  vk_shft : ARRAY[0..2] OF BYTE = (VK_SHIFT, VK_CONTROL, VK_MENU);
  flags   : ARRAY[FALSE..TRUE] OF INTEGER = (KEYEVENTF_KEYUP, 0);
BEGIN
  bShift := FALSE;
  FOR iCount := 1 TO LENGTH(sText)
  DO
  BEGIN
    ch := sText[iCount];
    IF ch >= #250
    THEN
    BEGIN
      byS := ORD(ch) - 250;
      bShift := NOT(ODD(byS));
      byC := vk_shft[byS SHR 1];
      wScancode := MapVirtualKey(byC, 0);
      Keybd_Event(byC, wScancode, flags[bShift], 0);
    END
    ELSE
    BEGIN
      wVK := 0;
      IF ch >= #240
      THEN
        byC := vk_keys[ORD(ch) - 240]
      ELSE
      BEGIN
        IF ch >= #228
        THEN
          byC := ORD(ch) - 116
        ELSE
        BEGIN
          IF ch < #32
          THEN
            byC := ORD(ch)
          ELSE
          BEGIN
            wVK := VkKeyScan(ch);
            byC := LoByte(wVK);
          END;
        END;
      END;

      wScancode := MapVirtualKey(byC, 0);
      IF NOT(bShift) AND (Hi(wVK) > 0)
      THEN
        Keybd_Event(VK_SHIFT, $2A, 0, 0);
      Keybd_Event(byC, wScancode, 0, 0);
      Keybd_Event(byC, wScancode, KEYEVENTF_KEYUP, 0);
      IF NOT(bShift) AND (Hi(wVK) > 0)
      THEN
        Keybd_Event(VK_SHIFT, $2A, KEYEVENTF_KEYUP, 0);
    END;
    Application.ProcessMessages;
  END;
END;

Just make sure that Word (or whatever) is the focused window and you'll have no problem! (Use char-codes to send system keys such as tab, enter, arrow keys etc.).

To actually get your app running, use ShellExecute or whatever. e.g.

ShellExecute(Form1.Handle, 'open', 'Notepad.exe', '', '', SW_SHOWNORMAL);

This should get notepad up and running (maybe) and make it the application in focus, so you can send your keystrokes directly to it

The Neil
0
 
LVL 17

Expert Comment

by:inthe
ID: 2575886
The TJPEGImage (on delphi cd)component is very good at color reducing a full color image down to 256 colors.
see the example below and instead of deleting the tempfile delete the opendialog filename ,and you can always assign the new jpg image back to a bitmap if wanted..

procedure TForm1.Button1Click(Sender: TObject);
var
  JP : TJPEGImage;
  IM : TImage;
  TempFileName : string;
begin
{Pop up a Open Dialog}
  OpenDialog1.Options := [ofNoChangeDir, ofFileMustExist];
  OpenDialog1.Filter := 'Bitmap Files (*.bmp)|*.bmp';
  if OpenDialog1.Execute then begin
  {Create a temporary TImage}
    IM := TImage.Create(nil);
  {Load the bitmap file}
    IM.Picture.LoadFromFile(OpenDialog1.FileName);
  {Create a temporary TJPEGImage}
    JP := TJPEGImage.Create;
  {Priorty on quality}
    JP.Performance := jpBestQuality;
  {Assign the bitmap to the JPEG}
    JP.Assign(IM.Picture.Graphic);
  {Free the temp image}
    IM.Free;
  {Make a temp file name with the extension of .jpg}
    TempFileName := 'test.jpg';
  {Save the JPEG to a temp file}
    JP.SaveToFile(TempFileName);
  {Free the JPEG}
    JP.Free;
  {Load the temp file to an image on the form}
    Image1.Picture.LoadFromFile(TempFileName);
  {Delete the temp file}
    DeleteFile(TempFileName);
  end;
end;
 
or see here:
http://www.efg2.com/lab/Graphics/Colors/CombinePf4bit.htm

or if want to use the keystroke stuff instead i suggest looking at:

http://community.borland.com/article/0,1410,10323,00.html

Regards Barry
0
 

Author Comment

by:evansj
ID: 2576689
I need to change a 24bit TGA file to an 8bit BMP file with gamma correction. If I can do it in D3 or D4 that's fine. If there are some graphic controls to do this, OK also.
0
 
LVL 17

Expert Comment

by:inthe
ID: 2576736
Hi
take a look at tga on this page:

http://www.efg2.com/lab/library/delphi/graphics/FileFormatsAndConversion.htm#Conversions

there are some units there that will help.
0
 

Author Comment

by:evansj
ID: 2576761
What header file contains the ShellExecute Method?
0
 
LVL 17

Expert Comment

by:inthe
ID: 2576803
shellapi is the one your after.
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2576817
For reading TGA files you can download my GraphicEx unit from www.lischke-online.de. It will read in a TGA file into a TBitmap (and makes so the conversion you are looking for) but does no gamma correction.

Ciao, Mike
0
 

Author Comment

by:evansj
ID: 2577050
What header file contains the ShellExecute Method?
0
 

Author Comment

by:evansj
ID: 2577197
What header file contains the ShellExecute Method?
0
 
LVL 5

Expert Comment

by:TheNeil
ID: 2577307
As inthe said, you need the ShellApi file (or is this a wonderful internet/microsoft repeat problem - grrrr)

The Neil =:)
0
 

Author Comment

by:evansj
ID: 2577597
Wonderful repeat problem...
Wonderful repeat problem...
Wonderful repeat problem...
Wonderful repeat problem...
Wonderful repeat problem...
0
 

Author Comment

by:evansj
ID: 2588116
To TheNeil: give me an example of sending say ALT-F, Alt-0, then a file name then Close.
0
 

Author Comment

by:evansj
ID: 2588135
To Lischke:
How would you load a TGA using your control?
0
 

Author Comment

by:evansj
ID: 2588148
To Lischke:
How would you load a TGA using your control?
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 10

Accepted Solution

by:
Lischke earned 300 total points
ID: 2588184
To use GraphicEx the easiest way is to include it somehow in your project (use clause of any unit of the project) and then using TPicture.LoadFromFile. Because GraphicEx registeres its image classes with TPicture you can use it to load them. TPicture will take care about picking the correct graphics class.

Ciao, Mike
0
 
LVL 5

Expert Comment

by:TheNeil
ID: 2588210
Here's a demo for Notepad. What it'll do is add some text ('Some text'), save it to a new file (newfile.txt), and then close the application

SendKeys('Some Text');
SendKeys(CHR(VK_Menu) + 'F');
SendKeys('S');
SendKeys('NewFile.txt');
SendKeys(CHR(VK_TAB) + CHR(VK_TAB) + CHR(VK_RETURN));
SendKeys(CHR(VK_MENU) + 'F');
SendKeys('X');

The Neil
0
 
LVL 5

Expert Comment

by:TheNeil
ID: 2588230
Here's a demo for Notepad. What it'll do is add some text ('Some text'), save it to a new file (newfile.txt), and then close the application

SendKeys('Some Text');
SendKeys(CHR(VK_Menu) + 'F');
SendKeys('S');
SendKeys('NewFile.txt');
SendKeys(CHR(VK_TAB) + CHR(VK_TAB) + CHR(VK_RETURN));
SendKeys(CHR(VK_MENU) + 'F');
SendKeys('X');

The Neil
0
 

Author Comment

by:evansj
ID: 2588263
To Lischke:
How would you load a TGA using your control?
0
 

Author Comment

by:evansj
ID: 2588334
To TheNeil: I tried sending the following, and all I got in the notepad windows was this FONewFile.txtO:

SendKeys(CHR(VK_Menu) + 'F');
SendKeys('O'); //open file
SendKeys('NewFile.txt'); //file name
SendKeys(CHR(VK_Menu) + 'O');//open file


0
 

Author Comment

by:evansj
ID: 2588361
To TheNeil: I tried sending the following, and all I got in the notepad windows was this FONewFile.txtO:

SendKeys(CHR(VK_Menu) + 'F');
SendKeys('O'); //open file
SendKeys('NewFile.txt'); //file name
SendKeys(CHR(VK_Menu) + 'O');//open file


0
 

Author Comment

by:evansj
ID: 2588385
To Lischke:
I put the reference to GraphicEx in the uses clause. I get an error when I try to load the TGA. It tells me unknown file extention (D3).
0
 
LVL 5

Expert Comment

by:TheNeil
ID: 2588394
Err...it works for me. What I've done in my sample app is ShellExecute Notepad, set a timer running (for 1 second) then when the timer fires do your SendKeys calls.

By the way, you'll need to add the following line after your SendKeys('NewFile.txt');

SendKeys(CHR(VK_RETURN));

Otherwise it won't 'click' the Ok button on the open dialog

The Neil
0
 

Author Comment

by:evansj
ID: 2588814
To TheNeil: I tried sending to Notepad ok. However, when I tried to send keystrokes to other apps (Paintshop Pro, Photoshop,etc) nothing happens. What about sending mouse clicks instead?
0
 

Author Comment

by:evansj
ID: 2590170
Adjusted points to 300
0
 

Author Comment

by:evansj
ID: 2590171
Common you gurus, help me out (if you can).
0
 

Author Comment

by:evansj
ID: 2590176
Ok runebj: What's your solution?
0
 
LVL 1

Expert Comment

by:runebj
ID: 2590457
Your first problem is to get the handle of the window that you want to send keystrokes to. You can get this by using a few different API calls.

The FindWindow API call is probably your best bet. I haven't used it too much so you'll need to do some experimenting with it. You have to know the windows class name or caption to use this funtion.

If you know the location of the window you can use WindowFromPoint.

Lastly you can use GetForegroundWindow to get the handle of the current window with focus.

Assuming that you have the window handle, this code (taken from my "virtual keyboard") will send keystrokes to any app:

type
  TKeypress   = record
                  CharCode: word;
                  Shift   : boolean;
                  Ctrl    : boolean;
                  Alt     : boolean;
                end;

procedure SendKey(pKeys: array of TKeypress);
var
  i          : integer;
  lThisThread: THandle;
  lThread    : THandle;
  lProcessID : cardinal;
begin
  //fForeground is the handle of the window you want to send keystrokes to
  if fForeground <> 0 then
  begin
    //fWinHandle is the handle of this window (sending from)
    if fWinHandle = 0 then
      fWinHandle := Parent.Handle;
    lThisThread := GetWindowThreadProcessID( fWinHandle, @lProcessID );
    lThread := GetWindowThreadProcessID( fForeground, @lProcessID );
    if AttachThreadInput( lThisThread, lThread, True ) then
    begin
      for i := 0 to Length( pKeys ) - 1 do
      begin
        if pKeys[ i ].Shift then Keybd_Event( VK_Shift, MapVirtualKey( VK_Shift, 0), 0, 0 );
        if pKeys[ i ].Ctrl then Keybd_Event( VK_Control, MapVirtualKey( VK_Control, 0), 0, 0 );
        if pKeys[ i ].Alt then Keybd_Event( VK_Menu, MapVirtualKey( VK_Menu, 0), 0, 0 );
        Keybd_Event( pKeys[ i ].CharCode, MapVirtualKey( pKeys[ i ].CharCode, 0 ), 0, 0 );
        Keybd_Event( pKeys[ i ].CharCode, MapVirtualKey( pKeys[ i ].CharCode, 0 ), KeyEventF_KeyUp, 0 );
        if pKeys[ i ].Alt then Keybd_Event( VK_Menu, MapVirtualKey( VK_Menu, 0), KeyEventF_KeyUp, 0 );
        if pKeys[ i ].Ctrl then Keybd_Event( VK_Control, MapVirtualKey( VK_Control, 0), KeyEventF_KeyUp, 0 );
        if pKeys[ i ].Shift then Keybd_Event( VK_Shift, MapVirtualKey( VK_Shift, 0), KeyEventF_KeyUp, 0 );
      end;
      AttachThreadInput( lThisThread, lThread, False );
    end;
  end;
end;

Before you call this function make sure to use "SetForegroundWindow( Handle )" in the calling procedure. This creates enough delay to let the foreground window get focus before you send keystrokes. If you have problems with the first keystrokes, then you need to add a delay of a few milliseconds before sending keystrokes.

The KeyPress structure contains the character code (virtual keykode) plus three booleans for shift, ctrl and alt. If you want to send Alt+F set keycode to "F" and the Alt flag to true.

Good luck
runebj
0
 

Author Comment

by:evansj
ID: 2599614
To Lischke:
I sucessfully loaded the GraphicEx library. I can load a TGA, descrease the color depth, and save it. How would you resize it to 640x480 and save it as a bitmap using your library?
0
 

Author Comment

by:evansj
ID: 2599646
To Lischke:
Never mind, I got it working. But does anyone know how to set the gamma correction on a bitmap?
0
 
LVL 10

Expert Comment

by:Lischke
ID: 2599930
Thank you, evansj, for the points. I've seldom seen such a respondible questioneer like you. Often the experts are discussion without ever getting a comment from the one who asked the question :-)

For image processing I still recommend Earl F. Glynns graphics page (http://www.efg2.com/lab/). I have not found code for gamma correction there, though, and I have to admit that I'm not so familiar with this as I am with other graphics stuff. After a short discussion with another programmer here in my company I think I got the basic idea.

Actually, gamma correction is a mapping from an input color value to a new output color value. As the range is always 0..255 you can easily use a looup table. Fill it with values from a curve (e.g. created by the user) and then iterate through all pixels and color parts and replace them accordingly. The usual way, though, is to use:

  O := pow(I, 1/gamma)

(with I and O in 0..1) to construct the LUT from. With this formula 0 maps always to 0 and 1 maps always to 1 (as so often seen with this little nifty square graph in various screen settings dialogs or Photoshop).

Ciao, Mike
0
 
LVL 1

Expert Comment

by:wmckie
ID: 2599963
Have a look at www.jps.net/gfody (I found this link from Earl F Glynn's reference lists) there are a couple of components and demos all with source, the Almost Photoshop demo has a gamma correction ability. This may not be exactly what you need but there may be some clues in the source.

Cheers - Walter McKie
0
 

Author Comment

by:evansj
ID: 2601868
To Lischke :
How would you optimize a 256 color reduction of a 24bit TGA? I can reduced the pixelformat to pf8bit, but the color optimization is windows standard 20 colors. I need some code snippets for using the GraphicEx to color optimize the palette before I save the reduced color bitmap (IYKWIM).
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

760 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

17 Experts available now in Live!

Get 1:1 Help Now