0

To check a process is running, one can use the "CreateToolHelp32SnapShot" Windows API function and enumerate the running processes. Example C/C++ code is given in the answer question: Check whether one specific process is running on windows with C++

My question is whether there is a faster way to find the answer without having to check each process in turn if you know you're looking for a specific process? We need to regularly check that our remote support tool, TeamViewer.exe, is running. Note that TeamViewer is written by a 3rd party, we have no control over it or knowledge of the internals of the EXE.

EDIT:

TeamViewer have provided some information about the internals of their process which can be used to speed this up in this instance (see accepted answer). The answer also shows how to speed up this check for any process (by saving the process ID).

AlainD
  • 5,413
  • 6
  • 45
  • 99
  • Wait for that specific process handle by *some* `WaitForSingleObject` sort of function (of course the `WaitForMultipleObjects` will fit better in real as it would allow you to interrupt the wait). – TLama Mar 19 '15 at 10:59
  • There are alternative ways to enumerator processes. However, can you tell us what performance problems you are having, and what your performance constraints are? Also, are you committed to polling? – David Heffernan Mar 19 '15 at 10:59
  • @David, I would really suggest to do Y rather than X here (no matter what performance is). – TLama Mar 19 '15 at 11:06
  • Its not so much that this check itself is slow (the above code typically runs in ~2-3ms), but that its' one of hundreds of things happening in a demanding image processing application. This came up during profiling as something that could benefit from a possible speed-up. Of course, we will look at slowing down the polling (or abandoning it altogether) and other design changes, but I'm also doing some research into what is best practice or achievable. – AlainD Mar 19 '15 at 11:06
  • 1
    @TLama WaitForSingleObject is the right way to wait until a process has terminated. Clearly. I'm trying to make the point that questions asking for "faster" solutions are often meaningless without details of the constraints. – David Heffernan Mar 19 '15 at 11:06
  • @TLama: Thanks for the comment about WaitForSingleObject...looking into that now and found a few related questions such as this one http://stackoverflow.com/questions/1591342/c-how-to-determine-if-a-windows-process-is-running. – AlainD Mar 19 '15 at 11:10
  • @TLama: Yes, that might work well. I'm thinking I could poll slowly to check when the remote supports starts. When detected, save the process ID, then use WaitForSingleObject or GetExitCodeProcess. Thanks! – AlainD Mar 19 '15 at 11:13
  • @Alain, a better application for the `WaitFor...` functions would be waiting until the process dies rather than polling if it's running. But after re-reading your question I'm not sure if that's what you are looking for. – TLama Mar 19 '15 at 11:13
  • Process ID is no good. They get re-used. You need to open a handle to the process and wait on that. `GetExitCodeProcess` involves polling. I don't understand why you seem so keen to poll. – David Heffernan Mar 19 '15 at 11:23
  • @DavidHeffernan: Its not that I'm keen to poll, but how do you get a handle to the process quickly without enumerating the running processes using, say, CreateToolHelp32SnapShot? I've implemented a solution using OpenProcess(PROCESS_ALL_ACCESS, False, m_cache.dwTeamViewerID); which is over 100 times faster than enumerating all process each time. But this only works once you have the PID of TeamViewer.exe...to get that still in the first place still requires the slow enumeration technique, checking for a process with the required name. – AlainD Mar 19 '15 at 15:18
  • You'll need to enumerate the processes until you have a process handle. At that point you can stop. Did you compare `EnumProcesses`. I suspect it will be faster that toolhelp. – David Heffernan Mar 19 '15 at 15:29
  • Haven't had time to actually measure EnumProcesses yet. I did some research (ie. bit of Googling) and the opinion was that the difference should be negligible because both CreateToolHelp32SnapShot and EnumProcesses call the same Windows APIs under-the-hood. As you say, once you have the PID you can use much faster techniques to see if the process is still running. – AlainD Mar 19 '15 at 17:04
  • Amazing how good questions sometimes get downvoted. Perhaps whomever that was would care to explain why (and review the accepted answer). – AlainD Mar 25 '15 at 12:42

4 Answers4

3

The first step you must take involves enumerating processes. Do this with either toolhelp or EnumProcesses. If you find your process, pass the PID to OpenProcess to get process handle. Then wait for the process to be signaled. This then obviates the need to poll, beyond the initial enumeration.

If your initial enumeration fails to find the process you will have to repeat the enumeration. Use whichever of toolhelp or EnumProcesses turns out to be more efficient. Perhaps throttle the polling. You may also choose to use WMI to be notified of new process creation and that way completely avoid the need for polling.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks, this is the approach I have taken. The initial enumeration is fairly time-consuming, but checking the process is alive using OpenProcess and the process ID is much quicker. The approach using WMI to be notifiedof process creation is an interesting idea I'll look into. – AlainD Mar 19 '15 at 22:17
1

After much research (and a response from TeamViewer support) I am posting a definitive answer, hope it helpful to others. Code is in Delphi but can be translated easily to C++.

* IF YOU HAVE NO INFO ABOUT THE INTERNALS OF THE PROCESS *

function IsRemoteSupportRunning() : Boolean;
var
    hSnapShot, hProcess: THandle;
    process: TProcessEntry32;
    bFound: Boolean;
begin
    bFound := False;
    if (cache.dwTeamViewerID = 0) then
        begin
        // TeamViewer not running...is it running now?
        try
            hSnapShot := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
            process.dwSize := Sizeof(TProcessEntry32);
            if (Process32First(hSnapShot, process)) then
                bFound := (AnsiCompareText(REMOTE_SUPPORT_EXE, process.szExeFile) = 0);

            if (not bFound) then
                begin
                while (Process32Next(hSnapShot, process)) do
                    begin
                    bFound := (AnsiCompareText(REMOTE_SUPPORT_EXE, process.szExeFile) = 0);
                    if (bFound) then
                        break;
                    end;
                end;

            CloseHandle(hSnapShot);
        except
        end;

        // If found, save the process ID
        if (bFound) then
            cache.dwTeamViewerID := process.th32ProcessID;
        end
    else
        begin
        // In a previous call to this function, TeamViewer was running...
        hProcess := OpenProcess(PROCESS_ALL_ACCESS, False, cache.dwTeamViewerID);
        if (hProcess > 0) then
            begin
            // Still running!
            bFound := True;
            CloseHandle(hProcess);
            end
        else
            begin
            // Process is no longer running
            cache.dwTeamViewerID := 0;
            end;
        end;

    Result := bFound;
end;

On my machine, this consumes ~1.5ms if TeamViewer.exe is not running. Once running and the PID is known, this drops to ~6.8µs.

* IF YOU HAVE SOME INFO ABOUT, OR HAVE CONTROL OVER, THE PROCESS *

There are a number of possibilities here. For example, the process may create (or you code the process to create) a shared memory object using this code:

CreateFileMapping(HWND($FFFFFFFF), nil, PAGE_READONLY, 0, 32, 'MyProcess');

You can now check for the process running by using this:

var
    hMapping: HWND;

hMapping := CreateFileMapping(HWND($FFFFFFFF), nil, PAGE_READONLY, 0, 32, 'MyProcess');
if (hMapping <> 0) then
    begin
    if (GetLastError() = ERROR_ALREADY_EXISTS) then
        bRunning := True;

    CloseHandle(hMapping);
    end;

Finally, TeamViewer support informed me about a named mutex object which you'll see in the code below. Note that this is specific to TeamViewer, but if you had control over the process, you could create a mutex and use this technique. On my system, this consumes ~2.6µs!

function IsRemoteSupportRunning() : Boolean;
var
    hProcess: THandle;
begin
    // Use OpenMutex to see if TeamViewer.exe is running...
    Result := False;
    hProcess := OpenMutex(MUTEX_ALL_ACCESS, False, PChar('TeamViewer_Win32_Instance_Mutex'));
    if (hProcess > 0) then
        begin
        bFound := True;
        CloseHandle(hProcess);
        end;
end;

TeamViewer informed me of a powerful tool WinObj to investigate objects in the system, https://technet.microsoft.com/de-de/sysinternals/bb896657.aspx. Check out the BaseNamedObjects. Mutexes appear as "Mutant".

AlainD
  • 5,413
  • 6
  • 45
  • 99
1

On Windows, you can leverage system() or _wsystem() to quickly determine if a process is running by name. If you want to send the whole process name, .exe included, just remove the characters .exe from the first line of the function.

static bool process_exists(std::wstring token)
{
    std::wstring cmd_query(std::wstring(L"tasklist|findstr /i \"") + token + L".exe\">nul 2>&1");
    return (_wsystem(cmd_query.data()) == 0) ? (true) : (false);
}
kayleeFrye_onDeck
  • 6,648
  • 5
  • 69
  • 80
  • 1
    This does work, however it is painfully slow, consuming ~150ms on one (older) development machine. Moreover, a command prompt briefly pops up to execute the command which creates an unfortunate visual flicker. Not recommended except for special cases. Very original, though! I had no idea of this technique nor the `_wsystem` API till now. Thanks! – AlainD Aug 30 '18 at 13:11
  • @AlainD I didn't know about it either until I was trying to whip up a short & simple function to check for running processes by-name for a module that is thankfully not averse to longer query times. I did multiple averages for queries for processes that existed/didn't exist as well as ones by low and high PID and alphabetical opposites. The range was about 220-255ms across about 8,000 iterations on an i7-7700T 2.9 Ghz with 16GB DDR4. A quarter-second is practically an eternity compared to low-ring query-only APIs but for automation (no polling!) purposes it was acceptable. – kayleeFrye_onDeck Aug 30 '18 at 18:36
0

Using Mutex to check the process running.

Creating a Mutex with specific name in the checking process and check this mutex exist in other process.

Process 1:

        using var mutex = new Mutex(true, LockId, out var created);

The LockId is a specific name.

    public const string LockId = "5742D257-CCCC-4F7A-8191-6362609C458D";

Process 2 can use Mutex.TryOpenExisting to check the mutex exist.

lindexi
  • 4,182
  • 3
  • 19
  • 65