I have a simple (windows) application that launches another application using the CreateProcess function. It then gets the correct hwnd by using EnumWindows and the process id of the newly created process.
After the hwnd has been gained, the 'main loop' of my applications begins. This loop continues until the application started by CreateProcess is no longer running.
Everything works perfectly, until I try to use PeekMessage to peek at the messages being sent to the application I have launched - It seems that no messages at all are being recognized by my application, but the program that was launched (via CreateProcess) is running as normal, doing everything it should..
What I am trying to achieve, is to remove certain messages from being sent to the application, mainly various F-keys (F1, F2..F12 keys), but also, if possible, I would like to change the menu shown in the application (I dont know the technical name for the menu I mean, its the one you see what you click on the application icon in the top right corner) - I want to add a small 'about' option.
If anyone could point out what I am doing wrong within my code, or to a better alternative for stopping certain keypresses from reaching the launched application, I would be very grateful.
Many thanks in advance. :)
Here is the code I currently have:
VOID PerformLaunch(LPWSTR lpAppName, LPTSTR lpCmdLine) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwLoopExitCode = NULL;
BOOL cpBool = FALSE;
BOOL finishedLoop = FALSE;
MSG message = { 0 };
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
cpBool = CreateProcess(lpAppName, lpCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!cpBool) {
MessageBox(Game_HWND, L"Could not start process!", L"Error:", MB_OK | MB_ICONERROR);
}
//-- Enumerate windows until Game_HWND && Game_Hook are not NULL.
while (Game_Hook == NULL) {
EnumWindows(MainEnumGameWindProc, pi.dwProcessId);
}
while (!finishedLoop) {
DWORD dwWC = WaitForSingleObject(pi.hProcess, 0);
if ((dwWC == WAIT_FAILED) || (dwWC == WAIT_OBJECT_0)|| (dwWC == WAIT_ABANDONED)) {
DWORD dwExitCode;
GetExitCodeProcess(pi.hProcess, &dwExitCode);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
dwLoopExitCode = dwExitCode;
finishedLoop = TRUE;
}
else {
if (PeekMessage(&message, Game_HWND, 0, 0, PM_REMOVE)) {
TranslateMessage(&message);
DispatchMessage(&message);
if (WM_QUIT == message.message) {
finishedLoop = TRUE;
break;
}
}
}
}
}
You can't intercept another message loop like that. Remember, that process will be doing its own message pump - it's not okay for you to be dispatching its messages, and I expect things would go very weird if you do.
The correct way is to set a hook. Use the SetWindowsHookEx function to install a hook on that window.
Reference for SetWindowsHookEx
PeekMessage retrieves messages only from the message queue, associated with the its calling thread. Window, which messages you are trying to peek belongs to the different thread, so PeekMessage with its handle will never give you anything. You could either install a hook, or subclass its window procedure.
Related
I have a 32-bit MFC application that uses a custom library that would be a nightmare to re-compile into x64. In general the app doesn't really need to run as 64-bit, except in one case -- and that is to render contents to display in a dialog window, which can benefit from a larger addressing space.
So my goal is to "imitate" CDialog::DoModal method but for a dialog in another process.
I built that dialog window as a standalone x64 MFC dialog-based application. It takes a file path as it's input parameter, does all the work internally, and returns simple user selection: OK, Cancel.
So I do the following from my main parent process:
//Error checks omitted for brevity
CString strCmd = L"D:\\C++\\MyDialogBasedApp.exe";
HWND hParWnd = this->GetSafeHwnd();
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_UNICODE | SEE_MASK_NOCLOSEPROCESS;
sei.nShow = SW_SHOW;
sei.lpVerb = _T("open");
sei.lpFile = strCmd.GetBuffer();
sei.hwnd = hParWnd;
BOOL bInitted = SUCCEEDED(::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
ShellExecuteEx(&sei);
DWORD dwProcID = ::GetProcessId(sei.hProcess);
//Try to get main Wnd handle for the child process
HWND hMainChildWnd = NULL;
for(;; ::Sleep(100))
{
hMainChildWnd = getHwndFromProcID(dwProcID);
if(hMainChildWnd)
break;
}
HWND hPrevParWnd = ::SetParent(hMainChildWnd, hParWnd);
if(hPrevParWnd)
{
//Wait for child process to close
::WaitForSingleObject(sei.hProcess, INFINITE);
//Reset parent back
::SetParent(hMainChildWnd, hPrevParWnd);
}
::CloseHandle(sei.hProcess);
if(bInitted)
::CoUninitialize();
where getHwndFromProcID is taken from here.
This kinda works, except of the following:
(1) There are two icons on the taskbar: one for my main app, and one for the child app. Is there a way not to show a child icon?
(2) I can switch focus from child window to parent and vice versa. In actual modal dialog window one cannot switch back to parent while child is open. Is there a way to do that?
(3) If I start interacting with the parent, it appears to be "hung up" and the OS will even show it on its title bar.
So I was curious if there's a way to resolve all these?
you need pass pointer of own window to child process
you need process windows messages, while you wait on child process
exit. WaitForSingleObject unacceptably here - need use
MsgWaitForMultipleObjectsEx
child process must set your window as self owner window at create
time - you not need call SetParent
with this all will be worked perfect. in your 32-bit MFC application you need use next code :
BOOL DoExternalModal(HWND hwnd, PCWSTR ApplicationName)
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
WCHAR CommandLine[32];
swprintf(CommandLine, L"*%p", hwnd);
if (CreateProcessW(ApplicationName, CommandLine, 0, 0, 0, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
MSG msg;
for (;;)
{
switch (MsgWaitForMultipleObjectsEx(1, &pi.hProcess, INFINITE, QS_ALLINPUT, 0))
{
case WAIT_OBJECT_0:
CloseHandle(pi.hProcess);
return TRUE;
case WAIT_OBJECT_0 + 1:
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
continue;
default: __debugbreak();
}
}
}
return FALSE;
}
in MyDialogBasedApp.exe let use MessageBox as demo dialog. we will be use your MFC window as first argument to it.
void ExeEntry()
{
int ec = -1;
if (PWSTR CommandLine = GetCommandLine())
{
if (CommandLine = wcschr(CommandLine, '*'))
{
HWND hwnd = (HWND)(ULONG_PTR)_wcstoi64(CommandLine + 1, &CommandLine, 16);
if (hwnd && !*CommandLine && IsWindow(hwnd))
{
ec = MessageBoxW(hwnd, L"aaa", L"bbb", MB_OK);
}
}
}
ExitProcess(ec);
}
with this code:
(1) There are only one icon on the taskbar for your main app
(2) You can not switch focus from child window to parent and vice versa. all work as actual modal dialog window
(3) parent not "hung up" because it processing windows messages (MsgWaitForMultipleObjectsEx) - your code is "hung up" because you not do this, but wait in WaitForSingleObject
A modal dialog box does two things that make it "modal":
The dialog's "owner" is set to the parent window.
The parent window is disabled.
I played around with this a little bit and while you can do these manually, the easiest way is to just pass the parent window handle to the DialogBox function (or the CDialog constructor in MFC).
Instead of doing all the work after ShellExecuteEx in the parent process, your child process can use FindWindow (or a similar mechanism) to get the parent window handle and use that to display the dialog.
What you are trying to do cannot safely be done. The blog entry Is it legal to have a cross-process parent/child or owner/owned window relationship? explains, that installing a cross-process owner/owned window relationship causes the system to call AttachThreadInput, and - as we all know - AttachThreadInput is like taking two threads and pooling their money into a joint bank account, where both parties need to be present in order to withdraw any money. This creates a very real potential for a dead lock. You can only safely prevent this from happening, if you control both participating threads. Since at least one thread uses a 3rd party application framework (MFC in this case), this is off limits.
Since we have established, that your proposed solution cannot safely be implemented, you need to look into alternatives. One solution might be to delegate the work to a 64-bit process for computation, and have the results passed back to your 32-bit process for display.
As my title states, I am trying to move data from a C++ application I am writing and input it into a field (specifically username and password fields) on any desktop application in windows. It needs to work for all applications.
Now I have already written a small program which copies data to the clipboard, and then simulates a Ctrl+V keyboard press to paste the data in. This however, feels like a terribly ugly way to do this. My question is, is there a better way?
Ps. From the research I have done everything seems to require that you modify the receiving application in some way. This option is unfortunately unavailable to me. So any solutions involving tweaking the receiving application won't be helpful.
Thank you for your help!
Sending keystrokes to another application is not a good solution. There are many potential problems, such as in C# sendkeys to other application to particular textfield. A better solution is to interface with the other program more directly. It requires a bit more technical understanding of how Windows works however. One of many advantages is that you can read text in the other application as easily as write it.
See my Clicking a Button in Another Application for a sample, but that is in C#. I hope the explanation at least is helpful. The same technique can be used to put data into a text box or text boxes then click a button. The WM_SETTEXT message would be used to put data into a textbox in another application. The following is a sample console program that puts text into Notepad.
#include "stdafx.h"
struct pidandhwnd {
DWORD dwProcessId;
HWND hwnd;
};
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
pidandhwnd *ppnh = (pidandhwnd *)lParam;
DWORD dwProcessId;
GetWindowThreadProcessId(hwnd, &dwProcessId);
if (ppnh->dwProcessId == dwProcessId)
{
ppnh->hwnd = hwnd;
return FALSE;
}
return TRUE;
}
int main()
{
TCHAR szCmdline[] = TEXT("Notepad.exe");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = NULL;
siStartInfo.hStdOutput = NULL;
siStartInfo.hStdInput = NULL;
LPARAM lParam = NULL;
pidandhwnd pnh;
const int ControlId = 15; // Edit control in Notepad
HWND hEditWnd;
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (!bSuccess) {
std::cout << "Process not started\n";
return 0;
}
std::cout << piProcInfo.dwProcessId << " Notepad Process Id\n";
WaitForInputIdle(piProcInfo.hProcess, 1000);
pnh.dwProcessId = piProcInfo.dwProcessId;
pnh.hwnd = NULL;
EnumDesktopWindows(NULL, EnumWindowsProc, (LPARAM)&pnh);
if (pnh.hwnd == NULL)
{
std::cout << "Notepad not found\n";
return 0;
}
//std::cout << "Notepad found\n";
// Get the edit box on Notepad
hEditWnd = GetDlgItem(pnh.hwnd, ControlId);
// Send the text
SendMessage(hEditWnd, WM_SETTEXT, NULL, (LPARAM)_T("This is from somewhere else."));
return 0;
}
I've created a program to identify existing maximized windows and log their locations using EnumWindows and GetWindowPlacement.
Assuming I know the location of a desired EXE, I can open it just by calling the external process caller. But what is the best way to identify the newly opened window and set its location? It's safe to assume that a program with the same name may already be open (two instances of cmd, for instance).
I believe once the window is identified I can set its location with SetWindowPos.
Is this the right question to be asking? Is there a way to open a program and receive a HWND handle back instead?
In the Event Hook vein, I have this code:
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, NULL, proc, 0, 0, WINEVENT_OUTOFCONTEXT );
BOOL result = CreateProcess(NULL, szPath, &saProcess, &saThread, FALSE, 0, NULL, NULL, &si, &piProcessC);
if (hook) {
UnhookWinEvent(hook);
}
Which creates a hook and then creates a process (I'm opening up Notepad++).
The proc function it calls is:
void CALLBACK proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG obj, LONG child, DWORD thr, DWORD time) {
if (IsWindow(hWnd)){
WINDOWPLACEMENT *wp = new WINDOWPLACEMENT();
wp->length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWnd, wp);
wp->rcNormalPosition.top = (long) 363;
wp->rcNormalPosition.bottom = (long) 1021;
wp->rcNormalPosition.left = (long) 1444;
wp->rcNormalPosition.right = (long) 2551;
BOOL tmp = SetWindowPlacement(hWnd, wp);
cout << "FOUND IT\n";
}
}
The proc function does not appear to get called, so is the hook not catching anything?
Oh, and I'm not sure the WINEVENT_OUTOFCONTEXT is right, this is just a simple EXE doing the calling.
I am writing a remote desktop application like TeamViewer in C++ on Windows 7 (x64) and Windows 8 (x64).
1. What made me stuck
I have implemented the mouse input and keyboard input by using SendInput(). I found SendInput() worked perfectly when the process ran under winsta0\desktop. But after the user locked the computer or the screensaver launched, it didn’t work.
If I run the process under winsta0\winlogon, SendInput() doesn’t work under winsta0\default.
2. What I have tried
I have tried using SetThreadDesktop() to switch the process from winsta0\desktop to winsta0\winlogon, but I got error 170: "The requested resource is in use" and I stucked.
3. What I want to know
I noticed that TeamViewer has a process named TeamViewer_Desktop.exe which can control mouse and keyboard under Winlogon, Default and Screensaver. How does it do it?
Can you provide the code to help me understand how to solve my question?
I want to know** how I can make my application switch between the default desktop and Winlogon desktop. So I can control the mouse and keyboard on a secured desktop without creating another process running under winlogon.exe.
You did the right thing: SetThreadDesktop is correct. The error is telling you that you have some resources open on the current desktop though, such as a window, and that prevents you from switching. If you had tried to produce a minimal test-case (as you are meant to do when asking questions here!) you would have found that out.
Cut out parts of your program until you find the chunk that's preventing you switching desktop. Some Windows APIs are nasty and prevent you switching desktop, so need to be called in a dedicated thread.
As #NicholasWilson said, SetThreadDesktop() is the correct way to switch a process between default desktop and winlogon desktop.
The error 170, "The requested resource is in use", occurred because I called MessageBox() before SetThreadDesktop() called. Also calling CreateWindow() can cause the error.
I think any function associated with GUI creation called before SetThreadDesktop() called can cause the error. So if you want to invoke SetThreadDesktop() successfully, you must be sure not to call any GUI creation function before you invoke SetThreadDesktop().
Code
Code here is how to switch the process to the specified desktop.
Usage: SetWinSta0Desktop(TEXT("winlogon")), SetWinSta0Desktop(TEXT("default"))
SetWinSta0Desktop() function:
BOOL SetWinSta0Desktop(TCHAR *szDesktopName)
{
BOOL bSuccess = FALSE;
HWINSTA hWinSta0 = OpenWindowStation(TEXT("WinSta0"), FALSE, MAXIMUM_ALLOWED);
if (NULL == hWinSta0) { ShowLastErrorMessage(GetLastError(), TEXT("OpenWindowStation")); }
bSuccess = SetProcessWindowStation(hWinSta0);
if (!bSuccess) { ShowLastErrorMessage(GetLastError(), TEXT("SetProcessWindowStation")); }
HDESK hDesk = OpenDesktop(szDesktopName, 0, FALSE, MAXIMUM_ALLOWED);
if (NULL == hDesk) { ShowLastErrorMessage(GetLastError(), TEXT("OpenDesktop")); }
bSuccess = SetThreadDesktop(hDesk);
if (!bSuccess) { ShowLastErrorMessage(GetLastError(), TEXT("SetThreadDesktop")); }
if (hDesk != NULL) { CloseDesktop(hDesk); }
if (hWinSta0 != NULL) { CloseWindowStation(hWinSta0); }
return bSuccess;
}
ShowLastErrorMessage() function:
void ShowLastErrorMessage(DWORD errCode, LPTSTR errTitle)
{
LPTSTR errorText = NULL;
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&errorText,
0,
NULL);
if ( NULL != errorText )
{
WCHAR msg[512] = {0};
wsprintf(msg, TEXT("%s:\nError Code: %u\n%s\n"), errTitle, errCode, errorText);
LocalFree(errorText);
errorText = NULL;
OutputDebugString(msg);
}
}
I want to send wm_close to another process, with which i want end that process safely.
int _tmain(int argc, _TCHAR* argv[])
{
DWORD SetOfPID;
SetOfPID = GetProcId(_T("abc.exe")); //this will return pid
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS,false, SetOfPID);
HWND hwnd = ::GetTopWindow(NULL);
while(hwnd)
{
DWORD pid;
DWORD dwThreadId = ::GetWindowThreadProcessId(hwnd, &pid);
if(pid == SetOfPID)
{
break;
}
hwnd = ::GetNextWindow(hwnd, GW_HWNDNEXT);
}
//DestroyWindow(hwnd);
bool temp = IsWindow(hwnd); **//this gives true**
LRESULT res = ::SendMessage(hwnd, WM_CLOSE, NULL, NULL);
DWORD err = GetLastError(); **//this gives 6**
CloseHandle(hwnd);
CloseHandle(h);
return 0;
}
this piece of code looks good, but the target process doesn't terminate, can somebody help me?
I would attempt to close a (Process with) Window(s) in the following order:
WM_CLOSE
WM_QUIT
WM_DESTROY
TerminateProcess().
Just my take as I am handling (disabling) WM_CLOSE for a Window and having difficulty distinguishing between User Close and close messages send by another master task. WM_QUIT seems to resolve my problem without using a custom WM_APP_CLOSE of my own. TerminateProcess is very much a last resort unclean exit to be avoided at all costs (it may leave handles (e.g. COM etc) and memory etc unfreed).
Are you sure that the window you are finding is the correct one? You can check easily with Spy++. Moreover, when searching for a window, I think it's better to use EnumWindows. I'm not sure your method is correct.
If the application does not process WM_CLOSE the DefWindowProc should handle this (by gracefully closing the application), however if the application is handling WM_CLOSE then it simply can choose to ignore it. Try sending the WM_DESTROY and WM_NCDESTROY messagees instead.