Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) KeySender

 

KeySender is a demonstration tool of Lallous' CSendKey class with which you can send virtual key presses to any window. For example, you can let KeySender type a notepad document for you (as seen in the screenshot).

 

 

 

 

 

 

External links

 

Technical facts

 

 

 

 

 

 

./ToolKeySender/ProjectKeySender.cpp

 

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("UnitFormKeySender.cpp", FormKeySender);
USEFORM("UnitFormAbout.cpp", FormAbout);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
        try
        {
                 Application->Initialize();
                 Application->Title = "KeySender";
                 Application->CreateForm(__classid(TFormKeySender), &FormKeySender);
                 Application->Run();
        }
        catch (Exception &exception)
        {
                 Application->ShowException(&exception);
        }
        catch (...)
        {
                 try
                 {
                         throw Exception("");
                 }
                 catch (Exception &exception)
                 {
                         Application->ShowException(&exception);
                 }
        }
        return 0;
}
//---------------------------------------------------------------------------

 

 

 

 

 

./ToolKeySender/UnitFormAbout.h

 

//---------------------------------------------------------------------------
/*
  KeySender, demonstration of CSendKey class by Lallous
  Copyright (C) 2010  Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// From http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#ifndef UnitFormAboutH
#define UnitFormAboutH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <Graphics.hpp>
//---------------------------------------------------------------------------
class TFormAbout : public TForm
{
__published: // IDE-managed Components
        TRichEdit *RichEditCopyrightKeySender;
        TImage *ImageAuthor;
        TPanel *PanelTop;
        TPanel *PanelTopRight;
        TPanel *PanelWebsite;
        TPanel *PanelLicence;
        TPanel *PanelDate;
        TPanel *PanelAuthor;
        TPanel *PanelCopyright;
        TPanel *PanelTitle;
        TPanel *PanelVersion;
        TRichEdit *RichEditCopyrightCSendKey;
private: // User declarations
public: // User declarations
        __fastcall TFormAbout(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TFormAbout *FormAbout;
//---------------------------------------------------------------------------
#endif

 

 

 

 

 

./ToolKeySender/UnitFormAbout.cpp

 

//---------------------------------------------------------------------------
/*
  KeySender, demonstration of CSendKey class by Lallous
  Copyright (C) 2010  Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// From http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include <boost/scoped_ptr.hpp>
#include "UnitFormAbout.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormAbout *FormAbout;
//---------------------------------------------------------------------------
__fastcall TFormAbout::TFormAbout(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

 

 

 

 

 

./ToolKeySender/UnitFormKeySender.h

 

//---------------------------------------------------------------------------
/*
  KeySender, demonstration of CSendKey class by Lallous
  Copyright (C) 2010  Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// From http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#ifndef UnitFormKeySenderH
#define UnitFormKeySenderH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TFormKeySender : public TForm
{
__published: // IDE-managed Components
        TButton *ButtonSend;
        TLabeledEdit *EditWindowTitle;
        TRichEdit *RichEdit;
        TButton *ButtonStartNotepad;
        TButton *ButtonAbout;
        void __fastcall ButtonSendClick(TObject *Sender);
        void __fastcall ButtonStartNotepadClick(TObject *Sender);
        void __fastcall ButtonAboutClick(TObject *Sender);
private: // User declarations
public: // User declarations
        __fastcall TFormKeySender(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TFormKeySender *FormKeySender;
//---------------------------------------------------------------------------
#endif

 

 

 

 

 

./ToolKeySender/UnitFormKeySender.cpp

 

//---------------------------------------------------------------------------
/*
  KeySender, demonstration of CSendKey class by Lallous
  Copyright (C) 2010  Richel Bilderbeek

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// From http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include <boost/scoped_ptr.hpp>
#include "UnitSendKeys.h"
#include "UnitFormKeySender.h"
#include "UnitFormAbout.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormKeySender *FormKeySender;
//---------------------------------------------------------------------------
__fastcall TFormKeySender::TFormKeySender(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TFormKeySender::ButtonSendClick(TObject *Sender)
{
  CSendKeys c;
  c.AppActivate(EditWindowTitle->Text.c_str());

  c.SetDelay(20);

  const int n = RichEdit->Lines->Count;
  for (int i=0; i!=n; ++i)
  {
    c.SendKeys(RichEdit->Lines->operator[](i).c_str());
    //Send an enter
    c.SendKeys("{enter}");
  }
}
//---------------------------------------------------------------------------
void __fastcall TFormKeySender::ButtonStartNotepadClick(TObject *Sender)
{
  CSendKeys c;
  c.SendKeys("{DELAY=100}@rnotepad~");
  EditWindowTitle->Text = "Untitled - Notepad";
}
//---------------------------------------------------------------------------

void __fastcall TFormKeySender::ButtonAboutClick(TObject *Sender)
{
  boost::scoped_ptr<TFormAbout> f(new TFormAbout(0));
  f->ShowModal();
}
//---------------------------------------------------------------------------

 

 

 

 

 

./ToolKeySender/UnitSendKeys.h

 

//---------------------------------------------------------------------------
#ifndef UnitSendKeysH
#define UnitSendKeysH
//---------------------------------------------------------------------------
/* Copyright (c) 2004 lallous <lallousx86@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
//---------------------------------------------------------------------------
// Modified by Bilderbikkel (RJCB)
// http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------
#include <windows.h>
#include <tchar.h>

class CSendKeys
{
private:
  bool m_bWait, m_bUsingParens, m_bShiftDown, m_bAltDown, m_bControlDown, m_bWinDown;
  DWORD  m_nDelayAlways, m_nDelayNow;

  //RJCB: Added static member variable
  static HWND enumwindowsProc_hwnd;
  //RJCB: Added static member variable
  static LPARAM enumwindowsProc_lParam;
  //RJCB: Removed the arguments
  //static BOOL CALLBACK enumwindowsProc(HWND hwnd, LPARAM lParam);
  //RJCB: Use static member variables instead
  static BOOL CALLBACK enumwindowsProc();

  void   CarryDelay();

  typedef BYTE KEYBOARDSTATE_t[256];
  struct enumwindow_t
  {
    LPTSTR str;
    HWND hwnd;
  };

  struct key_desc_t
  {
    LPCTSTR keyName;
    BYTE VKey;
    bool normalkey; // a normal character or a VKEY ?
  };

  enum
  {
    MaxSendKeysRecs  = 71,
    MaxExtendedVKeys = 10
  };

  /*
  Reference: VkKeyScan() / MSDN
  Bit Meaning
  --- --------
  1   Either SHIFT key is pressed.
  2   Either CTRL key is pressed.
  4   Either ALT key is pressed.
  8   The Hankaku key is pressed
  16  Reserved (defined by the keyboard layout driver).
  32  Reserved (defined by the keyboard layout driver).
  */
  static const WORD VKKEYSCANSHIFTON;
  static const WORD VKKEYSCANCTRLON;
  static const WORD VKKEYSCANALTON;
  static const WORD INVALIDKEY;

  static key_desc_t KeyNames[MaxSendKeysRecs];
  static const BYTE ExtendedVKeys[MaxExtendedVKeys];

  static bool BitSet(BYTE BitTable, UINT BitMask);

  void PopUpShiftKeys();

  static bool IsVkExtended(BYTE VKey);
  void SendKeyUp(BYTE VKey);
  void SendKeyDown(BYTE VKey, WORD NumTimes, bool GenUpMsg, bool bDelay = false);
  void SendKey(WORD MKey, WORD NumTimes, bool GenDownMsg);
  static WORD StringToVKey(LPCTSTR KeyString, int &idx);
  void KeyboardEvent(BYTE VKey, BYTE ScanCode, LONG Flags);

public:

  bool SendKeys(LPCTSTR KeysString, bool Wait = false);
  static bool AppActivate(HWND wnd);
  static bool AppActivate(LPCTSTR WindowTitle, LPCTSTR WindowClass = 0);
  void SetDelay(const DWORD delay) { m_nDelayAlways = delay; }
  CSendKeys();
};
//---------------------------------------------------------------------------
#endif

 

 

 

 

 

./ToolKeySender/UnitSendKeys.cpp

 

//---------------------------------------------------------------------------
#pragma hdrstop

#include "UnitSendKeys.h"
//---------------------------------------------------------------------------
/* Copyright (c) 2004 lallous <lallousx86@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.

The Original SendKeys copyright info
---------------------------------------
SendKeys (sndkeys32.pas) routine for 32-bit Delphi.
Written by Ken Henderson
Copyright (c) 1995 Ken Henderson <khen@compuserve.com>

History
----------
04/19/2004
  * Initial version development
04/21/2004
  * Added number of times specifier to special keys
  * Added {BEEP X Y}
  * Added {APPACTIVATE WindowTitle}
  * Added CarryDelay() and now delay works properly with all keys
  * Added SetDelay() member function
  * Fixed code in AppActivate that allowed to pass both NULL windowTitle/windowClass

05/21/2004
  * Fixed a bug in StringToVKey() that caused the search for RIGHTPAREN to be matched as RIGHT
  * Adjusted code so it compiles w/ VC6
05/24/2004
  * Added unicode support

Todo
-------
* perhaps add mousecontrol: mousemove+mouse clicks
* allow sending of normal keys multiple times as: {a 10}
*/

//---------------------------------------------------------------------------
// Modified by Bilderbikkel (RJCB)
// http://www.richelbilderbeek.nl
//---------------------------------------------------------------------------

//RJCB: Added static member variable
HWND CSendKeys::enumwindowsProc_hwnd = 0;
//RJCB: Added static member variable
LPARAM CSendKeys::enumwindowsProc_lParam;

const WORD CSendKeys::VKKEYSCANSHIFTON = 0x01;
const WORD CSendKeys::VKKEYSCANCTRLON  = 0x02;
const WORD CSendKeys::VKKEYSCANALTON   = 0x04;
const WORD CSendKeys::INVALIDKEY       = 0xFFFF;

const BYTE CSendKeys::ExtendedVKeys[MaxExtendedVKeys] =
{
    VK_UP,
    VK_DOWN,
    VK_LEFT,
    VK_RIGHT,
    VK_HOME,
    VK_END,
    VK_PRIOR, // PgUp
    VK_NEXT,  //  PgDn
    VK_INSERT,
    VK_DELETE
};

CSendKeys::CSendKeys()
{
  m_nDelayNow = m_nDelayAlways = 0;
}

// Delphi port regexps:
// ---------------------
// search: .+Name:'([^']+)'.+vkey:([^\)]+)\)
// replace: {"\1", \2}
//
// **If you add to this list, you must be sure to keep it sorted alphabetically
// by Name because a binary search routine is used to scan it.**
//
CSendKeys::key_desc_t CSendKeys::KeyNames[CSendKeys::MaxSendKeysRecs] =
{
  {_T("ADD"), VK_ADD},
  {_T("APPS"), VK_APPS},
  {_T("AT"), '@', true},
  {_T("BACKSPACE"), VK_BACK},
  {_T("BKSP"), VK_BACK},
  {_T("BREAK"), VK_CANCEL},
  {_T("BS"), VK_BACK},
  {_T("CAPSLOCK"), VK_CAPITAL},
  {_T("CARET"), '^', true},
  {_T("CLEAR"), VK_CLEAR},
  {_T("DECIMAL"), VK_DECIMAL},
  {_T("DEL"), VK_DELETE},
  {_T("DELETE"), VK_DELETE},
  {_T("DIVIDE"), VK_DIVIDE},
  {_T("DOWN"), VK_DOWN},
  {_T("END"), VK_END},
  {_T("ENTER"), VK_RETURN},
  {_T("ESC"), VK_ESCAPE},
  {_T("ESCAPE"), VK_ESCAPE},
  {_T("F1"), VK_F1},
  {_T("F10"), VK_F10},
  {_T("F11"), VK_F11},
  {_T("F12"), VK_F12},
  {_T("F13"), VK_F13},
  {_T("F14"), VK_F14},
  {_T("F15"), VK_F15},
  {_T("F16"), VK_F16},
  {_T("F2"), VK_F2},
  {_T("F3"), VK_F3},
  {_T("F4"), VK_F4},
  {_T("F5"), VK_F5},
  {_T("F6"), VK_F6},
  {_T("F7"), VK_F7},
  {_T("F8"), VK_F8},
  {_T("F9"), VK_F9},
  {_T("HELP"), VK_HELP},
  {_T("HOME"), VK_HOME},
  {_T("INS"), VK_INSERT},
  {_T("LEFT"), VK_LEFT},
  {_T("LEFTBRACE"), '{', true},
  {_T("LEFTPAREN"), '(', true},
  {_T("LWIN"), VK_LWIN},
  {_T("MULTIPLY"), VK_MULTIPLY},
  {_T("NUMLOCK"), VK_NUMLOCK},
  {_T("NUMPAD0"), VK_NUMPAD0},
  {_T("NUMPAD1"), VK_NUMPAD1},
  {_T("NUMPAD2"), VK_NUMPAD2},
  {_T("NUMPAD3"), VK_NUMPAD3},
  {_T("NUMPAD4"), VK_NUMPAD4},
  {_T("NUMPAD5"), VK_NUMPAD5},
  {_T("NUMPAD6"), VK_NUMPAD6},
  {_T("NUMPAD7"), VK_NUMPAD7},
  {_T("NUMPAD8"), VK_NUMPAD8},
  {_T("NUMPAD9"), VK_NUMPAD9},
  {_T("PERCENT"), '%', true},
  {_T("PGDN"), VK_NEXT},
  {_T("PGUP"), VK_PRIOR},
  {_T("PLUS"), '+', true},
  {_T("PRTSC"), VK_PRINT},
  {_T("RIGHT"), VK_RIGHT},
  {_T("RIGHTBRACE"), '}', true},
  {_T("RIGHTPAREN"), ')', true},
  {_T("RWIN"), VK_RWIN},
  {_T("SCROLL"), VK_SCROLL},
  {_T("SEPARATOR"), VK_SEPARATOR},
  {_T("SNAPSHOT"), VK_SNAPSHOT},
  {_T("SUBTRACT"), VK_SUBTRACT},
  {_T("TAB"), VK_TAB},
  {_T("TILDE"), '~', true},
  {_T("UP"), VK_UP},
  {_T("WIN"), VK_LWIN}
};


// calls keybd_event() and waits, if needed, till the sent input is processed
void CSendKeys::KeyboardEvent(BYTE VKey, BYTE ScanCode, LONG Flags)
{
  MSG KeyboardMsg;

  keybd_event(VKey, ScanCode, Flags, 0);

  if (m_bWait)
  {
    while (::PeekMessage(&KeyboardMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE))
    {
      ::TranslateMessage(&KeyboardMsg);
      ::DispatchMessage(&KeyboardMsg);
    }
  }
}

// Checks whether the specified VKey is an extended key or not
bool CSendKeys::IsVkExtended(BYTE VKey)
{
  for (int i=0;i<MaxExtendedVKeys;i++)
  {
    if (ExtendedVKeys[i] == VKey)
      return true;
  }
  return false;
}

// Generates KEYUP
void CSendKeys::SendKeyUp(BYTE VKey)
{
  BYTE ScanCode = LOBYTE(::MapVirtualKey(VKey, 0));

  KeyboardEvent(VKey,
                ScanCode,
                KEYEVENTF_KEYUP | (IsVkExtended(VKey) ? KEYEVENTF_EXTENDEDKEY : 0));
}

void CSendKeys::SendKeyDown(BYTE VKey, WORD NumTimes, bool GenUpMsg, bool bDelay)
{
  WORD Cnt = 0;
  BYTE ScanCode = 0;
  bool NumState = false;

  if (VKey == VK_NUMLOCK)
  {
    DWORD dwVersion = ::GetVersion();

    for (Cnt=1; Cnt<=NumTimes; Cnt++)
    {
      if (bDelay)
        CarryDelay();
      // snippet based on:
      // http://www.codeproject.com/cpp/togglekeys.asp
      if (dwVersion < 0x80000000)
      {
        ::keybd_event(VKey, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
        ::keybd_event(VKey, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
      }
      else
      {
        // Win98 and later
        if ( ((DWORD)(HIBYTE(LOWORD(dwVersion))) >= 10) )
        {
          // Define _WIN32_WINNT > 0x0400
          // to compile
          INPUT input[2] = {0};
          input[0].type = input[1].type = INPUT_KEYBOARD;
          input[0].ki.wVk = input[1].ki.wVk = VKey;
          input[1].ki.dwFlags = KEYEVENTF_KEYUP;
          ::SendInput(sizeof(input) / sizeof(INPUT), input, sizeof(INPUT));
        }
        // Win95
        else
        {
          KEYBOARDSTATE_t KeyboardState;
          NumState = GetKeyState(VK_NUMLOCK) & 1 ? true : false;
          GetKeyboardState(&KeyboardState[0]);
          if (NumState)
            KeyboardState[VK_NUMLOCK] &= ~1;
          else
            KeyboardState[VK_NUMLOCK] |= 1;

          SetKeyboardState(&KeyboardState[0]);
        }
      }
    }
    return;
  }

  // Get scancode
  ScanCode = LOBYTE(::MapVirtualKey(VKey, 0));

  // Send keys
  for (Cnt=1; Cnt<=NumTimes; Cnt++)
  {
    // Carry needed delay ?
    if (bDelay)
      CarryDelay();

    KeyboardEvent(VKey, ScanCode, IsVkExtended(VKey) ? KEYEVENTF_EXTENDEDKEY : 0);
    if (GenUpMsg)
      KeyboardEvent(VKey, ScanCode, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP);
  }
}

// Checks whether a bit is set
bool CSendKeys::BitSet(BYTE BitTable, UINT BitMask)
{
  return BitTable & BitMask ? true : false;
}

// Sends a single key
void CSendKeys::SendKey(WORD MKey, WORD NumTimes, bool GenDownMsg)
{
  // Send appropriate shift keys associated with the given VKey
  if (BitSet(HIBYTE(MKey), VKKEYSCANSHIFTON))
    SendKeyDown(VK_SHIFT, 1, false);

  if (BitSet(HIBYTE(MKey), VKKEYSCANCTRLON))
    SendKeyDown(VK_CONTROL, 1, false);

  if (BitSet(HIBYTE(MKey), VKKEYSCANALTON))
    SendKeyDown(VK_MENU, 1, false);

  // Send the actual VKey
  SendKeyDown(LOBYTE(MKey), NumTimes, GenDownMsg, true);

  // toggle up shift keys
  if (BitSet(HIBYTE(MKey), VKKEYSCANSHIFTON))
    SendKeyUp(VK_SHIFT);

  if (BitSet(HIBYTE(MKey), VKKEYSCANCTRLON))
    SendKeyUp(VK_CONTROL);

  if (BitSet(HIBYTE(MKey), VKKEYSCANALTON))
    SendKeyUp(VK_MENU);
}

// Implements a simple binary search to locate special key name strings
WORD CSendKeys::StringToVKey(LPCTSTR KeyString, int &idx)
{
  bool Found = false, Collided;
  int  Bottom = 0,
       Top = MaxSendKeysRecs,
       Middle = (Bottom + Top) / 2;
  WORD retval = INVALIDKEY;

  idx    = -1;

  do
  {
    Collided = (Bottom == Middle) || (Top == Middle);
    int cmp = _tcsnicmp(KeyNames[Middle].keyName, KeyString, _tcslen(KeyString));
    if (cmp == 0)
    {
      Found = true;
      retval = KeyNames[Middle].VKey;
      idx    = Middle;
      break;
    }
    else
    {
      if (cmp < 0)
        Bottom = Middle;
      else
        Top = Middle;
      Middle = (Bottom + Top) / 2;
    }
  } while (!(Found || Collided));

  return retval;
}

// Releases all shift keys (keys that can be depressed while other keys are being pressed
// If we are in a modifier group this function does nothing
void CSendKeys::PopUpShiftKeys()
{
  if (!m_bUsingParens)
  {
    if (m_bShiftDown)
      SendKeyUp(VK_SHIFT);
    if (m_bControlDown)
      SendKeyUp(VK_CONTROL);
    if (m_bAltDown)
      SendKeyUp(VK_MENU);
    if (m_bWinDown)
      SendKeyUp(VK_LWIN);
    m_bWinDown = m_bShiftDown = m_bControlDown = m_bAltDown = false;
  }
}

// Sends a key string
bool CSendKeys::SendKeys(LPCTSTR KeysString, bool Wait)
{
  WORD MKey, NumTimes;
  TCHAR KeyString[300] = {0};
  bool retval  = false;
  int  keyIdx;

  LPTSTR pKey = (LPTSTR) KeysString;
  TCHAR  ch;

  m_bWait = Wait;

  m_bWinDown = m_bShiftDown = m_bControlDown = m_bAltDown = m_bUsingParens = false;

  while (ch = *pKey)
  {
    switch (ch)
    {
    // begin modifier group
    case _TXCHAR('('):
      m_bUsingParens = true;
      break;

    // end modifier group
    case _TXCHAR(')'):
      m_bUsingParens = false;
      PopUpShiftKeys(); // pop all shift keys when we finish a modifier group close
      break;

    // ALT key
    case _TXCHAR('%'):
      m_bAltDown = true;
      SendKeyDown(VK_MENU, 1, false);
      break;

    // SHIFT key
    case _TXCHAR('+'):
      m_bShiftDown = true;
      SendKeyDown(VK_SHIFT, 1, false);
      break;

    // CTRL key
    case _TXCHAR('^'):
      m_bControlDown = true;
      SendKeyDown(VK_CONTROL, 1, false);
      break;

    // WINKEY (Left-WinKey)
    case '@':
      m_bWinDown = true;
      SendKeyDown(VK_LWIN, 1, false);
      break;

    // enter
    case _TXCHAR('~'):
      SendKeyDown(VK_RETURN, 1, true);
      PopUpShiftKeys();
      break;

    // begin special keys
    case _TXCHAR('{'):
      {
        LPTSTR p = pKey+1; // skip past the beginning '{'
        size_t t;

        // find end of close
        while (*p && *p != _TXCHAR('}'))
          p++;

        t = p - pKey;
        // special key definition too big?
        if (t > sizeof(KeyString))
          return false;

        // Take this KeyString into local buffer
        _tcsncpy(KeyString, pKey+1, t);

        KeyString[t-1] = _TXCHAR('\0');
        keyIdx = -1;

        pKey += t; // skip to next keystring

        // Invalidate key
        MKey = INVALIDKEY;

        // sending arbitrary vkeys?
        if (_tcsnicmp(KeyString, _T("VKEY"), 4) == 0)
        {
          p = KeyString + 4;
          MKey = _ttoi(p);
        }
        else if (_tcsnicmp(KeyString, _T("BEEP"), 4) == 0)
        {
          p = KeyString + 4 + 1;
          LPTSTR p1 = p;
          DWORD frequency, delay;

          if ((p1 = _tcsstr(p, _T(" "))) != NULL)
          {
            *p1++ = _TXCHAR('\0');
            frequency = _ttoi(p);
            delay = _ttoi(p1);
            ::Beep(frequency, delay);
          }
        }
        // Should activate a window?
        else if (_tcsnicmp(KeyString, _T("APPACTIVATE"), 11) == 0)
        {
          p = KeyString + 11 + 1;
          AppActivate(p);
        }
        // want to send/set delay?
        else if (_tcsnicmp(KeyString, _T("DELAY"), 5) == 0)
        {
          // Advance to parameters
          p = KeyString + 5;
          // set "sleep factor"
          if (*p == _TXCHAR('='))
            m_nDelayAlways = _ttoi(p + 1); // Take number after the '=' character
          else
            // set "sleep now"
            m_nDelayNow = _ttoi(p);
        }
        // not command special keys, then process as keystring to VKey
        else
        {
          MKey = StringToVKey(KeyString, keyIdx);
          // Key found in table
          if (keyIdx != -1)
          {
            NumTimes = 1;

            // Does the key string have also count specifier?
            t = _tcslen(KeyNames[keyIdx].keyName);
            if (_tcslen(KeyString) > t)
            {
              p = KeyString + t;
              // Take the specified number of times
              NumTimes = _ttoi(p);
            }

            if (KeyNames[keyIdx].normalkey)
              MKey = ::VkKeyScan(KeyNames[keyIdx].VKey);
          }
        }

        // A valid key to send?
        if (MKey != INVALIDKEY)
        {
          SendKey(MKey, NumTimes, true);
          PopUpShiftKeys();
        }
      }
      break;

      // a normal key was pressed
    default:
      // Get the VKey from the key
      MKey = ::VkKeyScan(ch);
      SendKey(MKey, 1, true);
      PopUpShiftKeys();
    }
    pKey++;
  }

  m_bUsingParens = false;
  PopUpShiftKeys();
  return true;
}
//---------------------------------------------------------------------------
bool CSendKeys::AppActivate(HWND wnd)
{
  if (wnd == NULL)
    return false;

  ::SendMessage(wnd, WM_SYSCOMMAND, SC_HOTKEY, (LPARAM) wnd);
  ::SendMessage(wnd, WM_SYSCOMMAND, SC_RESTORE, (LPARAM) wnd);
  
  ::ShowWindow(wnd, SW_SHOW);
  ::SetForegroundWindow(wnd);
  ::SetFocus(wnd);

  return true;
}

//RJCB: Removed the arguments
//BOOL CALLBACK CSendKeys::enumwindowsProc(HWND hwnd, LPARAM lParam)
BOOL CALLBACK CSendKeys::enumwindowsProc()
{
  //RJCB: Removed argument
  //enumwindow_t *t = (enumwindow_t *) lParam;
  //RJCB: Now use member variable instead
  enumwindow_t *t = (enumwindow_t *) enumwindowsProc_lParam;

  LPTSTR wtitle = 0, wclass = 0, str = t->str;

  if (!*str)
    str++;
  else
  {
    wtitle = str;
    str += _tcslen(str) + 1;
  }

  if (*str)
    wclass = str;

  bool bMatch(false);

  if (wclass)
  {
    TCHAR szClass[300];
    //RJCB: Remove member function argument
    //if (::GetClassName(hwnd, szClass, sizeof(szClass)))
    //RJCB: Use member variable instead
    if (::GetClassName(enumwindowsProc_hwnd, szClass, sizeof(szClass)))
      bMatch |= (_tcsstr(szClass, wclass) != 0);
  }

  if (wtitle)
  {
    TCHAR szTitle[300];
    //RJCB: Remove member function argument
    //if (::GetWindowText(hwnd, szTitle, sizeof(szTitle)))
    //RJCB: Use member variable instead
    if (::GetWindowText(enumwindowsProc_hwnd, szTitle, sizeof(szTitle)))
      bMatch |= (_tcsstr(szTitle, wtitle) != 0);
  }

  if (bMatch)
  {
    //RJCB: Removed member function argument hwnd
    //t->hwnd = hwnd;
    //RJCB: Now use member variable instead
    t->hwnd = enumwindowsProc_hwnd;
    return false;
  }
  return true;
}

// Searchs and activates a window given its title or class name
bool CSendKeys::AppActivate(LPCTSTR WindowTitle, LPCTSTR WindowClass)
{
  HWND w;

  w = ::FindWindow(WindowClass, WindowTitle);
  if (w == NULL)
  {
    // Must specify at least a criteria
    if (WindowTitle == NULL && WindowClass == NULL)
      return false;

    // << Code to serialize the windowtitle/windowclass in order to send to EnumWindowProc()
    size_t l1(0), l2(0);
    if (WindowTitle)
      l1 = _tcslen(WindowTitle);
    if (WindowClass)
      l2 = _tcslen(WindowClass);

    LPTSTR titleclass = new TCHAR [l1 + l2 + 5];

    memset(titleclass, '\0', l1+l2+5);

    if (WindowTitle)
      _tcscpy(titleclass, WindowTitle);

    titleclass[l1] = 0;

    if (WindowClass)
      _tcscpy(titleclass+l1+1, WindowClass);

    // >>

    enumwindow_t t;

    t.hwnd = NULL;
    t.str  = titleclass;
    //RJCB: [C++ Error] SendKeys.cpp(626): E2034 Cannot convert 'int (__stdcall *)(void *,long)' to 'int (__stdcall *)()'
    //RJCB: Removed the arguments
    //::EnumWindows(enumwindowsProc, (LPARAM) & t);
    //RJCB: Initializes static member variables
    CSendKeys::enumwindowsProc_lParam = (LPARAM) & t;
    CSendKeys::enumwindowsProc_hwnd = 0; //?
    //RJCB: Use static member variables instead
    ::EnumWindows(enumwindowsProc, (LPARAM) & t);
    w = t.hwnd;
    delete [] titleclass;
  }

  if (w == NULL)
    return false;

  return AppActivate(w);
}

// Carries the required delay and clears the m_nDelaynow value
void CSendKeys::CarryDelay()
{
  // Should we delay once?
  if (!m_nDelayNow)
    // should we delay always?
    m_nDelayNow = m_nDelayAlways;

  // No delay specified?
  if (m_nDelayNow)
    ::Sleep(m_nDelayNow); //::Beep(100, m_nDelayNow);

  // clear SleepNow
  m_nDelayNow = 0;
}
//---------------------------------------------------------------------------
#pragma package(smart_init)

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml