diff --git a/.gitignore b/.gitignore index 4f40838..2f4e377 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,4 @@ native/uiabridge/release native/uiabridge/Debug !native/uiabridge/bin !native/MsgHook/bin -!native/MsgHookTest/bin +!native/SetMsgHook/bin diff --git a/native/MsgHook.sln b/native/MsgHook.sln new file mode 100644 index 0000000..a18befb --- /dev/null +++ b/native/MsgHook.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MsgHook", "MsgHook\MsgHook.vcxproj", "{8E038A94-7D02-49E9-B9F6-5224D9D11225}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SetMsgHook", "SetMsgHook\SetMsgHook.vcxproj", "{34A73B55-8A93-4FB5-83CE-7759C1D23348}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Debug|Win32.ActiveCfg = Debug|Win32 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Debug|Win32.Build.0 = Debug|Win32 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Debug|x64.ActiveCfg = Debug|x64 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Debug|x64.Build.0 = Debug|x64 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Release|Win32.ActiveCfg = Release|Win32 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Release|Win32.Build.0 = Release|Win32 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Release|x64.ActiveCfg = Release|x64 + {8E038A94-7D02-49E9-B9F6-5224D9D11225}.Release|x64.Build.0 = Release|x64 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Debug|Win32.ActiveCfg = Debug|Win32 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Debug|Win32.Build.0 = Debug|Win32 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Debug|x64.ActiveCfg = Debug|Win32 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Release|Win32.ActiveCfg = Release|Win32 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Release|Win32.Build.0 = Release|Win32 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Release|x64.ActiveCfg = Release|Win32 + {34A73B55-8A93-4FB5-83CE-7759C1D23348}.Release|x64.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/native/MsgHook/MsgHook.cpp b/native/MsgHook/MsgHook.cpp index 20149a7..f383607 100644 --- a/native/MsgHook/MsgHook.cpp +++ b/native/MsgHook/MsgHook.cpp @@ -8,10 +8,9 @@ // MsgHook.cpp : Defines the exported functions for the DLL application. // - #include "stdafx.h" - +//to fix export declaration had to add .def file LRESULT CALLBACK CwpHookProc(int nCode, WPARAM wParam, LPARAM lParam) { COPYDATASTRUCT CDS; @@ -55,7 +54,11 @@ LRESULT CALLBACK CwpHookProc(int nCode, WPARAM wParam, LPARAM lParam) } } - BOOL bRes = (BOOL)SendMessage(pData->g_hWnd, WM_COPYDATA, 0, (LPARAM)(VOID*)&CDS); // ask the controlling program if the hook should be passed + //printf("debug: sending to hwnd (%ld) msg %d, wParam %ld, lParam %ld\n", pData->g_hWnd, Event.nCode, Event.wParam, Event.lParam); + if (cwps->hwnd != pData->g_hWnd) + { + BOOL bRes = (BOOL)SendMessage(pData->g_hWnd, WM_COPYDATA, 0, (LPARAM)(VOID*)&CDS); // ask the controlling program if the hook should be passed + } } return CallNextHookEx(pData->g_CwpHook, nCode, wParam, lParam); // pass hook to next handler //return bRes; // Don't tell the other hooks about this message. @@ -70,7 +73,7 @@ LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam) CDS.cbData = sizeof(Event); CDS.lpData = &Event; - //if (nCode == HC_ACTION) + if (nCode >=0 && nCode == HC_ACTION) { //For WH_GETMESSAGE hook a pointer to a MSG structure that contains details about the message. MSG *msg = (MSG *)lParam; @@ -84,19 +87,48 @@ LRESULT CALLBACK MsgHookProc(int nCode, WPARAM wParam, LPARAM lParam) //if (msg->message == WM_SETTEXT && msg->lParam != 0) // _tcscpy_s(Event.lParamStr, 25, (const wchar_t*)Event.lParam); //if (msg->message == WM_COMMAND || msg->message == WM_MENUCOMMAND) //infinite loop? - BOOL bRes = (BOOL)SendMessage(pData->g_hWnd, WM_COPYDATA, 0, (LPARAM)(VOID*)&CDS); // ask the controlling program if the hook should be passed + if (msg->hwnd != pData->g_hWnd) + { + BOOL bRes = (BOOL)SendMessage(pData->g_hWnd, WM_COPYDATA, 0, (LPARAM)(VOID*)&CDS); // ask the controlling program if the hook should be passed + } } return CallNextHookEx(pData->g_MsgHook, nCode, wParam, lParam); // pass hook to next handler //return bRes; // Don't tell the other hooks about this message. } +//support for 32-bit/64-bit apps means the dll might be different to match the process we want to see +extern "C" __declspec(dllexport) BOOL SetCustomMsgHookDll(const TCHAR * hookDll, const char * hookDllProcName) +{ + HMODULE dll = LoadLibrary(hookDll); //should provide full dll path and filename + if (dll == NULL) + { + TCHAR errorStr[200]; + _stprintf_s(errorStr, _T("Error loading hook library %s"), hookDll); + MessageBox(0, errorStr, _T("Set Hook Dll Error"), 0); + return false; + } + HOOKPROC addr = (HOOKPROC)GetProcAddress(dll, hookDllProcName); //should provide the 'CwpHookProc' + if (addr == NULL) + { + char errorStr[200]; + sprintf_s(errorStr, "Error loading hook library procedure %s", hookDllProcName); + MessageBoxA(0, errorStr, "Set Hook Dll Error", 0); + return false; + } + pData->g_hInstance = dll; + pData->g_CwpHookProc = addr; + return true; +} + extern "C" __declspec(dllexport) BOOL SetMsgHook(HWND callerHWnd, DWORD threadId) { - if(bStartingProcess) // if we're just starting the DLL for the first time, +// if(bStartingProcess) // if we're just starting the DLL for the first time, { pData->g_hWnd = callerHWnd; // remember the windows and hook handle for further instances - pData->g_CwpHook = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CwpHookProc, (HINSTANCE)pData->g_hInstance, threadId); + if (pData->g_CwpHookProc == NULL) + pData->g_CwpHookProc = (HOOKPROC)CwpHookProc; + pData->g_CwpHook = SetWindowsHookEx(WH_CALLWNDPROC, pData->g_CwpHookProc, (HINSTANCE)pData->g_hInstance, threadId); //pData->g_MsgHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)MsgHookProc, (HINSTANCE)pData->g_hInstance, threadId); if (pData->g_CwpHook == NULL) { TCHAR tmp[100]; @@ -106,11 +138,21 @@ extern "C" __declspec(dllexport) BOOL SetMsgHook(HWND callerHWnd, DWORD threadId return (pData->g_CwpHook != NULL); //pData->g_CwpHook != NULL && } - else + /*else { //MessageBox(0, _T("Error: Not starting process"), _T("Set Hook Error"), 0); return false; - } + }*/ +} + +extern "C" __declspec(dllexport) HHOOK GetCurrentHookHandle() +{ + return pData->g_CwpHook; //if NULL hook isn't running +} + +extern "C" __declspec(dllexport) void SetGlobalDLLInstance(HANDLE dllInstance) +{ + pData->g_hInstance = dllInstance; } extern "C" __declspec(dllexport) BOOL RemoveHook() @@ -127,7 +169,93 @@ extern "C" __declspec(dllexport) BOOL RemoveHook() BOOL ret = UnhookWindowsHookEx(pData->g_CwpHook); pData->g_hWnd = NULL; // reset data pData->g_CwpHook = NULL; + pData->g_CwpHookProc = NULL; return ret; } return false; +} + + +//testing if process 64 bit, needed to verify this dll can hook & attach to target process +typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); +LPFN_ISWOW64PROCESS fnIsWow64Process; + +extern "C" __declspec(dllexport) BOOL IsCurrentProcess64Bit() +{ + return IsProcess64Bit(_getpid()); +} + +extern "C" __declspec(dllexport) BOOL IsProcess64Bit(DWORD procId) +{ + SYSTEM_INFO stInfo; + GetNativeSystemInfo(&stInfo); // if native system is x86 skip wow64 test + if (stInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + return false; //printf( "Processor Architecture: Intel x86\n"); + + BOOL bIsWow64 = FALSE; + //IsWow64Process is not available on all supported versions of Windows. + //Use GetModuleHandle to get a handle to the DLL that contains the function + //and GetProcAddress to get a pointer to the function if available. + + fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); + if(fnIsWow64Process != NULL) + { + HANDLE procHandle = NULL;//GetCurrentProcess(); + procHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, procId); + if (!fnIsWow64Process(procHandle, &bIsWow64)) + { + //handle error + } + CloseHandle(procHandle); + if (bIsWow64) // NOT a native 64bit process + return false; + return true;// is a native 64bit process + } + return false; //some error finding function "IsWow64Process" assume not 64-bit +} + +extern "C" __declspec(dllexport) DWORD GetProcessMainThreadId(DWORD procId) +{ + +#ifndef MAKEULONGLONG + #define MAKEULONGLONG(ldw, hdw) ((ULONGLONG(hdw) << 32) | ((ldw) & 0xFFFFFFFF)) +#endif +#ifndef MAXULONGLONG + #define MAXULONGLONG ((ULONGLONG)~((ULONGLONG)0)) +#endif + + DWORD dwMainThreadID = 0; + ULONGLONG ullMinCreateTime = MAXULONGLONG; + //includes all threads in the system + HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap != INVALID_HANDLE_VALUE) { + THREADENTRY32 th32; + th32.dwSize = sizeof(THREADENTRY32); + BOOL bOK = TRUE; + //Enumerate all threads in the system and filter on th32OwnerProcessID = pid + for (bOK = Thread32First(hThreadSnap, &th32); bOK ; bOK = Thread32Next(hThreadSnap, &th32)) { + //if (th32.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(th32.th32OwnerProcessID)) { + if (th32.th32OwnerProcessID == procId && (th32.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(th32.th32OwnerProcessID))) { + //_tprintf(_T("DEBUG Enumerate Process (%ld) Thread Id: %ld\n"), procId, th32.th32ThreadID); + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, th32.th32ThreadID); + if (hThread) { + FILETIME afTimes[4] = {0}; + if (GetThreadTimes(hThread, &afTimes[0], &afTimes[1], &afTimes[2], &afTimes[3])) { + ULONGLONG ullTest = MAKEULONGLONG(afTimes[0].dwLowDateTime, afTimes[0].dwHighDateTime); + if (ullTest && ullTest < ullMinCreateTime) { //check each thread's creation time + ullMinCreateTime = ullTest; + dwMainThreadID = th32.th32ThreadID; // let it be main thread + } + } + CloseHandle(hThread); //must close opened thread + } + } + } +#ifndef UNDER_CE + CloseHandle(hThreadSnap); //close thread snapshot +#else + CloseToolhelp32Snapshot(hThreadSnap); //close thread snapshot +#endif + } + return dwMainThreadID; //returns main thread id or returns 0 if can't find it } \ No newline at end of file diff --git a/native/MsgHook/MsgHook.def b/native/MsgHook/MsgHook.def new file mode 100644 index 0000000..0176c73 --- /dev/null +++ b/native/MsgHook/MsgHook.def @@ -0,0 +1,5 @@ +LIBRARY MsgHook + +EXPORTS + CwpHookProc @1 + MsgHookProc @2 \ No newline at end of file diff --git a/native/MsgHook/MsgHook.vcxproj b/native/MsgHook/MsgHook.vcxproj index 108ce52..5f6a80f 100644 --- a/native/MsgHook/MsgHook.vcxproj +++ b/native/MsgHook/MsgHook.vcxproj @@ -87,6 +87,7 @@ Windows true + MsgHook.def @@ -99,6 +100,7 @@ Windows true + MsgHook.def @@ -115,6 +117,7 @@ true true true + MsgHook.def mkdir "$(ProjectDir)bin" @@ -135,6 +138,7 @@ copy /Y "$(TargetPath)" "$(ProjectDir)bin\MsgHook$(PlatformArchitecture)$(Target true true true + MsgHook.def mkdir "$(ProjectDir)bin" @@ -142,6 +146,10 @@ copy /Y "$(TargetPath)" "$(ProjectDir)bin\MsgHook$(PlatformArchitecture)$(Target + + + + @@ -149,6 +157,7 @@ copy /Y "$(TargetPath)" "$(ProjectDir)bin\MsgHook$(PlatformArchitecture)$(Target + diff --git a/native/MsgHook/MsgHook.vcxproj.filters b/native/MsgHook/MsgHook.vcxproj.filters index 6e37813..8a2e05b 100644 --- a/native/MsgHook/MsgHook.vcxproj.filters +++ b/native/MsgHook/MsgHook.vcxproj.filters @@ -22,6 +22,14 @@ Resource Files + + Source Files + + + Resource Files + + + @@ -39,6 +47,9 @@ Header Files + + Header Files + diff --git a/native/MsgHook/MsgHookTest.aps b/native/MsgHook/MsgHookTest.aps index ab709a6..0f73f84 100644 Binary files a/native/MsgHook/MsgHookTest.aps and b/native/MsgHook/MsgHookTest.aps differ diff --git a/native/MsgHook/MsgHookTest.rc b/native/MsgHook/MsgHookTest.rc index 8aaa3ec..5857274 100644 Binary files a/native/MsgHook/MsgHookTest.rc and b/native/MsgHook/MsgHookTest.rc differ diff --git a/native/MsgHook/MsgHookWindow.cpp b/native/MsgHook/MsgHookWindow.cpp index 7d975b9..58a306d 100644 --- a/native/MsgHook/MsgHookWindow.cpp +++ b/native/MsgHook/MsgHookWindow.cpp @@ -11,6 +11,7 @@ #include "stdafx.h" #include "resource.h" #include "MsgLookup.h" +#include "ResExtract.h" //#include "MsgHookTest.h" //#include "MsgHook.h" @@ -24,18 +25,25 @@ HWND mainHwnd = NULL; HMENU mainMenu = NULL; HWND txtbox = NULL; HWND targetHwnd = NULL; +DWORD targetPid = 0; const int txtboxSpacing = 2; +long msgCount = 0; -bool filterWmCommand = true; +//message filters flags +bool filterWmCommand = false; bool filterWmNotify = false; bool filterCustom = false; bool filterAbove = false; +TCHAR dll32bitName[500] = _T(""); +TCHAR dll64bitName[500] = _T(""); +char dllProcName[500] = "CwpHookProc"; //#define MAX_TEST_SIZE 100 //TCHAR targetClassname[MAX_TEST_SIZE] = _T("Notepad"); -TCHAR targetClassname[MAX_TEST_SIZE] = _T("WordPadClass"); +TCHAR targetProcessId[MAX_TEST_SIZE] = _T(""); +TCHAR targetClassname[MAX_TEST_SIZE] = _T(""); TCHAR targetHwndStr[MAX_TEST_SIZE] = _T(""); TCHAR testWmSettextL[MAX_TEST_SIZE] = _T("This is a test"); TCHAR testWmSettextW[MAX_TEST_SIZE] = _T("0"); @@ -117,30 +125,75 @@ void InitMsgFiltersAndLookup() void StartMessageHook() { AppendText(txtbox, _T("Starting Message Hook\r\n")); - targetHwnd = FindWindow(targetClassname, NULL); - - if (_tcscmp(targetHwndStr, _T("")) != 0) //if target HWND was used then override classname hwnd + //targetHwnd = FindWindow(targetClassname, NULL); + + TCHAR tmp[500]; + + DWORD tid = 0; + if (_tcscmp(targetHwndStr, _T("")) != 0) //if target HWND was used { TCHAR *stopStr; targetHwnd = (HWND)_tcstol(targetHwndStr, &stopStr, 10); + tid = GetWindowThreadProcessId(targetHwnd, NULL); + _stprintf_s(tmp, _T("Target Handle: %ld, and Thread Id: %ld\r\n"), targetHwnd, tid); } - DWORD tid = GetWindowThreadProcessId(targetHwnd, NULL); - + targetPid = 0; + if (_tcscmp(targetProcessId, _T("")) != 0) //if target pid was used + { + TCHAR *stopStr; + targetPid = (DWORD)_tcstol(targetProcessId, &stopStr, 10); + tid = GetProcessMainThreadId(targetPid); + _stprintf_s(tmp, _T("Target PId: %ld, and Thread Id: %ld\r\n"), targetPid, tid); + } + InitMsgFiltersAndLookup(); //InitializeMsgLookup(); - TCHAR tmp[50]; - _stprintf_s(tmp, _T("Target Handle: %ld, and Thread Id: %ld\r\n"), targetHwnd, tid); AppendText(txtbox, tmp); //block self/global msg hook - if (targetHwnd == NULL || tid == 0) { - AppendText(txtbox, _T("Target window not found\r\n")); + if (tid == 0) { + AppendText(txtbox, _T("Target thread not found\r\n")); return; } - //if (InitMsgHook(mainHwnd, tid)) + if (targetPid != 0) // handle various types of bit matching + { + BOOL current64bit = IsCurrentProcess64Bit(); + if (IsProcess64Bit(targetPid) && current64bit) + { + _stprintf_s(tmp, _T("Target PId (%ld) is a matching 64 bit process\r\n"), targetPid); + SetCustomMsgHookDll(dll64bitName, dllProcName); + } + else if(!IsProcess64Bit(targetPid) && !current64bit) + { + _stprintf_s(tmp, _T("Target PId (%ld) is a matching 32 bit process\r\n"), targetPid); + SetCustomMsgHookDll(dll32bitName, dllProcName); + } + else + { + _stprintf_s(tmp, _T("Target PId (%ld) is a not matching bit process\r\n"), targetPid); + AppendText(txtbox, tmp); + TCHAR *dllname = dll32bitName; + TCHAR *exename = _T("SetMsgHook32.exe"); + int setMsgHookRes = IDR_SETMH32; + if (IsProcess64Bit(targetPid)) + { + dllname = dll64bitName; + exename = _T("SetMsgHook64.exe"); + setMsgHookRes = IDR_SETMH64; + } + _stprintf_s(tmp, _T("%s %s 0 %d"), exename, dllname, targetPid); + RunResource(setMsgHookRes, tmp); + //EnableMenuItem(mainMenu, ID_FILE_STOPHOOK, MF_ENABLED); + //EnableMenuItem(mainMenu, ID_FILE_STARTHOOK, MF_DISABLED | MF_GRAYED); + _tcscat_s(tmp, 500, _T("\r\n")); + AppendText(txtbox, tmp); + return; + } + AppendText(txtbox, tmp); + } if (SetMsgHook(mainHwnd, tid)) { EnableMenuItem(mainMenu, ID_FILE_STOPHOOK, MF_ENABLED); @@ -158,6 +211,7 @@ void StopMessageHook() AppendText(txtbox, TEXT("Stopping Message Hook\r\n")); //KillHook(); RemoveHook(); + msgCount = 0; } bool OnCopyData(COPYDATASTRUCT* pCopyDataStruct) // WM_COPYDATA lParam will have this struct @@ -193,10 +247,11 @@ bool OnCopyData(COPYDATASTRUCT* pCopyDataStruct) // WM_COPYDATA lParam will have } if (_tcscmp(msgName, _T("")) != 0) { + ++msgCount; TCHAR msgHwndClassname[20]; GetClassName(Event.hWnd, msgHwndClassname, 20); TCHAR tmp[200]; - _stprintf_s(tmp, _T("hwnd: %ld (%s), msg: %s (%ld), wparam: '%s'[%ld], lparam: '%s'{%ld}\r\n"), Event.hWnd, msgHwndClassname, msgName, Event.nCode, Event.wParamStr, Event.wParam, Event.lParamStr,Event.lParam); + _stprintf_s(tmp, _T("<%07ld> hwnd: %ld (%s), msg: %s (%ld), wparam: '%s'[%ld], lparam: '%s'{%ld}\r\n"), msgCount, Event.hWnd, msgHwndClassname, msgName, Event.nCode, Event.wParamStr, Event.wParam, Event.lParamStr,Event.lParam); AppendText(txtbox, tmp); } } @@ -225,6 +280,11 @@ void SendWmCommand() //ID_TESTMSGS_WM long wparam = _tcstol(testWmCommandW, &stopStr, 10); long lparam = _tcstol(testWmCommandL, &stopStr, 10); SendMessage(sendHwnd, WM_COMMAND, wparam, lparam); + + /* + TCHAR tmp[500]; + _stprintf_s(tmp, _T("hook handle %ld\r\n"), (long)GetCurrentHookHandle()); + AppendText(txtbox, tmp); */ } void HotKeyPressed(WPARAM wParam) @@ -254,6 +314,19 @@ extern "C" __declspec(dllexport) void CreateMsgHookWindow(LPTSTR lpCmdLine) int APIENTRY StartWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { + // get this Dlls path, by default set both 32 and 64 bit names the same + if (_tcscmp(dll32bitName, _T("")) == 0 && _tcscmp(dll64bitName, _T("")) == 0) + { + HMODULE hm = NULL; + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,(LPCWSTR) &StartWinMain, &hm)) + { + int ret = GetLastError(); + fprintf(stderr, "GetModuleHandle returned %d\n", ret); + } + GetModuleFileName(hm, dll32bitName, sizeof(dll32bitName)); + GetModuleFileName(hm, dll64bitName, sizeof(dll64bitName)); + //MessageBox(0, dll32bitname, dll64bitname, 0); + } UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); @@ -275,6 +348,17 @@ int APIENTRY StartWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR l hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MSGHOOKTEST)); + if (lpCmdLine != NULL) //process command line args + { + if (_tcslen(lpCmdLine) > 0) + { + TCHAR *stopStr; + targetPid = (DWORD)_tcstol(lpCmdLine, &stopStr, 10); + _stprintf_s(targetProcessId, _T("%ld"), (long)targetPid); + StartMessageHook(); + } + } + // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { @@ -315,7 +399,7 @@ ATOM MyRegisterClass(HINSTANCE hInstance) wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; - wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MSGHOOKTEST)); + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MSGHOOKICO)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_MSGHOOKTEST); @@ -419,6 +503,29 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case ID_TESTMSGS_WMCOM: SendWmCommand(); break; + case ID_PROC64TEST: + if (_tcscmp(targetProcessId, _T("")) != 0) //if target pid was used + { + TCHAR tmp[500]; + TCHAR *stopStr; + targetPid = (DWORD)_tcstol(targetProcessId, &stopStr, 10); + BOOL current64bit = IsCurrentProcess64Bit(); + if (IsProcess64Bit(targetPid) && current64bit) + _stprintf_s(tmp, _T("Target pid (%ld) is a matching 64 bit process\r\n"), targetPid); + else if(!IsProcess64Bit(targetPid) && !current64bit) + _stprintf_s(tmp, _T("Target pid (%ld) is a matching 32 bit process\r\n"), targetPid); + else if (IsProcess64Bit(targetPid)) + _stprintf_s(tmp, _T("Target pid (%ld) is 64 bit process\r\n"), targetPid); + else + _stprintf_s(tmp, _T("Target pid (%ld) is 32 bit process\r\n"), targetPid); + AppendText(txtbox, tmp); + //ExtractResource(IDR_SETMH32, _T("SetMsgHook32.exe")); + //_stprintf_s(tmp, _T(" %s %ld %d"), dll32bitName, (long)mainHwnd, targetPid); + //RunResource(IDR_SETMH32, tmp); + + //MessageBox(0, , _T("64 bit Test"), 0); + } + break; case ID_FILE_SETTINGS: DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc); break; @@ -468,7 +575,8 @@ INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_INITDIALOG: { //IDC_EDIT1 - SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, 0 , (LPARAM)targetClassname); + //SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, 0 , (LPARAM)targetClassname); + SendDlgItemMessage(hDlg, IDC_TARGETPID, WM_SETTEXT, 0 , (LPARAM)targetProcessId); if (filterWmCommand) SendDlgItemMessage(hDlg, IDC_CHECK_CMD, BM_SETCHECK, BST_CHECKED, 0); if (filterWmNotify) @@ -489,7 +597,8 @@ INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) case WM_COMMAND: if (LOWORD(wParam) == IDOK) //only save on OK { - GetDlgItemText(hDlg, IDC_EDIT1, targetClassname, 100); + //GetDlgItemText(hDlg, IDC_EDIT1, targetClassname, MAX_TEST_SIZE); + GetDlgItemText(hDlg, IDC_TARGETPID, targetProcessId, MAX_TEST_SIZE); GetDlgItemText(hDlg, IDC_WMCOMW, testWmCommandW, MAX_TEST_SIZE); GetDlgItemText(hDlg, IDC_WMCOML, testWmCommandL, MAX_TEST_SIZE); GetDlgItemText(hDlg, IDC_WMSETW, testWmSettextW, MAX_TEST_SIZE); diff --git a/native/MsgHook/ResExtract.h b/native/MsgHook/ResExtract.h new file mode 100644 index 0000000..e7662ce --- /dev/null +++ b/native/MsgHook/ResExtract.h @@ -0,0 +1,86 @@ +//This function will extract a binary resource. +// +//IDR_SETMH64 BINARY MOVEABLE PURE "..\\SetMsgHook\\bin\\SetMsgHook64.exe" +//IDR_SETMH32 BINARY MOVEABLE PURE "..\\SetMsgHook\\bin\\SetMsgHook32.exe" + +#include "stdafx.h" + +void ExtractResource(const WORD nID, LPCTSTR szFilename) +{ + const HINSTANCE hInstance = (HINSTANCE)pData->g_hInstance;//GetModuleHandle(NULL); + HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(nID), _T("BINARY"));// _ASSERTE(hResource); + if (hResource == NULL) // no resource found. + { + //MessageBoxA(0, "error, no resource found", "error", 0); + printf("error, resource %d not found\n", nID); + return; + } + HGLOBAL hFileResource = LoadResource(hInstance, hResource);// _ASSERTE(hFileResource); + LPVOID lpFile = LockResource(hFileResource); + + DWORD dwSize = SizeofResource(hInstance, hResource); + + // Open the file and filemap + HANDLE hFile = CreateFile(szFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE hFilemap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL); + + // Get a pointer to write to + LPVOID lpBaseAddress = MapViewOfFile(hFilemap, FILE_MAP_WRITE, 0, 0, 0); + + // Write the file + CopyMemory(lpBaseAddress, lpFile, dwSize); + + // Unmap the file and close the handles + UnmapViewOfFile(lpBaseAddress); + CloseHandle(hFilemap); + CloseHandle(hFile); + +} + +void RunResource(const WORD nID, LPWSTR params) +{ + TCHAR tmpFilename[500]; + TCHAR tmpPath[500]; + GetTempPath(500, tmpPath); + if (GetTempFileName(tmpPath, _T(""), 0, tmpFilename) == 0) + { + MessageBox(0,_T("Error getting temp file name"), _T("Error"), 0); + return; + } + + ExtractResource(nID, tmpFilename); + //MessageBox(0, tmpFilename, _T("tmp file2"), 0); + + STARTUPINFO si; + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + + PROCESS_INFORMATION pi; + ZeroMemory( &pi, sizeof(pi) ); + + si.wShowWindow = SW_MINIMIZE; + + // Start the child process. + if(!CreateProcess(tmpFilename, // No module name (use command line) + params, // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi )) // Pointer to PROCESS_INFORMATION structure + { + MessageBox(0, _T("CreateProcess failed"), _T("error"), 0); + printf( "CreateProcess failed (%d).\n", GetLastError() ); + return; + } + //MessageBox(0, tmpFilename, _T("tmp file3"), 0); + + // Wait until child process exits. + //WaitForSingleObject( pi.hProcess, INFINITE ); + // Close process and thread handles. + //CloseHandle( pi.hProcess ); + //CloseHandle( pi.hThread ); +} \ No newline at end of file diff --git a/native/MsgHook/bin/MsgHook32.dll b/native/MsgHook/bin/MsgHook32.dll index c5b0807..493932a 100644 Binary files a/native/MsgHook/bin/MsgHook32.dll and b/native/MsgHook/bin/MsgHook32.dll differ diff --git a/native/MsgHook/bin/MsgHook64.dll b/native/MsgHook/bin/MsgHook64.dll index 780332f..7c1bd4c 100644 Binary files a/native/MsgHook/bin/MsgHook64.dll and b/native/MsgHook/bin/MsgHook64.dll differ diff --git a/native/MsgHook/dllmain.cpp b/native/MsgHook/dllmain.cpp index 5882787..f9560c6 100644 --- a/native/MsgHook/dllmain.cpp +++ b/native/MsgHook/dllmain.cpp @@ -15,6 +15,7 @@ BOOL APIENTRY DllMain( HMODULE hModule, { case DLL_PROCESS_ATTACH: { + //printf("debug DLL_PROCESS_ATTACH hModule: %ld\n", (long)hModule); TCHAR szBaseName[_MAX_FNAME], szTmp[_MAX_FNAME]; memset((void *)&szBaseName, '\0', sizeof(TCHAR) * _MAX_FNAME); @@ -23,6 +24,10 @@ BOOL APIENTRY DllMain( HMODULE hModule, //_wsplitpath(szTmp, NULL, NULL, szBaseName, NULL); wcscat_s(szBaseName, TEXT("MsgHookSharedMem")); // add specifier string + if (IsCurrentProcess64Bit()) + wcscat_s(szBaseName, TEXT("64")); // add bit specifier + else + wcscat_s(szBaseName, TEXT("32")); // add bit specifier hMappedFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(GLOBALDATA), szBaseName); pData = (GLOBALDATA*)MapViewOfFile(hMappedFile, FILE_MAP_WRITE, 0, 0, 0); @@ -34,6 +39,13 @@ BOOL APIENTRY DllMain( HMODULE hModule, pData->g_hWnd = NULL; // and initialize the other handles pData->g_CwpHook = NULL; pData->g_MsgHook = NULL; + pData->g_CwpHookProc = NULL; + } + else + { + //open + hMappedFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, szBaseName); + pData = (GLOBALDATA*)MapViewOfFile(hMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); } DisableThreadLibraryCalls((HMODULE)hModule); } diff --git a/native/MsgHook/org_synthuse_MsgHook.cpp b/native/MsgHook/org_synthuse_MsgHook.cpp index 3dc824b..b851c18 100644 --- a/native/MsgHook/org_synthuse_MsgHook.cpp +++ b/native/MsgHook/org_synthuse_MsgHook.cpp @@ -8,6 +8,30 @@ #include "stdafx.h" #include "org_synthuse_MsgHook.h" +/* + * Class: org_synthuse_MsgHook + * Method: initialize + * Signature: (Ljava/lang/String;Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_initialize(JNIEnv *env, jobject obj, jstring jdll32bitname, jstring jdll64bitname) +{ + const char *tdll32bitname = env->GetStringUTFChars(jdll32bitname, 0);//convert string + + memset((void *)&dll32bitName, '\0', sizeof(TCHAR) * MAX_TEST_SIZE); // set TCHAR array to all 0 + int tstrLen = MultiByteToWideChar(CP_UTF8, 0, tdll32bitname, (int)strlen(tdll32bitname), NULL, 0); //get t len + MultiByteToWideChar(CP_UTF8, 0, tdll32bitname, (int)strlen(tdll32bitname), dll32bitName, tstrLen); // convert char to tchar + + env->ReleaseStringUTFChars(jdll32bitname, tdll32bitname); //release string + + const char *tdll64bitname = env->GetStringUTFChars(jdll64bitname, 0);//convert string + + memset((void *)&dll64bitName, '\0', sizeof(TCHAR) * MAX_TEST_SIZE); // set TCHAR array to all 0 + tstrLen = MultiByteToWideChar(CP_UTF8, 0, tdll64bitname, (int)strlen(tdll64bitname), NULL, 0); //get t len + MultiByteToWideChar(CP_UTF8, 0, tdll64bitname, (int)strlen(tdll64bitname), dll64bitName, tstrLen); // convert char to tchar + + env->ReleaseStringUTFChars(jdll64bitname, tdll64bitname); //release string + return true; +} /* * Class: org_synthuse_MsgHook @@ -48,6 +72,12 @@ JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_setMsgHookWindowTargetClass return true; } +JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_setMsgHookWindowTargetPid(JNIEnv *env, jobject obj, jint jpid) +{ + _stprintf_s(targetProcessId, _T("%ld"), (long)jpid); + return true; +} + /* * Class: org_synthuse_MsgHook * Method: setMessageHook diff --git a/native/MsgHook/org_synthuse_MsgHook.h b/native/MsgHook/org_synthuse_MsgHook.h index 6e1e704..5bd9535 100644 --- a/native/MsgHook/org_synthuse_MsgHook.h +++ b/native/MsgHook/org_synthuse_MsgHook.h @@ -7,6 +7,14 @@ #ifdef __cplusplus extern "C" { #endif +/* + * Class: org_synthuse_MsgHook + * Method: initialize + * Signature: (Ljava/lang/String;Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_initialize + (JNIEnv *, jobject, jstring, jstring); + /* * Class: org_synthuse_MsgHook * Method: createMsgHookWindow @@ -25,11 +33,11 @@ JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_setMsgHookWindowTargetHwnd /* * Class: org_synthuse_MsgHook - * Method: setMsgHookWindowTargetClass - * Signature: (Ljava/lang/String;)Z + * Method: setMsgHookWindowTargetPid + * Signature: (I)Z */ -JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_setMsgHookWindowTargetClass - (JNIEnv *, jobject, jstring); +JNIEXPORT jboolean JNICALL Java_org_synthuse_MsgHook_setMsgHookWindowTargetPid + (JNIEnv *, jobject, jint); /* * Class: org_synthuse_MsgHook diff --git a/native/MsgHook/resource.h b/native/MsgHook/resource.h index efdecfc..24aed16 100644 Binary files a/native/MsgHook/resource.h and b/native/MsgHook/resource.h differ diff --git a/native/MsgHook/stdafx.h b/native/MsgHook/stdafx.h index d146995..055ffbf 100644 --- a/native/MsgHook/stdafx.h +++ b/native/MsgHook/stdafx.h @@ -11,11 +11,14 @@ // Windows Header Files: #include #include +#include #include #include #include +#include //CreateToolhelp32Snapshot #pragma comment( lib, "psapi.lib" ) +//#pragma comment( lib, "kernel32.lib" ) // TODO: reference additional headers your program requires here @@ -37,14 +40,23 @@ typedef struct //HHOOK g_hHook; HWND g_hWnd; HANDLE g_hInstance; + HOOKPROC g_CwpHookProc; }GLOBALDATA; #ifndef GLOBAL_VARS_H // header guards #define GLOBAL_VARS_H extern "C" __declspec(dllexport) void CreateMsgHookWindow(LPTSTR lpCmdLine); +extern "C" __declspec(dllexport) BOOL SetCustomMsgHookDll(const TCHAR * hookDll, const char * hookDllProcName); extern "C" __declspec(dllexport) BOOL SetMsgHook(HWND callerHWnd, DWORD threadId); +extern "C" __declspec(dllexport) HHOOK GetCurrentHookHandle(); +extern "C" __declspec(dllexport) void SetGlobalDLLInstance(HANDLE dllInstance); extern "C" __declspec(dllexport) BOOL RemoveHook(); +extern "C" __declspec(dllexport) BOOL IsCurrentProcess64Bit(); +extern "C" __declspec(dllexport) BOOL IsProcess64Bit(DWORD procId); +extern "C" __declspec(dllexport) DWORD GetProcessMainThreadId(DWORD procId); + +//void ExtractResource(const WORD nID, LPCTSTR szFilename); //Global variables , remember not to initialize here extern HANDLE hMappedFile; @@ -53,6 +65,10 @@ extern bool bStartingProcess; #define MAX_TEST_SIZE 100 extern TCHAR targetHwndStr[MAX_TEST_SIZE]; +extern TCHAR targetProcessId[MAX_TEST_SIZE]; extern TCHAR targetClassname[MAX_TEST_SIZE]; +extern TCHAR dll32bitName[500]; +extern TCHAR dll64bitName[500]; +extern char dllProcName[500]; #endif \ No newline at end of file diff --git a/native/SetMsgHook/MsgHook.h b/native/SetMsgHook/MsgHook.h new file mode 100644 index 0000000..929d8e1 --- /dev/null +++ b/native/SetMsgHook/MsgHook.h @@ -0,0 +1,129 @@ +/* + * Copyright 2014, Synthuse.org + * Released under the Apache Version 2.0 License. + * + * last modified by ejakubowski7@gmail.com +*/ + +#include + +typedef struct +{ + HHOOK g_CwpHook; + HHOOK g_MsgHook; + //HHOOK g_hHook; + HWND g_hWnd; + HANDLE g_hInstance; + HOOKPROC g_CwpHookProc; +}GLOBALDATA; + +//#define MSGHOOKER_FILE TEXT("MsgHook.dll") +TCHAR MSGHOOK_DLL_NAME[MAX_NAME_SIZE] = _T("MsgHook.dll"); + +HINSTANCE msgHookDll; + +//void CreateMsgHookWindow(LPTSTR lpCmdLine) +typedef VOID (* CREATEMSGHOOKWINDOW)(LPTSTR); +CREATEMSGHOOKWINDOW CreateMsgHookWindow; + +//BOOL SetCustomMsgHookDll(const TCHAR * hookDll, const char * hookDllProcName) +typedef BOOL (* SETCUSTOMMSGHOOKDLL)(LPCTSTR, LPCSTR); +SETCUSTOMMSGHOOKDLL SetCustomMsgHookDll; + +//BOOL SetMsgHook(HWND callerHWnd, DWORD threadId) +typedef BOOL (* SETMSGHOOK)(HWND, DWORD); +SETMSGHOOK SetMsgHook; + +//HHOOK GetCurrentHookHandle() +typedef HHOOK (* GETCURRENTHOOKHANDLE)(VOID); +GETCURRENTHOOKHANDLE GetCurrentHookHandle; + +//void SetGlobalDLLInstance(HANDLE dllInstance) +typedef VOID (* SETGLOBALDLLINSTANCE)(HANDLE); +SETGLOBALDLLINSTANCE SetGlobalDLLInstance; + +//BOOL RemoveHook() +typedef BOOL (* REMOVEHOOK)(VOID); +REMOVEHOOK RemoveHook; + +// DWORD GetProcessMainThreadId(DWORD procId) +typedef DWORD (* GETPROCESSMAINTHREADID)(DWORD); +GETPROCESSMAINTHREADID GetProcessMainThreadId; + + +typedef struct +{ + HWND hWnd; + int nCode; + DWORD dwHookType; + WPARAM wParam; + LPARAM lParam; + TCHAR wParamStr[25]; + TCHAR lParamStr[25]; +}HEVENT; + +/* +typedef struct { + DWORD vkCode; + DWORD scanCode; + DWORD flags; + DWORD time; + ULONG_PTR dwExtraInfo; +} KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT; +*/ + +void MsgHook_CreateMsgHookWindow(LPTSTR args) +{ + msgHookDll = LoadLibrary(MSGHOOK_DLL_NAME); + if (msgHookDll != NULL) + { + CreateMsgHookWindow = (CREATEMSGHOOKWINDOW)GetProcAddress(msgHookDll, "CreateMsgHookWindow"); + if (CreateMsgHookWindow) + { + CreateMsgHookWindow(args); + } + } +} + +BOOL MsgHook_SetMsgHook(HWND hw, int threadId) +{ + msgHookDll = LoadLibrary(MSGHOOK_DLL_NAME); + if (msgHookDll != NULL) + { + SetMsgHook = (SETMSGHOOK)GetProcAddress(msgHookDll, "SetMsgHook"); + GetCurrentHookHandle = (GETCURRENTHOOKHANDLE)GetProcAddress(msgHookDll, "GetCurrentHookHandle"); + SetGlobalDLLInstance = (SETGLOBALDLLINSTANCE)GetProcAddress(msgHookDll, "SetGlobalDLLInstance"); + RemoveHook = (REMOVEHOOK)GetProcAddress(msgHookDll, "RemoveHook"); + if (SetMsgHook) + { + //printf("LoadLibrary MSGHOOK %ld\n", (long)msgHookDll); + SetGlobalDLLInstance(msgHookDll); + return SetMsgHook(hw, threadId); + } + } + return false; +} + +void MsgHook_RemoveHook() +{ + if (RemoveHook) + RemoveHook(); + + if (msgHookDll != NULL) + FreeLibrary(msgHookDll); +} + +DWORD MsgHook_GetProcessMainThreadId(DWORD procId) +{ + msgHookDll = LoadLibrary(MSGHOOK_DLL_NAME); + if (msgHookDll != NULL) + { + GetProcessMainThreadId = (GETPROCESSMAINTHREADID)GetProcAddress(msgHookDll, "GetProcessMainThreadId"); + if (GetProcessMainThreadId) + { + return GetProcessMainThreadId(procId); + } + } + printf("error, failed loading library"); + return 0; +} diff --git a/native/SetMsgHook/ReadMe.txt b/native/SetMsgHook/ReadMe.txt new file mode 100644 index 0000000..48ecfa9 --- /dev/null +++ b/native/SetMsgHook/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : SetMsgHook Project Overview +======================================================================== + +AppWizard has created this SetMsgHook application for you. + +This file contains a summary of what you will find in each of the files that +make up your SetMsgHook application. + + +SetMsgHook.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +SetMsgHook.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +SetMsgHook.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named SetMsgHook.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/native/SetMsgHook/SetMsgHook.cpp b/native/SetMsgHook/SetMsgHook.cpp new file mode 100644 index 0000000..b93cb8f --- /dev/null +++ b/native/SetMsgHook/SetMsgHook.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2014, Synthuse.org + * Released under the Apache Version 2.0 License. + * + * last modified by ejakubowski7@gmail.com +*/ +// SetMsgHook.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "MsgHook.h" + + +int _tmain(int argc, _TCHAR* argv[]) +{ + if (argc == 1) //no args passed, show MsgHook Viewer gui + { + MsgHook_CreateMsgHookWindow(NULL); + return 0; + } + + HWND hookHwnd = NULL; + long procId = 0; + TCHAR *stopStr; + + for (int i = 1 ; i < argc ; i++) + { + if (_tcscmp(argv[i], _T("?")) == 0 || _tcscmp(argv[i], _T("-?")) == 0 || argc == 3 || argc > 4) + { + printf("SetMsgHook version 1.0 by Edward Jakubowski \n\n"); + printf("Usage: SetMsgHook.exe [(MSG_HOOK_DLL) (MSG_HOOK_HWND_OR_ZERO) (TARGET_PID)] [MSG_HOOK_DLL] [?] \n\n"); + printf(" Additional Notes:\n"); + printf(" Message Hook Viewer Gui - To open the gui you must provide the path to the msg hook dll as the "); + printf("only argument when running SetMsgHook.exe. Also setting the (MSG_HOOK_HWND) argument to 0 (zero) "); + printf("will start the gui and message hook on the given Process Id.\n\n"); + HWND currentHwnd = FindWindow(_T("MSGHOOKVIEW"), NULL); + printf(" Current MSG_HOOK_HWND: %ld\n", (long)currentHwnd); + return 0; + } + if (i == 1) + _tcsncpy_s(MSGHOOK_DLL_NAME, MAX_NAME_SIZE, argv[i], _TRUNCATE); + if (i == 2) + hookHwnd = (HWND)_tcstol(argv[i], &stopStr, 10); + if (i == 3) + procId = (long)_tcstol(argv[i], &stopStr, 10); + } + + if (argc == 2) //one arg passed (dll), show MsgHook Viewer gui + { + printf("Starting msg hook viewer..."); + MsgHook_CreateMsgHookWindow(NULL); + return 0; + } + if (argc == 4 && hookHwnd == 0) + { + printf("Starting msg hook viewer on pid %ld...", (long)procId); + TCHAR tmp[100]; + _stprintf_s(tmp, _T("%ld"), (long)procId); + MsgHook_CreateMsgHookWindow(tmp); + return 0; + } + + char tmp[MAX_NAME_SIZE]; + size_t convertedCnt = 0; + wcstombs_s(&convertedCnt, tmp, MAX_NAME_SIZE, MSGHOOK_DLL_NAME, _TRUNCATE); + printf("MsgHook DLL: %s, HWND: %ld, PID: %ld", tmp, (long)hookHwnd, procId); + DWORD threadId = MsgHook_GetProcessMainThreadId(procId); + printf(", ThreadId: %ld\n", (long)threadId); + + if (MsgHook_SetMsgHook(hookHwnd, threadId)) + printf("Hook successfully initialized\n"); + else + { + printf("Hook failed to initialize\n"); + return -1; + } + + //don't exit SetMsgHook until hooked process exits + HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, procId); + while(WaitForSingleObject(process, 0) == WAIT_TIMEOUT) + { + Sleep(1000); //check once per second + if (GetCurrentHookHandle() == NULL) + { + printf("unhooked."); + break; + } + } + CloseHandle(process); + MsgHook_RemoveHook(); + + //_getch(); + printf("done."); + return 0; +} + diff --git a/native/SetMsgHook/SetMsgHook.vcxproj b/native/SetMsgHook/SetMsgHook.vcxproj new file mode 100644 index 0000000..d9ee930 --- /dev/null +++ b/native/SetMsgHook/SetMsgHook.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {34A73B55-8A93-4FB5-83CE-7759C1D23348} + Win32Proj + SetMsgHook + + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + false + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + mkdir "$(ProjectDir)bin" +copy /Y "$(TargetPath)" "$(ProjectDir)bin\SetMsgHook$(PlatformArchitecture)$(TargetExt)" + + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + mkdir "$(ProjectDir)bin" +copy /Y "$(TargetPath)" "$(ProjectDir)bin\SetMsgHook$(PlatformArchitecture)$(TargetExt)" + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + mkdir "$(ProjectDir)bin" +copy /Y "$(TargetPath)" "$(ProjectDir)bin\SetMsgHook$(PlatformArchitecture)$(TargetExt)" + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + mkdir "$(ProjectDir)bin" +copy /Y "$(TargetPath)" "$(ProjectDir)bin\SetMsgHook$(PlatformArchitecture)$(TargetExt)" + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/native/SetMsgHook/SetMsgHook.vcxproj.filters b/native/SetMsgHook/SetMsgHook.vcxproj.filters new file mode 100644 index 0000000..ff22488 --- /dev/null +++ b/native/SetMsgHook/SetMsgHook.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/native/SetMsgHook/SetMsgHook.vcxproj.user b/native/SetMsgHook/SetMsgHook.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/native/SetMsgHook/SetMsgHook.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/native/SetMsgHook/bin/SetMsgHook32.exe b/native/SetMsgHook/bin/SetMsgHook32.exe new file mode 100644 index 0000000..8f9ede1 Binary files /dev/null and b/native/SetMsgHook/bin/SetMsgHook32.exe differ diff --git a/native/SetMsgHook/bin/SetMsgHook64.exe b/native/SetMsgHook/bin/SetMsgHook64.exe new file mode 100644 index 0000000..4581d77 Binary files /dev/null and b/native/SetMsgHook/bin/SetMsgHook64.exe differ diff --git a/native/SetMsgHook/build.bat b/native/SetMsgHook/build.bat new file mode 100644 index 0000000..626076c --- /dev/null +++ b/native/SetMsgHook/build.bat @@ -0,0 +1,6 @@ +REM set path=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin;%path% + +%WinDir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /p:configuration=release /p:platform=x64 %* +%WinDir%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe /p:configuration=release /p:platform=win32 %* + +pause \ No newline at end of file diff --git a/native/SetMsgHook/stdafx.cpp b/native/SetMsgHook/stdafx.cpp new file mode 100644 index 0000000..77e5e87 --- /dev/null +++ b/native/SetMsgHook/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// SetMsgHook.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/native/SetMsgHook/stdafx.h b/native/SetMsgHook/stdafx.h new file mode 100644 index 0000000..fe28c4b --- /dev/null +++ b/native/SetMsgHook/stdafx.h @@ -0,0 +1,23 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include +#include + + +// TODO: reference additional headers your program requires here + +#ifndef GLOBAL_VARS_H // header guards +#define GLOBAL_VARS_H + +#define MAX_NAME_SIZE 500 +extern TCHAR MSGHOOK_DLL_NAME[MAX_NAME_SIZE]; + +#endif \ No newline at end of file diff --git a/native/SetMsgHook/targetver.h b/native/SetMsgHook/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/native/SetMsgHook/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/src/org/synthuse/Api.java b/src/org/synthuse/Api.java index f3d26f3..231e4db 100644 --- a/src/org/synthuse/Api.java +++ b/src/org/synthuse/Api.java @@ -20,12 +20,14 @@ import com.sun.jna.platform.win32.WinDef.*; import com.sun.jna.platform.win32.Advapi32Util; import com.sun.jna.platform.win32.BaseTSD.LONG_PTR; import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; +import com.sun.jna.platform.win32.WinBase.SYSTEM_INFO; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinReg; import com.sun.jna.platform.win32.WinUser; import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER; import com.sun.jna.platform.win32.WinUser.WNDENUMPROC; +import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.StdCallLibrary.StdCallCallback; import com.sun.jna.win32.W32APIOptions; @@ -323,6 +325,9 @@ public class Api { boolean GetDiskFreeSpaceEx(String lpDirectoryName, LARGE_INTEGER.ByReference lpFreeBytesAvailable, LARGE_INTEGER.ByReference lpTotalNumberOfBytes, LARGE_INTEGER.ByReference lpTotalNumberOfFreeBytes); int GetLastError(); Pointer OpenProcess(int dwDesiredAccess, boolean bInheritHandle, Pointer pointer); + boolean CloseHandle(HANDLE hObject); + void GetNativeSystemInfo(SYSTEM_INFO lpSystemInfo); + boolean IsWow64Process(HANDLE hProcess, IntByReference Wow64Process); } @@ -627,6 +632,36 @@ public class Api { return false; } + public static boolean isProcess64bit(int pid) + { + try { + SYSTEM_INFO lpSystemInfo = new SYSTEM_INFO(); + Kernel32.instance.GetNativeSystemInfo(lpSystemInfo); + if (lpSystemInfo.processorArchitecture.dwOemID.intValue() == 0) + { + System.out.println("intel x86"); //not a 64 bit os + return false; + } + + Pointer process = Kernel32.instance.OpenProcess(Api.PROCESS_QUERY_INFORMATION | Api.PROCESS_VM_READ, false, new Pointer(pid)); + IntByReference isWow64 = new IntByReference(0); + if (!Kernel32.instance.IsWow64Process(new HANDLE(process), isWow64)) + { + //handle error + } + //System.out.println("isProcess64bit " + pid + " = " + isWow64.getValue()); + Kernel32.instance.CloseHandle(new HANDLE(process)); + if (isWow64.getValue() == 1) + return false; + return true; + //CloseHandle() + } catch(Exception ex) + { + ex.printStackTrace(); + } + return false; + } + public static HWND FindMainWindowFromPid(final long targetProcessId) { final List resultList = new ArrayList(); diff --git a/src/org/synthuse/CommandPopupMenu.java b/src/org/synthuse/CommandPopupMenu.java index 0bbe7b9..e4e52cb 100644 --- a/src/org/synthuse/CommandPopupMenu.java +++ b/src/org/synthuse/CommandPopupMenu.java @@ -117,6 +117,9 @@ public class CommandPopupMenu extends JPopupMenu { CommandMenuItem mntmSendCommandMsg = new CommandMenuItem("sendCommandMsg", 4); mnWinMessages.add(mntmSendCommandMsg); + CommandMenuItem mntmSendMessage = new CommandMenuItem("sendMessage", 5); + mnWinMessages.add(mntmSendMessage); + CommandMenuItem mntmSetcursorposition = new CommandMenuItem("setCursorPosition", 3); mnWinMessages.add(mntmSetcursorposition); @@ -232,6 +235,8 @@ public class CommandPopupMenu extends JPopupMenu { actionStr += "with | |"; if (paramCount > 3) actionStr += " and | |"; + if (paramCount > 4) + actionStr += " and | |"; return actionStr; } } diff --git a/src/org/synthuse/CommandProcessor.java b/src/org/synthuse/CommandProcessor.java index 5bfb2ac..be41135 100644 --- a/src/org/synthuse/CommandProcessor.java +++ b/src/org/synthuse/CommandProcessor.java @@ -24,6 +24,7 @@ public class CommandProcessor implements Runnable{ public static boolean DEFAULT_QUIET = false; //by default is quiet enabled protected CommandProcessor CommandProcessor = null; + public int executeCount = 0; public int executeErrorCount; public String lastError = ""; public String currentCommandText = ""; @@ -94,6 +95,7 @@ public class CommandProcessor implements Runnable{ events.statusChanged("Executing Test Script..."); //CommandProcessor cmdProcessor = new CommandProcessor(); scriptErrorCount = 0; + executeCount = 0; lastError = ""; long startTime = System.nanoTime(); String[] lines = scriptStr.split("\n"); @@ -101,7 +103,7 @@ public class CommandProcessor implements Runnable{ if (!line.trim().startsWith("|")) continue; //skip if it doesn't start with bar - String[] parsed = line.split("\\|"); + String[] parsed = line.trim().split("\\|"); // //System.out.println("line: " + line); @@ -117,6 +119,8 @@ public class CommandProcessor implements Runnable{ execute(parsed[2].trim(), new String[] {parsed[4].trim(), parsed[6].trim()}); if (parsed.length == 9) execute(parsed[2].trim(), new String[] {parsed[4].trim(), parsed[6].trim(), parsed[8].trim()}); + if (parsed.length == 11) + execute(parsed[2].trim(), new String[] {parsed[4].trim(), parsed[6].trim(), parsed[8].trim(), parsed[10].trim()}); if (executeErrorCount > 0) //check if any errors occurred ++scriptErrorCount; @@ -128,7 +132,7 @@ public class CommandProcessor implements Runnable{ String forcedStr = "Completed"; if (STOP_PROCESSOR.get()) forcedStr = "Stopped"; - events.statusChanged("Script Execution " + forcedStr + " with " + scriptErrorCount + " error(s) in " + new DecimalFormat("#.###").format(seconds) + " seconds"); + events.statusChanged("Script Execution " + forcedStr + " " + executeCount + " command(s) with " + scriptErrorCount + " error(s) in " + new DecimalFormat("#.###").format(seconds) + " seconds"); events.executionCompleted(); if (scriptErrorCount > 0 && !isQuiet) { @@ -142,6 +146,7 @@ public class CommandProcessor implements Runnable{ } public Object execute(String command, String[] args) { + ++executeCount; executeErrorCount = 0; currentCommandText = command; String joinedArgs = ""; @@ -221,6 +226,8 @@ public class CommandProcessor implements Runnable{ return win.cmdSelectContextMenuId(args); if (command.equals("sendCommandMsg")) return win.cmdSendCommandMsg(args); + if (command.equals("sendMessage")) + return win.cmdSendMessage(args); if (command.equals("windowMinimize")) return win.cmdWindowMinimize(args); if (command.equals("windowMaximize")) diff --git a/src/org/synthuse/MsgHook.java b/src/org/synthuse/MsgHook.java index 002b1be..1afc2a1 100644 --- a/src/org/synthuse/MsgHook.java +++ b/src/org/synthuse/MsgHook.java @@ -19,14 +19,23 @@ import javax.swing.JOptionPane; public class MsgHook { - + public static String targetdllName = ""; + public static String dll64bitName = ""; + public static String dll32bitName = ""; static { String loadFailedMsg = "Failed to load MsgHook library.\n"; //System.out.println("SynthuseDlg.config.disableUiaBridge: " + SynthuseDlg.config.disableUiaBridge); String archDataModel = System.getProperty("sun.arch.data.model");//32 or 64 bit try { - loadNativeLibraryFromJar("/MsgHook" + archDataModel + ".dll"); + targetdllName = "/MsgHook" + archDataModel + ".dll"; + dll64bitName = SaveNativeLibraryFromJar("/MsgHook64.dll"); //need to save both 32 and 64 bit dlls for hooking both types + dll32bitName = SaveNativeLibraryFromJar("/MsgHook32.dll"); + if (archDataModel.equals("32")) + System.load(dll32bitName); + else + System.load(dll64bitName); + } catch (Exception ex) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); @@ -36,7 +45,7 @@ public class MsgHook { } } - public static void loadNativeLibraryFromJar(String path) { + public static String SaveNativeLibraryFromJar(String path) { // Obtain filename from path String[] parts = path.split("/"); String filename = (parts.length > 1) ? parts[parts.length - 1] : null; @@ -58,7 +67,7 @@ public class MsgHook { } if (!temp.exists()) { //some reason the temp file wasn't create so abort System.out.println("File " + temp.getAbsolutePath() + " does not exist."); - return; + return ""; } // Prepare buffer for data copying @@ -68,7 +77,7 @@ public class MsgHook { InputStream is = MsgHook.class.getResourceAsStream(path); if (is == null) { //check if valid System.out.println("File " + path + " was not found inside JAR."); - return; + return ""; } // Open output stream and copy data between source file in JAR and the temporary file OutputStream os = null; @@ -83,23 +92,25 @@ public class MsgHook { e.printStackTrace(); } // Finally, load the library - System.load(temp.getAbsolutePath()); + return temp.getAbsolutePath(); } - //public native boolean initialize(int hwnd); + public native boolean initialize(String dll32bitName, String dll64bitName); public native boolean createMsgHookWindow(); public native boolean setMsgHookWindowTargetHwnd(int hwnd); - public native boolean setMsgHookWindowTargetClass(String classname); + public native boolean setMsgHookWindowTargetPid(int pid); public native boolean setMessageHook(int hwnd, int threadId); public native boolean removeMessageHook(); //public native boolean shutdown(); - public static Thread createMsgHookWinThread(final long targetHwnd) + public static Thread createMsgHookWinThread(final long targetHwnd, final int targetPid) { Thread t = new Thread() { public void run() { MsgHook mh = new MsgHook(); - mh.setMsgHookWindowTargetClass(""); + mh.initialize(dll32bitName, dll64bitName); + if (targetPid != 0) + mh.setMsgHookWindowTargetPid(targetPid); if (targetHwnd != 0) mh.setMsgHookWindowTargetHwnd((int)targetHwnd); mh.createMsgHookWindow(); diff --git a/src/org/synthuse/SynthuseDlg.java b/src/org/synthuse/SynthuseDlg.java index 228c317..1d643fa 100644 --- a/src/org/synthuse/SynthuseDlg.java +++ b/src/org/synthuse/SynthuseDlg.java @@ -29,6 +29,7 @@ import com.jgoodies.forms.layout.RowSpec; import com.jgoodies.forms.factories.FormFactory; */ import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.ptr.PointerByReference; import java.awt.FlowLayout; import java.awt.event.ActionListener; @@ -50,7 +51,7 @@ public class SynthuseDlg extends JFrame { /** * */ - public static String VERSION_STR = "1.2.1"; + public static String VERSION_STR = "1.2.2"; public static String RES_STR_MAIN_ICON = "/org/synthuse/img/gnome-robots.png"; public static String RES_STR_REFRESH_IMG = "/org/synthuse/img/rapidsvn.png"; @@ -77,6 +78,7 @@ public class SynthuseDlg extends JFrame { public static Config config = new Config(Config.DEFAULT_PROP_FILENAME); private String dialogResult = ""; private String lastDragHwnd = ""; + private int lastDragPid = 0; private String lastRuntimeId =""; private JComboBox cmbXpath; private JButton btnTestIde; @@ -195,10 +197,10 @@ public class SynthuseDlg extends JFrame { */ long lastHwndLong = 0; try { - lastHwndLong = Long.parseLong(lastDragHwnd); + //lastHwndLong = Long.parseLong(lastDragHwnd); } catch (Exception ex) { } - MsgHook.createMsgHookWinThread(lastHwndLong); + MsgHook.createMsgHookWinThread(lastHwndLong, lastDragPid); } } @@ -510,6 +512,10 @@ public class SynthuseDlg extends JFrame { String handleStr = Api.GetHandleAsString(hwnd); String classStr = WindowsEnumeratedXml.escapeXmlAttributeValue(Api.getWindowClassName(hwnd)); String parentStr = Api.GetHandleAsString(User32.instance.GetParent(hwnd)); + PointerByReference pointer = new PointerByReference(); + User32.instance.GetWindowThreadProcessId(hwnd, pointer); + int pid = pointer.getPointer().getInt(0); + String enumProperties = ""; if (!SynthuseDlg.config.isUiaBridgeDisabled()) enumProperties = uiabridge.getWindowInfo(targetX, targetY, WindowInfo.UIA_PROPERTY_LIST_ADV); @@ -524,6 +530,7 @@ public class SynthuseDlg extends JFrame { } lastDragHwnd = handleStr; lastRuntimeId = runtimeId; + lastDragPid = pid; //lastDragHwnd = (hwnd + ""); if (framework.equals(UiaBridge.FRAMEWORK_ID_WPF) || framework.equals(UiaBridge.FRAMEWORK_ID_SILVER)) {// WPF and Silverlight apps don't expose their child windows boundaries the same as win32 apps diff --git a/src/org/synthuse/TestIdeFrame.java b/src/org/synthuse/TestIdeFrame.java index 9bbd259..aa14ddd 100644 --- a/src/org/synthuse/TestIdeFrame.java +++ b/src/org/synthuse/TestIdeFrame.java @@ -12,7 +12,9 @@ import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; +import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.ImageIcon; +import javax.swing.JFileChooser; import javax.swing.JToolBar; import javax.swing.JButton; import javax.swing.JScrollPane; @@ -26,6 +28,7 @@ import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.Toolkit; +import java.io.*; import javax.swing.JLabel; @@ -36,7 +39,8 @@ public class TestIdeFrame extends JFrame { public static String RES_STR_RUN_IMG = "/org/synthuse/img/arrow-right-3.png"; public static String RES_STR_CLEAR_IMG = "/org/synthuse/img/user-trash-2.png"; public static String RES_STR_COPY_IMG = "/org/synthuse/img/edit-copy-7.png"; - + public static String RES_STR_SAVE_IMG = "/org/synthuse/img/document-save-6.png"; + public static String RES_STR_OPEN_IMG = "/org/synthuse/img/document-open-folder.png"; /** * */ @@ -46,6 +50,8 @@ public class TestIdeFrame extends JFrame { private JButton btnRun; private JButton btnClear; private JButton btnCopy; + private JButton btnSave; + private JButton btnOpen; private JLabel lblStatus; /** @@ -68,32 +74,30 @@ public class TestIdeFrame extends JFrame { btnRun = new JButton("Run"); btnRun.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { - if (btnRun.getText().equals("Run")) { - btnRun.setText("Stop"); - btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_STOP_IMG))); - CommandProcessor.STOP_PROCESSOR.set(false); - CommandProcessor.executeThreaded(txtTest.getText(), new CommandProcessor.Events() { - @Override - public void statusChanged(String status) { - lblStatus.setText(status); - } - @Override - public void executionCompleted() { - btnRun.setText("Run"); - btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_RUN_IMG))); - } - }); - } - else { - CommandProcessor.STOP_PROCESSOR.set(true); - //btnRun.setText("Run"); - //btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_RUN_IMG))); - } + runTestScript(); } }); btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_RUN_IMG))); toolBar.add(btnRun); + btnSave = new JButton("Save"); + btnSave.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + saveTestScript(); + } + }); + btnSave.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_SAVE_IMG))); + toolBar.add(btnSave); + + btnOpen = new JButton("Open"); + btnOpen.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + openTestScript(); + } + }); + btnOpen.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_OPEN_IMG))); + toolBar.add(btnOpen); + btnClear = new JButton("Clear"); btnClear.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { @@ -135,5 +139,83 @@ public class TestIdeFrame extends JFrame { }); super.setAlwaysOnTop(SynthuseDlg.config.isAlwaysOnTop()); } + + public void runTestScript() + { + if (btnRun.getText().equals("Run")) { + btnRun.setText("Stop"); + btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_STOP_IMG))); + CommandProcessor.STOP_PROCESSOR.set(false); + CommandProcessor.executeThreaded(txtTest.getText(), new CommandProcessor.Events() { + @Override + public void statusChanged(String status) { + lblStatus.setText(status); + } + @Override + public void executionCompleted() { + btnRun.setText("Run"); + btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_RUN_IMG))); + } + }); + } + else { + CommandProcessor.STOP_PROCESSOR.set(true); + //btnRun.setText("Run"); + //btnRun.setIcon(new ImageIcon(SynthuseDlg.class.getResource(RES_STR_RUN_IMG))); + } + } + + private void saveTestScript() + { + JFileChooser fChoose = new JFileChooser(); + fChoose.setFileFilter(new FileNameExtensionFilter("Text Files", "txt", "text")); + int result = fChoose.showSaveDialog(this); + if (result == JFileChooser.CANCEL_OPTION) + return; + File file = fChoose.getSelectedFile(); + if (fChoose.getFileFilter().getDescription().startsWith("Text") && !file.getAbsolutePath().toLowerCase().endsWith(".txt")) + file = new File(file.getAbsolutePath() + ".txt"); //append extension if not already there + + FileWriter fw = null; + try { + fw = new FileWriter(file); + fw.write(txtTest.getText()); + fw.flush(); + fw.close(); + fw = null; + } catch (Exception e) { + e.printStackTrace(); + } + if (fw != null) + try { fw.close(); } catch (Exception e){ e.printStackTrace(); }; + lblStatus.setText("Script Saved: " + file.getAbsolutePath()); + } + + private void openTestScript() + { + JFileChooser fChoose = new JFileChooser(); + fChoose.setFileFilter(new FileNameExtensionFilter("Text Files", "txt", "text")); + int result = fChoose.showOpenDialog(this); + if (result == JFileChooser.CANCEL_OPTION) + return; + File file = fChoose.getSelectedFile(); + BufferedReader br = null; + try { + br = new BufferedReader(new FileReader(file)); + String line = ""; + txtTest.setText(""); + while((line = br.readLine()) != null){ + txtTest.append(line + System.getProperty("line.separator")); + //System.out.println(line); + } + br.close(); + br = null; + } catch (Exception e) { + e.printStackTrace(); + } + if (br != null) + try { br.close(); } catch (Exception e){ e.printStackTrace(); }; + lblStatus.setText("Script Loaded: " + file.getAbsolutePath()); + } } diff --git a/src/org/synthuse/WindowInfo.java b/src/org/synthuse/WindowInfo.java index eb39f49..e9a0ece 100644 --- a/src/org/synthuse/WindowInfo.java +++ b/src/org/synthuse/WindowInfo.java @@ -22,6 +22,7 @@ import com.sun.jna.platform.win32.WinDef.LPARAM; import com.sun.jna.platform.win32.WinDef.LRESULT; import com.sun.jna.platform.win32.WinDef.RECT; import com.sun.jna.platform.win32.WinDef.WPARAM; +import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.ptr.PointerByReference; public class WindowInfo { @@ -50,6 +51,7 @@ public class WindowInfo { public int menus = 0; public HMENU menu = null; public boolean useUiaBridge = false; + public boolean is64bit = false; public Map extra = null; @@ -146,13 +148,10 @@ public class WindowInfo { useUiaBridge = true; } else { - PointerByReference pointer = new PointerByReference(); User32.instance.GetWindowThreadProcessId(hWnd, pointer); pid = pointer.getPointer().getInt(0); - Pointer process = Kernel32.instance.OpenProcess(Api.PROCESS_QUERY_INFORMATION | Api.PROCESS_VM_READ, false, pointer.getValue()); - Psapi.instance.GetModuleBaseNameW(process, null, buffer2, 512); - processName = Native.toString(buffer2); + getProcessInfo(); //test to see if uiaBridge should be used on this parent if (this.className.startsWith("HwndWrapper") || this.className.startsWith("WindowsForms")) useUiaBridge = true; @@ -204,6 +203,8 @@ public class WindowInfo { this.hwndStr = ""; //if (this.framework == null) // this.framework = "na"; + if(this.controlType.equals("window")) + this.isChild = false; return; } // non-wildcard mode @@ -234,7 +235,9 @@ public class WindowInfo { value = value.substring(0, MAX_TEXT_SIZE); if (this.hwndStr == null) this.hwndStr = ""; - + getProcessInfo(); + if(this.controlType.equals("window")) + this.isChild = false; /* this.rect = new RECT(); try { @@ -250,6 +253,18 @@ public class WindowInfo { */ } + private void getProcessInfo() + { + if (pid == 0) + return; + char[] buffer = new char[1026]; + Pointer process = Kernel32.instance.OpenProcess(Api.PROCESS_QUERY_INFORMATION | Api.PROCESS_VM_READ, false, new Pointer(pid)); + Psapi.instance.GetModuleBaseNameW(process, null, buffer, 512); + processName = Native.toString(buffer); + Kernel32.instance.CloseHandle(new HANDLE(process)); + is64bit = Api.isProcess64bit((int)pid); + } + public static String getRuntimeIdFromProperties(String enumProperties) { diff --git a/src/org/synthuse/WindowsEnumeratedXml.java b/src/org/synthuse/WindowsEnumeratedXml.java index a9d92e3..52b135e 100644 --- a/src/org/synthuse/WindowsEnumeratedXml.java +++ b/src/org/synthuse/WindowsEnumeratedXml.java @@ -195,6 +195,7 @@ public class WindowsEnumeratedXml implements Runnable{ if (!w.controlType.isEmpty()) win.setAttribute("type", w.controlType); if (!w.isChild) { + //win.setAttribute("parent", "yes"); parentCount++; if (w.processName != null && !w.processName.isEmpty()) { if (!processList.containsKey(w.pid+"")) @@ -203,11 +204,20 @@ public class WindowsEnumeratedXml implements Runnable{ if (w.processName != null) win.setAttribute("PROCESS", w.processName.toUpperCase()); } + if (w.pid != 0) + { + if (w.is64bit) + win.setAttribute("bits", "64"); + else + win.setAttribute("bits", "32"); + } } if (w.pid != 0) win.setAttribute("pid", w.pid+""); //else //win.setAttribute("parent", w.parent + ""); // not really needed since child node is append to parent node + + if (w.extra != null) { for(String extraName: w.extra.keySet()) { win.setAttribute(extraName, w.extra.get(extraName)+""); diff --git a/src/org/synthuse/commands/WindowsCommands.java b/src/org/synthuse/commands/WindowsCommands.java index 9fdf007..738aa1a 100644 --- a/src/org/synthuse/commands/WindowsCommands.java +++ b/src/org/synthuse/commands/WindowsCommands.java @@ -176,5 +176,23 @@ public class WindowsCommands extends BaseCommand { return true; } + + public boolean cmdSendMessage(String[] args) { + if (!checkArgumentLength(args, 4)) + return false; + WinPtr handle = findHandleWithXpath(args[0]); //xpath to HWND is first argument + if (handle.isEmpty()) + return false; + int msg = Integer.parseInt(args[1]); + int id = Integer.parseInt(args[2]); //context menu id is supplied as second argument + int idLparam = Integer.parseInt(args[3]); //context menu id is supplied as second argument + handle.convertToNativeHwnd(); + //LRESULT result = + //System.out.println("Send Message WM_COMMAND to " + handle.toString() + " PARAMS: " + id + ", " + idLparam); + //api.user32.PostMessage(handle.hWnd, Api.WM_COMMAND, new WPARAM(id), new LPARAM(0)); + api.user32.SendMessage(handle.hWnd, msg, new WPARAM(id), new LPARAM(idLparam)); + + return true; + } } diff --git a/src/org/synthuse/img/document-open-folder.png b/src/org/synthuse/img/document-open-folder.png new file mode 100644 index 0000000..98b8a94 Binary files /dev/null and b/src/org/synthuse/img/document-open-folder.png differ diff --git a/src/org/synthuse/img/document-save-6.png b/src/org/synthuse/img/document-save-6.png new file mode 100644 index 0000000..7229bb4 Binary files /dev/null and b/src/org/synthuse/img/document-save-6.png differ diff --git a/src/org/synthuse/test/WpfMockTestAppx64.exe b/src/org/synthuse/test/WpfMockTestAppx64.exe new file mode 100644 index 0000000..4b5b4ad Binary files /dev/null and b/src/org/synthuse/test/WpfMockTestAppx64.exe differ