Orhan Kurtulan
Orhan Kurtulan

Reputation: 63

getting a specific window name in c++

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.

  1. I need to get all window names

  2. 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

Answers (3)

Qaz
Qaz

Reputation: 61970

I don't think there's really any easier way by using the raw winapi, but here goes:

  1. Use the Toolhelp32 API to get a list of process IDs whose executable names match "notepad.exe".
  2. Enumerate the windows to find any whose PID matches one in the list.
  3. Grab the title of that window and do what you will with it.

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

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145429

How to obtain titles of Notepad windows.

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]
> _

How to write modern Unicode C++ Windows programs.

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.


How to use C++ when other requirements force the language 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

David Heffernan
David Heffernan

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

Related Questions