i'm coding a wrapper for a CMD window (and potentially any shell), which purpose is to keep a shell instance always opened out of a screen's border. Moving the mouse to the border of the screen would cause the window moving down. (the window is a topmost window).
The point is to have a terminal always accessible as process (not taking space in the taskbar) and which hides when not being used.
One useful feature would be to force the focus on that window, so that, once it starts moving in your screen you can directly start typing without clicking on it to give it focus.
I'm coding all of that in c++ within visual studio with sfml support (that program itself has many sfml graphic windows, and that prompt is the only non-graphic one). Sfml in the code relative to that wrapper is only used to get mouse coordinates, in the form of sf::Mouse::getPosition().x/y.
When i run the program from within visual studio, whether in debug or in release mode, it all works fine. I can focus some other window, do stuff there, and as soon as i move the mouse in the position which makes the prompt window move in the screen, if i start typing without clicking on the actual page, the prompt will actually start capturing keyboard input as intended.
However, if i run the program as a stand-alone executable, this behaviour is no longer achieved. It appears the prompt does get focus indeed, since the "typing cursor" is there, but the window does not capture the actual keyboard input, which is weird.
Relevant code is as follows:
//create terminal window beg
STARTUPINFO prompt_startupinfo;
PROCESS_INFORMATION prompt_processinfo;
HWND prompt_window;
Win::spawn_prompt(&prompt_startupinfo, &prompt_processinfo, &prompt_window);
//always on bottom
SetWindowPos(prompt_window, HWND_TOP, 0, -PROMPT_HEIGHT + 2, Screen::get_width(), PROMPT_HEIGHT, SWP_SHOWWINDOW);
SetWindowLong(prompt_window, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
SetWindowLong(prompt_window, GWL_STYLE, WS_POPUP | WS_VISIBLE);
//create terminal window end
bool outside = false;
bool exiting = false;
while (IsWindow(prompt_window))
{
//Console move beg
int my = sf::Mouse::getPosition().y;
int mx = sf::Mouse::getPosition().x;
RECT rect;
GetWindowRect(prompt_window, &rect);
int wy = rect.bottom;
if ((my <= wy + 1) and not exiting)
{
if ((not outside) or (mx < 32))
{
if (wy < PROMPT_HEIGHT)
{
wy += WINDOW_SPEED * 4;
outside = false;
if (wy > PROMPT_HEIGHT)
{
wy = PROMPT_HEIGHT;
}
}
SetForegroundWindow(prompt_window);
}
}
else if (wy > 0)
{
wy -= WINDOW_SPEED * 4;
exiting = true;
if (wy <= 0)
{
wy = 0;
outside = true;
exiting = false;
}
}
SetWindowPos(prompt_window, 0, 0, wy - PROMPT_HEIGHT, 0, 0, SWP_NOSIZE);
//Console move end
Sleep(1000 / 60);
}
As a quick note, when running from within visual studio, the desired behaviour is achieved by just having the SetForegroundWindow(prompt_window); call, no even need for SetFocus(prompt_window);
Just for the sake of completion here is the Win::spawn_prompt function:
HWND Win::find_main_window(unsigned long process_id)
{
handle_data data;
data.process_id = process_id;
data.best_handle = 0;
EnumWindows(enum_windows_callback, (LPARAM)&data);
return data.best_handle;
}
BOOL CALLBACK Win::enum_windows_callback(HWND handle, LPARAM lParam)
{
handle_data& data = *(handle_data*)lParam;
unsigned long process_id = 0;
GetWindowThreadProcessId(handle, &process_id);
if (data.process_id != process_id || !is_main_window(handle))
{
return TRUE;
}
data.best_handle = handle;
return FALSE;
}
BOOL Win::is_main_window(HWND handle)
{
return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}
bool Win::spawn_prompt(STARTUPINFO* prompt_startupinfo, PROCESS_INFORMATION* prompt_processinfo, HWND* prompt_window)
{
// additional information
STARTUPINFO si;
PROCESS_INFORMATION pi;
pi.hProcess;
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// start the program up
CreateProcess(L"C:\\Windows\\System32\\cmd.exe", // the path
L"", // 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 (removed extra parentheses)
);
Sleep(1000);
HWND pw = Win::find_main_window(pi.dwProcessId);
*prompt_startupinfo = si;
*prompt_processinfo = pi;
*prompt_window = pw;
return false;
}
Ok i solved the problem.
Since that "set focus" event should have been triggered only when user's mouse was in a certain position, the most immediate workaround i could think of was simulating a click in that position.
Thanks for the hints #Daniel Sęk,
I'd still appreciate a more clean and less workaround-ish solution, so any further hint would be appreciated.
EDIT:
despite the window being topmost, sometimes the currently focused window still sits over the cmd window, and if that focused window is at the coordinates where i simulate the click, it captures that click making mentioned solution not always reliable.
Related
I am attempting to disable a window under certain conditions using EnableWindow(hWnd, false);
According to the documentation, this should "disable mouse and keyboard input to the specified window".
The problem I am seeing is that it does, in fact, disable like it says, except if the cursor is currently inside of a text box in the window and the window has the focus that does not get disabled. I was thinking of doing some sort of code to take the focus off of the window as well.
Is there a better way to go about this?
Note: The window being disabled is a binary ran via _spawnl().
I'm not sure if this is a Windows feature or a bug. Either way, disabling the foreground window is not a good idea.
If you are able to modify the program you start with _spawnl() then that is a better solution. You could make it respond to WM_APP or something like that when you need to control it.
If it is a 3rd-party application then you are left with hacks.
You could try to change the foreground window with SetForegroundWindow but this will only work if you do it very soon after _spawnl() before your thread loses the foreground lock. Using LockSetForegroundWindow before _spawnl() might be able to help you keep the lock longer. There are also various other hacks to change the foreground with AttachThreadInput etc.
If you don't want to change the foreground I was able to come up with a workaround:
ShellExecute(NULL, NULL, TEXT("Notepad"), NULL, NULL, SW_SHOW);
Sleep(2000);
HWND hNP = FindWindow(TEXT("Notepad"), NULL);
Sleep(2000); // Start typing in Notepad now...
if (hNP)
{
DWORD tid = GetWindowThreadProcessId(hNP, NULL);
GUITHREADINFO gti;
gti.cbSize = sizeof(gti);
if (tid && GetGUIThreadInfo(tid, >i))
{
HWND hChild = NULL;
if (gti.hwndFocus != hNP && gti.hwndFocus)
{
EnableWindow(hChild = gti.hwndFocus, false);
}
if (GetForegroundWindow() == hNP)
{
SendNotifyMessage(hNP, WM_ACTIVATE, WA_INACTIVE, NULL);
SendNotifyMessage(hNP, WM_ACTIVATE, WA_ACTIVE, NULL);
SendNotifyMessage(hNP, WM_SETFOCUS, NULL, NULL);
// SendNotifyMessage(hNP, WM_NCACTIVATE, false, NULL); // Uncomment to make it look like it is inactive
}
EnableWindow(hNP, false);
if (hChild)
{
EnableWindow(hChild, true);
}
}
MessageBox(NULL, TEXT("Done?"), NULL, MB_TOPMOST);
SetForegroundWindow(hNP);
PostMessage(hNP, WM_CLOSE, 0, 0);
}
This is certainly not optimal, it leaves Notepad in a state where it looks like it is enabled but it is really not. The idea is to disable the focused child window and trigger a fake activation change and forcing the focus to change. It might not work with other applications, who knows.
If you are willing to risk a deadlock you can do this instead:
DWORD tid = GetWindowThreadProcessId(hNP, NULL);
GUITHREADINFO gti;
gti.cbSize = sizeof(gti);
if (tid && GetGUIThreadInfo(tid, >i))
{
if (GetForegroundWindow() == hNP)
{
if (AttachThreadInput(GetCurrentThreadId(), tid, true))
{
SetFocus(NULL);
AttachThreadInput(GetCurrentThreadId(), tid, false);
}
}
EnableWindow(hNP, false);
}
I know this may sound to be a duplicate question but trust me it's not.
I have referred this question, but was not of much help as I am trying with a console application and the answerer himself tells he does not know the reason why ShowCursor(FALSE) does not work for console applications.
This thread did not help me either.
Here are the things I tried:
Using ShowCursor():
while(ShowCursor(false)>=0); //did not work
I first suspected that it was because of this statement in the msdn :
When Windows starts up, it checks if you have a mouse. If so, then the cursor show count is initialized to zero; otherwise, it is initialized to negative one.
I thought maybe in the latest windows, it doesn't recognize the connected mouse or the trackpad as an installed mouse and maybe that's why it didn't work. The following code shows it is not the case:
void UsingShowCursor()
{
CURSORINFO info;
info.cbSize = sizeof(CURSORINFO);
cout << ShowCursor(FALSE);
cout << ShowCursor(FALSE);
cout << ShowCursor(FALSE);
GetCursorInfo( &info ); //info.flags is CURSOR_SHOWING
}
Because I get -1, -2, -3. That means the initial show cursor count is obviously 0 and it does identify the installed mouse.
And another thing to note is that the GetCursorInfo() also tells that the cursor is showing.
Using SetCursor()
void UsingSetCursor()
{
HCURSOR prev = SetCursor(NULL);
int i = 0;
while(i++<10)
{
cout<<i<<endl;
Sleep(1000);
}
if( SetCursor(prev) == NULL ) //check if the previos cursor was NULL
cout<<"cursor was hidden and shown after 10 secs\n";
}
Doesn't work either.
This also did not work:
SetCursor(LoadCursor(NULL, NULL));
Edit:
Using LoadImage
Did not work either.
void UsingLoadImage()
{
// Save a copy of the default cursor
HANDLE arrowHandle = LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_SHARED);
HCURSOR hcArrow = CopyCursor(arrowHandle);
HCURSOR noCursorHandle = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR,1,1,LR_SHARED); //a single pixel thick cursor so that it wont be visible
HCURSOR noCursor = CopyCursor(noCursorHandle);
SetSystemCursor(noCursor, OCR_NORMAL);
int i =0 ;
while(i++<10)
{
cout<<i<<endl;
Sleep(1000);
}
//revert to previous cursor
SetSystemCursor(hcArrow, OCR_NORMAL);
DestroyCursor(hcArrow);
}
What can be the mistake? How can we hide the mouse for a console application?
You can use LoadImage() to achieve what you want. Here is the modified working version of the function UsingLoadImage() you quoted in the question. You have to include a cursor resource file to your visual studio project. Download the cursor from here or create your own.
Resource Files->Add->Existng Item and browse to the nocursor.cur file.
void UsingLoadImage()
{
// Save a copy of the default cursor
HANDLE arrowHandle = LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_SHARED);
HCURSOR hcArrow = CopyCursor(arrowHandle);
// Set the cursor to a transparent one to emulate no cursor
HANDLE noCursorHandle = LoadImage(GetModuleHandle(NULL), L"nocursor.cur", IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE); //worked
//HANDLE noCursorHandle = LoadCursorFromFile(L"nocursor.cur"); //this also worked
HCURSOR noCursor = CopyCursor(noCursorHandle);
SetSystemCursor(noCursor, OCR_NORMAL);
int i =0 ;
while(i++<10)
{
cout<<i<<endl;
Sleep(1000);
}
SetSystemCursor(hcArrow, OCR_NORMAL);
DestroyCursor(hcArrow);
}
This would replace the normal arrow cursor with the transparent one. If you want to hide all the other cursor like the text, loading, hand cursors etc you have to hide them individually. If you don't want that to be the case, then you should opt out of the console application as many commenters have pointed out.
Hope this helps.
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 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.