c++ WinApi : GetClientRect fails - c++

I am coding a screensaver program running on Windows.
In preview mode, Windows calls the program this way :
Screensaver.exe /p ParentWindowHandle
However, when I make this call in my program :
BOOL res = GetClientRect(parentWindowHandle, rect)
res is FALSE, rect is NULL and I get ERROR_INVALID_WINDOW_HANDLE with GetLastError()
GetWindowRect gives me the same results.
But, if I make a call to BOOL res = IsWindow(parentWindowHandle) instead, I get res == TRUE. Does this not mean I have a valid window handle ?
The code looks like this :
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
unsigned int handle = GetHandleFromCommandLine(pCmdLine); // Custom function (tested and approved :) )
HWND parentWindowHandle = (HWND) handle;
LPRECT rect = NULL;
BOOL res = GetClientRect(parentWindowHandle, rect);
// here, rect == NULL, res == FALSE and GetLastError() returns ERROR_INVALID_WINDOW_HANDLE
// ...
// ...
}

On 64-bit Windows, a window handle is 64 bits and cannot fit in an unsigned int, so your cast is producing a value that is an invalid window handle. You should modify your GetHandleFromCommandLine function so that it returns a proper HWND, not an unsigned int, and no type cast is necessary.
Also, GetClientRect returns the rectangle by storing it into the value pointed at by the second parameter. If you pass it NULL, it has nowhere to store that, so it will either crash or fail with an invalid parameter error. To avoid that, pass in the address of a local variable:
RECT rect;
GetClientRect(..., &rect);

Related

Drawing on window at background level on Windows only works once and requires a relog to work again

I'm having an issue where if I get ProgMan to create a WorkerW window behind the desktop icons (as described by https://stackoverflow.com/a/56132585/5923516), it allows me to draw to it the first time, but if I stop it and then try and run it again, it fails to draw. By 'fails to draw', I mean that I don't see any errors, but it just doesn't do anything. I've also tried to debug it and none of the effected values change (i.e. the wallpaper_hwnd variable points to the same place, and none of the SDL functions return error values) I've verified this behaviour on both Windows 10 and 11, both using different video drivers. The only way I've found to fix this is to log out then log back in, which doesn't really help.
Once exited I also noticed that the background instantly changes back to the wallpaper picture/colour. But I don't know if that information is useful.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shellapi.h>
// Can also be <SDL.h> depending on the install of SDL
#include <SDL2/SDL.h>
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
HWND ShellHandle = FindWindowEx(hwnd, NULL, "SHELLDLL_DefView", NULL);
HWND *ret = (HWND *) lParam;
if (ShellHandle) {
*ret = FindWindowEx(NULL, hwnd, "WorkerW", NULL);
}
return true;
}
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int ShowCmd) {
// Get the ProgMan Window
HWND progman = FindWindow("ProgMan", NULL);
// Get ProgMan to create a WorkerW
SendMessageTimeout(progman, 0x052C, 0, 0, SMTO_NORMAL, 1000, nullptr);
// Find the WorkerW that was spawned
HWND wallpaper_hwnd = nullptr;
EnumWindows(EnumWindowsProc, (LPARAM) &wallpaper_hwnd);
// Init SDL2
SDL_Init(SDL_INIT_VIDEO);
// Create an SDL Window from this
SDL_Window* sdl_window = SDL_CreateWindowFrom((void*)wallpaper_hwnd);
// Create SDL Renderer from the SDL Window
SDL_Renderer* renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
// Clear the window as a test
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
// Sleep for 1 second so that the change is actually visible
Sleep(1000);
// Clean up
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(sdl_window);
SDL_Quit();
// In my testing this doesn't fix it:
// DestroyWindow(wallpaper_hwnd);
return 0;
}
If anyone can work out why I'm getting these issues then I would greatly appreciate it.
EDIT:
After messing around a bit, I've found that wintabbing makes it work again. This still isn't a very good solution, however.

Get processID using part of WINDOW heading

i use the following program in c++ ,in Visual C++ 6.0, to inform me with message box when the MS Paint program is opened. It uses the exact name of the WINDOW of MS Paint,which is "Untitled - Paint" . However now i need to make the program inform me with message box when i know only a part of the name of the actual WINDOW , for example , if the window is "Abcdefgh - Paint" and i set the string name in this way - std::wstring windowName(L"Paint"); - the program to work again. Using the following 3 rows of code the program works fine when the actual WINDOW name is the exact name of the MS Paint window:
HWND windowHandle = FindWindowW(NULL, windowName.c_str());
DWORD* processID = new DWORD;
GetWindowThreadProcessId(windowHandle, processID);
But it will not work if the string windowName is just part of the name, i mean if it is "Paint".
Can someone show me how to do this? I thought to take a list of the names of all opened WINDOWS and to compare them with my part of the real name, i mean to search match of the substring "Paint" in their names, but i don't know how to get all opened windows.
Also, this is very important, my computer is old and i am using Visual C++ 6.0, so i can not use all the evolution features of C++ and the program environments nowadays, i mean , i can not use code which is compiled correctly in .NET but does not compiles in Visual C++ 6.0.
Thanks
#include "stdafx.h"
#include < iostream>
#include < string>
#include < windows.h>
#include < sstream>
#include < ctime>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
std::wstring windowName(L"Untitled - Paint");
while(true)
{
Sleep(1000*5);
time_t t = time(0); // get time now
struct tm * now = localtime( & t );
int tday = now->tm_mday;
int tmin = now->tm_min;
int thour = now->tm_hour;
HWND windowHandle = FindWindowW(NULL, windowName.c_str());
DWORD* processID = new DWORD;
GetWindowThreadProcessId(windowHandle, processID);
char probaintstr[20];
sprintf(probaintstr,"%d",*processID);
if(strlen(probaintstr) <=5 )
{
Sleep(1000*10);
MessageBox(NULL,"niama go Notepad ili Wordpad","zaglavie",MB_OK);
}
else {
}
}
return 0;
}
You can use EnumWindows, for example
BOOL CALLBACK enumWindow(HWND hwnd, LPARAM lp)
{
std::string str(512, 0);
int len = GetWindowText(hwnd, &str[0], str.size());
if (str.find("Paint") != std::string::npos)
{
MessageBox(0, str.c_str(), 0, 0);
}
return true;
}
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
EnumWindows(enumWindow, 0);
return 0;
}
Or you can use FindWindowEx and look for classname. The classname for MS Paint is "MSPaintApp". You can find this information from "Spy" utility for windows.
int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
for (HWND hwnd = NULL;;)
{
hwnd = FindWindowExA(NULL, hwnd, "MSPaintApp", 0);
if (!hwnd)
break;
std::string str(512, 0);
int len = GetWindowText(hwnd, &str[0], 512);
str.resize(len);
if (str.find("Paint") != std::string::npos)
MessageBox(0, str.c_str(), 0, 0);
}
return 0;
}
For process id you don't need to allocate memory. Just use a reference:
DWORD processID;
GetWindowThreadProcessId(windowHandle, &processID);

How to detect whether the focused window is an Edit 'type' control?

How can I detect whether the focused window is an Edit 'type' control? One method I am aware of is using Microsoft Active Accessibility which is looking like it will involve alot of effort using this method.
Is there another method I could use that is simpler?
My use-case is: when an edit control has the focus, store that hwnd.
// Callback set by SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL, (WINEVENTPROC)&winEventProc, 0, 0, WINEVENT_SKIPOWNPROCESS);
void CALLBACK KeyboardComponent::winEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject,
LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
// if hwnd == "Edit Control" store hwnd to send key input events
// One technique but not comprehensive
TCHAR wndClassName[255];
GetClassName(hwnd, wndClassName, 255);
if (_tcsicmp(wndClassName, _T("edit")) == 0)
targetEdit = hwnd;
// Class names I am receiving are subclassed or new window classes that look and operate like Edit controls.
// Ie when clicking the Firefox address bar I get: MozillaWindowClass
// Ie when clicking the Chrome address bar I get: Chrome_WidgetWin_1
}
Active Accessibility is the correct solution. You can use AccessibleObjectFromEvent() to get an IAccessible interface for the HWND that is triggering your winEventProc hook, and then check the IAccessible::AccRole property for ROLE_SYSTEM_TEXT:
ROLE_SYSTEM_TEXT
The object represents selectable text that allows edits or is designated as read-only.
For example:
// Callback set by SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, NULL, (WINEVENTPROC)&winEventProc, 0, 0, WINEVENT_SKIPOWNPROCESS);
void CALLBACK KeyboardComponent::winEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject,
LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
IAccessible* pAcc = NULL;
VARIANT varChild;
HRESULT hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &pAcc, &varChild);
if ((hr == S_OK) && (pAcc != NULL))
{
VARIANT varRole;
hr = pAcc->get_accRole(varChild, &varRole);
if ((hr == S_OK) && (varRole.vt == VT_I4) && (varRole.lVal == ROLE_SYSTEM_TEXT))
{
// ...
}
pAcc->Release();
}
}
For reliable result try using RealGetWindowClass, it should deal with a case when window was subclassed. If you don't care about that use regular GetClassName.
In case you want to handle controls that are not derived from standard Edit class, none of that will work of course.
You can use this code to know if focused window is Edit control
CWnd* pControl;
pControl = this->GetFocus();
if(pControl->IsKindOf(RUNTIME_CLASS(CEdit))){
//----
-----
----//
}
Get more details Here
Convert Cwnd to HWND
pControl->GetSafeHwnd();

CloseWindow doesn't minimize window from the process I just launched

Well I want to minimize the window after starting the window but it doesn't take effect and I don't know what I'm doing wrong. Can you please point out the mistake in the code below?
Nothing happens other than that a window is opened.
HWND g_hwnd;
int g_nFound;
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam);
HWND GetHwndFromPID(DWORD dwProcessId)
{
g_hwnd = NULL;
g_nFound = 0;
EnumWindows(FindHwndFromPID, (LPARAM)dwProcessId);
if (g_hwnd) // we found one...
return (g_hwnd);
// nothing found :-(
return (NULL);
}
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam)
{
DWORD dwPID2Find = (DWORD)lParam;
DWORD dwPID = 0;
if (GetWindowThreadProcessId(hwnd, &dwPID))
{
if (dwPID == dwPID2Find)
{
g_hwnd = hwnd;
return (FALSE);
}
}
return (TRUE);
}
int main
{
..../
if (!CreateProcessA(NULL, // No module name (use command line)
command_line.GetBuffer(),
NULL, // Process handle not inheritable
NULL, // Thread handle not inhberitable
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
)
{
//... error handling
return 0;
}
WaitForInputIdle(pi.hProcess, 1000);
HWND hwnds = GetHwndFromPID(pi.dwProcessId);
printf("Process Handle %d, hwnd id: %p ",pi.dwProcessId, hwnds);
CloseWindow(hwnds);
That code is supposed to minimize the window but I don't know why it doesn't.
You are going about this the wrong way. The official and documented way to ask a spawned process to run initially minimized is to use the STARTUPINFO structure when calling CreateProcess(), eg:
int main()
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cbSize = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_MINIMIZE;
//...
if (!CreateProcessA(..., &si, ...))
{
//... error handling
return 0;
}
//...
return 0;
}
STARTUPINFO structure
wShowWindow
If dwFlags specifies STARTF_USESHOWWINDOW, this member can be any of the values that can be specified in the nCmdShow parameter for the ShowWindow function, except for SW_SHOWDEFAULT. Otherwise, this member is ignored.
For GUI processes, the first time ShowWindow is called, its nCmdShow parameter is ignored wShowWindow specifies the default value. In subsequent calls to ShowWindow, the wShowWindow member is used if the nCmdShow parameter of ShowWindow is set to SW_SHOWDEFAULT.
ShowWindow function
nCmdShow [in]
Type: int
Controls how the window is to be shown. This parameter is ignored the first time an application calls ShowWindow, if the program that launched the application provides a STARTUPINFO structure. Otherwise, the first time ShowWindow is called, the value should be the value obtained by the WinMain function in its nCmdShow parameter.
...
The first time an application calls ShowWindow, it should use the WinMain function's nCmdShow parameter as its nCmdShow parameter. Subsequent calls to ShowWindow must use one of the values in the given list, instead of the one specified by the WinMain function's nCmdShow parameter.
As noted in the discussion of the nCmdShow parameter, the nCmdShow value is ignored in the first call to ShowWindow if the program that launched the application specifies startup information in the structure. In this case, ShowWindow uses the information specified in the STARTUPINFO structure to show the window. On subsequent calls, the application must call ShowWindow with nCmdShow set to SW_SHOWDEFAULT to use the startup information provided by the program that launched the application. This behavior is designed for the following situations:
• Applications create their main window by calling CreateWindow with the WS_VISIBLE flag set.
• Applications create their main window by calling CreateWindow with the WS_VISIBLE flag cleared, and later call ShowWindow with the SW_SHOW flag set to make it visible.
because the application window may not belong to the started process, but instead it belongs to its child process, you have to go deeper in FindHwndFromPID by including the parent process in the comparison.
also we shall not count on WaitForInputIdle() by it self, you have to give the created process enough time to fully initialize.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tlhelp32.h>
HWND g_hwnd;
int g_nFound;
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam);
/*___________________________________________________________________________________________________
*/
HWND GetHwndFromPID(DWORD dwProcessId)
{
g_hwnd = NULL;
g_nFound = 0;
EnumWindows(FindHwndFromPID, (LPARAM)dwProcessId);
if (g_hwnd) // we found one...
return (g_hwnd);
// nothing found :-(
return (NULL);
}
/*___________________________________________________________________________________________________
*/
DWORD GetParentProcess(DWORD pid){
PROCESSENTRY32 p32={sizeof(PROCESSENTRY32)};
DWORD ParentPID=0;
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 );
if(Process32First(hSnapShot,&p32)){
do{
if(p32.th32ProcessID==pid){
ParentPID = p32.th32ParentProcessID;
break;
}
}while(Process32Next(hSnapShot,&p32));
}
CloseHandle(hSnapShot);
return ParentPID;
}
/*___________________________________________________________________________________________________
*/
int __stdcall WindowText(HWND hWnd,LPSTR lpString,int nMaxCount){
int ret;
ret=SendMessage(hWnd,WM_GETTEXTLENGTH,0,0);
if(ret){
ret=SendMessage(hWnd,WM_GETTEXT,nMaxCount,(LPARAM)lpString);
}
return ret;
}
/*___________________________________________________________________________________________________
*/
BOOL CALLBACK FindHwndFromPID(HWND hwnd, LPARAM lParam){
DWORD dwPID2Find = (DWORD)lParam;
DWORD dwPID = 0;
if(GetWindowLong(hwnd,GWLP_HWNDPARENT) || !IsWindowVisible(hwnd))
return 1;
if (GetWindowThreadProcessId(hwnd, &dwPID)){
if ((dwPID == dwPID2Find) || ( GetParentProcess(dwPID) == dwPID2Find)){
g_hwnd = hwnd;
return (FALSE);
}
}
return (TRUE);
}
/*___________________________________________________________________________________________________
*/
int main(){
PROCESS_INFORMATION pi;
STARTUPINFO si={sizeof(si)};
TCHAR exename[]=TEXT("write.exe"); // writable buffer (for Unicode bug.)
if (!CreateProcess(NULL,exename,NULL, NULL,FALSE, 0, NULL,NULL,&si, &pi)){
return 0;
}
//WaitForInputIdle(pi.hProcess, 1000); // this alown will not always work (process may have children)
// give enough time to process to fully initialize
// put all in a loop until you
// get the window handle or timeout.
HWND hwnds = GetHwndFromPID(pi.dwProcessId);
for( int i=0 ;i<1000;i++ ){
if(hwnds)
break;
Sleep(10);
hwnds = GetHwndFromPID(pi.dwProcessId);
}
printf("Process Handle %d, hwnd id: %p ",pi.dwProcessId, hwnds);
CloseWindow(hwnds);
return 0;
}

A crash in injected / hooked target application

I have injected my DLL into a target application where I've hooked few WINAPI-functions
as well. One of them is DrawTextExW. I'm trying to replace all 'l' letters to '!' before
it prints it out. My solution works fine for a few seconds, but then the target application crashes. I really don't understand why.
Here's the function:
Edit - Working solution:
int WINAPI DetouredDrawTextExW(__in HDC hdc,
__inout LPWSTR lpchText,
__in int cchText,
__inout LPRECT lprc,
__in UINT dwDTFormat,
__in LPDRAWTEXTPARAMS lpDTParams)
{
std::wstring s_wc(lpchText, cchText);
std::replace(s_wc.begin(), s_wc.end(), L'l', L'!');
return ::DrawTextExW(hdc, const_cast<wchar_t *>(s_wc.c_str()),
s_wc.length(), lprc, dwDTFormat, lpDTParams);
}
So, can somebody point it out to me what I'm doing wrong?
I see that you ignore cchText, could you be receiving an non-NULL-terminated string with a positive value for cchText, resulting in reading past the end of the string into invalid memory? That error would present as a Win32 exception in the constructor of s_wc, though.
Also, you aren't checking for DT_MODIFYSTRING in the dwDTFormat parameter. If that flag is present, then ::DrawTextExW() could be overwriting invalid memory. That would present as a Win32 exception in ::DrawTextExW() or perhaps as a C++ exception in the s_wc destructor.
edit
Here's uncompiled, untested code that I believe obeys the contract of ::DrawTextExW()
int WINAPI DetouredDrawTextExW(__in HDC hdc,
__inout LPWSTR lpchText,
__in int cchText,
__inout LPRECT lprc,
__in UINT dwDTFormat,
__in LPDRAWTEXTPARAMS lpDTParams)
{
std::vector<wchar_t> v_wc;
int strSize = cchText == -1 ? wcslen(lpchText) : cchText;
v_wc.resize(strSize + 4);
std::copy(lpchText, lpchText + strSize, &v_wc.front());
std::replace(v_wc.begin(), v_wc.end() - 4, L'l', L'!');
int result = ::DrawTextExW(hdc, &v_wc.front(),
strSize, lprc, dwDTFormat, lpDTParams);
if (dwDTFormat & DT_MODIFYSTRING)
{
std::copy(&v_wc.front(), &v_wc.front() + v_wc.size(), lpchText);
}
}