Getting process title & user

Hi, I've got my list of processes being dynamically grabbed and would like help in identifying the window title(if any) and whether the user was the one who initiated it.
This is the code I have so far for my function (wxExe is a custom class if you're wondering):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifdef WIN32
    if (appWait > 500 || user == true) {
        appWait = 0;
        PROCESSENTRY32 *pe32 = new PROCESSENTRY32 [sizeof(PROCESSENTRY32)];
        HANDLE app = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        hexLBSoftware->Clear();
        if (app != INVALID_HANDLE_VALUE) {
            wxString s; int i = 0;
            Process32First(app, pe32);
            do { i++; } while(Process32Next(app, pe32));
            Process32First(app, pe32);
            apps = new wxExe [i]; i = 0;
            do {
                apps[i].id = pe32->th32ProcessID;
                apps[i].exe.Printf(pe32->szExeFile);
                apps[i].name = "???";
                apps[i].hwnd = app;
                s = apps[i].exe << " - " << apps[i].name;
                hexLBSoftware->Append(s); i++;
            } while(Process32Next(app, pe32)); CloseHandle(app);
            apps[i - 1].last = true; CloseHandle(app);
        }
    } else { appWait++; }
#endif 
Do you have a particular problem with your code??

Andy

P.S. This line does look a bit odd. You're allocating enough storage for sizeof(PROCESSENTRY32) copies of the struct PROCESSENTRY32.

PROCESSENTRY32 *pe32 = new PROCESSENTRY32 [sizeof(PROCESSENTRY32)];
Last edited on
That line was copied, about 5-10% of the rest was adapted from same source. As it stands it compiles fine and runs fine but for the purpose I need it I will have to further adapt the code but before that I need to learn how to get the window title and the user that initiated it without using the .NET API. It needs to rely only on code backwards compatible with Windows 98 and I cannot find the information so even a reference would be helpful enough for me to continue. As for the line mentioned I'll try removing the = onwards and see what happens - can't be anything major right?...

Edit: Nope, caused run time error, full line is needed.
Last edited on
CreateToolhelp32Snapshot, etc. was introduced in Windows 98/Windows 200, so you should be ok.

Once you get the process, you need to:
(a) use EnumWindows to find the top level windows associated with a process
(b) or use Thread32First/Thread32Next to enumerate the thread and use EnumThreadWindows or GetGUIThreadInfo to find the windows

The username is Windows NT, etc specific. This needs Use OpenProcessToken, GetTokenInformation, and LookupAccountSid

Andy

P.S. You should remove the first, extra calls to Process32First/Process32Next
Last edited on
Thanks, I'll try it as soon as I get the chance.

Quick question, if I remove the first calls to Process32First/Next then how do I set the size for my array dynamically (I only started learning C/C++ properly in the last couple of days), do I just use app[i] = new...?
Last edited on
A new problem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifdef WIN32
    if (appWait > 500 || user == true) {
        appWait = 0;
        PROCESSENTRY32 *pe32 = new PROCESSENTRY32 [sizeof(PROCESSENTRY32)];
        THREADENTRY32 *te32; GUITHREADINFO *ti32;
        te32->dwSize = sizeof(THREADENTRY32);
        HANDLE app = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
        LPTSTR t;
        hexLBSoftware->Clear();
        if (app != INVALID_HANDLE_VALUE) {
            wxString s; int i = 0;
            if (Thread32First(app, te32)) {
                do {
                    GetGUIThreadInfo(te32->th32ThreadID,ti32);
                    if (ti32->hwndActive) {
                        pe32 = getApp(te32->th32OwnerProcessID);
                        if (pe32 != NULL) i++;
                    }
                } while(Thread32Next(app, te32));
                Thread32First(app, te32);
                apps = new xApp [i]; i = 0;
                do {
                    apps[i].id = te32->th32OwnerProcessID;
                    GetGUIThreadInfo(te32->th32ThreadID,ti32);
                    if (ti32->hwndActive) {
                        pe32 = getApp(apps[i].id);
                        if (pe32 != NULL) {
                            apps[i].exe.Printf(pe32->szExeFile);
                            GetWindowText(ti32->hwndActive,t,sizeof(LPTSTR));
                            apps[i].name.Printf(t);
                            apps[i].hwnd = ti32->hwndActive;
                            s = apps[i].exe << " - " << apps[i].name;
                            hexLBSoftware->Append(s); i++;
                        }
                    }
                } while(Thread32Next(app, te32));
                apps[i - 1].last = true; CloseHandle(app);
            }
        }
    } else { appWait++; }
#endif
}

During run time this line complains of an Access Violation
te32->dwSize = sizeof(THREADENTRY32);
Any ideas on what's causing it?
Sorry, I missed the apps = new wxExe [i];. I would use a std::vector instead of newing an array, and then just push_back the new information.

If you want to stick with the "new" approach, you do need the first set of Process32First/Next calls. Though I suppose you could just be wasteful and over allocate. (My PC is currently running 88 processes, according to Task Manager, so 256 would prob. be pretty safe.)

te32->dwSize = sizeof(THREADENTRY32); is dying as you haven't allocated storage for te32.

As you only need one THREADENTRY32, I would code it along the lines of.

1
2
3
4
5
    THREADENTRY32 te32 = {0};
    te32.dwSize = sizeof(THREADENTRY32);
    ...
    if (Thread32First(app, &te32)) {
        ...


That way, you have less delete calls to worry about (to balance the new calls)

Andy
Last edited on
Thanks, by the way apps doesn't get deleted at all while the process is running because it's part of an object. The reason I need apps is because later I need to use the values for Hooking a user determined app, sort of like a general purpose trainer really. I'm first setting up the hook/hack info before beginning on the hack code which I'll be fine with for the most part as I found the reference while researching what the functions did & required.
by the way apps doesn't get deleted at all

I didn't intedn to imply that you were forgetting to delete anything. Just commenting that you could save yourself a bit of work by not new-ing your THREADENTRY32 variable.

The same should hold for the GUITHREADINFO and PROCESSENTRY32 variables.

Andy
Last edited on
To save a bit of hassle I've set up the project on google code, just trying to change upload settings for TortoiseSVN and I'll upload all my code there. For anyone who wants to use the program when it's ready just look for "hacking gui" (use quotes, hex was taken and hx was too short apparently so I went with a historic reference "renegade"). Finally found an example of EnumWindows in use so now I'm changing my code to make use of it - I didn't understand how to use it properly before.

Edit: new run time error and I haven't a clue where or what it is (debug comes up with nothing), any chance you can look over the code from HexFrm.cpp and see if you can identify what might be causing it. Address: http://code.google.com/p/renegade/
Last edited on
One glaring point I caught in this post is that you want this to maintain backward compatibility with Windows 98. You haven't done this yet. Personally I don't believe this is something you NEED to do, but if it's still a goal of yours then you are going in the wrong direction.

Also EnumWindows() only passes you the handle of each window, do you know how you're going to go about getting the rest of the data? Why would you think this is easier then using "tlhelp32.h"?
Last edited on
Well I tried tlhelp32.h but wasn't getting any text going to the list box so I thought I'd try an easier method for the time being and work on backward compatibility once I get a working version compiled since I can then just make an if statement for testing code that can replace non backward compatible code. I switched to EnumWindows in the hope that I would get window titles out of it. The reason it needs to be backward compatible with 98 is because I don't know for sure what version of windows down loaders will have.

Edit: Just realised why I wasn't getting text (I think), however I still get a run time error which pin points to one line but I haven't figured out what's wrong yet. In case you figure it out before me I'll post the current code here.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
WX_DECLARE_OBJARRAY(xApp, apps);
WX_DEFINE_OBJARRAY(apps);
void HexFrm::getApps(bool user) {
#ifdef WIN32
    if (appWait > 500 || user == true) {
        appWait = 0;
        wxString s;
        xApp a;
        PROCESSENTRY32 pe32;
        THREADENTRY32 te32 = {0};
        GUITHREADINFO *ti32;
        LPTSTR t;
        pe32.dwSize = sizeof(PROCESSENTRY32);
        te32.dwSize = sizeof(THREADENTRY32);
        HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        HANDLE ht;
        if (hp != INVALID_HANDLE_VALUE && Process32First(hp, &pe32)) {
            do {
                ht = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pe32.th32ProcessID);
                try {
                    Thread32First(ht, &te32);
                    GetGUIThreadInfo(te32.th32ThreadID,ti32) != 0;
                    GetWindowText(ti32->hwndFocus, t, sizeof(LPTSTR)); // this line
                    s.Printf(t);
                    hexLBSoftware->Append(s);
                    //apps->Add((xApp)a, 1);
                } catch (e) {}
            } while (Process32Next(hp, &pe32));
        }
Last edited on
I got there first, forgot why but now I just can't understand why I still don't get any window titles, this is the current code of my loop:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                ht = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pe32.th32ProcessID);
                Thread32First(ht, &te32);
                do {
                    GetGUIThreadInfo(te32.th32ThreadID,&ti32) != 0;
                    hwnd = GetWindow(ti32.hwndActive, GW_OWNER);
                    if (hwnd == NULL) hwnd = ti32.hwndActive;
                    tl = GetWindowTextLength(hwnd);
                    if (GetWindowText(hwnd, t, tl)) {
                        s.Printf("%s: %s", pe32.szExeFile, t);
                        break;
                    } s.Printf(pe32.szExeFile);
                } while (Thread32Next(ht,&te32));
                if (s != "") {
                    hexLBSoftware->Append(s);
                } //apps->Add((xApp)a, 1); 
GetWindowText does not work on another processes, see MSDN. Use SendMessage with WM_GETTEXT instead.
Thanks, I've switch the GetWindow* with SendMessage(hwnd, WM_GETTEXT | WM_GETTEXTLENGTH (not same call)..., but I'm currently failing to get a length on windows I know for a fact should not return zero. I'm trying to figure out why by myself but if you do feel like giving an example line of WM_GETTEXTLENGTH in use then I would have gotten past the first hurdle of this topic and reduce the output from every process to those likely to have been initiated by the user which should be good enough for now.
Not matter what example I look at on the net it seems that I'm doing things right so I do not understand why I am still not getting the titles, here's my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
WX_DECLARE_OBJARRAY(xApp, apps);
WX_DEFINE_OBJARRAY(apps);
void HexFrm::getApps(bool user) {
#ifdef WIN32
    if (appWait > 500 || user == true) {
        // variable creation
        appWait = 0; wxString s; xApp a; char* t; WPARAM tl; HWND hwnd; HANDLE ht;
        PROCESSENTRY32 pe32; THREADENTRY32 te32 = {0}; GUITHREADINFO ti32;
        pe32.dwSize = sizeof(PROCESSENTRY32); te32.dwSize = sizeof(THREADENTRY32);
        HANDLE hp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        // main code
        hexLBSoftware->Clear();
        if (hp != INVALID_HANDLE_VALUE && Process32First(hp, &pe32)) {
            do {
                ht = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pe32.th32ProcessID);
                Thread32First(ht, &te32);
                do {
                    GetGUIThreadInfo(te32.th32ThreadID,&ti32) != 0;
                    hwnd = GetWindow(ti32.hwndActive, GW_OWNER);
                    if (hwnd == NULL)
                        hwnd = ti32.hwndActive;
                    tl = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
                    if (tl > 0) {
                        SendMessage(hwnd, WM_GETTEXT, tl, (LPARAM)&t);
                        s.Printf("%s: %s", pe32.szExeFile, t);
                        break;
                    } s.Printf(pe32.szExeFile);
                } while (Thread32Next(ht,&te32));
                if (s != "") {
                    hexLBSoftware->Append(s);
                    //apps->Add((xApp)a, 1);
                }
            } while (Process32Next(hp, &pe32));
        }
        /*
            if (pe32.th32ProcessID != 0) {
                apps[i].id = pid;
                apps[i].exe.Printf(pe32.szExeFile);
                GetWindowText(ti32->hwndActive,t,sizeof(LPTSTR));
                apps[i].name.Printf(t);
                apps[i].hwnd = ti32->hwndActive;
                s = apps[i].exe << " - " << apps[i].name;
                hexLBSoftware->Append(s); i++;
            }
        }*/
        CloseHandle(ht); CloseHandle(hp);
    } else { appWait++; }
#endif
}
Last edited on
Without delving too much into the code, I see that you pass &t as the LPARAM. Wrong. You pass t, not &t. Furthermore, you must allocate memory before trying to obtain the text. This is why you call for WM_GETTEXTLENGTH first!, to know how much memory to allocate.

1
2
3
4
5
6
tl = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
if (tl)
{
    t = new TCHAR[++tl];
    SendMessage(hwnd, WM_GETTEXT, tl, (LPARAM)t);
...


The above should be the right way.

EDIT: Note how I use TCHAR and you use char. You are doing it the hard way: You are mixing char with the macro function names. If you are going to use the char data type, you must use the function names that end with "A", like SendMessageA().
Last edited on
Thanks, I'll try as soon as I get to use my computer again. Had a couple of break-ins so I moved my computer to relatives until I find a place to move to (currently in a hostel) or until the culprit is caught. I'll be setting up a hidden camera in my room to catch them in the act if they try again since it's unlikely they would know I moved my computer since no one else was in when I did so.
Topic archived. No new replies allowed.