CreateToolhelp32Snapshot: INVALID_HANDLE_VALUE (ERROR_PARTIAL_COPY) - c++

This code is running in a 64-bit application. The target application is 32-bit.
Every time I run this code, CreateToolhelp32Snapshot() returns INVALID_HANDLE_VALUE and then GetLastError() returns ERROR_PARTIAL_COPY. So it skips the loop and returns false.
BOOL HookInjector::InjectIntoProcess(DWORD pID)
{
//Get Handle to Remote Process
HANDLE Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
....
//Check to see if 64-bit or 32-bit application
IsWow64Process(Proc, &isWow64);
size_t szCurProc = sizeof(void*); //returns 8
if (isWow64)
{
__debugbreak();
//Get list of all Modules associated with the Process
HANDLE hProc32Module;
do {
hProc32Module = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE32, pID);
}
while ((hProc32Module == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_BAD_LENGTH));
if (hProc32Module == INVALID_HANDLE_VALUE) {
__debugbreak();
DWORD err = GetLastError(); //just to see the error code which is 0x12b
return false;
}
//Find the module for Kernel.dll and get the base address of it
MODULEENTRY32 entryModule;
entryModule.dwSize = sizeof(MODULEENTRY32);
BOOL isGetModuleSuccess = Module32First(hProc32Module, &entryModule);
DWORD errEndofList = GetLastError();
BOOL isSuccessful = false;
while (errEndofList != ERROR_NO_MORE_FILES && isGetModuleSuccess)
{
if (_tcscmp(entryModule.szModule, KERNEL32_DLL)){
isSuccessful = true;
break;
}
isGetModuleSuccess = Module32Next(hProc32Module, &entryModule);
errEndofList = GetLastError();
}
if (!isSuccessful)
{
__debugbreak();
CloseHandle(hProc32Module);
return false;
}
//Get handle for Kernel.dll module
hKernel32 = entryModule.hModule;
CloseHandle(hProc32Module);
}
else
{
....

According to the documentation, CreateToolhelp32Snapshot() only fails with ERROR_PARTIAL_COPY when CreateToolhelp32Snapshot() is called by a 32bit process trying to access a 64bit process:
If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).
Make sure your app really is compiled for 64bit to begin with. TH32CS_SNAPMODULE32 only makes sense to use when CreateToolhelp32Snapshot() is being called in a 64bit process:
TH32CS_SNAPMODULE32
0x00000010
Includes all 32-bit modules of the process specified in th32ProcessID in the snapshot when called from a 64-bit process.
You are also not taking into account that GetLastError() is only updated when API functions fail, unless documented otherwise. Your loops are assuming that GetLastError() is updated after every API call, that is simply not true.
Try something more like this instead:
BOOL HookInjector::InjectIntoProcess(DWORD pID)
{
//Get Handle to Remote Process
HANDLE Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
....
DWORD dwFlag;
#ifdef _WIN64
//Check if Remote Process is a 32-bit application
BOOL isWow64 = FALSE;
IsWow64Process(Proc, &isWow64);
if (!isWow64) return false;
// TH32CS_SNAPMODULE32 includes 32bit modules when used by a 64bit process...
dwFlag = TH32CS_SNAPMODULE32;
#else
// TH32CS_SNAPMODULE includes 32bit modules when used by a 32bit process...
dwFlag = TH32CS_SNAPMODULE;
#endif
__debugbreak();
//Get list of all Modules associated with the Process
HANDLE hProc32Module;
do {
hProc32Module = CreateToolhelp32Snapshot(dwFlag, pID);
}
while ((hProc32Module == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_BAD_LENGTH));
if (hProc32Module == INVALID_HANDLE_VALUE) {
__debugbreak();
return false;
}
//Find the module for Kernel.dll and get the base address of it
hKernel32 = NULL;
MODULEENTRY32 entryModule = {0};
entryModule.dwSize = sizeof(MODULEENTRY32);
BOOL isGetModuleSuccess = Module32First(hProc32Module, &entryModule);
while (isGetModuleSuccess) {
if (_tcscmp(entryModule.szModule, KERNEL32_DLL)) {
hKernel32 = entryModule.hModule;
break;
}
isGetModuleSuccess = Module32Next(hProc32Module, &entryModule);
}
if (!hKernel32) {
__debugbreak();
CloseHandle(hProc32Module);
return false;
}
CloseHandle(hProc32Module);
....
}

According to the documentation, CreateToolhelp32Snapshot() only fails with ERROR_PARTIAL_COPY when CreateToolhelp32Snapshot() is called by a 32bit process trying to access a 64bit process:
If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).
This is plain wrong as you could have deduced from the excerpt you posted. Where does it say it ONLY fails with ERROR_PARTIAL_COPY (299) when the caller is 32bit trying to access 64bit? It doesn't.
If the documentation would be complete, which it is not, then you would be right to assume that the stated behaviour is the only way to generate an ERROR_PARTIAL_COPY error code. Sadly the documentation is not complete.
For example if you start a process with the CREATE_SUSPENDED flag the CreateToolhelp32Snapshot API will set the error code to ERROR_PARTIAL_COPY when queried for modules regardless of the bitness of the host or target application. The reason it fails is because the DLLs aren't loaded until after the main thread is resumed and therefore the PebLdr pointer in the PEB structure is NULL.
Basically anything which prevents read of process memory (missing address in PEB, unmapped segment, etc.) can cause ERROR_PARTIAL_COPY as its description states:
ERROR_PARTIAL_COPY 299 (0x12B)
Only part of a ReadProcessMemory or WriteProcessMemory request was completed.

Related

Logoff remote system using c++

I am looking for an API to logoff current user on remote computer. Two
functions ExitWindows and InitiateSystemShutdown seem not exactly what I
want. The first one doesn't accept computer name, the second one doesn't
have logoff option.is it possible to logoff current user
on remote computer ?. Can someone tell me how to achieve this in a
C++ program?
I knew that you want to shutdown system by using exitwindows function.
However, if you want to shut down the remote system in your own process, you need to use the exitwindowsEX function and write a program that specifies the process ID.
The relevant function references are as follows:
https://learn.microsoft.com/zh-cn/windows/win32/shutdown/how-to-shut-down-the-system
The following are specific codes:
#pragma region
#include<windows.h>
#pragma warning(disable:4996)
BOOL ReSetWindows(DWORD dwFlags, BOOL bForce)
{
if (dwFlags != EWX_LOGOFF && dwFlags != EWX_REBOOT && dwFlags != EWX_SHUTDOWN)
return FALSE;
OSVERSIONINFO osvi = { 0 };
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if (!GetVersionEx(&osvi))
{
return FALSE;
}
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
//EnableShutDownPriv();
}
dwFlags |= (bForce != FALSE) ? EWX_FORCE : EWX_FORCEIFHUNG;
return ExitWindowsEx(dwFlags, 0);
}
int main()
{
ReSetWindows(EWX_LOGOFF, false);//logoff
//ReSetWindows(EWX_REBOOT, true);//restart
//ReSetWindows(EWX_SHUTDOWN, true);//shutdown
}
=======================
Caution!!!Please save your important file before running or running in the virtual machine
WTSLogoffSession? but the concept of a current user on a remote machine does not really exist, you would have to inspect the sessions with WTSEnumerateSessions+WTSQuerySessionInformation if you want to find a specific user. This only makes sense in a environment where there is a NT domain so you can match against a domain SID. Without a domain, all you can do is match against the username which might be enough for you.
This is the program specifies the process ID:
DWORD GetProcessIDByName(LPCTSTR szProcessName)
{
STARTUPINFO st;
PROCESS_INFORMATION pi;
PROCESSENTRY32 ps;
HANDLE hSnapshot;
DWORD dwPID = 0;
ZeroMemory(&st, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
st.cb = sizeof(STARTUPINFO);
ZeroMemory(&ps, sizeof(PROCESSENTRY32));
ps.dwSize = sizeof(PROCESSENTRY32);
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)//
{
return dwPID;
}
if (!Process32First(hSnapshot, &ps))
{
return dwPID;
}
do
{
if (lstrcmpi(ps.szExeFile, szProcessName) == 0)
{
dwPID = ps.th32ProcessID;
}
} while (Process32Next(hSnapshot, &ps));
CloseHandle(hSnapshot);
return dwPID;//
}
You need this line of code in your main function:
DWORD pId = GetProcessIDByName("\\\.exe");
Closing remote system by local machine is easy. Closing local machine by remote machine sounds like a virus. No offense, it is difficult to implement. Maybe you can try using socket to communicate local machine with virtual machine.

CreateRemoteThread succeeded, but LoadLibrary failed for some target app

I am using CreateRemoteThread() + LoadLibrary() method to inject code.
Everything is OK when I running my injector in my Windows7 64bit OS laptop, and it still work in Windows Server 2012 R2 64bit for some target app.
BUT, in this Windows Server 2012 environment, for some target app, which is old MFC application, the CreateRemoteThread succeeded but the DllMain did not get called and I found the LoadLibrary() seems failed, by using GetExitCodeThread() on the created remote thread.
For the memory to write in target process, I counted the terminating 0 byte.
Also, I already knew the kernel32.dll address are the same for both the Windows 7 and Windows Server 2012, using the method introduced in below URL answer part.
CreateRemoteThread fails,maybe the lpBaseAddress in the target process is invalid,but it is allocated by the system?
The GetExitCodeThread() below got an zero exit code.
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, NULL, NULL);
if(hThread == NULL) {
OutputDebugString(_T("Error: the remote thread could not be created.\n"));
writeLog("Error: the remote thread could not be created.");
}
else {
DWORD dResult = WAIT_OBJECT_0;
dResult = WaitForSingleObject(hThread, 1000*3);// the thread may already exited, so do not wait INFINITE
DWORD dwExitCode = 0;
GetExitCodeThread(hThread, &dwExitCode);
if(dwExitCode == 0)
{
writeLog("Error: LoadLibraryA failed.");
}
else
{
OutputDebugString(_T("Success: the remote thread was successfully created.\n"));
writeLog("Success: the remote thread was successfully created.");
}
}
Do you have any idea what should I suspect next?
To summarize, in below diagram, you can see the only fail is only when I run injector on Windows Server 2012 to inject into some old MFC application.
In below diagram, there is the information about HOW old the MFC application is:
I am trying to provide enough information, kindly let me know if you need some more information.
below is the complete code for inject my dll:
void inject(int procID, char* pszHookDll)
{
g_nTargetProcId = procID;
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);
g_hTargetProc = process;
BOOL bInit = SymInitialize(g_hTargetProc, g_sPdbFolder, TRUE);// for analysing the information spy.dll send out
if(process == NULL) {
writeLog("Error: the specified process couldn't be found.");
}
/*
* Get address of the LoadLibrary function.
*/
LPVOID addr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if(addr == NULL) {
writeLog("Error: the LoadLibraryA function was not found inside kernel32.dll library.");
}
//addr = getProcAddrInTargetProcess(procID, process);
/*
* Allocate new memory region inside the process's address space.
*/
int nBufSize = strlen(pszHookDll)+1;
LPVOID arg = (LPVOID)VirtualAllocEx(process, NULL, nBufSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if(arg == NULL) {
writeLog("Error: the memory could not be allocated inside the chosen process.");
}
/*
* Write the argument to LoadLibraryA to the process's newly allocated memory region.
*/
int n = WriteProcessMemory(process, arg, pszHookDll, nBufSize, NULL);
if(n == 0) {
writeLog("Error: there was no bytes written to the process's address space.");
}
/*
* Inject our DLL into the process's address space.
*/
HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, NULL, NULL);
if(hThread == NULL) {
writeLog("Error: the remote thread could not be created.");
}
else {
DWORD dResult = WAIT_OBJECT_0;
dResult = WaitForSingleObject(hThread, 1000*3);
DWORD dwExitCode = 0;
GetExitCodeThread(hThread, &dwExitCode);
if(dwExitCode == 0)
{
writeLog("Error: LoadLibraryA failed.");
}
else
{
OutputDebugString(_T("Success: the remote thread was successfully created.\n"));
writeLog("Success: the remote thread was successfully created.");
}
}
/*
* Close the handle to the process, becuase we've already injected the DLL.
*/
//CloseHandle(process);close after symcleanup
}
I got the reason: it was a dependency problem.
Here are the dependencies of spy.dll:
The spy.dll depends on msvcr100d.dll, which is not available by default on my windows Server 2012 environment.
The new MFC app I mentioned was deployed together with msvcr100d.dll on Windows Server 2012, so there was no problem.
Thanks buffy and Remy!!

Windows Event Viewer holds a lock on my EXE file

I'm curious about something. I'm developing a Windows service and log all the diagnostic events into the Windows Event Log. So when the service is running I open the Event Viewer (from Administrative tools) to view the results of my service's operation.
This works great except for the moment when I need to uninstall my program (again, for the testing purposes.) For some weird reason the Event Viewer holds a lock on the .exe image file for my service so the uninstaller fails to delete it with the error code ERROR_SHARING_VIOLATION:
The process cannot access the file because it is being used by another process.
This happens only on Vista and later OS and seems not to be an issue on XP.
Any idea how to make Event Viewer release the file lock? (I'm asking about programmatic approach. I can obviously close it manually, but that's not what I'm after.)
I released the lock this way:
Start -> Services
Locate Windows Event Log
Right click -> Restart
There's a less known feature introduced in Vista called Restart Manager that can help you release file locks via a user-mode code. Since you tagged it as C++, based on this article here's a small code sample to do that:
#include <RestartManager.h>
#pragma comment(lib ,"Rstrtmgr.lib")
BOOL ReleaseFileLock(LPCTSTR pFilePath)
{
BOOL bResult = FALSE;
DWORD dwSession;
WCHAR szSessionKey[CCH_RM_SESSION_KEY+1] = { 0 };
DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
if (dwError == ERROR_SUCCESS)
{
dwError = RmRegisterResources(dwSession, 1, &pFilePath,
0, NULL, 0, NULL);
if (dwError == ERROR_SUCCESS)
{
UINT nProcInfoNeeded = 0;
UINT nProcInfo = 0;
RM_PROCESS_INFO rgpi[1];
DWORD dwReason;
dwError = RmGetList(dwSession, &nProcInfoNeeded,
&nProcInfo, rgpi, &dwReason);
if (dwError == ERROR_SUCCESS ||
dwError == ERROR_MORE_DATA)
{
if(nProcInfoNeeded > 0)
{
//If current process does not have enough privileges to close one of
//the "offending" processes, you'll get ERROR_FAIL_NOACTION_REBOOT
dwError = RmShutdown(dwSession, RmForceShutdown, NULL);
if (dwError == ERROR_SUCCESS)
{
bResult = TRUE;
}
}
else
bResult = TRUE;
}
}
}
RmEndSession(dwSession);
SetLastError(dwError);
return bResult;
}
I just met same problem. The DLL was locked by svchost.exe process (Windows Audio, DHCP Client, Windows Event Log, TCP/IP NetBIOS Helper, Security Center, Task Scheduler)
Solution: close Event Viewer! :)

C++ How to Check if an Outlook Process is already running

I am new to C++ programming but I would like to know if there is an easy way to check if an Outlook Process is already running, or in other words check if Outlook is already open.
Can anyone show me, please?
Thanks
You can check the running instance of the outlook OLE:
static const bool isOutlookRunning()
{
CLSID clsid;
HRESULT hr = CLSIDFromProgID( _T("Outlook.Application"), &clsid );
if( hr != S_OK ) return false;
IUnknown *pUnknown = NULL;
hr = GetActiveObject( clsid, 0, &pUnknown );
return ( hr == S_OK );
}
This code will not work if the app from you invoke it is running in different user security context than Outlook.
Its better to check for the process name.
//////////////////////////////////////////////////////////////////////
// IsProcessRunning
//////////////////////////////////////////////////////////////////////
bool IsProcessRunning(string &strProcessFullPathName)
{
// Get the list of process identifiers.
DWORD dwProcesses[2048];
DWORD dwSizeNeeded = 0;
DWORD dwProcessesCount = 0;
if ( !EnumProcesses( dwProcesses, sizeof(dwProcesses), &dwSizeNeeded ) )
{
_dwLastErrorCode = GetLastError();
_strLastError = "EnumProcesses";
return false;
}
dwProcessesCount = dwSizeNeeded / sizeof(DWORD);
string strToCheck = strProcessFullPathName;
transform(strToCheck.begin(), strToCheck.end(), strToCheck.begin(), tolower);
for(int i=0; i<(int)dwProcessesCount; i++ )
{
if( dwProcesses[i] != 0 )
{
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcesses[i]);
if(hProcess)
{
HMODULE hModule = NULL;
dwSizeNeeded = 0;
char cName[MAX_PATH];
if(EnumProcessModules(hProcess, &hModule, sizeof(DWORD), &dwSizeNeeded) )
{
if(GetModuleFileNameEx(hProcess, hModule, cName, MAX_PATH ) != 0)
{
string strName(cName);
// Convert to LowerCase
transform(strName.begin(), strName.end(), strName.begin(), tolower);
if(strName == strToCheck)
{
CloseHandle(hProcess);
return true;
}
}
}
CloseHandle(hProcess);
}
}
}
return false;
}
I assume that you are using Windows as platform, since Outlook normally runs under a Windows OS.
For a simple check you can use the Windows API functions EnumProcesses() and GetModuleBaseName() to determine the executable filename of the running processes and check if the executable name is "outlook.exe". A reference for the functions can be found in the MSDN.
Be aware that the this check may fail if the user runs another program that is using the same executable filename as Outlook. Another trap with the provided solution might be the access rights a user needs for the specified API calls.
Improvements might be to check the version information in the executable file too by using GetFileVersionInfo().
You can use CreateToolhelp32Snapshot and iterate through the running processes. If you need to heck for Outlook again (say you need to poll for the process), then save the process ID and use OpenProcess (many times faster). More details are given in the answer to this question:
Check whether one specific process is running on windows with C++

C++, How to determine if a Windows Process is running?

This is concerning Windows XP processes.
I have a process running, let's call it Process1. Process1 creates a new process, Process2, and saves its id.
Now, at some point Process1 wants Process2 to do something, so it first needs to make sure that Process2 is still alive and that the user has not not killed it.
How can I check that this process is still running?
Since I created it, I have the Process ID, I would think there is some library function along the lines of IsProcessIDValid( id ) but I can't find it on MSDN
You can use GetExitCodeProcess. It will return STILL_ACTIVE (259) if the process is still running (or if it happened to exit with that exit code :( ).
The process handle will be signaled if it exits.
So the following will work (error handling removed for brevity):
BOOL IsProcessRunning(DWORD pid)
{
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
DWORD ret = WaitForSingleObject(process, 0);
CloseHandle(process);
return ret == WAIT_TIMEOUT;
}
Note that process ID's can be recycled - it's better to cache the handle that is returned from the CreateProcess call.
You can also use the threadpool API's (SetThreadpoolWait on Vista+, RegisterWaitForSingleObject on older platforms) to receive a callback when the process exits.
EDIT: I missed the "want to do something to the process" part of the original question. You can use this technique if it is ok to have potentially stale data for some small window or if you want to fail an operation without even attempting it. You will still have to handle the case where the action fails because the process has exited.
#include <cstdio>
#include <windows.h>
#include <tlhelp32.h>
/*!
\brief Check if a process is running
\param [in] processName Name of process to check if is running
\returns \c True if the process is running, or \c False if the process is not running
*/
bool IsProcessRunning(const wchar_t *processName)
{
bool exists = false;
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry))
while (Process32Next(snapshot, &entry))
if (!wcsicmp(entry.szExeFile, processName))
exists = true;
CloseHandle(snapshot);
return exists;
}
The solution provided by #user152949, as it was noted in commentaries, skips the first process and doesn't break when "exists" is set to true. Let me provide a fixed version:
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
bool IsProcessRunning(const TCHAR* const executableName) {
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (!Process32First(snapshot, &entry)) {
CloseHandle(snapshot);
return false;
}
do {
if (!_tcsicmp(entry.szExeFile, executableName)) {
CloseHandle(snapshot);
return true;
}
} while (Process32Next(snapshot, &entry));
CloseHandle(snapshot);
return false;
}
I found this today, it is from 2003. It finds a process by name, you don't even need the pid.
\#include windows.h
\#include tlhelp32.h
\#include iostream.h
int FIND_PROC_BY_NAME(const char *);
int main(int argc, char *argv[])
{
// Check whether a process is currently running, or not
char szName[100]="notepad.exe"; // Name of process to find
int isRunning;
isRunning=FIND_PROC_BY_NAME(szName);
// Note: isRunning=0 means process not found, =1 means yes, it is found in memor
return isRunning;
}
int FIND_PROC_BY_NAME(const char *szToFind)
// Created: 12/29/2000 (RK)
// Last modified: 6/16/2003 (RK)
// Please report any problems or bugs to kochhar#physiology.wisc.edu
// The latest version of this routine can be found at:
// http://www.neurophys.wisc.edu/ravi/software/killproc/
// Check whether the process "szToFind" is currently running in memory
// This works for Win/95/98/ME and also Win/NT/2000/XP
// The process name is case-insensitive, i.e. "notepad.exe" and "NOTEPAD.EXE"
// will both work (for szToFind)
// Return codes are as follows:
// 0 = Process was not found
// 1 = Process was found
// 605 = Unable to search for process
// 606 = Unable to identify system type
// 607 = Unsupported OS
// 632 = Process name is invalid
// Change history:
// 3/10/2002 - Fixed memory leak in some cases (hSnapShot and
// and hSnapShotm were not being closed sometimes)
// 6/13/2003 - Removed iFound (was not being used, as pointed out
// by John Emmas)
{
BOOL bResult,bResultm;
DWORD aiPID[1000],iCb=1000,iNumProc,iV2000=0;
DWORD iCbneeded,i;
char szName[MAX_PATH],szToFindUpper[MAX_PATH];
HANDLE hProc,hSnapShot,hSnapShotm;
OSVERSIONINFO osvi;
HINSTANCE hInstLib;
int iLen,iLenP,indx;
HMODULE hMod;
PROCESSENTRY32 procentry;
MODULEENTRY32 modentry;
// PSAPI Function Pointers.
BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * );
BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *,
DWORD, LPDWORD );
DWORD (WINAPI *lpfGetModuleBaseName)( HANDLE, HMODULE,
LPTSTR, DWORD );
// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ;
BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ;
BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ;
BOOL (WINAPI *lpfModule32First)(HANDLE,LPMODULEENTRY32) ;
BOOL (WINAPI *lpfModule32Next)(HANDLE,LPMODULEENTRY32) ;
// Transfer Process name into "szToFindUpper" and
// convert it to upper case
iLenP=strlen(szToFind);
if(iLenP<1 || iLenP>MAX_PATH) return 632;
for(indx=0;indx<iLenP;indx++)
szToFindUpper[indx]=toupper(szToFind[indx]);
szToFindUpper[iLenP]=0;
// First check what version of Windows we're in
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
bResult=GetVersionEx(&osvi);
if(!bResult) // Unable to identify system version
return 606;
// At Present we only support Win/NT/2000 or Win/9x/ME
if((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) &&
(osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS))
return 607;
if(osvi.dwPlatformId==VER_PLATFORM_WIN32_NT)
{
// Win/NT or 2000 or XP
// Load library and get the procedures explicitly. We do
// this so that we don't have to worry about modules using
// this code failing to load under Windows 95, because
// it can't resolve references to the PSAPI.DLL.
hInstLib = LoadLibraryA("PSAPI.DLL");
if(hInstLib == NULL)
return 605;
// Get procedure addresses.
lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*))
GetProcAddress( hInstLib, "EnumProcesses" ) ;
lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *,
DWORD, LPDWORD)) GetProcAddress( hInstLib,
"EnumProcessModules" ) ;
lpfGetModuleBaseName =(DWORD (WINAPI *)(HANDLE, HMODULE,
LPTSTR, DWORD )) GetProcAddress( hInstLib,
"GetModuleBaseNameA" ) ;
if( lpfEnumProcesses == NULL ||
lpfEnumProcessModules == NULL ||
lpfGetModuleBaseName == NULL)
{
FreeLibrary(hInstLib);
return 605;
}
bResult=lpfEnumProcesses(aiPID,iCb,&iCbneeded);
if(!bResult)
{
// Unable to get process list, EnumProcesses failed
FreeLibrary(hInstLib);
return 605;
}
// How many processes are there?
iNumProc=iCbneeded/sizeof(DWORD);
// Get and match the name of each process
for(i=0;i<iNumProc;i++)
{
// Get the (module) name for this process
strcpy(szName,"Unknown");
// First, get a handle to the process
hProc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,
aiPID[i]);
// Now, get the process name
if(hProc)
{
if(lpfEnumProcessModules(hProc,&hMod,sizeof(hMod),&iCbneeded) )
{
iLen=lpfGetModuleBaseName(hProc,hMod,szName,MAX_PATH);
}
}
CloseHandle(hProc);
// Match regardless of lower or upper case
if(strcmp(_strupr(szName),szToFindUpper)==0)
{
// Process found
FreeLibrary(hInstLib);
return 1;
}
}
}
if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
{
// Win/95 or 98 or ME
hInstLib = LoadLibraryA("Kernel32.DLL");
if( hInstLib == NULL )
return FALSE ;
// Get procedure addresses.
// We are linking to these functions of Kernel32
// explicitly, because otherwise a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in the Kernel 32.
lpfCreateToolhelp32Snapshot=
(HANDLE(WINAPI *)(DWORD,DWORD))
GetProcAddress( hInstLib,
"CreateToolhelp32Snapshot" ) ;
lpfProcess32First=
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress( hInstLib, "Process32First" ) ;
lpfProcess32Next=
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress( hInstLib, "Process32Next" ) ;
lpfModule32First=
(BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32))
GetProcAddress( hInstLib, "Module32First" ) ;
lpfModule32Next=
(BOOL(WINAPI *)(HANDLE,LPMODULEENTRY32))
GetProcAddress( hInstLib, "Module32Next" ) ;
if( lpfProcess32Next == NULL ||
lpfProcess32First == NULL ||
lpfModule32Next == NULL ||
lpfModule32First == NULL ||
lpfCreateToolhelp32Snapshot == NULL )
{
FreeLibrary(hInstLib);
return 605;
}
// The Process32.. and Module32.. routines return names in all uppercase
// Get a handle to a Toolhelp snapshot of all the systems processes.
hSnapShot = lpfCreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS, 0 ) ;
if( hSnapShot == INVALID_HANDLE_VALUE )
{
FreeLibrary(hInstLib);
return 605;
}
// Get the first process' information.
procentry.dwSize = sizeof(PROCESSENTRY32);
bResult=lpfProcess32First(hSnapShot,&procentry);
// While there are processes, keep looping and checking.
while(bResult)
{
// Get a handle to a Toolhelp snapshot of this process.
hSnapShotm = lpfCreateToolhelp32Snapshot(
TH32CS_SNAPMODULE, procentry.th32ProcessID) ;
if( hSnapShotm == INVALID_HANDLE_VALUE )
{
CloseHandle(hSnapShot);
FreeLibrary(hInstLib);
return 605;
}
// Get the module list for this process
modentry.dwSize=sizeof(MODULEENTRY32);
bResultm=lpfModule32First(hSnapShotm,&modentry);
// While there are modules, keep looping and checking
while(bResultm)
{
if(strcmp(modentry.szModule,szToFindUpper)==0)
{
// Process found
CloseHandle(hSnapShotm);
CloseHandle(hSnapShot);
FreeLibrary(hInstLib);
return 1;
}
else
{ // Look for next modules for this process
modentry.dwSize=sizeof(MODULEENTRY32);
bResultm=lpfModule32Next(hSnapShotm,&modentry);
}
}
//Keep looking
CloseHandle(hSnapShotm);
procentry.dwSize = sizeof(PROCESSENTRY32);
bResult = lpfProcess32Next(hSnapShot,&procentry);
}
CloseHandle(hSnapShot);
}
FreeLibrary(hInstLib);
return 0;
}
Another way of monitoring a child-process is to create a worker thread that will :
call CreateProcess()
call WaitForSingleObject() // the worker thread will now wait till the child-process finishes execution. it's possible to grab the return code (from the main() function) too.
You can never check and see if a process is running, you can only check to see if a process was running at some point in the recent past. A process is an entity that is not controlled by your application and can exit at any moment in time. There is no way to guaranteed that a process will not exit in between the check to see if it's running and the corresponding action.
The best approach is to just do the action required and catch the exception that would be thrown if the process was not running.
call EnumProcesses() and check if the PID is in the list.
http://msdn.microsoft.com/en-us/library/ms682629%28VS.85%29.aspx
JaredPar is right in that you can't know if the process is running. You can only know if the process was running at the moment you checked. It might have died in the mean time.
You also have to be aware the PIDs can be recycled pretty quickly. So just because there's a process out there with your PID, it doesn't mean that it's your process.
Have the processes share a GUID. (Process 1 could generate the GUID and pass it to Process 2 on the command line.) Process 2 should create a named mutex with that GUID. When Process 1 wants to check, it can do a WaitForSingleObject on the mutex with a 0 timeout. If Process 2 is gone, the return code will tell you that the mutex was abandoned, otherwise you'll get a timeout.
You may find if a process (given its name or PID) is running or not by iterating over the running processes simply by taking a snapshot of running processes via CreateToolhelp32Snapshot, and by using Process32First and Process32Next calls on that snapshot.
Then you may use th32ProcessID field or szExeFile field of the resulting PROCESSENTRY32 struct depending on whether you want to search by PID or executable name. A simple implementation can be found here.
While writing a monitoring tool, i took a slightly different approach.
It felt a bit wasteful to spin up an extra thread just to use WaitForSingleObject or even the RegisterWaitForSingleObject (which does that for you). Since in my case i don't need to know the exact instant a process has closed, just that it indeed HAS closed.
I'm using the GetProcessTimes() instead:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms683223(v=vs.85).aspx
GetProcessTimes() will return a FILETIME struct for the process's ExitTime only if the process has actually exited. So is just a matter of checking if the ExitTime struct is populated and if the time isn't 0;
This solution SHOULD account the case where a process has been killed but it's PID was reused by another process. GetProcessTimes needs a handle to the process, not the PID. So the OS should know that the handle is to a process that was running at some point, but not any more, and give you the exit time.
Relying on the ExitCode felt dirty :/
This is a solution that I've used in the past. Although the example here is in VB.net - I've used this technique with c and c++. It bypasses all the issues with Process IDs & Process handles, and return codes. Windows is very faithful in releasing the mutex no matter how Process2 is terminated. I hope it is helpful to someone...
**PROCESS1 :-**
Randomize()
mutexname = "myprocess" & Mid(Format(CDbl(Long.MaxValue) * Rnd(), "00000000000000000000"), 1, 16)
hnd = CreateMutex(0, False, mutexname)
' pass this name to Process2
File.WriteAllText("mutexname.txt", mutexname)
<start Process2>
<wait for Process2 to start>
pr = WaitForSingleObject(hnd, 0)
ReleaseMutex(hnd)
If pr = WAIT_OBJECT_0 Then
<Process2 not running>
Else
<Process2 is running>
End If
...
CloseHandle(hnd)
EXIT
**PROCESS2 :-**
mutexname = File.ReadAllText("mutexname.txt")
hnd = OpenMutex(MUTEX_ALL_ACCESS Or SYNCHRONIZE, True, mutexname)
...
ReleaseMutex(hnd)
CloseHandle(hnd)
EXIT
char tmp[200] = "taskkill /f /im chrome.exe && \"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\"
while (1)
{
FILE* f;
f = _popen("tasklist", "r");
char b[512];
bzero(b, 512);
while (fgets(b, 512, f) != NULL)
{
if (strncmp(b, "chrome.exe", 8) == 0)
{
printf("Chrome running!\n");
system(tmp);
}
else
{
printf("Chrome NOT running!\n");
}
}
Sleep(1000);
}