Browse Source

Add custum keys to paste and copy

git-svn-id: svn://svn.code.sf.net/p/ditto-cp/code/trunk@400 595ec19a-5cb4-439b-94a8-42fb3063c22c
sabrogden 18 years ago
parent
commit
6a7591bb36
9 changed files with 890 additions and 36 deletions
  1. 19 19
      CP_Main.cpp
  2. 8 2
      CP_Main.vcproj
  3. 26 0
      Misc.cpp
  4. 1 0
      Misc.h
  5. 62 10
      Options.cpp
  6. 8 2
      Options.h
  7. 3 3
      ProcessPaste.cpp
  8. 687 0
      SendKeys.cpp
  9. 76 0
      SendKeys.h

+ 19 - 19
CP_Main.cpp

@@ -16,6 +16,7 @@
 #include "HyperLink.h"
 #include "OptionsSheet.h"
 #include "DittoCopyBuffer.h"
+#include "SendKeys.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
@@ -444,7 +445,7 @@ void CCP_MainApp::SendPaste(bool bActivateTarget)
 	keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
 	keybd_event(VK_MENU, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
 	keybd_event(VK_LWIN, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
-	
+
 	Sleep(50);
 
 	if(bActivateTarget && !ActivateTarget())
@@ -455,19 +456,17 @@ void CCP_MainApp::SendPaste(bool bActivateTarget)
 
 	PumpMessageEx();
 
-	Sleep(1);
-
-	keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
-	keybd_event('V', 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
-
-	Sleep(1);
-
-	PumpMessageEx();
+	CString csPasteToApp = GetProcessName(m_hTargetWnd);
+	CString csPasteString = g_Opt.GetPasteString(csPasteToApp);
 
-	keybd_event('V', 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
-	keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+	CString csMessage;
+	csMessage = "Sending paste to app " + csPasteToApp + " key stroke " + csPasteString;
+	Log(csMessage);
 
-	PumpMessageEx();
+	CSendKeys send;
+	//CString cs("^v");
+	//CString cs("%e{DELAY=50}p");
+	send.SendKeys(csPasteString);
 }
 
 // sends Ctrl-V to the TargetWnd
@@ -494,15 +493,16 @@ void CCP_MainApp::SendCopy()
 
 	PumpMessageEx();
 
-	Sleep(50);
+	CString csPasteToApp = GetProcessName(m_hTargetWnd);
+	CString csPasteString = g_Opt.GetCopyString(csPasteToApp);
 
-	keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
-	keybd_event('C', 0, KEYEVENTF_EXTENDEDKEY | 0, 0);
+	CString csMessage;
+	csMessage = "Sending copy to app " + csPasteToApp + " key stroke " + csPasteString;
+	Log(csMessage);
 
-	Sleep(50);
-
-	keybd_event('C', 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
-	keybd_event(VK_CONTROL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
+	CSendKeys send;
+	//CString cs("^c");
+	send.SendKeys(csPasteString);
 }
 
 // sends Ctrl-X to the TargetWnd

+ 8 - 2
CP_Main.vcproj

@@ -1829,6 +1829,9 @@
 			<File
 				RelativePath=".\SaveAnimation.cpp">
 			</File>
+			<File
+				RelativePath=".\SendKeys.cpp">
+			</File>
 			<File
 				RelativePath="StdAfx.cpp">
 				<FileConfiguration
@@ -2822,6 +2825,9 @@
 			<File
 				RelativePath=".\SaveAnimation.h">
 			</File>
+			<File
+				RelativePath=".\SendKeys.h">
+			</File>
 			<File
 				RelativePath="ServerDefines.h">
 			</File>
@@ -2987,7 +2993,7 @@
 				</FileConfiguration>
 			</File>
 			<File
-				RelativePath="Debug\focus.dll">
+				RelativePath="Release\focus.dll">
 				<FileConfiguration
 					Name="Release|Win32"
 					ExcludedFromBuild="TRUE">
@@ -3014,7 +3020,7 @@
 				</FileConfiguration>
 			</File>
 			<File
-				RelativePath="Release\focus.dll">
+				RelativePath="Debug\focus.dll">
 				<FileConfiguration
 					Name="Release|Win32"
 					ExcludedFromBuild="TRUE">

+ 26 - 0
Misc.cpp

@@ -4,6 +4,7 @@
 #include "OptionsSheet.h"
 #include "TextConvert.h"
 #include "AlphaBlend.h"
+#include "Tlhelp32.h"
 
 // Debug Functions
 
@@ -1498,4 +1499,29 @@ __int64 GetLastWriteTime(const CString &csFile)
 	}
 
 	return nLastWrite;
+}
+
+CString GetProcessName(HWND hWnd) 
+{
+	DWORD Id;
+	GetWindowThreadProcessId(hWnd, &Id);
+
+	PROCESSENTRY32 processEntry = { 0 };
+
+	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+	processEntry.dwSize = sizeof(PROCESSENTRY32);
+
+	if (Process32First(hSnapShot, &processEntry)) 
+	{
+		do 
+		{
+			if (processEntry.th32ProcessID == Id) 
+			{
+				return processEntry.szExeFile;
+			}
+		} while(Process32Next(hSnapShot, &processEntry));
+	}
+	CloseHandle(hSnapShot);
+
+	return "";
 }

+ 1 - 0
Misc.h

@@ -97,6 +97,7 @@ CString GetFileName(CString csFileName);
 BOOL EnsureWindowVisible(CRect *pcrRect);
 
 __int64 GetLastWriteTime(const CString &csFile);
+CString GetProcessName(HWND hWnd);
 
 //Message to the main window to show icon or not
 #define WM_SHOW_TRAY_ICON		WM_USER + 200

+ 62 - 10
Options.cpp

@@ -342,17 +342,29 @@ CString CGetSetOptions::GetIniFileName(bool bLocalIniFile)
 	return csPath;
 }
 
-long CGetSetOptions::GetProfileLong(CString csName, long lDefaultValue)
+long CGetSetOptions::GetProfileLong(CString csName, long lDefaultValue, CString csNewPath)
 {
 	if(m_bFromIni && !m_bInConversion)
 	{
-		return GetPrivateProfileInt(_T("Ditto"), csName, lDefaultValue, m_csIniFileName);
+		CString csApp(_T("Ditto"));
+
+		if(csNewPath.IsEmpty() == FALSE)
+		{
+			csApp += "-" + csNewPath;
+		}
+
+		return GetPrivateProfileInt(csApp, csName, lDefaultValue, m_csIniFileName);
+	}
+
+	CString csPath(_T(REG_PATH));
+	if(csNewPath.IsEmpty() == FALSE)
+	{
+		csPath += "\\" + csNewPath;
 	}
 
 	HKEY hkKey;
 
-	long lResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T(REG_PATH),
-		NULL, KEY_READ, &hkKey);
+	long lResult = RegOpenKeyEx(HKEY_CURRENT_USER, csPath, NULL, KEY_READ, &hkKey);
 
 	if(lResult != ERROR_SUCCESS)
 		return lDefaultValue;
@@ -371,20 +383,31 @@ long CGetSetOptions::GetProfileLong(CString csName, long lDefaultValue)
 	return lDefaultValue;
 }
 
-CString CGetSetOptions::GetProfileString(CString csName, CString csDefault)
+CString CGetSetOptions::GetProfileString(CString csName, CString csDefault, CString csNewPath)
 {
 	if(m_bFromIni && !m_bInConversion)
 	{
+		CString csApp(_T("Ditto"));
+
+		if(csNewPath.IsEmpty() == FALSE)
+		{
+			csApp += "-" + csNewPath;
+		}
+
 		TCHAR cString[MAX_PATH];
-		GetPrivateProfileString(_T("Ditto"), csName, csDefault, cString, sizeof(cString), m_csIniFileName);
+		GetPrivateProfileString(csApp, csName, csDefault, cString, sizeof(cString), m_csIniFileName);
 
 		return cString;
 	}
 
-	HKEY hkKey;
+	CString csPath(_T(REG_PATH));
+	if(csNewPath.IsEmpty() == FALSE)
+	{
+		csPath += "\\" + csNewPath;
+	}
 
-	long lResult = RegOpenKeyEx(HKEY_CURRENT_USER, _T(REG_PATH),
-		NULL, KEY_READ, &hkKey);
+	HKEY hkKey;
+	long lResult = RegOpenKeyEx(HKEY_CURRENT_USER, csPath, NULL, KEY_READ, &hkKey);
 
 	TCHAR szString[256];
 	ZeroMemory(szString, sizeof(szString));
@@ -1781,4 +1804,33 @@ void CGetSetOptions::SetGroupWndPinned(bool bSet)
 bool CGetSetOptions::GetAllowFriends()
 {
 	return (GetProfileLong("AllowFriends", TRUE) == TRUE);
-}
+}
+
+CString CGetSetOptions::GetPasteString(CString csAppName)
+{
+	CString csPasteString = GetProfileString(csAppName, _T(""), _T("PasteStrings"));
+	if(csPasteString.IsEmpty())
+		return GetDefaultPasteString();
+
+	return csPasteString;
+}
+
+CString CGetSetOptions::GetDefaultPasteString()
+{
+	return GetProfileString(_T("DefaultPasteString"), _T("^v"));
+}
+
+CString CGetSetOptions::GetCopyString(CString csAppName)
+{
+	CString csCopyString = GetProfileString(csAppName, _T(""), _T("CopyStrings"));
+	if(csCopyString.IsEmpty())
+		return GetDefaultCopyString();
+
+	return csCopyString;
+}
+
+CString CGetSetOptions::GetDefaultCopyString()
+{
+	return GetProfileString(_T("DefaultCopyString"), _T("^c"));
+}
+

+ 8 - 2
Options.h

@@ -84,9 +84,9 @@ public:
 	static BOOL GetProfileFont(CString csSection, LOGFONT &font);
 
 	static BOOL SetProfileLong(CString csName, long lValue);
-	static long GetProfileLong(CString csName, long lDefaultValue = -1);
+	static long GetProfileLong(CString csName, long lDefaultValue = -1, CString csNewPath = _T(""));
 
-	static CString GetProfileString(CString csName, CString csDefault);
+	static CString GetProfileString(CString csName, CString csDefault, CString csNewPath = _T(""));
 	static BOOL	SetProfileString(CString csName, CString csValue);
 
 	static LPVOID GetProfileData(CString csName, DWORD &dwLength);
@@ -370,6 +370,12 @@ public:
 	static bool GetGroupWndPinned();
 	static void SetGroupWndPinned(bool bSet);
 
+	static CString GetPasteString(CString csAppName);
+	static CString GetDefaultPasteString();
+
+	static CString GetCopyString(CString csAppName);
+	static CString GetDefaultCopyString();
+
 	static bool GetAllowFriends();
 };
 

+ 3 - 3
ProcessPaste.cpp

@@ -56,17 +56,17 @@ BOOL CProcessPaste::DoPaste()
 		// when new data is put on the Clipboard
 		m_pOle = NULL; // m_pOle should not be accessed past this point
 
-#ifndef _DEBUG
+//#ifndef _DEBUG
 		if(m_bSendPaste)
 		{
 			theApp.SendPaste(m_bActivateTarget);
 		}
-#else
+//#else
 		if(m_bActivateTarget)
 		{
 			theApp.ActivateTarget();
 		}
-#endif
+//#endif
 		return TRUE;
 	}
 	return FALSE;

+ 687 - 0
SendKeys.cpp

@@ -0,0 +1,687 @@
+#include "stdafx.h"
+#include "sendkeys.h"
+
+/* 
+* ----------------------------------------------------------------------------- 
+* Copyright (c) 2004 lallous <[email protected]>
+* 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 <[email protected]>
+
+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() method
+  * 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}
+
+*/
+
+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;
+}
+
+BOOL CALLBACK CSendKeys::enumwindowsProc(HWND hwnd, LPARAM lParam)
+{
+  enumwindow_t *t = (enumwindow_t *) 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];
+    if (::GetClassName(hwnd, szClass, sizeof(szClass)))
+      bMatch |= (_tcsstr(szClass, wclass) != 0);
+  }
+
+  if (wtitle)
+  {
+    TCHAR szTitle[300];
+    if (::GetWindowText(hwnd, szTitle, sizeof(szTitle)))
+      bMatch |= (_tcsstr(szTitle, wtitle) != 0);
+  }
+
+  if (bMatch)
+  {
+    t->hwnd = 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;
+    ::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;
+}
+
+/*
+Test Binary search
+void CSendKeys::test()
+{
+  WORD miss(0);
+  for (int i=0;i<MaxSendKeysRecs;i++)
+  {
+    char *p = (char *)KeyNames[i].keyName;
+    WORD v = StringToVKeyB(p);
+    if (v == INVALIDKEY)
+    {
+      miss++;
+    }
+  }
+}
+*/
+
+/*
+Search in a linear manner
+WORD CSendKeys::StringToVKey(const char *KeyString, int &idx)
+{
+for (int i=0;i<MaxSendKeysRecs;i++)
+{
+size_t len = strlen(KeyNames[i].keyName);
+if (strnicmp(KeyNames[i].keyName, KeyString, len) == 0)
+{
+idx = i;
+return KeyNames[i].VKey;
+}
+}
+idx = -1;
+return INVALIDKEY;
+}
+*/

+ 76 - 0
SendKeys.h

@@ -0,0 +1,76 @@
+#ifndef __SENDKEYS_04192004__INC__
+#define __SENDKEYS_04192004__INC__
+
+#include <windows.h>
+#include <tchar.h>
+// Please see SendKeys.cpp for copyright and usage issues.
+
+class CSendKeys
+{
+private:
+  bool m_bWait, m_bUsingParens, m_bShiftDown, m_bAltDown, m_bControlDown, m_bWinDown;
+  DWORD  m_nDelayAlways, m_nDelayNow;
+
+  static BOOL CALLBACK enumwindowsProc(HWND hwnd, LPARAM lParam);
+  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