Code:
#undef UNICODE
#undef _UNICODE
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <iostream>
#include <shlwapi.h>
#include <sstream>
#include <iostream>
#pragma comment(lib, "shlwapi.lib")
// our enumerators
BOOL WINAPI ChildEnumerator(HWND hwnd, LPARAM lParam);
BOOL WINAPI ParentEnumerator(HWND hwnd, LPARAM lParam);
// Struct masquerading as a text sink
struct Buffer
{
// constructor sets up where to dump the final buffer, defaults to cout
inline Buffer(std::ostream& out = std::cout) : out(out){}
// destructor dumps it
inline ~Buffer()
{
Dump();
}
// this allows nicer syntax for print operations, similar to printf.
// Less typing required both here and invocation than operator()
// with no real loss IMHO
template<class T>
Buffer& operator,(const T& data)
{
buf << data;
return *this;
}
// this is retained from cout syntax, so we can see where the
// data is being streamed to just by looking at the first output
// it's usage is not necessary
template<class T>
Buffer& operator<<(const T& data)
{
buf << data;
return *this;
}
// dumps the text to the output stream specified in the constructor
inline void Dump()
{
out << buf.str();
out << std::endl;
}
// the ultimate output stream, and our buffer
private:
std::ostream& out;
std::ostringstream buf;
};
void OutputWindowProperties(HWND hwnd, Buffer& printer)
{
char winText[512] = {0};
// Get the text it's displaying
if(SendMessage(hwnd, WM_GETTEXT, 512, reinterpret_cast<LPARAM>(winText)))
{
printer << "\tText: ", winText, '\n';
}
// Find out which file created it
if(GetWindowModuleFileName(hwnd, winText, 512))
{
printer << "\tCreated by: ", PathFindFileName(winText), '\n';
}
// see what thread/process it came from
DWORD procID = 0;
DWORD threadID = GetWindowThreadProcessId(hwnd, &procID);
printer << "\tOwning Process ID: ", procID, " Thread ID: ", threadID, '\n';
// get some misc info
WINDOWINFO wInfo = {sizeof(wInfo), 0};
GetWindowInfo(hwnd, &wInfo);
// se if we can get the class it belongs to
WNDCLASSEX wcl = {sizeof(wcl), 0};
if(GetClassInfoEx(reinterpret_cast<HINSTANCE>(GetWindowLongPtr(hwnd, GWLP_HINSTANCE)),
reinterpret_cast<LPCSTR>(wInfo.atomWindowType), &wcl))
{
printer << "\tClassInfo:\n\t\tName: ";
// try and get the name of the class
if(GetAtomName(wInfo.atomWindowType, winText, 512))
{
printer << winText, '\n';
}
// if we can't, see if the class belongs to the current process
// if it does, we can dereference the string
else if(GetCurrentProcessId() == procID)
{
printer << wcl.lpszClassName, '\n';
}
// otherwise, just print the class id
else
{
printer << wInfo.atomWindowType, '\n';
}
// output the wndproc address in hex
printer << "\t\tWndProc at address: ", std::hex, wcl.lpfnWndProc, std::dec, '\n';
}
// finally the window X/Y location
printer << "\tX Location: ", wInfo.rcWindow.left, ", Y Location: ", wInfo.rcWindow.top, '\n';
}
// called for each top level window
BOOL WINAPI ParentEnumerator(HWND hwnd, LPARAM lParam)
{
// our dumping ground
Buffer printer;
// output window handles in hex, then change the number format back to decimal
printer << "Top-Level Window - ", std::hex, hwnd, std::dec, '\n';
// Get its' properties
OutputWindowProperties(hwnd, printer);
// go through any children and do the same, passing in the text buffer
// so we can have a roughly hierachichal structure to the output
EnumChildWindows(hwnd, ChildEnumerator, reinterpret_cast<LPARAM>(&printer));
// keep calling us for windows we haven't encountered yet
return TRUE;
}
// called for each child window
BOOL WINAPI ChildEnumerator(HWND hwnd, LPARAM lParam)
{
// get the text buffer we passed in
Buffer& printer = *(reinterpret_cast<Buffer*>(lParam));
// everything else is the same as the one above
printer << "\n Child Window ", std::hex, hwnd, std::dec, '\n';
OutputWindowProperties(hwnd, printer);
return TRUE;
}
int main(int argc, char *argv[])
{
// start the chain of window enumeration
// warning, will spew out lots of data
EnumWindows(ParentEnumerator, 0);
}