All Visual Studio 2013 files except the icons/resource file.
Recently included window switching by titlebar text. All actions
are recorded on separate lines except for strings of text.
Includes DirectX headers and some communications functions that
are unused. Sound file paths are processed on startup, so they
should play regardless of location.
hookEXE.cpp
hookDLL.cpp
definitions.h
#define DLLIMPORT __declspec(dllimport)
#include <afxwin.h>//MFC core and standard components
//#include <d3d9.h>
#include <mmsystem.h>
#include "definitions.h"
#include "resource.h"//UI ID's
//#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"winmm.lib")//was in Properties/Linker/Input...
char StringOut[256]={};
HWND hWndoCurrent=NULL;
POINT PointCurrent={};
static const UINT DLL_INPUT_NOTICE=::RegisterWindowMessage(_T("DLL_INPUT_NOTICE"));
class CMyApp: public CWinApp
{//CMyApp
public:
CMyApp() {}
virtual BOOL InitInstance();
virtual int ExitInstance();
protected:
//{{AFX_MSG(CMyApp)
afx_msg void OnAppAbout();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};//CMyApp
class CMainFrame: public CFrameWnd
{//main frame window paints message
public:
CMainFrame() {}
virtual ~CMainFrame() {}
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
DECLARE_DYNCREATE(CMainFrame)
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
afx_msg LRESULT OnDLLInputNotice(WPARAM wParam,LPARAM lParam);
};//main frame window paints message
CMyApp theApp;
BEGIN_MESSAGE_MAP(CMyApp,CWinApp)
//{{AFX_MSG_MAP(CMyApp)
ON_COMMAND(ID_APP_ABOUT,OnAppAbout)
//}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_NEW,CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN,CWinApp::OnFileOpen)
END_MESSAGE_MAP()
DLLIMPORT BOOL hookDLLInit();//call when your app starts
BOOL CMyApp::InitInstance()
{//app starting, initialize DLL and load main window
CMainFrame* pMainFrame=new CMainFrame;//create main frame
if(!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);//show main frame
pMainFrame->UpdateWindow();
m_pMainWnd=pMainFrame;
//RUN, initialize DLL last so DLL startup code can see window
if(!hookDLLInit())
{//RUN, initialize DLL
AfxMessageBox(_T("Error initializing DLL"));
}//RUN, initialize DLL
return TRUE;
}//app starting, initialize DLL and load main window
DLLIMPORT void hookDLLTerm();//call when your app terminates
int CMyApp::ExitInstance()
{//app shutting down
hookDLLTerm();//terminate DLL (unhook)
return CWinApp::ExitInstance();
}//app shutting down
void CMyApp::OnAppAbout()
{//run About dialog
CDialog(IDD_ABOUTBOX).DoModal();
}//run About dialog
IMPLEMENT_DYNCREATE(CMainFrame,CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_WM_PAINT()
//}}AFX_MSG_MAP
ON_REGISTERED_MESSAGE(DLL_INPUT_NOTICE,&CMainFrame::OnDLLInputNotice)
END_MESSAGE_MAP()
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{//before creating window, set size
BOOL bRet=CFrameWnd::PreCreateWindow(cs);
cs.cx=800;
cs.cy=800;
return bRet;
}//before creating window, set size
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{//create main window
if(CFrameWnd::OnCreate(lpCreateStruct)==-1)
return -1;
return 0;
}//create main window
void CMainFrame::OnPaint()
{//paint message
CPaintDC dc(this);
SetTextColor(dc,GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(dc,GetSysColor(COLOR_WINDOW));
CString s
(
"This free utility helps expert users write scripts for Vocola 2/DNS. I disclaim any responsibility for your use of this program. If you are confused, ask in the forums or email me.\r\n"
"\r\n"
"Usage...\r\n"
"\r\n"
"The numeric keypad must be OFF while recording. It may be ON while not recording.\r\n"
"\r\n"
"Start and stop recording...\r\n"
"\r\n"
"To start recording, press keypad Delete/point. To stop, press keypad Delete/point again. You should hear a sound when recording starts and stops. Then, your recorded actions are in the clipboard ready for pasting into a VCL file.\r\n"
"\r\n"
"Recording mouse actions...\r\n"
"\r\n"
"To record a click without positioning, don't move the mouse before the click. The position will be recorded if the mouse moves more than 12 pixels after the last action. Hold ALT and click to record a point and click relative to the current window. All dragging is relative to the button down position.\r\n"
"\r\n"
"Window switching...\r\n"
"\r\n"
"Recorded automatically. The titlebar search text is case sensitive but not an exact match. You may remove text from the beginning and/or end of the recorded text but the carrot symbols must remain. Adjust the WaitForWindow() line according to Vocola rules.\r\n"
"\r\n"
"Recording a text string on one line...\r\n"
"\r\n"
"Press keypad 4 to start recording text on one line. Press keypad 6 to stop that and continue recording other stuff.\r\n"
"\r\n"
"Changes to the program will be made without changes to this description. \r\n"
"\r\n"
"This is extra...\r\n"
"\r\n"
"When the keypad is disabled, keypad zero is a global double-click. While recording, the keypad number keys 1-9 are additional functions. A second set is available by holding control/Ctrl while pressing 1-9 (currently for timing). To see the list of functions, just start a recording and press those nine numbers (except 4 and 6) one after another. Then stop the recording and paste the results."
);
CRect rc;
GetClientRect(&rc);
dc.DrawText(s,&rc,DT_LEFT|DT_VCENTER|DT_WORDBREAK);
}//paint message
INPUT SI[10]={};
DLLIMPORT void ContinuePlaying(BOOL status);
afx_msg LRESULT CMainFrame::OnDLLInputNotice(WPARAM wParam,LPARAM lParam)
{//message from DLL
switch(wParam)
{//MSG, match input with action
case Pad3PgDn://AoE2
{//MSG-MACT, collect idle workers
ZeroMemory(SI,sizeof(INPUT));
SI[0].type=INPUT_KEYBOARD;
//release current object
SI[0].ki.wVk=VK_0;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
//find idle worker
SI[0].ki.wVk=VK_OEM_PERIOD;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
Sleep(100);
//ungarrison
SI[0].ki.wVk=VK_Z;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
//add to group 1
SI[0].ki.wVk=VK_SHIFT;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.wVk=VK_1;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.wVk=VK_SHIFT;
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
//assign to group 1
SI[0].ki.wVk=VK_CONTROL;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.wVk=VK_1;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.wVk=VK_CONTROL;
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
//return view
SI[0].ki.wVk=VK_BACK;
SI[0].ki.dwFlags=0;//press
SendInput(1,&SI[0],sizeof(INPUT));
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,&SI[0],sizeof(INPUT));
}//MSG-MACT, collect idle workers
}//MSG, match input with action
return 0;
}//message from DLL
#define DLLEXPORT __declspec(dllexport)
#include <afxwin.h>//MFC core and standard components
#include <mmsystem.h>
#include "definitions.h"
#pragma comment(lib,"winmm.lib")//was in Properties/Linker/Input...
#pragma comment(linker,"/SECTION:.hookDLL,RWS")//replaced (hookDLL.def)
//the following global data is SHARED among all instances of the DLL
//(processes), these are system-wide globals
#pragma data_seg (".hookDLL")//defined above
BYTE KeyboardState[256]={};
HHOOK hKeyboardHook=NULL;//one instance for all processes
HHOOK hMouseHook=NULL;//one instance for all processes
HWND WndoCurrent=NULL;
HWND WndoEXE=NULL;
INPUT SI[10];
POINT PointContextDn={};
POINT PointCurrent={};
POINT PointScreenDn={};
POINT PointScreenUp={};
POINT PointStored={};
RECT WndoBorder={};
bool AfterFirstUpAction=true;
bool OnOneLine=false;
bool GetTitle=false;
bool MouseDouble=false;
bool MouseDown=false;
bool MouseShifted=false;
bool Printable=false;
bool Recording=false;
char BtnName[16]={};
char BtnNumber[2]={};
char ClickContext[32]={};
char Coordinate[8]={};
char KeyInput[2]={};
char KeyName[16]={};
char KeyString[256]={};
char Modifier[32]={};
char NumPad[16]={};
char PointDn[16]={};
char PointUp[16]={};
char Script[4096]={};
char SoundFolder[256]={};
char SoundPathError[256]={};
char SoundPathNotice[256]={};
char SoundPathStart[256]={};
char SoundPathStop[256]={};
char StringOut[256]={};
char TitleBar1[256]={};
char TitleBar2[256]={};
int LastParentheses=')';
#pragma data_seg ()
static const UINT DLL_INPUT_NOTICE=::RegisterWindowMessage(_T("DLL_INPUT_NOTICE"));
class ChookEXEDll:public CWinApp
{//app object for this DLL
public:
ChookEXEDll()
{
}
~ChookEXEDll()
{
}
}//app object for this DLL
theDll;//one-and-only, normal (per-process) static global
BOOL CALLBACK EnumWinHandle(HWND hWnd,LPARAM lparam)
{//look for matching window titlebar
if(IsWindowVisible(hWnd))
{//FND, normal window titles
GetWindowText(hWnd,TitleBar1,256);
if(strstr(TitleBar1,KeyString))
{//FND-NRM, activate the destination window
//FND-NRM-ACT, press alt key
ZeroMemory(SI,sizeof(INPUT));
SI[0].type=INPUT_KEYBOARD;
SI[0].ki.wVk=VK_MENU;
SI[0].ki.dwFlags=0;//press
SendInput(1,SI,sizeof(INPUT));
if(IsIconic(hWnd))
{//FND-NRM-ACT, restore destination window if minimized
OpenIcon((hWnd));
}//FND-NRM-ACT, restore destination window if minimized
else
{//FND-NRM-ACT, switch to destination window
SetForegroundWindow(hWnd);
}//FND-NRM-ACT, switch to destination window
//FND-NRM-ACT, release alt key
SI[0].ki.dwFlags=KEYEVENTF_KEYUP;
SendInput(1,SI,sizeof(INPUT));
return FALSE;//stop enumerating
}//FND-NRM, activate the destination window
}//FND, normal window titles
return TRUE;//continue enumerating
}//look for matching window titlebar
LRESULT CALLBACK KeyboardHook(int nCode,WPARAM wParam,LPARAM lParam)
{//key action
if(UnknownAction)
{//K, unusable message
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K, unusable message
if(GetTitle)
{//K, reading input for window switching
if(KeyUp)
{//K-READ, upstroke
if((wParam==VK_6)&&(GetKeyState(VK_SHIFT)&0x8000))
{//K-READ-UP, switch windows
OutputDebugString(KeyString);
OutputDebugString("The End\n");
EnumWindows(EnumWinHandle,NULL);
GetTitle=false;
*KeyString='\0';
}//K-READ-UP, switch windows
else
{//K-READ-UP, add characters to titlebar search string
if(wParam!=VK_SHIFT)
{//K-READ-UP-ADD, printable character
GetKeyboardState(KeyboardState);
ToAscii(wParam,MapVirtualKey(wParam,0),KeyboardState,(LPWORD)KeyInput,0);
strcat(KeyString,KeyInput);
}//K-READ-UP-ADD, printable character
}//K-IN, add characters to titlebar search string
}//K-READ, upstroke
return 1;
}//K, reading input for window switching
if((wParam==VK_6)&&(GetKeyState(VK_SHIFT)&0x8000))
{//K, begin reading titlebar text for window switching
if(KeyUp)
{//K-IN, upstroke
GetTitle=true;
}//K-IN, upstroke
return 1;
}//K, begin reading titlebar text for window switching
if(!Extended)
{//K, not extended
switch(wParam)
{//K-NEX, match input with action
case Pad0Insert:
{//K-NEX-MACT, global double-click
if(KeyUp)
{//K-NEX-MACT-DBL, upstroke
return 1;
}//K-NEX-MACT-DBL, upstroke
if(KeyWasDn)
{//K-NEX-MACT-DBL, repeat stroke
return 1;
}//K-NEX-MACT-DBL, repeat stroke
//K-NEX-MACT-DBL, downstroke
ZeroMemory(SI,sizeof(INPUT));
SI[0].type=SI[1].type=SI[2].type=SI[3].type=INPUT_MOUSE;
SI[0].mi.dwFlags=SI[2].mi.dwFlags=MOUSEEVENTF_LEFTDOWN;
SI[1].mi.dwFlags=SI[3].mi.dwFlags=MOUSEEVENTF_LEFTUP;
SendInput(4,SI,sizeof(INPUT));
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
return 1;
}//K-NEX-MACT, global double-click
case PadPDelete:
{//K-NEX-MACT, designated start/stop control key
if(KeyUp)
{//K-NEX-MACT-CON, upstroke
if(Recording)
{//K-NEX-MACT-CON-UP, stop recording in progress
Recording=false;
if(MouseDown)
{//K-NEX-MACT-CON-UP-END, voiced "G" (button down from script)
strcat(Script," Keys.SendInput({leftbutton_hold}) WAIT(1) \n");
}//K-NEX-MACT-CON-UP-END, voiced "G" (button down from script)
if(OpenClipboard(NULL))
{//K-NEX-MACT-CON-UP-END, place script in clipboard
_itoa(strlen(Script),StringOut,10);
OutputDebugString(StringOut);
OutputDebugString("SCRIPT\n");
OutputDebugString(Script);
OutputDebugString("END\n");
HGLOBAL clipbuffer;
char *buffer;
EmptyClipboard();
clipbuffer=GlobalAlloc(GMEM_DDESHARE,strlen(Script)+1);
buffer=(char*)GlobalLock(clipbuffer);
strcpy(buffer,Script);
GlobalUnlock(clipbuffer);
SetClipboardData(CF_TEXT,clipbuffer);
CloseClipboard();
PlaySound(SoundPathStop,NULL,SND_FILENAME|SND_ASYNC);
}//K-NEX-MACT-CON-UP-END, place script in clipboard
else
{//K-NEX-MACT-CON-UP-END, error opening clipboard
PlaySound(SoundPathError,NULL,SND_FILENAME|SND_ASYNC);
}//K-NEX-MACT-CON-UP-END, error opening clipboard
}//K-NEX-MACT-CON-UP, stop recording in progress
else
{//K-NEX-MACT-CON-UP, start recording
AfterFirstUpAction=true;
MouseDown=false;
Recording=true;
*Script='\0';
*TitleBar2='\0';
GetCursorPos(&PointScreenUp);
WndoCurrent=GetForegroundWindow();
GetWindowText(WndoCurrent,TitleBar1,255);
PlaySound(SoundPathStart,NULL,SND_FILENAME|SND_ASYNC);
}//K-NEX-MACT-CON-UP, start recording
}//K-NEX-MACT-CON, upstroke
return 1;//eat ctrl strokes
}//K-NEX-MACT, designated start/stop control key
}//K-NEX, match input with action
}//K, not extended
if(Recording)
{//K, recording in progress
if(!KeyUp)
{//K-R, key is down
if(KeyWasDn)
{//K-R-DN, eat repeat strokes
return 1;
}//K-R-DN, eat repeat strokes
}//K-R, key is down
if(MouseShifted)
{//K-R, shift upstroke after modified click
MouseShifted=false;
strcat(Script," Keys.SendInput({shift_release}) WAIT(1) \n");
GetCursorPos(&PointCurrent);
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K-R, shift upstroke after modified click
*NumPad='\0';
if(!Extended)
{//K-R, not extended
if(GetKeyState(VK_CONTROL)&0x8000)
{//K-R-NEX, ctrl modified
switch(wParam)
{//K-R-NEX-MOD, seeking numpad keys
case Pad0Insert:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
//for double click
break;
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad1End:
strcat(NumPad,"10");
break;
case Pad2Dn:
strcat(NumPad,"20");
break;
case Pad3PgDn:
strcat(NumPad,"40");
break;
case Pad4Left:
strcat(NumPad,"80");
break;
case Pad5Clear:
strcat(NumPad,"160");
break;
case Pad6Right:
strcat(NumPad,"320");
break;
case Pad7Home:
strcat(NumPad,"640");
break;
case Pad8Up:
strcat(NumPad,"1280");
break;
case Pad9PgUp:
strcat(NumPad,"2560");
break;
}//K-R-NEX-MOD, seeking numpad keys
if(*NumPad)
{//K-R-NEX-MOD, function key
if(KeyUp)
{//K-R-NEX-MOD-NUM, upstroke
AfterFirstUpAction=true;
strcat(Script," Wait(");
strcat(Script,NumPad);
strcat(Script,") WAIT(1) \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-MOD-NUM, upstroke
return 1;//eat all function keystrokes
}//K-R-NEX-MOD, function key
}//K-R-NEX, ctrl modified
else
{//K-R-NEX, unmodified
switch(wParam)
{//K-R-NEX-NOD, seeking numpad keys
case Pad0Insert:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," #unmodified keypad 0 is unused \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad1End:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," Keys.SendInput({scrolllock}) WAIT(1) #record point\n");
GetCursorPos(&PointStored);
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad2Dn:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
GetCursorPos(&PointCurrent);
strcat(Script," SetMousePosition(0,");
strcat(Script,_ltoa(PointCurrent.x,Coordinate,10));
strcat(Script,",");
strcat(Script,_ltoa(PointCurrent.y,Coordinate,10));
strcat(Script,") WAIT(1) #set during recording\n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad3PgDn:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," Keys.SendInput({ctrl+scrolllock}) WAIT(1) #goto recorded point\n");
SetCursorPos(PointStored.x,PointStored.y);
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad4Left:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, start condensed recording mode
OnOneLine=true;
strcat(Script," \"");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, start condensed recording mode
return 1;
case Pad5Clear:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," PlaySound(\"D:\\files\\wav\\special effects\\whadok1.wav\") WAIT(1) \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad6Right:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, end condensed recording mode
OnOneLine=false;
strcat(Script,"\" WAIT(1) \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, end condensed recording mode
return 1;
case Pad7Home:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," Keys.SendInput({shift_hold}) WAIT(1) \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad8Up:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," #unmodified keypad 8 is unused \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
case Pad9PgUp:
if(KeyUp)
{//K-R-NEX-NOD-SEK-PAD, upstroke
strcat(Script," Keys.SendInput({shift_release}) WAIT(1) \n");
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-R-NEX-NOD-SEK-PAD, upstroke
return 1;
}//K-R-NEX-NOD, seeking numpad keys
}//K-R-NEX, unmodified
}//K-R, not extended
if(OnOneLine)
{//K, reading characters usually for a text box/field
if(wParam!=VK_SHIFT)
{//K-READ, printable characters
if(KeyUp)
{//K-READ-NFT, upstroke
GetKeyboardState(KeyboardState);
ToAscii(wParam,MapVirtualKey(wParam,0),KeyboardState,(LPWORD)KeyInput,0);
strcat(Script,KeyInput);
}//K-READ-NFT, upstroke
}//K-READ, printable characters
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K, reading characters usually for a text box/field
if(KeyUp)
{//K-R, upstroke
if(AfterFirstUpAction)
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
AfterFirstUpAction=true;
*Modifier='\0';
if(GetKeyState(VK_CONTROL)&0x8000)
strcat(Modifier,"ctrl+");
if(GetKeyState(VK_MENU)&0x8000)
strcat(Modifier,"alt+");
if(GetKeyState(VK_SHIFT)&0x8000)
strcat(Modifier,"shift+");
*KeyName='\0';
Printable=false;
switch(wParam)
{//K-R-UP-SW
case 0x08://8, REC
strcat(KeyName,"BackSpace");
break;
case 0x09://9, REC
strcat(KeyName,"Tab");
break;
case 0x0D://13, REC
strcat(KeyName,"Enter");
break;
case 0x13://19, REC
strcat(KeyName,"Break");
break;
case 0x14://20, REC
strcat(KeyName,"CapsLock");
break;
case 0x1B://27, REC
strcat(KeyName,"Esc");
break;
case 0x20://32, REC
strcat(KeyName," ");
Printable=true;
break;
case 0x21://33, REC
strcat(KeyName,"PgUp");
break;
case 0x22://34, REC
strcat(KeyName,"PgDn");
break;
case 0x23://35, REC
strcat(KeyName,"End");
break;
case 0x24://36, REC
strcat(KeyName,"Home");
break;
case 0x25://37, REC
strcat(KeyName,"Left");
break;
case 0x26://38, REC
strcat(KeyName,"Up");
break;
case 0x27://39, REC
strcat(KeyName,"Right");
break;
case 0x28://40, REC
strcat(KeyName,"Down");
break;
case 0x2C://44, REC
strcat(KeyName,"PrintScreen");
break;
case 0x2D://45, REC
strcat(KeyName,"Insert");
break;
case 0x2E://46, REC
strcat(KeyName,"Del");
break;
case 0x30://48, REC
strcat(KeyName,"0");
Printable=true;
break;
case 0x31://49, REC
strcat(KeyName,"1");
Printable=true;
break;
case 0x32://50, REC
strcat(KeyName,"2");
Printable=true;
break;
case 0x33://51, REC
strcat(KeyName,"3");
Printable=true;
break;
case 0x34://52, REC
strcat(KeyName,"4");
Printable=true;
break;
case 0x35://53, REC
strcat(KeyName,"5");
Printable=true;
break;
case 0x36://54, REC
strcat(KeyName,"6");
Printable=true;
break;
case 0x37://55, REC
strcat(KeyName,"7");
Printable=true;
break;
case 0x38://56, REC
strcat(KeyName,"8");
Printable=true;
break;
case 0x39://57, REC
strcat(KeyName,"9");
Printable=true;
break;
case 0x41://65, REC
strcat(KeyName,"a");
Printable=true;
break;
case 0x42://66, REC
strcat(KeyName,"b");
Printable=true;
break;
case 0x43://67, REC
strcat(KeyName,"c");
Printable=true;
break;
case 0x44://68, REC
strcat(KeyName,"d");
Printable=true;
break;
case 0x45://69, REC
strcat(KeyName,"e");
Printable=true;
break;
case 0x46://70, REC
strcat(KeyName,"f");
Printable=true;
break;
case 0x47://71, REC
strcat(KeyName,"g");
Printable=true;
break;
case 0x48://72, REC
strcat(KeyName,"h");
Printable=true;
break;
case 0x49://73, REC
strcat(KeyName,"i");
Printable=true;
break;
case 0x4A://74, REC
strcat(KeyName,"j");
Printable=true;
break;
case 0x4B://75, REC
strcat(KeyName,"k");
Printable=true;
break;
case 0x4C://76, REC
strcat(KeyName,"l");
Printable=true;
break;
case 0x4D://77, REC
strcat(KeyName,"m");
Printable=true;
break;
case 0x4E://78, REC
strcat(KeyName,"n");
Printable=true;
break;
case 0x4F://79, REC
strcat(KeyName,"o");
Printable=true;
break;
case 0x50://80, REC
strcat(KeyName,"p");
Printable=true;
break;
case 0x51://81, REC
strcat(KeyName,"q");
Printable=true;
break;
case 0x52://82, REC
strcat(KeyName,"r");
Printable=true;
break;
case 0x53://83, REC
strcat(KeyName,"s");
Printable=true;
break;
case 0x54://84, REC
strcat(KeyName,"t");
Printable=true;
break;
case 0x55://85, REC
strcat(KeyName,"u");
Printable=true;
break;
case 0x56://86, REC
strcat(KeyName,"v");
Printable=true;
break;
case 0x57://87, REC
strcat(KeyName,"w");
Printable=true;
break;
case 0x58://88, REC
strcat(KeyName,"x");
Printable=true;
break;
case 0x59://89, REC
strcat(KeyName,"y");
Printable=true;
break;
case 0x5A://90, REC
strcat(KeyName,"z");
Printable=true;
break;
case 0x5B://91, REC
strcat(KeyName,"Win");
break;
case 0x5C://92, REC
strcat(KeyName,"Win");
break;
case 0x5D://93, REC
strcat(KeyName,"Apps");
break;
case 0x70://112, REC
strcat(KeyName,"F1");
break;
case 0x71://113, REC
strcat(KeyName,"F2");
break;
case 0x72://114, REC
strcat(KeyName,"F3");
break;
case 0x73://115, REC
strcat(KeyName,"F4");
break;
case 0x74://116, REC
strcat(KeyName,"F5");
break;
case 0x75://117, REC
strcat(KeyName,"F6");
break;
case 0x76://118, REC
strcat(KeyName,"F7");
break;
case 0x77://119, REC
strcat(KeyName,"F8");
break;
case 0x78://120, REC
strcat(KeyName,"F9");
break;
case 0x79://121, REC
strcat(KeyName,"F10");
break;
case 0x7A://122, REC
strcat(KeyName,"F11");
break;
case 0x7B://123, REC
strcat(KeyName,"F12");
break;
case 0x90://144, REC
strcat(KeyName,"NumLock");
break;
case 0xBA://186, REC
strcat(KeyName,";");
Printable=true;
break;
case 0xBB://187, REC
strcat(KeyName,"=");
Printable=true;
break;
case 0xBC://188, REC
strcat(KeyName,",");
Printable=true;
break;
case 0xBD://189, REC
strcat(KeyName,"-");
Printable=true;
break;
case 0xBE://190, REC
strcat(KeyName,".");
Printable=true;
break;
case 0xBF://191, REC
strcat(KeyName,"/");
Printable=true;
break;
case 0xDB://219, REC
strcat(KeyName,"[");
Printable=true;
break;
case 0xDC://220, REC
strcat(KeyName,"\\");
Printable=true;
break;
case 0xDD://221, REC
strcat(KeyName,"]");
Printable=true;
break;
case 0xDE://222, REC
strcat(KeyName,"'");
Printable=true;
break;
default:
{//upstroke unknown
PlaySound(SoundPathError,NULL,SND_FILENAME|SND_ASYNC);
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//upstroke unknown
}//K-R-UP-SW
if(*KeyName)
{//K-R-UP, regular key
if(Printable&&!*Modifier)
{//K-R-UP-REG, unmodified printable
strcat(Script," \"");
strcat(Script,KeyName);
strcat(Script,"\" WAIT(1) \n");
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K-R-UP-REG, unmodified printable
else
{//K-R-UP-REG, modified or unprintable
strcat(Script," {");
if(*Modifier)
strcat(Script,Modifier);
strcat(Script,KeyName);
strcat(Script,"} WAIT(1) \n");
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K-R-UP-REG, modified or unprintable
}//K-R-UP, regular key
}//K-R, upstroke
else
{//K-R, downstroke, prepare to process next upstroke
AfterFirstUpAction=false;
WndoCurrent=GetForegroundWindow();
//check for new window
GetWindowText(WndoCurrent,TitleBar2,255);
if(strcmp(TitleBar1,TitleBar2))
{//K-R-DN, different window
strcpy(TitleBar1,TitleBar2);
if(strcmp(TitleBar2,"Task Switching"))
{//K-R-DN-DIF, normal window
if(strcmp(TitleBar2,""))
{//K-R-DN-DIF-NRM, has title
strcat(Script," \"^");
strcat(Script,TitleBar2);
strcat(Script,"^\" WAIT(1) \n");
strcat(Script," WaitForWindow(\"");
strcat(Script,TitleBar2);
strcat(Script,"\") WAIT(1) \n");
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K-R-DN-DIF-NRM, has title
}//K-R-DN-DIF, normal window
else
{//K-R-DN-DIF, task switching
strcat(Script," SendSystemKeys({alt+Tab}) WAIT(1) \n");
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K-R-DN-DIF, task switching
}//K-R-DN, different window
}//K-R, downstroke, prepare to process next upstroke
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//K, recording in progress
if(!Extended)
{//K, not extended
switch(wParam)
{//K-NEX, watch for the key
case Pad3PgDn:
{//K-NEX-MACT, collect idle workers
if(KeyUp)
{//K-NEX-MACT-IDLE, upstroke
PostMessage(WndoEXE,DLL_INPUT_NOTICE,Pad3PgDn,lParam);
PlaySound(SoundPathNotice,NULL,SND_FILENAME|SND_ASYNC);
}//K-NEX-MACT-IDLE, upstroke
return 1;//all strokes
}//K-NEX-MACT, collect idle workers
case GetOrSetPtrPos:
{//K-NEX-MACT, get/set pointer position
if(KeyUp)
{//K-NEX-MACT-GSPOS, upstroke
if(GetKeyState(VK_CONTROL)&0x8000)
{//K-NEX-MACT-GSPOS-UP, ctrl modified
SetCursorPos(PointStored.x,PointStored.y);
AfterFirstUpAction=true;
}//K-NEX-MACT-GSPOS-UP, ctrl modified
else
{//K-NEX-MACT-GSPOS-UP, not modified
GetCursorPos(&PointStored);
}//K-NEX-MACT-GSPOS-UP, not modified
}//K-NEX-MACT-GSPOS, upstroke
return 1;//all strokes
}//K-NEX-MACT, get/set pointer position
}//K-NEX, watch for the key
}//K, not extended
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//key action
LRESULT CALLBACK MouseHook(int nCode,WPARAM wParam,LPARAM lParam)
{//mouse action
if(MouseMoveMessage)
{//M, ignore mouse movement
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M, ignore mouse movement
if(UnknownAction)
{//M, unusable mouse message
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//M, unusable mouse message
if(Recording)
{//M-R
if(DoubleClick)
{//M-R, double-click
if(strchr(Script,LastParentheses)!=NULL)
{//M-R-DCLK, ready to modify
MouseDouble=true;
*(strrchr(Script,'B'))='\0';
strcat(Script,"ButtonClick(1,2) WAIT(1) \n");
}//M-R-DCLK, ready to modify
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//M-R, double-click
if(BtnEitherDn)
{//M-R, either button down
AfterFirstUpAction=false;
MouseDown=true;
GetCursorPos(&PointScreenDn);
WndoCurrent=GetForegroundWindow();
//check for new window
GetWindowText(WndoCurrent,TitleBar2,255);
if(strcmp(TitleBar1,TitleBar2))
{//M-R-DN, focus is new/different window
strcpy(TitleBar1,TitleBar2);
if(strcmp(TitleBar2,""))
{//M-R-DN-DIF, window has title
strcat(Script," \"^");
strcat(Script,TitleBar2);
strcat(Script,"^\" WAIT(1) \n");
strcat(Script," WaitForWindow(\"");
strcat(Script,TitleBar2);
strcat(Script,"\") WAIT(1) \n");
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//M-R-DN-DIF, window has title
}//M-R-DN, focus is new/different window
if(GetKeyState(VK_MENU)&0x8000)
{//M-R-DN, use window coordinates
AfterFirstUpAction=true;
strcpy(ClickContext," SetMousePosition(1,");
GetWindowRect(WndoCurrent,&WndoBorder);
PointContextDn={PointScreenDn.x-WndoBorder.left,PointScreenDn.y-WndoBorder.top};
}//M-R-DN, use window coordinates
else
{//M-R-DN, use screen coordinates
strcpy(ClickContext," SetMousePosition(0,");
PointContextDn=PointScreenDn;
}//M-R-DN, use screen coordinates
if(GetKeyState(VK_SHIFT)&0x8000)
{//M-R-DN, shift key held
if(!MouseShifted)
{//M-R-DN-SHFDN, set/record shift downstroke only once
MouseShifted=true;
strcat(Script," Keys.SendInput({shift_hold}) WAIT(1) \n");
}//M-R-DN-SHFDN, set/record shift downstroke only once
}//M-R-DN, shift key held
*PointDn='\0';
if(PointerMoved)
{//M-R-DN, pointer moved before button down
strcat(PointDn,_ltoa(PointContextDn.x,Coordinate,10));
strcat(PointDn,",");
strcat(PointDn,_ltoa(PointContextDn.y,Coordinate,10));
}//M-R-DN, pointer moved before button down
return::CallNextHookEx(hKeyboardHook,nCode,wParam,lParam);
}//M-R, either button down
if(BtnEitherUp)
{//M-R, either button up
if(MouseDouble)
{//M-R-UP, second part of a double-click
MouseDouble=false;
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M-R-UP, second part of a double-click
if(!MouseDown)
{//M-R-UP, voiced "X" (button up from script)
strcat(Script," Keys.SendInput({leftbutton_release}) WAIT(1) \n");
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M-R-UP, voiced "X" (button up from script)
MouseDown=false;
GetCursorPos(&PointScreenUp);
if(BtnLeftClick)
{//M-R-UP, left button action
strcpy(BtnName,"leftbutton");
strcpy(BtnNumber,"1");
}//M-R-UP, left button action
if(BtnRightClick)
{//M-R-UP, right button action
strcpy(BtnName,"rightbutton");
strcpy(BtnNumber,"2");
}//M-R-UP, right button action
*PointUp='\0';
if(PointerMoved)
{//M-R-UP, pointer moved before button up
strcat(PointUp,_ltoa(PointScreenUp.x-PointScreenDn.x,Coordinate,10));
strcat(PointUp,",");
strcat(PointUp,_ltoa(PointScreenUp.y-PointScreenDn.y,Coordinate,10));
}//M-R-UP, pointer moved before button up
if(*PointDn)
{//M-R-UP, pointer moved before button down
strcat(Script,ClickContext);
strcat(Script,PointDn);
strcat(Script,") WAIT(1) \n");
if(*PointUp)
{//M-R-UP-MOV, point, click, and drag
strcat(Script," Keys.SendInput({");
strcat(Script,BtnName);
strcat(Script,"_hold}) WAIT(1) \n");
strcat(Script," SetMousePosition(2,");
strcat(Script,PointUp);
strcat(Script,") WAIT(1) \n");
strcat(Script," Keys.SendInput({");
strcat(Script,BtnName);
strcat(Script,"_release}) WAIT(1) \n");
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M-R-UP-MOV, point, click, and drag
else
{//M-R-UP-MOV, point and click
strcat(Script," ButtonClick(");
strcat(Script,BtnNumber);
strcat(Script,",1) WAIT(1) \n");
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M-R-UP-MOV, point and click
}//M-R-UP, pointer moved before button down
else
{//M-R-UP, action starts at current location
if(*PointUp)
{//M-R-UP-CUR, just drag
strcat(Script," Keys.SendInput({");
strcat(Script,BtnName);
strcat(Script,"_hold}) WAIT(1) \n");
strcat(Script," SetMousePosition(2,");
strcat(Script,PointUp);
strcat(Script,") WAIT(1) \n");
strcat(Script," Keys.SendInput({");
strcat(Script,BtnName);
strcat(Script,"_release}) WAIT(1) \n");
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M-R-UP-CUR, just drag
else
{//M-R-UP-CUR, just click
strcat(Script," ButtonClick(");
strcat(Script,BtnNumber);
strcat(Script,",1) WAIT(1) \n");
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//M-R-UP-CUR, just click
}//M-R-UP, action starts at current location
}//M-R, either button up
}//M-R
return::CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}//mouse action
DLLEXPORT BOOL hookDLLInit()
{//initialize DLL
if(hKeyboardHook==NULL)
{//RUN, install keyboard & mouse hooks
HINSTANCE hInst=theDll.m_hInstance;
hKeyboardHook=SetWindowsHookEx(WH_KEYBOARD,KeyboardHook,hInst,0);
hMouseHook=SetWindowsHookEx(WH_MOUSE,MouseHook,hInst,0);
}//RUN, install keyboard & mouse hooks
//RUN, enable program sounds
GetCurrentDirectory(256,SoundFolder);
char*pch;
pch=strchr(SoundFolder,'\\');
while(pch!=NULL)
{//RUN, format program path
*pch='/';
pch=strchr(pch+1,'\\');
}//RUN, format program path
strcpy(SoundPathStart,SoundFolder);
strcat(SoundPathStart,"/titatoo1.wav");
strcpy(SoundPathStop,SoundFolder);
strcat(SoundPathStop,"/doedit1.wav");
strcpy(SoundPathError,SoundFolder);
strcat(SoundPathError,"/vuut1.wav");
strcpy(SoundPathNotice,SoundFolder);
strcat(SoundPathNotice,"/beep1.wav");
//RUN, get a handle on program window
WndoEXE=FindWindow(NULL,"hookEXE");
return TRUE;
}//initialize DLL
DLLEXPORT void hookDLLTerm()
{//terminate DLL, remove hooks
BOOL bRet1=UnhookWindowsHookEx(hKeyboardHook);
BOOL bRet2=UnhookWindowsHookEx(hMouseHook);
}//terminate DLL, remove hooks
DLLEXPORT void ContinuePlaying(BOOL status)
{//continue playing or not
if(status)
{//continue playing
PostMessage(WndoEXE,DLL_INPUT_NOTICE,0,0);
}//continue playing
return;
}//continue playing or not
#define UnknownAction (nCode!=HC_ACTION)
#define BtnEitherDn (wParam==WM_LBUTTONDOWN)||(wParam==WM_NCLBUTTONDOWN)||(wParam==WM_RBUTTONDOWN)||(wParam==WM_NCRBUTTONDOWN)
#define BtnEitherUp (wParam==WM_LBUTTONUP)||(wParam==WM_NCLBUTTONUP)||(wParam==WM_RBUTTONUP)||(wParam==WM_NCRBUTTONUP)
#define BtnLeftDn (wParam==WM_LBUTTONDOWN)||(wParam==WM_NCLBUTTONDOWN)
#define BtnLeftClick (wParam==WM_LBUTTONUP)||(wParam==WM_NCLBUTTONUP)
#define BtnRightClick (wParam==WM_RBUTTONUP)||(wParam==WM_NCRBUTTONUP)
#define BtnRightDn (wParam==WM_RBUTTONDOWN)||(wParam==WM_NCRBUTTONDOWN)
#define DoubleClick (wParam==WM_LBUTTONDBLCLK)
#define MouseMoveMessage (wParam==WM_MOUSEMOVE)||(wParam==WM_NCMOUSEMOVE)
#define PointerMoved (abs(PointScreenUp.x-PointScreenDn.x)>12)||(abs(PointScreenUp.y-PointScreenDn.y)>12)
#define Extended (lParam&0x01000000)
#define KeyUp (lParam&0x80000000)
#define KeyWasDn (lParam&0x40000000)
//#define StopPlaying (wParam==VK_FINAL)//interprocess communication
#define GetOrSetPtrPos VK_SCROLL//record or restore pointer position
#define SwitchToWindow VK_LAUNCH_APP1//record or restore pointer position
#define Pad0Insert VK_INSERT
#define Pad1End VK_END
#define Pad2Dn VK_DOWN
#define Pad3PgDn VK_NEXT
#define Pad4Left VK_LEFT
#define Pad5Clear VK_CLEAR
#define Pad6Right VK_RIGHT
#define Pad7Home VK_HOME
#define Pad8Up VK_UP
#define Pad9PgUp VK_PRIOR
#define PadPDelete VK_DELETE
#define VK_A 0x41
#define VK_B 0x42
#define VK_C 0x43
#define VK_D 0x44
#define VK_E 0x45
#define VK_F 0x46
#define VK_G 0x47
#define VK_H 0x48
#define VK_I 0x49
#define VK_J 0x4A
#define VK_K 0x4B
#define VK_L 0x4C
#define VK_M 0x4D
#define VK_N 0x4E
#define VK_O 0x4F
#define VK_P 0x50
#define VK_Q 0x51
#define VK_R 0x52
#define VK_S 0x53
#define VK_T 0x54
#define VK_U 0x55
#define VK_V 0x56
#define VK_W 0x57
#define VK_X 0x58
#define VK_Y 0x59
#define VK_Z 0x5A
#define VK_0 0x30
#define VK_1 0x31
#define VK_2 0x32
#define VK_3 0x33
#define VK_4 0x34
#define VK_5 0x35
#define VK_6 0x36
#define VK_7 0x37
#define VK_8 0x38
#define VK_9 0x39