Reputation: 63
I use this code to get the window name:
#include <Windows.h>
#include <stdio.h>
int main() {
TCHAR title[500];
int i=0;
while(i<10) {
GetWindowText(GetForegroundWindow(), title, 500);
printf("%s\n",title);
i++;
system("pause");
}
}
However, it gets only the foreground window.
I need to get all window names
Or, actually, I need to get one specific window name which belongs to the "notepad.exe" process.
Thanks for your help :)
Upvotes: 6
Views: 26974
Reputation: 61970
I don't think there's really any easier way by using the raw winapi, but here goes:
Here's the code I came up with:
#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
#include <tlhelp32.h>
bool isNotepad(const PROCESSENTRY32W &entry) {
return std::wstring(entry.szExeFile) == L"notepad.exe";
}
BOOL CALLBACK enumWindowsProc(HWND hwnd, LPARAM lParam) {
const auto &pids = *reinterpret_cast<std::vector<DWORD>*>(lParam);
DWORD winId;
GetWindowThreadProcessId(hwnd, &winId);
for (DWORD pid : pids) {
if (winId == pid) {
std::wstring title(GetWindowTextLength(hwnd) + 1, L'\0');
GetWindowTextW(hwnd, &title[0], title.size()); //note: C++11 only
std::cout << "Found window:\n";
std::cout << "Process ID: " << pid << '\n';
std::wcout << "Title: " << title << "\n\n";
}
}
return TRUE;
}
int main() {
std::vector<DWORD> pids;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// Do use a proper RAII type for this so it's robust against exceptions and code changes.
auto cleanupSnap = [snap] { CloseHandle(snap); };
PROCESSENTRY32W entry;
entry.dwSize = sizeof entry;
if (!Process32FirstW(snap, &entry)) {
cleanupSnap();
return 0;
}
do {
if (isNotepad(entry)) {
pids.emplace_back(entry.th32ProcessID);
}
} while (Process32NextW(snap, &entry));
cleanupSnap();
EnumWindows(enumWindowsProc, reinterpret_cast<LPARAM>(&pids));
}
Going through in order:
First note the wide versions of functions and strings. TCHAR
is not good to use and it would be a shame if one of the titles happened to have UTF-16 in it.
isNotepad
just checks the executable name member of the PROCESSENTRY32W
structure to see whether it equals "notepad.exe". This assumes Notepad uses this process name, and that nothing that isn't Notepad uses the process name. To eliminate false positives, you'd have to do more checking, but you can never be too sure.
In enumWindowsProc
, take note that lParam
is actually a pointer to a vector of PIDs (to save ourselves from having to use a global). This constitutes the cast at the beginning of the function. Next, we get the PID of the window we found. Then, we loop through the list of PIDs passed in and check whether it matches any. If it does, I chose to grab the title and output the PID and the window title. Note that using a standard string as a buffer is only guaranteed to work in C++11, and must not have the extra null character (not part of the length) overwritten. Lastly, we return TRUE
so that it keeps enumerating until it has gone through every top-level window.
Onto main
, the first thing you see is our initially empty list of PIDs. We take a snapshot of the processes and go through them. We use the helper function, isNotepad
to check whether the process is "notepad.exe", and if so, store its PID. Lastly, we call EnumWindows
to enumerate the windows, and pass in the list of PIDs, disguised as the required LPARAM
.
It's a bit tricky if you haven't done this sort of thing, but I hope it makes sense. If you want the child windows, the correct thing to do would be to add a EnumChildWindowsProc
and call EnumChildWindows
with that in the spot where I output information about the found window. If I'm correct, you don't need to recursively call EnumChildWindows
to get grandchildren etc., as they will be included in the first call.
Upvotes: 10
Reputation: 145429
You ask,
“I need to get one specific window name which belongs to the "notepad.exe" process”
Well, C++ is the wrong language for that task. It’s a task more naturally and simpler done by scripting. For example, here’s a Windows batch file that reports the titles of all the Notepad windows:
@echo off
for /f "usebackq delims=, tokens=1,9" %%t in (`tasklist /v /fo csv`) do (
if %%t=="notepad.exe" echo %%u
)
Example use:
[d:\dev\misc\so\notepad_window_name] > titles "Untitled - Notepad" [d:\dev\misc\so\notepad_window_name] > _
Also, in addition to the language choice, consider that your C++ code advertises via its use of the TCHAR
type, that it can be compiled as both Unicode and ANSI – and yet it cannot be compiled as Unicode due to the use of printf
. Which means that the over-silly TCHAR
misled you into introducing a bug. Simply don't use the T
stuff, like TCHAR
: it's only a way to obfuscate the code and introduce bugs.
Here’s code that exemplifies how to create a Unicode-only program.
In contrast to the batch file this only retrieves the title of one Notepad window:
#include <iostream> // std::wcout, std::endl
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <string> // std::wstring
using namespace std;
#define UNICODE
#include <windows.h>
int main()
{
HWND const window = FindWindow( L"Notepad", nullptr );
if( window == 0 )
{
wcerr << "!Didn't find any Notepad window." << endl;
}
else
{
int const nAttempts = 3;
for( int i = 1; i <= nAttempts; ++i )
{
int const bufferSize = 1 + GetWindowTextLength( window );
wstring title( bufferSize, L'\0' );
int const nChars = GetWindowText( window, &title[0], bufferSize );
if( nChars == 0 || nChars < GetWindowTextLength( window ) )
{
Sleep( 20 ); continue;
}
title.resize( nChars );
wcout << "'" << title << "'" << endl;
return EXIT_SUCCESS;
}
wcerr << "!Found a Notepad window but unable to obtain the title." << endl;
}
return EXIT_FAILURE;
}
So, C++ is the wrong language choice, and TCHAR
is the entirely wrong data type choice.
If for some reason you do need the code as C++, and you do need all the Notepad window titles, then the batch file won’t do, and the above C++ code won’t do. In this case, use the Windows EnumWindows
API function as David Hefferman suggest. And to avoid obfuscation, subtle bugs and misleading others who read the code, use wchar_t
based strings, not TCHAR
, as exemplified by the code above.
Upvotes: 0
Reputation: 613491
Call EnumWindows
. You supply a callback function that is called once for each top-level window. You can then check the properties of each window with your specific criterion. You can call GetWindowText
and then check that against the value that you are looking for.
Upvotes: 3