0

I'm trying to get the name of all my open processes. This is what I have:

   #include "stdafx.h"
#include <Psapi.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int iCmdShow)
{
    bool _result;
    DWORD *pProcessIds = new DWORD[1000];
    DWORD cb;
    DWORD pBytesReturned;

    _result =  EnumProcesses(pProcessIds, 1000, &pBytesReturned);

    HANDLE hndProccesse;


    for (int i = 0; i < pBytesReturned / sizeof(DWORD); ++i)
    {
        hndProccesse =   OpenProcess(STANDARD_RIGHTS_ALL,  false, *pProcessIds);
        DWORD _len;

        DWORD _len2 =0;
        LPWSTR lpFilename = new WCHAR[100];
        _len =GetModuleFileNameEx(hndProccesse,NULL, lpFilename, _len2);
        DWORD _errr;
        _errr =  GetLastError();
        MessageBox(NULL, lpFilename, NULL, 0);
        CloseHandle(hndProccesse);

        pProcessIds ++;



    }

    return 0;
}

Everything is working fine upto GetModuleFileNameEx which is giving an Access Denied Error (5).

Also this is whats displaying on the message box: enter image description here

Any Ideas?

Math4123
  • 1,267
  • 4
  • 12
  • 23
  • how about trying your program in administrator? – Young Hyun Yoo Feb 14 '15 at 07:43
  • 1
    `GetLastError` is meaningless if the function didn't actually fail. You don't seem to be checking for failure. – Jonathan Potter Feb 14 '15 at 07:45
  • @YoungHyunYoo I tried starting VS as an administrator and its still experiencing the same error. – Math4123 Feb 14 '15 at 07:50
  • You're looping 1000 times and completely ignoring the pBytesReturned parameter? You need to do: `for (int i = 0; i < pBytesReturned / sizeof(DWORD); ++i);` – Brandon Feb 14 '15 at 07:59
  • Why did you do `HANDLE hndProccesse = new HANDLE;`? That isn't the cause of your bug, but is incorrect for other reasons. – andlabs Feb 14 '15 at 08:00
  • Here's another bug with your code: check what you're doing with `_len2`. Check also how `_len2` differs from the size of your array. (This will also lead to why your message box displays garbage.) – andlabs Feb 14 '15 at 08:03
  • And you still didn't update the post to take into account what @JonathanPotter told you. The return value of `GetModuleFileNameEx()` (which you store in `_len`) determines whether or not an error occurred; MSDN has the details. Once you make sure `GetModuleFileNameEx()` indicates an error has occurred, *then* you call `GetLastError()` to see what the error actually is. This is because `GetLastError()` is not necessarily set back to a no-error state if a function succeeds. The same applies to all Windows API functions. – andlabs Feb 14 '15 at 08:06
  • @andlabs ah brilliant, post that as the answer and I'll mark it solved. – Math4123 Feb 14 '15 at 08:07

2 Answers2

3

The fourth argument to GetModuleFileNameEx() has to be the size of the array passed in the third argument. You pass in _len2. But you set _len2 to zero, not to the size of your lpFilename array (100). So GetModuleFileNameEx() sees it has nothing to do, and doesn't even touch your lpFilename array. Heap data isn't necessarily initialized to zeroes, so lpFilename still contains random garbage, hence the random message box contents.

I am going to guess that GetModuleFileNameEx() returned zero because it didn't need to write anything, but didn't set the last error code because nothing failed, so the access denied error is left over from an earlier part of the program.

Some more notes:

Keep in mind what Jonathan Potter said about the proper way to check error returns from Windows API functions. You have you return value from GetModuleFileNameEx() in _len. MSDN says GetModuleFileNameEx() returns zero on error. So you need to check _len to see if it equals zero before getting the last error value, otherwise it won't be meaningful. As mentioned earlier, GetModuleFileNameEx() doesn't have to clear the last error value if it succeeds.

HANDLE hndProccesse = new HANDLE; is definitely wrong, but is not a bug in your program (it is a memory leak, though!). HANDLE itself is a pointer, which is why the new was allowed to run. But doing so is meaningless, as HANDLEs are returned by the operating system and generally shouldn't be used as pointers. Consider them opaque values instead.

On the subject of memory leaks, you never delete[] each of the lpFilenames you create in your loop, nor do you delete[] pProcessIds. This might not be important for the small program you posted above, but if your program ever grows you'll definitely want to fix that.

In general, use MAX_PATH as the nominal length of a filename buffer instead of 100. This is what the various shell functions use. (Longer won't hurt either, but shorter will.)

andlabs
  • 11,290
  • 1
  • 31
  • 52
1

You need to use FormatMessage to get a useful error description. An example is shown below.. This example is exactly like yours but all it does is incorporate error checking. IT DOES NOT SOLVE THE PROBLEM!

#include <windows.h>
#include <Psapi.h>

void ShowError(DWORD err)
{
    LPTSTR lpMsgBuf = nullptr;
    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, nullptr);
    MessageBoxA(NULL, lpMsgBuf, "ERROR", 0);
    LocalFree(lpMsgBuf);
}

int main()
{
    DWORD* pProcessIds = new DWORD[1000];
    DWORD pBytesReturned = 0;

    bool res =  EnumProcesses(&pProcessIds[0], 1000, &pBytesReturned);

    if (!res)
    {
        DWORD err = GetLastError();
        MessageBoxW(NULL, L"ENUMPROCESSES FAILED", L"Error", 0);
        ShowError(err);
        delete[] pProcessIds;
        return EXIT_FAILURE;
    }

    for (unsigned int i = 0; i < pBytesReturned / sizeof(DWORD); ++i)
    {
        if (pProcessIds[i] == 0) //error.. process id is 0..
            continue;

        wchar_t lpFilename[256] = {0};
        HANDLE hndProccess =   OpenProcess(STANDARD_RIGHTS_ALL,  false, pProcessIds[i]);

        if (hndProccess == NULL || hndProccess == INVALID_HANDLE_VALUE)
        {
            DWORD err = GetLastError();
            MessageBoxW(NULL, L"FAILED TO OPEN PROCESS", L"ERROR", 0);
            ShowError(err);
            delete[] pProcessIds;
            return EXIT_FAILURE;
        }

        int len = GetModuleFileNameExW(hndProccess, NULL, lpFilename, sizeof(lpFilename) / sizeof(lpFilename[0]));
        if (len <= 0)
        {
            DWORD err = GetLastError();
            if (err)
            {
                MessageBoxW(NULL, L"FAILED TO GET MODULEFILENAME", L"ERROR", 0);
                ShowError(err);
                delete[] pProcessIds;
                return EXIT_FAILURE;
            }
        }          

        CloseHandle(hndProccess);

        MessageBoxW(NULL, lpFilename, L"NAME", 0);
    }

    delete[] pProcessIds;
    return 0;
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
  • I meant to fix the call after `CloseHandle` but I forgot and went to sleep. That's why I had them all in temporary variables. For the checking only upon failure, I realise that.. However, I only posted this to show the OP how to get a useful error message from the return value of `GetLastError`. That being said, I fixed it. – Brandon Feb 14 '15 at 22:10