How do I execute a .bat-file in a DETACHED_PROCESS? - c++

I have a simple C++ console application that starts notepad.exe and loads the file D:\MyTextFile.txt and then the console application exits, but notepad is still running. The code works great:
int _tmain(int argc, _TCHAR* argv[])
{
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory( &pi, sizeof(pi) );
WCHAR pCmd[] = {'n','o','t','e','p','a','d','.','e','x','e',' ','D',':','\\','M','y','T','e','x','t','F','i','l','e','.','t','x','t',0};
BOOL result = CreateProcess
(
_T("C:\\Windows\\System32\\notepad.exe"), // Module name
pCmd, // Command line (as modifiable array)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set bInheritHandles to FALSE
DETACHED_PROCESS, // Detach process
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (returned)
);
return (result) ? 0 : -1;
}
However, if I replace notepad with cmd and MyTextFile.txt with MyBatFile.bat then it doesn't work. Contents of MyBatFile.bat:
C:\Windows\System32\notepad.exe D:\MyTextFile.txt
Modified console application:
int _tmain(int argc, _TCHAR* argv[])
{
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory( &pi, sizeof(pi) );
WCHAR pCmd[] = {'c','m','d','.','e','x','e',' ','/','C',' ','D',':','\\','M','y','B','a','t','F','i','l','e','.','b','a','t',0};
BOOL result = CreateProcess
(
_T("C:\\Windows\\System32\\cmd.exe"), // Module name
pCmd, // Command line (as modifiable array)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set bInheritHandles to FALSE
DETACHED_PROCESS, // Detach process
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (returned)
);
return (result) ? 0 : -1;
}
When I execute the above code I see a command prompt flashing by very quickly, but it doesn't seem to execute MyBatFile.bat. However, if I replace DETACHED_PROCESS with CREATE_UNICODE_ENVIRONMENT then MyBatFile.bat gets executed, but since the process is no longer detached the command prompt hangs until I close notepad, which is undesired. Does anybody know how I can modify my code to be able to execute MyBatFile.bat in a detached process?

I can't explain why, but it seems to work for .bat-files (via cmd.exe) if I choose CREATE_NO_WINDOW instead of DETACHED_PROCESS. The following code seems to work for both .exe-files and .bat-files:
// RunDetached.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
int getIndexOfStringIgnoreCare(WCHAR* bigStingToSearchThrough, WCHAR* subStingToFind);
int getFirstIndexOfChar(WCHAR* stringToInvestigate, int startIndex, WCHAR charToLookFor);
int getLastIndexOfChar(WCHAR* stringToInvestigate, int startIndex, WCHAR charToLookFor);
int _tmain(int argc, _TCHAR* argv[])
{
WCHAR* pOriginalCmd = ::GetCommandLine();
// Test code (modify paths to RunDetached.exe and MyFile.txt appropriately)
// pOriginalCmd = _T("\"D:\\My Visual Studio Projects\\RunDetached\\debug\\RunDetached.exe\" \"C:\\Windows\\System32\\notepad.exe\" \"D:\\1.txt\"");
int CmdLen = (int)wcslen(pOriginalCmd);
// Determine where certain characters are located (excl means the particular index is not included, e.g.
// if indexExcl is 5 then index 4 is the last included index).
int beginningOf1stArg = getFirstIndexOfChar(pOriginalCmd, 0, L'\"');
int endOf1stArgExcl = getFirstIndexOfChar(pOriginalCmd, beginningOf1stArg + 1, L'\"') + 1;
int beginningOf2ndArg = getFirstIndexOfChar(pOriginalCmd, endOf1stArgExcl + 1, L'\"');
int endOf2ndArgExcl = getFirstIndexOfChar(pOriginalCmd, beginningOf2ndArg + 1, L'\"') + 1;
int beginningOf3rdArg = getFirstIndexOfChar(pOriginalCmd, endOf2ndArgExcl + 1, L'\"');
int endOfLastArgExcl = getLastIndexOfChar (pOriginalCmd, CmdLen - 1, L'\"') + 1;
int beginningOfFileName = getLastIndexOfChar (pOriginalCmd, endOf2ndArgExcl - 2, L'\\') + 1;
int endOfFileNameExcl = endOf2ndArgExcl - 1;
if ((beginningOf1stArg < 0) || (endOf1stArgExcl < 0) || (beginningOf2ndArg < 0) || (endOf2ndArgExcl < 0) ||
(endOfLastArgExcl < 0) || (beginningOfFileName < 0) || (endOfFileNameExcl < 0))
{
return -1;
}
// Determine the application to execute including full path. E.g. for notepad this should be:
// C:\Windows\System32\notepad.exe (without any double-quotes)
int lengthOfApplicationNameAndPathInChars = (endOf2ndArgExcl -1) - (beginningOf2ndArg + 1); // Skip double-quotes
WCHAR* lpApplicationNameAndPath = (WCHAR*)malloc(sizeof(WCHAR) * (lengthOfApplicationNameAndPathInChars + 1));
memcpy(lpApplicationNameAndPath, &pOriginalCmd[beginningOf2ndArg + 1], sizeof(WCHAR) * (lengthOfApplicationNameAndPathInChars));
lpApplicationNameAndPath[lengthOfApplicationNameAndPathInChars] = (WCHAR)0; // Null terminate
// Determine the command argument. Must be in modifyable memory and should start with the
// application name without the path. E.g. for notepad with command argument D:\MyFile.txt:
// "notepad.exe" "D:\MyFile.txt" (with the double-quotes).
WCHAR* modifiedCmd = NULL;
if (0 < beginningOf3rdArg)
{
int lengthOfApplicationNameInChars = endOfFileNameExcl - beginningOfFileName; // Application name without path
int lengthOfRestOfCmdInChars = CmdLen - beginningOf3rdArg;
int neededCmdLengthInChars = 1 + lengthOfApplicationNameInChars + 2 + lengthOfRestOfCmdInChars; // Two double-quotes and one space extra
modifiedCmd = (WCHAR*)malloc(sizeof(WCHAR) * (neededCmdLengthInChars + 1)); // Extra char is null-terminator
modifiedCmd[0] = L'\"'; // Start with double-quoute
memcpy(&modifiedCmd[1], &pOriginalCmd[beginningOfFileName], sizeof(WCHAR) * (lengthOfApplicationNameInChars));
modifiedCmd[1 + (lengthOfApplicationNameInChars)] = L'\"';
modifiedCmd[1 + (lengthOfApplicationNameInChars) + 1] = L' ';
memcpy(&modifiedCmd[1 + (lengthOfApplicationNameInChars) + 2], &pOriginalCmd[beginningOf3rdArg], sizeof(WCHAR) * lengthOfRestOfCmdInChars);
modifiedCmd[neededCmdLengthInChars] = (WCHAR)0;
}
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory( &pi, sizeof(pi) );
BOOL result = CreateProcess // Start the process
(
lpApplicationNameAndPath, // Module name and full path
modifiedCmd, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set bInheritHandles to FALSE
(0 <= getIndexOfStringIgnoreCare // Special case for cmd.exe (don't
(lpApplicationNameAndPath, L"cmd.exe")) ? // know why but it seems to work)
CREATE_NO_WINDOW : DETACHED_PROCESS,
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (returned)
);
free(lpApplicationNameAndPath);
if (modifiedCmd != NULL)
{
free(modifiedCmd);
}
if (result) return 0;
wchar_t msg[2048];
FormatMessage
(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
::GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
msg, sizeof(msg),
NULL
);
fputws(msg, stderr);
_flushall();
return -1;
}
bool compareCharsIgnoreCase(WCHAR char1, WCHAR char2)
{
if (char1 == char2)
{
return true;
}
const int UPPER_LOWER_CASE_OFFSET_IN_ASCII_TABLE = 'a' - 'A';
if ((L'A' <= char1) && (char1 <= L'Z'))
{
return ((char1 + UPPER_LOWER_CASE_OFFSET_IN_ASCII_TABLE) == char2);
}
if ((L'a' <= char1) && (char1 <= L'z'))
{
return ((char1 - UPPER_LOWER_CASE_OFFSET_IN_ASCII_TABLE) == char2);
}
return false;
}
int getIndexOfStringIgnoreCare(WCHAR* bigStringToSearchThrough, WCHAR* subStringToFind)
{
if ((bigStringToSearchThrough == NULL) || (subStringToFind == NULL))
{
return -1;
}
int bigStringLen = (int)wcslen(bigStringToSearchThrough);
int subStringLen = (int)wcslen(subStringToFind);
if ((5000 < bigStringLen) || (5000 < subStringLen)) // Sanity check
{
return -1;
}
for (int i = 0; i < (bigStringLen - subStringLen + 1); i++)
{
for (int j = 0; j < subStringLen; j++)
{
if (!compareCharsIgnoreCase(bigStringToSearchThrough[i + j], subStringToFind[j]))
{
break;
}
else if ((j + 1) == subStringLen)
{
return i;
}
}
}
return -1;
}
int getFirstIndexOfChar(WCHAR* stringToInvestigate, int startIndex, WCHAR charToLookFor)
{
int stringLen = (int)wcslen(stringToInvestigate);
if (5000 < stringLen) // Sanity check
{
return -1;
}
for (int i = startIndex; i < stringLen; i++)
{
if (stringToInvestigate[i] == charToLookFor)
{
return i;
}
}
return -1;
}
int getLastIndexOfChar(WCHAR* stringToInvestigate, int startIndex, WCHAR charToLookFor)
{
int stringLen = (int)wcslen(stringToInvestigate);
if (5000 < stringLen) // Sanity check
{
return -1;
}
for (int i = min(stringLen - 1, startIndex); 0 <= i; i--)
{
if (stringToInvestigate[i] == charToLookFor)
{
return i;
}
}
return -1;
}
So, with the above code you can do both
"RunDetached.exe" "C:\Windows\System32\notepad.exe" "D:\MyTextFile.txt"
and
"RunDetached.exe" "C:\Windows\System32\cmd.exe" "/c" "D:\MyBatFile.bat"
and your calling application won't hang. It's important to enclose each and every argument with double-quotes because I use those in the code to find the different arguments.

Related

Why does multi threading mess up my file watcher?

I am trying to make a file watcher that watches multiple directories simultaneously and i found a function that watches one directory and it works well. So I tried to create multiple threads with the same function and just give them the different paths of the directories as parameters. And it still works, but not perfectly. It does provide me with the first change in one directory, but then it gets stuck for that one and in the other directories the changes i make are printed as if they wore made in the first directory. I think the threads are getting messed up and I don`t know why. Here is the function
void watch_directory(LPCSTR path, int i)
{
char buf[2048];
DWORD nRet;
BOOL result = TRUE;
char filename[MAX_PATH];
DirInfo[0].hDir = CreateFile(path, GENERIC_READ | FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (DirInfo[0].hDir == INVALID_HANDLE_VALUE)
{
return; //cannot open folder
}
lstrcpy(DirInfo[0].lpszDirName, path);
OVERLAPPED PollingOverlap;
FILE_NOTIFY_INFORMATION* pNotify;
int offset;
PollingOverlap.OffsetHigh = 0;
PollingOverlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
while (result)
{
result = ReadDirectoryChangesW(
DirInfo[0].hDir,// handle to the directory to be watched
&buf,// pointer to the buffer to receive the read results
sizeof(buf),// length of lpBuffer
TRUE,// flag for monitoring directory or directory tree
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_SIZE,
&nRet,// number of bytes returned
&PollingOverlap,// pointer to structure needed for overlapped I/O
NULL);
WaitForSingleObject(PollingOverlap.hEvent, INFINITE);
offset = 0;
int rename = 0;
char oldName[260];
char newName[260];
do
{
pNotify = (FILE_NOTIFY_INFORMATION*)((char*)buf + offset);
strcpy(filename, "");
int filenamelen = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName, pNotify->FileNameLength / 2, filename, sizeof(filename), NULL, NULL);
filename[pNotify->FileNameLength / 2] = '\0';
cout << pNotify->Action << i << filename << endl;
offset += pNotify->NextEntryOffset;
} while (pNotify->NextEntryOffset); //(offset != 0);
}
/*label:*/ CloseHandle(DirInfo[0].hDir);
}
And here is the way I create the threads:
vector <string> dirs;
vector <thread> threads;
dirs.push_back("F:\\Arhitecturi\\test0");
dirs.push_back("F:\\Arhitecturi\\test1");
for (int i = 0; i < dirs.size(); i++)
{
threads.push_back(thread(watch_directory, dirs[i].c_str(), i));
}
for (int i = 0; i < threads.size(); i++)
{
threads[i].join();
}

QueryWorkingSet always returning false

I'm trying to get the size of allocated are for a process, descriminated by image, private and mapped. I'm using QueryWorkingSet to get the Working Set Information and then the Working Set Block.
When i called it for the first time, the GetLastError method return 24 which is to be expected so the next time I call QueryWorkingSet I set a diferent size for the block but then im getting an error code of 998.
Am I using QueryWorkingSet wrong or Im getting the handle for the process with the wrong access rights or Im the resize is not enough?
#include "pch.h"
#include <Windows.h>
#include<WinDef.h>
#include <psapi.h>
#include <iostream>
typedef struct {
DWORD img;
DWORD map;
DWORD prv;
} CommitCounters, *PCommitCounters;
BOOL GetCommitCountersFromProcess(_In_ int pid, _Out_ PCommitCounters committedCounter ) {
int numberOfTries = 3;
SYSTEM_INFO si;
GetSystemInfo(&si);
DWORD pageSz = si.dwPageSize;
PSAPI_WORKING_SET_INFORMATION wsi, *pwsi;
pwsi = &wsi;
DWORD ws_size;
MEMORY_BASIC_INFORMATION mbi, *pmbi;
pmbi = &mbi;
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
wsi.NumberOfEntries = 0;
QueryWorkingSet(processHandle, &wsi, sizeof(pwsi));
BOOL res = false;
committedCounter->img = 0;
committedCounter->map = 0;
committedCounter->prv = 0;
while (numberOfTries > 0) {
DWORD lastError = GetLastError();
//ERROR_BAD_LENGTH
if (lastError == 24) {
ws_size = sizeof(wsi) + sizeof(PSAPI_WORKING_SET_INFORMATION) + sizeof(PSAPI_WORKING_SET_BLOCK) * wsi.NumberOfEntries;
pwsi = (PSAPI_WORKING_SET_INFORMATION*) malloc(ws_size);
pwsi->NumberOfEntries = wsi.NumberOfEntries;
BOOL resQws = QueryWorkingSet(processHandle, &wsi, ws_size);
DWORD teste = sizeof(wsi);
if (resQws) {
for (int i = 0; i < pwsi->NumberOfEntries; i++) {
PSAPI_WORKING_SET_BLOCK ws_block = pwsi->WorkingSetInfo[1];
//Access page information.
SIZE_T size = VirtualQuery((LPCVOID*)ws_block.VirtualPage, &mbi, 1);
if (size != 0 && pmbi ->State == 0x1000) {
switch (pmbi->Type)
{
case 0x1000000: // MEM_IMAGE
committedCounter->img += pageSz;
break;
case 0x40000: //MEM_MAPPED
committedCounter->map += pageSz;
break;
case 0x20000: //MEM_PRIVATE
committedCounter->prv += pageSz;
break;
}
}
else if (size == 0) {
return res;
}
}
CloseHandle(processHandle);
res = true;
return res;
}
free(pwsi);
}
numberOfTries--;
}
CloseHandle(processHandle);
return false;
}
You have a typo in your code. Just change:
BOOL resQws = QueryWorkingSet(processHandle, &wsi, ws_size);
to:
BOOL resQws = QueryWorkingSet(processHandle, pwsi, ws_size);
And then the call succeeds.
There may be further errors but I did not investigate those.

Synchronize 2 processes using a mutex

I have 2 processes:
The first one creates a memory mapped region, a mutex and spawns
the second process. Then writes some pairs of numbers in the memory mapped region.
The second one opens the memory mapped region, opens the mutex and then reads the numbers written by the process 1.
I intended the first process to write a pair of numbers and the second one to immediately read it.
The process 2 seems to be starving.
What did I do wrong?
Process 1:
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
int main()
{
DWORD memSize = 400 * sizeof(DWORD);
HANDLE map_file = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, memSize, TEXT("mem1"));
if (map_file == NULL)
{
_tprintf(_T("(Parent) File mapping is null\n"));
return 1;
}
char* map_ptr = (char *) MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);
if (map_ptr == NULL)
{
_tprintf(_T("(Parent) PTR is null \n"));
}
HANDLE hMutex = CreateMutex(NULL, TRUE, _T("mt"));
LPTSTR szCmdline = _tcsdup(TEXT("C:\\Users\\cristi\\source\\repos\\process_synchronization_reader\\Debug\\process_synchronization_reader.exe"));
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(NULL, szCmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
_tprintf(_T("Process created\n"));
}
_tprintf(_T("pare ca s-a creat"));
for (int i = 1; i <= 200; ++i)
{
WaitForSingleObject(hMutex, INFINITE);
_tprintf(_T("(Parent %d) writing from the parent\n"), i);
DWORD a, b;
CopyMemory((LPVOID) &a, map_ptr, sizeof(DWORD));
map_ptr += sizeof (DWORD);
CopyMemory((LPVOID) &b, map_ptr, sizeof(DWORD));
map_ptr += sizeof(DWORD);
ReleaseMutex(hMutex);
}
int n;
cin >> n;
CloseHandle(map_file);
return 0;
}
Process 2:
#include "stdafx.h"
#include <windows.h>
int main()
{
HANDLE map_file = OpenFileMapping(FILE_MAP_READ, FALSE, TEXT("mem1"));
if (map_file == NULL)
{
_tprintf(_T("(Child) File mapping is null\n"));
return 1;
}
char* map_ptr = (char *) MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);
if (map_ptr == NULL)
{
_tprintf(_T("(Child) PTR is null \n"));
}
_tprintf(_T("(CHILD) BEfore reading the first number\n"));
HANDLE hMutex = OpenMutex(SYNCHRONIZE, TRUE, _T("mt"));
for (int i = 1; i <= 200; i++)
{
WaitForSingleObject(hMutex, INFINITE);
DWORD a = i;
DWORD b = 2 * i;
CopyMemory((LPVOID) map_ptr, &a, sizeof(DWORD));
map_ptr += sizeof(DWORD);
CopyMemory((LPVOID) map_ptr, &b, sizeof(DWORD));
map_ptr += sizeof(DWORD);
_tprintf(_T("[================================================]\n"));
_tprintf(_T("( %d %d )\n"), a, b);
_tprintf(_T("[=================================================]\n"));
ReleaseMutex(hMutex);
}
return 0;
}
for got sequential write/read from shared memory we need 2 events (let name it Low and High).
first thread:
write data
signal Low event
wait on High event or break loop
goto 1
second thread:
wait on Low event
read data
break loop or signal High event
goto 1
unlike this solution mutex can not provide a sequence of reading / writing. mutex guarantee that until one thread will be access shared data (read or write) another thread will be not do this in concurrent. but this can not prevent several times in a row for write or read. really - insert messagebox in begin of process 2 - before he first time try acquire mutex - first process already many time acquire and release mutex. or if one thread will be suspended between release and wait for mutex - meanwhile another thread many time wait and release it. so code can look like:
struct SHARED_DATA
{
ULONG id;
ULONG nLoops;
BOOL bTask;
};
DWORD proc2(SHARED_DATA* p)
{
if (HANDLE hLowEvent = OpenEvent(SYNCHRONIZE, FALSE, L"LowEvent"))
{
if (HANDLE hHighEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE, L"hHighEvent"))
{
ULONG id = GetCurrentThreadId();
for(;;)
{
if (WaitForSingleObject(hLowEvent, INFINITE) != WAIT_OBJECT_0)
{
break;
}
// ++ checking for sequence
if (p->id == id)
{
__debugbreak();// must never be
}
p->id = id;
// -- checking for sequence
if (!p->bTask)
{
// no more task
break;
}
// task done
p->bTask = FALSE;
// signal to #1
if (!SetEvent(hHighEvent))
{
break;
}
}
CloseHandle(hHighEvent);
}
CloseHandle(hLowEvent);
}
return 0;
}
DWORD proc1(SHARED_DATA* p)
{
if (HANDLE hLowEvent = CreateEvent(0, FALSE, FALSE, L"LowEvent"))
{
if (HANDLE hHighEvent = CreateEvent(0, FALSE, FALSE, L"hHighEvent"))
{
ULONG id = GetCurrentThreadId();
p->nLoops = 0x1000;
p->id = 0;
p->bTask = FALSE;
// exec proc2 here
goto __1;
do
{
if (WaitForSingleObject(hHighEvent, INFINITE) != WAIT_OBJECT_0)
{
break;
}
if (p->bTask)
{
__debugbreak();
}
// ++ checking for sequence
if (p->id == id)
{
__debugbreak();// must never be
}
__1:
p->id = id;
// -- checking for sequence
p->bTask = 0 < --p->nLoops;
// signal to #2
if (!SetEvent(hLowEvent))
{
break;
}
} while (p->nLoops);
CloseHandle(hHighEvent);
}
CloseHandle(hLowEvent);
}
return 0;
}
You create a mutex as initially owned (the second argument to CreateMutex is TRUE) and then you call a wait-function on it. So even after calling ReleaseMutex, it is still owned by a main thread of the first process.
Either change the argument to FALSE, or skip calling WaitForSingleObject for the first loop iteration.

How to get the number of remaining threads after terminating a thread in windows thread? c++

After terminating each thread using the below program, I need to print the remaining threads with ids every time, for which I am using GetExitCodeThread function but it is returning some garbage value.What could I be doing wrong?Also, how to print the remaining threads after getting the exitCode correct?
#define NUM_THREADS 10
#include <windows.h>
#include <stdio.h>
#include <process.h>
typedef struct
{
int Id;
HANDLE hTerminate;
} ThreadArgs;
unsigned _stdcall ThreadFunc( void *pArgs )
{
LPDWORD exitCode;
HANDLE hTerminate = ((ThreadArgs *)pArgs)->hTerminate;
int id = ((ThreadArgs *)pArgs)->Id;
// run until we are told to terminate while (1)
while(1)
{
// Check to see if we should terminate
if (WaitForSingleObject(hTerminate, 0) == WAIT_OBJECT_0)
{
// Terminate Thread - we call ResetEvent to
// return the terminate thread to its non-
// signaled state, then exit the while() loop
printf ("Terminating Thread %d\n", id);
GetExitCodeThread(hTerminate,exitCode);
printf("%d",exitCode);
ResetEvent(hTerminate);
break;
}
// we can do our work now ...
// simulate the case that it takes
// to do the work the thread has to do
Sleep(1000);
}
_endthreadex(0);
return 0;
}
int main(int argc, char* argv[])
{
int i=0;
unsigned int threadID[NUM_THREADS];
HANDLE hThread[NUM_THREADS];
ThreadArgs threadArgs[NUM_THREADS];
// Create 10 threads
printf("Total number of threads= %d\n", NUM_THREADS);
for (i = 0; i < NUM_THREADS;i++)
{
printf("Thread number %d \n",i);
}
for (int i = 0; i<NUM_THREADS;i++)
{
threadArgs[i].Id = i;
threadArgs[i].hTerminate = CreateEvent(NULL,TRUE,FALSE,NULL);
hThread[i] = (HANDLE)_beginthreadex(NULL,0,&ThreadFunc,&threadArgs[i], 0, &threadID[i]);
}
printf("To kill a thread (gracefully), press 0-9, "" then <Enter>. \n");
printf("Press any other key to exit.\n");
while (1)
{
int c = getc(stdin);
if (c == '\n')
continue;
if (c < '0' || c > '9')
break;
SetEvent(threadArgs[c -'0'].hTerminate);
}
return 0;
}
GetExitCodeThread() expects a HANDLE to a thread object, but you are passing it a HANDLE to an event object instead. You are also passing it an uninitialized pointer to write the exit code to. As such, GetExitCodeThread() is goes to fail with an error that you are ignoring, and the exit code will not be assigned any meaningful value.
Not that it matters, because GetExitCodeThread() is useless to call inside a thread that is still running, it will set the exit code to STILL_ACTIVE. You are supposed to call GetExitCodeThread() in a different thread than the one that is being terminated.
Try something more like this instead:
#include <windows.h>
#include <stdio.h>
#include <process.h>
#define MAX_THREADS 10
typedef struct
{
int Id;
DWORD dwThreadId;
HANDLE hThread;
HANDLE hTerminate;
} ThreadArgs;
unsigned __stdcall ThreadFunc( void *arg )
{
ThreadArgs *pArgs = (ThreadArgs *) arg;
// run until we are told to terminate while (1)
while(1)
{
// Check to see if we should terminate
if (WaitForSingleObject(pArgs->hTerminate, 0) == WAIT_OBJECT_0)
{
// Terminate Thread - exit the while() loop
printf ("Thread %d terminate signal detected\n", pArgs->Id);
break;
}
// we can do our work now ...
// simulate the case that it takes
// to do the work the thread has to do
Sleep(1000);
}
return 0;
}
int main(int argc, char* argv[])
{
int i;
ThreadArgs threadArgs[MAX_THREADS];
int numThreadsRunning = 0;
memset(&ThreadArgs, 0, sizeof(ThreadArgs));
// Create 10 threads
printf("Creating %d threads\n", MAX_THREADS);
for (i = 0; i < MAX_THREADS; ++i)
{
printf("Thread number %d: ", i);
threadArgs[i].Id = i;
threadArgs[i].hTerminate = CreateEvent(NULL, TRUE, FALSE, NULL);
threadArgs[i].hThread = (HANDLE) _beginthreadex(NULL, 0, &ThreadFunc, &threadArgs[i], 0, &threadArgs[i].dwThreadId);
if (threadArgs[i].hThread != NULL)
{
printf("Created\n");
++numThreadsRunning;
}
else
printf("Not Created!\n");
}
printf("Threads running: %d\n", numThreadsRunning);
printf("To kill a thread (gracefully), press 0-%d, then <Enter>.\n", MAX_THREADS-1);
printf("Press any other key to exit.\n");
while (1)
{
int c = getc(stdin);
if (c == '\n')
continue;
if ((c < '0') || (c > '9'))
break;
int id = c - '0';
if (threadArgs[id].hThread != NULL)
{
printf ("Signaling Thread %d to Terminate\n", id);
SetEvent(threadArgs[id].hTerminate);
WaitForSingleObject(threadArgs[id].hThread, INFINITE);
DWORD exitCode = 0;
GetExitCodeThread(threadArgs[id].hThread, &exitCode);
CloseHandle(threadArgs[id].hThread);
threadArgs[id].hThread = NULL;
printf ("Thread %d Terminated. Exit Code: %u\n", id, exitCode);
--numThreadsRunning;
printf ("Threads still running: %d\n", numThreadsRunning);
}
else
printf ("Thread %d is not running\n", id);
}
if (numThreadsRunning > 0)
{
printf ("Signaling remaining Threads to Terminate\n");
HANDLE hThreads[MAX_THREADS];
DWORD numThreads = 0;
for (i = 0; i < MAX_THREADS; ++i)
{
if (threadArgs[i].hThread != NULL)
{
hThreads[numThreads] = threadArgs[i].hThread;
++numThreads;
SetEvent(threadArgs[i].hTerminate);
}
}
WaitForMultipleObjects(numThreads, hThreads, TRUE, INFINITE);
for (i = 0; i < MAX_THREADS; ++i)
{
if (hThreads[i].hThread)
CloseHandle(hThreads[i].hThread);
if (hThreads[i].hTerminate)
CloseHandle(hThreads[i].hTerminate);
}
printf ("Threads Terminated\n");
}
return 0;
}
Have a look at this msdn article:
Traversing the Thread List
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686852(v=vs.85).aspx
There is sample code on how to list the threads for a process.
~snip
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
// Forward declarations:
BOOL ListProcessThreads( DWORD dwOwnerPID );
void printError( TCHAR* msg );
int main( void )
{
ListProcessThreads(GetCurrentProcessId() );
return 0;
}
BOOL ListProcessThreads( DWORD dwOwnerPID )
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
if( hThreadSnap == INVALID_HANDLE_VALUE )
return( FALSE );
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32 );
// Retrieve information about the first thread,
// and exit if unsuccessful
if( !Thread32First( hThreadSnap, &te32 ) )
{
printError( TEXT("Thread32First") ); // Show cause of failure
CloseHandle( hThreadSnap ); // Must clean up the snapshot object!
return( FALSE );
}
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
do
{
if( te32.th32OwnerProcessID == dwOwnerPID )
{
_tprintf( TEXT("\n THREAD ID = 0x%08X"), te32.th32ThreadID );
_tprintf( TEXT("\n base priority = %d"), te32.tpBasePri );
_tprintf( TEXT("\n delta priority = %d"), te32.tpDeltaPri );
}
} while( Thread32Next(hThreadSnap, &te32 ) );
_tprintf( TEXT("\n"));
// Don't forget to clean up the snapshot object.
CloseHandle( hThreadSnap );
return( TRUE );
}
void printError( TCHAR* msg )
{
DWORD eNum;
TCHAR sysMsg[256];
TCHAR* p;
eNum = GetLastError( );
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, eNum,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
sysMsg, 256, NULL );
// Trim the end of the line and terminate it with a null
p = sysMsg;
while( ( *p > 31 ) || ( *p == 9 ) )
++p;
do { *p-- = 0; } while( ( p >= sysMsg ) &&
( ( *p == '.' ) || ( *p < 33 ) ) );
// Display the message
_tprintf( TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg );
}

Updating the PATH environment variable in Windows using C++

I am trying to launch a new process from my current process. I am using CreateProcess() to launch it. The issue is that I need to have certain directories in my PATH to successfully do so. Here is my current implementation but it doesn't work. What am I doing wrong?
// Environment variables
char *env = new char[2048];
char *ptr = env;
char temp[MAX_PATH] = "PATH=";
strcpy(ptr, strcat(temp, plugin_path));
ptr += strlen(ptr) + 1;
char temp2[MAX_PATH] = "PATH=";
strcpy(ptr, strcat(temp, lib_path));
ptr += strlen(ptr) + 1;
*ptr = '\0';
// Execute
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// error checking required
if(!CreateProcess(
NULL, // application name
command_path, // app.exe
NULL,
NULL,
TRUE,
0,
env, // environment
NULL,
&si,
&pi)) {
std::cout << GetLastError();
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
std::cout << "Process Started!";
Please let me know if anything else is required.
EDIT: Somebody mentioned below that I need to be a little more specific. It doesn't work in the sense that the environment variables don't get passed. It fails because the library path is not in PATH. The createProcess does actually launch it though.
EDIT2: Here's the updated code. Same problem. Further, CreateProcess throws error 1087 which doesn't seem to exist in the docs.
// Environment variables
char env[2048];
char *ptr = env;
char *path_path = getenv("PATH");
// copy original path
memcpy(ptr, path_path, strlen(path_path));
ptr += strlen(ptr) + 1;
memcpy(ptr, ";", 1);
ptr++;
// copy plugin path
memcpy(ptr, plugin_path, strlen(plugin_path));
ptr += strlen(plugin_path) + 1;
memcpy(ptr, ";", 1);
ptr++;
// copy libpath
memcpy(ptr, lib_path, strlen(lib_path));
ptr += strlen(lib_path) + 1;
memcpy(ptr, ";", 1);
ptr++;
// double null terminated
memcpy(ptr, "\0\0", 2);
std::cout << "ENV : " << env << std::endl;
// error checking required
if(!CreateProcess(
NULL, // application name
command_path, // app.exe
NULL,
NULL,
TRUE,
0,
env, // environment
NULL,
&si,
&pi)) {
std::cout << GetLastError();
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
std::cout << "Process Started!";
The PATH variable is a single variable. Different directories are listed in that variable, separated by semi-colons. But you've attempted to define the variable twice. That is the mistake.
The code should be something like this (assuming that you want to extend the existing path):
char *env = new char[2048]; // fingers crossed this is enough
strcpy(env, "PATH=");
strcat(env, getenv("PATH"));
strcat(env, ";");
strcat(env, plugin_path);
strcat(env, ";");
strcat(env, lib_path);
env[strlen(env)+1] = '\0';
Although this code (as is yours in the question) is simply begging for a buffer overrun.
It would be so much easier if you used C++ facilities to build your strings. For instance:
std::stringstream ss;
ss << "PATH=" << getenv("PATH");
ss << ";" << plugin_path;
ss << ";" << lib_path;
ss << '\0';
std::string env = ss.str();
Then pass env.c_str() to CreateProcess.
Not only does this make the code easier to read and verify, you know that you won't overrun any buffers.
I also note that you are passing an environment that has only one variable defined in it, namely PATH. It might be better if you started from the environment of the calling process, added the extra directories to PATH, and then passed that as the environment for the new process.
#include <iostream>
#include <windows.h>
#include <cstring>
#include "tchar.h"
void SetUserVariablePath(){
HKEY hkey;
long regOpenResult;
const char key_name[] = "Environment";
const char path[]="D:/custom_command"; //new_value path need to update
regOpenResult = RegOpenKeyEx(HKEY_CURRENT_USER,key_name, 0, KEY_ALL_ACCESS, &hkey);
LPCSTR stuff = "VVS_LOGGING_PATH"; //Variable Name
RegSetValueEx(hkey,stuff,0,REG_SZ,(BYTE*) path, strlen(path));
RegCloseKey(hkey);
}
void GetUserVariablePath(){
static const char path[] = "VVS_LOGGING_PATH" ; //Variable Name
static BYTE buffer1[1000000] ;
DWORD buffsz1 = sizeof(buffer1) ;
{
//HKEY_CURRENT_USER\Environment
const char key_name[] = "Environment";
HKEY key ;
if( RegOpenKeyExA( HKEY_CURRENT_USER, key_name, 0, KEY_QUERY_VALUE, std::addressof(key) ) == 0 &&
RegQueryValueExA( key, path, nullptr, nullptr, buffer1, std::addressof(buffsz1) ) == 0 )
{
std::cout << "The updated value of the user variable is : " << reinterpret_cast<const char*>(buffer1) << '\n' ;
}
}
}
int main()
{
SetUserVariablePath();
GetUserVariablePath();
return 0;
}