This is c# code. I tried to clean it up all pretty like. It is really nothing much, but its encapsulates the movement keys well, as well as provide an interface for pressing any other key (for example, the number keys, for attacks), as well as user chatting. Below this code you can see my altered SendText code. Note that my class here does not handle arrow keys / special keys very well. You'll have to program those yourself. There is some nice code to help you with that at www.shynd.com/code/PostMessage.cs
As for using this code, just create a new wKeyManager object, and start calling functions like startTurningLeft(). It will handle the appropriate keypresses based on the key bindings you gave it when you created the object. My bot monitors the player's rotation and calls the stop functions of my wKeyManager, which it handles within 5 milliseconds, thus giving me accuracy up to 5 degrees or so, while still keeping key-presses at 100 ms intervals while the bot wants to hold a key down. Note that this class will eternally send key presses unless told to stop with the Stop(..) functions
Hope this is helpful to someone, anyway.
Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
using com.ms.win32; //Add a reference to "vjslib" for this to work. Or just export the Win32 functions yourself
namespace LetsReadWin
{
class wKeyManager
{
private struct wMovementKeyState
{
public string keyName; //this is used for debug output (optional)
public bool hold;
public bool release;
public bool releaseOK;
public short VKey;
public void SetKeyBind(short virtualKeyCode, string keyDescription)
{
keyName = keyDescription;
VKey = virtualKeyCode;
}
}
#region Variables
//DEBUG tools:
private System.Windows.Forms.TextBox debugConsole;
string debugStr = "";
//Some simple windows constants:
const int WM_KEYDOWN = 0x0100;
const int WM_KEYUP = 0x0101;
const int WM_CHAR = 0x0102;
//movement keys:
private const int numKeys = 4;
private const int fwd = 0;
private const int bck = 1;
private const int left = 2;
private const int right = 3;
//private const int st_left = 4; //strafe keys are not implemented right now
//private cons tint st_right = 5; //strafe keys are not implemented right now
private wMovementKeyState[] key = new wMovementKeyState[numKeys];
//other special key sequences we can do:
bool jumpBack = false;
//Text (Chat) that the user wants to send:
private int txtSentIndex = -1;
private List<char> txtToSend = new List<char>();
//The targeted windows:
Process focusWind;
private bool destroying = false;
private Thread doKeysThread;
#endregion
#region Static Functions
public static void holdKey(short vkey, ref Process targProc)
{
int ScanCode = User32.MapVirtualKey(vkey, 0);
User32.PostMessage(targProc.MainWindowHandle.ToInt32(), WM_KEYDOWN, vkey, (ScanCode << 16) | 0x00100001);
}
public static void releaseKey(short vkey, ref Process targProc)
{
uint ScanCode = (uint)User32.MapVirtualKey(vkey, 0);
User32.PostMessage(targProc.MainWindowHandle.ToInt32(), WM_KEYUP, vkey, (ScanCode << 16) | 0xC1000001);
}
#endregion
#region Public Functions
~wKeyManager()
{
Destroy();
}
/// <summary>
/// The only constructor for this class.
/// Starts a thread on private function DoKeys()
/// </summary>
/// <param name="fWind">a process object representing the process we want to send keystrokes to.</param>
/// <param name="fProf">gives us key bindings for forward, back, left, right, strafe left, and strafe right</param>
public wKeyManager(Process fWind, wProfile fProf)
{
if (fWind == null) throw new Exception("Process object is null (wKeyManager constructor).");
if (fProf == null) throw new Exception("Profile object is null (wKeyManager constructor).");
focusWind = fWind;
SetNewKeybinds(fProf);
//setup the doKeysThread:
doKeysThread = new Thread(new ThreadStart(this.doKeys));
doKeysThread.Priority = ThreadPriority.Highest;
//Start it up. It won't actually do anything at all until a startMoving(..) function, etc. is called:
doKeysThread.Start();
}
public void SetNewKeybinds(wProfile fProf)
{
key[fwd].SetKeyBind(fProf.GetKeyBind(wActions.Forward).key, "FORWARD");
key[bck].SetKeyBind(fProf.GetKeyBind(wActions.Backward).key, "BACKWARD");
key[left].SetKeyBind(fProf.GetKeyBind(wActions.Left).key, "TURNLEFT");
key[right].SetKeyBind(fProf.GetKeyBind(wActions.Right).key, "TURNRIGHT");
}
/// <summary>
/// This will stop the thread that was created and render this object un-usable
/// </summary>
public void Destroy()
{
destroying = true;
}
/// <summary>
/// Tells the thread to perform one small jump backward (only once).
/// Calling this function many times at once will still only result in one jump.
/// </summary>
public void JumpBackward()
{
jumpBack = true;
}
public void SetDebugOutput(System.Windows.Forms.TextBox txtKeyboard)
{
debugConsole = txtKeyboard;
}
public void QueueTextForChat(string p)
{
//put each char in the string into a list of chars
//The thread will go through each char one at a time, later, and send the appropriate message
foreach (char cc in p)
txtToSend.Add(cc);
}
#region Start Movement Functions
public void startMovingForward()
{
key[fwd].hold = true;
key[fwd].release = false;
}
public void startMovingBack()
{
key[bck].hold = true;
key[bck].release = false;
}
/// <summary>
/// This function will do nothing if stopTurningLeft or stopAllMovement was called in the last 10 ms
/// </summary>
public void startTurningLeft()
{
if (key[left].release == false)
{
key[left].hold = true;
if (key[right].hold == true)
stopTurningRight();
}
}
/// <summary>
/// This function will do nothing if stopTurningRight or stopAllMovement was called in the last 10 ms
/// </summary>
public void startTurningRight()
{
if (key[right].release == false)
{
key[right].hold = true;
if (key[left].hold == true)
stopTurningLeft();
}
}
#endregion
#region Stop Movement Functions
public void stopMovingForward()
{
key[fwd].release = true;
key[fwd].hold = false;
}
public void stopMovingBack()
{
key[bck].release = true;
key[bck].hold = false;
}
public void stopTurningLeft()
{
key[left].release = true;
key[left].hold = false;
}
public void stopTurningRight()
{
key[right].release = true;
key[right].hold = false;
}
/// <summary>
/// This is the same as calling all of the StopMoving(...) functions all at once
/// </summary>
public void stopAllMovement()
{
stopTurningLeft();
stopTurningRight();
stopMovingBack();
stopMovingForward();
//stopStrafingLeft();
//stopStrafingRight();
}
#endregion
#region Is Moving Functions
public bool IsMovingForward()
{
return ((key[fwd].hold) && (key[fwd].releaseOK));
}
public bool IsMovingBackward()
{
return ((key[bck].hold) && (key[bck].releaseOK));
}
public bool IsTurningLeft()
{
return ((key[left].hold) && (key[left].releaseOK));
}
public bool IsTurningRight()
{
return ((key[right].hold) && (key[right].releaseOK));
}
#endregion
/// <summary>
/// PressKey will send a WM_KEYDOWN and WM_KEYUP message to our target process immediately.
/// This function will NOT queue the key press and wait for the thread to deliver it.
/// </summary>
/// <param name="key">A single character to send</param>
/// <returns></returns>
public bool pressKey(char key)
{
return pressKey(User32.VkKeyScan(key));
}
/// <summary>
/// PressKey will send a WM_KEYDOWN and WM_KEYUP message to our target process immediately.
/// This function will NOT queue the key press and wait for the thread to deliver it.
/// </summary>
/// <param name="vKey">The virtual key code for a single character to send</param>
/// <returns></returns>
public bool pressKey(short vKey)
{
if (vKey == -1)
{
//MessageBox.Show("Error in [pressKey(short vKey);] no such key.");
return false;
}
else
{
holdKey(vKey, ref focusWind);
releaseKey(vKey, ref focusWind);
return true;
}
}
#endregion
#region Private Functions
private void keyChecking(bool holdOK, ref wMovementKeyState mks)
{
//add a line to our debug string:
debugStr += (mks.keyName + "=[H: " + mks.hold + ", R: " + mks.release + ", R-ok: " + mks.releaseOK + "] ... ");
//the logic of this function:
//If the user wants to hold this key down, then check to see if it is OK to actually send a Message.
//If it is ok to actually send a message to our target window, then set the "releaseOK" variable
//"releaseOK" is set so that when we no longer want to hold down this key, we can send (or not send) the
//appropriate message
if (mks.hold == true)
{
debugStr += ("Holding " + mks.keyName);
if (holdOK)
{
//We are told it is OK to actually send a message, so do that
holdKey(mks.VKey, ref focusWind);
mks.releaseOK = true;
}
}
else if (mks.release == true)
{
debugStr += ("Releasing " + mks.keyName);
if (mks.releaseOK == true)
{
//We are told it is OK to actually send a release key message, so do that
//(If "releaseOK" is not true, then the key was never Actually pressed in the first place)
releaseKey(mks.VKey, ref focusWind);
}
mks.release = false;
mks.releaseOK = false;
}
else
//The user doesn't care about this key right now, not trying to hold or release it.
mks.releaseOK = false;
debugStr += ("\r\n");
}
/// <summary>
/// This function checks to see if any new text has been added to our buffer "txtToSend" since the last
/// time we looked at the buffer. If there is, we de-press all currently pressed keys, send the text,
/// Then press down the appropriate keys again.
/// </summary>
private void HandleChatMessages()
{
if (txtToSend.Count > txtSentIndex + 1)
{
if ((key[fwd].releaseOK) && (key[fwd].hold)) releaseKey(key[fwd].VKey, ref focusWind);
if ((key[bck].releaseOK) && (key[bck].hold)) releaseKey(key[bck].VKey, ref focusWind);
if ((key[left].releaseOK) && (key[left].hold)) releaseKey(key[left].VKey, ref focusWind);
if ((key[right].releaseOK) && (key[right].hold)) releaseKey(key[right].VKey, ref focusWind);
//dont change any variables
if (txtToSend.Count > txtSentIndex)
{
string strToSend = "";
for (txtSentIndex = txtSentIndex + 1; txtSentIndex < txtToSend.Count; txtSentIndex++)
{
strToSend += txtToSend[txtSentIndex].ToString();
//System.Windows.Forms.SendKeys.SendWait(strToSend);
//releaseKey(User32.VkKeyScan(txtToSend[txtSentIndex]), ref focusWind);
}
txtSentIndex--; //for loops always add an extra one on the end. we want this to be the index of the last completed index.
if (strToSend != "")
PostMessageFuncs.PostMessage.SendKeys(focusWind.MainWindowHandle.ToInt32(), strToSend);
//NOTE ON SENDKEYS:
//PostMessageFuncs namespace
//PostMessage class
//Base code obtained from: www.shynd.com/code/PostMessage.cs
//And modified slightly to match this implementation
}
if ((key[fwd].releaseOK) && (key[fwd].hold)) holdKey(key[fwd].VKey, ref focusWind);
if ((key[bck].releaseOK) && (key[bck].hold)) holdKey(key[bck].VKey, ref focusWind);
if ((key[left].releaseOK) && (key[left].hold)) holdKey(key[left].VKey, ref focusWind);
if ((key[right].releaseOK) && (key[right].hold)) holdKey(key[right].VKey, ref focusWind);
}
}
/// <summary>
/// this function automatically releases forward if it was being pressed, then jumps a short distance backward
/// </summary>
private void HandleJumpBack()
{
if (jumpBack == true)
{
debugStr += "Jumping backward. Ignoring other forward and backward variables.\n";
if (debugConsole != null)
debugConsole.Text = debugStr;
//turn off the jump back switch:
jumpBack = false;
//Release the forward key if it was being pressed:
if (key[fwd].releaseOK == true)
releaseKey(key[fwd].VKey, ref focusWind);
key[fwd].hold = false;
key[fwd].release = false;
key[fwd].releaseOK = false;
//Execute the jump-back:
holdKey(key[bck].VKey, ref focusWind);
Thread.Sleep(100);
holdKey(key[bck].VKey, ref focusWind);
Thread.Sleep(100);
holdKey(key[bck].VKey, ref focusWind);
Thread.Sleep(100);
pressKey((short)VK.VK_SPACE);
Thread.Sleep(30);
holdKey(key[bck].VKey, ref focusWind);
Thread.Sleep(70);
holdKey(key[bck].VKey, ref focusWind);
Thread.Sleep(100);
releaseKey(key[bck].VKey, ref focusWind);
}
}
private void doKeys()
{
try
{
long next_key_check = DateTime.Now.Ticks;
while (destroying == false)
{
//this loop needs to check all the keys,
//it should only hold down a key every 100 ms
Thread.Sleep(5);
debugStr = "";
HandleChatMessages();
HandleJumpBack();
bool hOK = (DateTime.Now.Ticks >= next_key_check);
if (hOK) next_key_check = DateTime.Now.Ticks + (wCooldownManager.TicksPerMillisecond * 100); //100 ms, or 1000000 ticks.
//TicksPerMillisecond = 10000;
keyChecking(hOK, ref key[fwd]);
keyChecking(hOK, ref key[bck]);
if (key[left].release && !key[right].release)
{
keyChecking(hOK, ref key[left]);
keyChecking(hOK, ref key[right]);
}
else
{
keyChecking(hOK, ref key[right]);
keyChecking(hOK, ref key[left]);
}
if ((debugConsole != null) && (debugStr != ""))
debugConsole.Text = debugStr; //the debug output here is NOT thread safe
}
}
catch
{
//clean up!
}
}
#endregion
}
}
Code:
[DllImport("user32.dll", EntryPoint = "PostMessage")]
private static extern int _PostMessage(int hWnd, int msg, int wParam, uint lParam);
[DllImport("user32.dll", EntryPoint = "MapVirtualKey")]
private static extern int _MapVirtualKey(int uCode, int uMapType);
/// <summary>
/// Sends keystrokes to the specified window
/// </summary>
/// <param name="hWnd">Window's hWnd</param>
/// <param name="keys">String of keys to send</param>
/// <returns>Returns number of keystrokes sent, -1 if an error occurs</returns>
public static int SendKeys(int hWnd, string keys)
{
if (hWnd <= 0 || keys.Length == 0)
return -1;
int ret = 0, i = 0;
System.Text.StringBuilder str = new System.Text.StringBuilder(keys.ToUpper());
str.Replace(Convert.ToChar("`"), Convert.ToChar(0xC0));
str.Replace(Convert.ToChar("~"), Convert.ToChar(0xC0));
str.Replace(Convert.ToChar("-"), Convert.ToChar(0xBD));
str.Replace(Convert.ToChar("="), Convert.ToChar(0xBB));
str.Replace(Convert.ToChar("/"), Convert.ToChar(System.Windows.Forms.Keys.Oem2));
str.Replace(Convert.ToChar("."), Convert.ToChar(System.Windows.Forms.Keys.OemPeriod));
str.Replace(Convert.ToChar(";"), Convert.ToChar(System.Windows.Forms.Keys.Oemplus));
//int map1 = _MapVirtualKey(Convert.ToInt32(Convert.ToChar(")")), 1);
//int map2 = _MapVirtualKey(Convert.ToInt32(Convert.ToChar("(")), 1);
//str.Replace(Convert.ToChar("("), Convert.ToChar(LetsReadWin.VK.VK_LEFTPAREN)); //186 should be 40 and 41 though...
//str.Replace(Convert.ToChar(")"), Convert.ToChar(LetsReadWin.VK.VK_RIGHTPAREN)); //243
str.Replace("{TAB}", Convert.ToChar(0x9).ToString());
str.Replace("{ENTER}", Convert.ToChar(0xD).ToString());
str.Replace("{ESC}", Convert.ToChar(0x1B).ToString());
str.Replace("{F1}", Convert.ToChar(0x70).ToString());
str.Replace("{F2}", Convert.ToChar(0x71).ToString());
str.Replace("{F3}", Convert.ToChar(0x72).ToString());
str.Replace("{F4}", Convert.ToChar(0x73).ToString());
str.Replace("{F5}", Convert.ToChar(0x74).ToString());
str.Replace("{F6}", Convert.ToChar(0x75).ToString());
str.Replace("{F7}", Convert.ToChar(0x76).ToString());
str.Replace("{F8}", Convert.ToChar(0x77).ToString());
str.Replace("{F9}", Convert.ToChar(0x78).ToString());
str.Replace("{F10}", Convert.ToChar(0x79).ToString());
str.Replace("{F11}", Convert.ToChar(0x7A).ToString());
str.Replace("{F12}", Convert.ToChar(0x7B).ToString());
str.Replace("{SHIFTD}", Convert.ToChar(0xC1).ToString());
str.Replace("{SHIFTU}", Convert.ToChar(0xC2).ToString());
for (int ix = 1; ix <= str.Length; ++ix)
{
char chr = str[i];
if (Convert.ToInt32(chr) == 0xC1)
{
_PostMessage(hWnd, 0x100, 0x10, 0x002A0001);
_PostMessage(hWnd, 0x100, 0x10, 0x402A0001);
Thread.Sleep(1);
}
else if (Convert.ToInt32(chr) == 0xC2)
{
_PostMessage(hWnd, 0x101, 0x10, 0xC02A0001);
Thread.Sleep(1);
}
if (Convert.ToInt32(chr) == 0x28) // ( = 0x38 ?
{
Thread.Sleep(65);
_PostMessage(hWnd, 0x102, 0x28, 0x00090001);
Thread.Sleep(1);
}
else if (Convert.ToInt32(chr) == 0x29) // ) = 0x39 ?
{
Thread.Sleep(65);
_PostMessage(hWnd, 0x102, 0x29, 0x000A0001);
Thread.Sleep(1);
}
else
{
if (Convert.ToInt32(chr) == 0xD)
{
Thread.Sleep(300); //This is the ENTER key. Wow needs a lot of time to process it... apparently
}
ret = _MapVirtualKey(Convert.ToInt32(chr), 0);
if (_PostMessage(hWnd, 0x100, Convert.ToInt32(chr), MakeLong(1, ret)) == 0)
return -1;
Thread.Sleep(5);
if (_PostMessage(hWnd, 0x101, Convert.ToInt32(chr), (MakeLong(1, ret) + 0xC0000000)) == 0)
return -1;
if (Convert.ToInt32(chr) == 0xD)
{
Thread.Sleep(300); //This is the ENTER key. Wow needs a lot of time to process it...
}
}
i++;
}
return i;
}