Link to home
Start Free TrialLog in
Avatar of sidwelle
sidwelleFlag for United States of America

asked on

C++, sytem SET windows environment variables

If I can call simple cmd line commands from the system command in C++,
Why can't I use the system function to set environment variables ?


int main(int argc, char* argv[])
{
	//printf("Hello World!\n");

	system("SET MyVar=MyValue\r\n");
	//system("dir");

	return 0;
}

Open in new window

Avatar of Kent Olsen
Kent Olsen
Flag of United States of America image

You can, you just can't use the result.  :)

The system() command spawns a process that is exactly like the one that is running the calling program.  When the process ends, control is returned back to your program.

If the call to the system() function passes a command to execute the SET command, the command is executed in the new process and the process ends.  It doesn't affect the process where your program is running.


Kent
Exactly. If you want to set an environment variable persistently, you need to do that in the registry under HKEY_CURRENT_USER\Environment or HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment - using 'system()', you can do that like
system("REG.exe ADD HKCU\\Environment /v MyVar /t REG_SZ /d MyValue");

Open in new window


Yet I'd rather suggest using the regular registry APIs
Avatar of sidwelle

ASKER

But neither one of these cmds get the value back to parent process 'this console'

how do I get the value back to this console ?
Check out if this works for you :

#include <iostream>
#include <cstdlib>
int main()
{
    putenv("PATH=C:\");
    cout<<getenv("PATH");  
}

Open in new window


-Rahul
If you want to set that for your console process only, use
SetEnvironmentVariable("MyVar","MyValue");

// ...

char buf[MAX_PATH];

GetEnvironmentVariable("MyVar",buf,MAX_PATH);

Open in new window


or


#include <stdlib.h>
#include <stdio.h>

// ...

_putenv("MyVar=MyValue");

char* pVal = getenv("MyVar");

Open in new window

Hi sid,

What are you trying to get back?  The system parameter that you're trying to change?


Kent
I am trying to set an Environment variable that will persist after my app has terminated.
>>I am trying to set an Environment variable that will persist after my app has
>>terminated.

Then you'll have to go the registry way, sorry.
Avatar of cup
cup

Alternatively, if you want to do it the really old fashioned Windows NT way, get setx from http://www.microsoft.com/download/en/details.aspx?id=4200

This is part of the windows resource kit.  Then use setx instead of set in your system call.  Does the same thing as the registry call but it isn't half as complicated.
SOLUTION
Avatar of lomo74
lomo74
Flag of Italy image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ok --
here's a first working solution.
 
// SetEnv.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <SetupAPI.h>
#include <stdlib.h>
#include <stdio.h>
#include <Psapi.h>
#include <tlhelp32.h>

#pragma comment(lib,"Psapi.lib")

BOOL ShowErr(void)
{
	DWORD dwerr = GetLastError();
	if(ERROR_SUCCESS == dwerr)
	{
		printf("No errors to report\n");
		return FALSE;
	}
	LPVOID lpMsgBuf;
	FormatMessage( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dwerr,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL 
	);
	printf("FAILED with code %x: %s\n", dwerr, (char*)lpMsgBuf);
	LocalFree( lpMsgBuf );
	return TRUE;
}

BOOL WINAPI GetParentPID( PROCESSENTRY32& procentry )
{
	OSVERSIONINFO  osver ;
	HINSTANCE      hInstLib ;
	HANDLE         hSnapShot ;
	BOOL           bContinue ;

	// ToolHelp Function Pointers.
	HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ;
	BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ;
	BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ;

	// Check to see if were running under Windows95 or
	// Windows NT.
	osver.dwOSVersionInfoSize = sizeof( osver ) ;
	if( !GetVersionEx( &osver ) )
	{
		return FALSE ;
	}

	if( osver.dwPlatformId != VER_PLATFORM_WIN32_NT )
	{
		ShowErr();
		return FALSE;
	}

	hInstLib = LoadLibraryA( "Kernel32.DLL" ) ;
	if( hInstLib == NULL )
	{
		ShowErr();
		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" ) ;
	if( lpfProcess32Next == NULL ||
		lpfProcess32First == NULL ||
		lpfCreateToolhelp32Snapshot == NULL )
	{
		FreeLibrary( hInstLib ) ;
		return FALSE ;
	}

	// Get a handle to a Toolhelp snapshot of the systems
	// processes.
	hSnapShot = lpfCreateToolhelp32Snapshot(
		TH32CS_SNAPPROCESS, 0 ) ;
	if( hSnapShot == INVALID_HANDLE_VALUE )
	{
		ShowErr();
		FreeLibrary( hInstLib ) ;
		return FALSE ;
	}

	// Get the first process' information.
	memset((LPVOID)&procentry,0,sizeof(PROCESSENTRY32));
	procentry.dwSize = sizeof(PROCESSENTRY32) ;
	bContinue = lpfProcess32First( hSnapShot, &procentry ) ;
	DWORD pid = 0;
	// While there are processes, keep looping.
	DWORD  crtpid= GetCurrentProcessId();
	while( bContinue )
	{
		if(crtpid == procentry.th32ProcessID)
			pid =  procentry.th32ParentProcessID;

		procentry.dwSize = sizeof(PROCESSENTRY32) ;
		bContinue = !pid && lpfProcess32Next( hSnapShot, &procentry );

	}//while ends


	// Free the library.
	FreeLibrary( hInstLib ) ;

	return pid?TRUE:FALSE ;
}

typedef struct _DATA
{
	LPVOID SetEnvironmentVariableWAddr;
	wchar_t *lpName;
	wchar_t *lpValue;
} THRDDATA, *LPTHRDDATA;

static DWORD __declspec(naked) WINAPI ThreadProc(LPTHRDDATA pData)
{
	__asm
	{
		//standard prolog
		PUSH		EBP
		MOV			EBP, ESP

		//move pData in EBX
		PUSH		EBX
		MOV			EBX, DWORD PTR [EBP+0x08]

		//call BOOL bRet = pData->SetEnvironmentVariableWAddr(pData->lpName, pData->lpValue)
		PUSH		DWORD PTR [EBX+0x08]
		PUSH		DWORD PTR [EBX+0x04]
		CALL		DWORD PTR [EBX]

		//return bRet ? 0 : 1
		NEG			EAX
		SBB			EAX, EAX
		INC			EAX

		POP			EBX

		//standard epilog
		POP			EBP
		RET			0x0004
	}
}

int wmain(int argc, wchar_t **argv)
{
	if (argc < 2)
		return 1;

	wchar_t *szEnvVar = argv[1];
	wchar_t *szEnvValue = argv[2];

	PROCESSENTRY32 selfprocentry;
	if(GetParentPID(selfprocentry ))
	{
		// open process. this must be our cmd.exe
		HANDLE hProc = NULL;
		if ((hProc = OpenProcess(PROCESS_CREATE_THREAD |
			PROCESS_VM_OPERATION |
			PROCESS_VM_READ |
			PROCESS_VM_WRITE |
			PROCESS_QUERY_INFORMATION, FALSE, selfprocentry.th32ParentProcessID)) != NULL)
		{
			// calc space needed for code and data
			size_t len1 = wcslen(szEnvVar) + 1;
			size_t len2 = wcslen(szEnvValue) + 1;
			HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");

			size_t codesize = 32;
			size_t datasize = sizeof(THRDDATA);
			size_t stringsize1 = len1 * sizeof(wchar_t);
			size_t stringsize2 = len2 * sizeof(wchar_t);

			// allocate space in the remote process
			LPVOID pCode = (LPVOID)VirtualAllocEx(hProc, NULL, codesize + datasize + stringsize1 + stringsize2,
				MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

			if (pCode != NULL)
			{
				// set up pointers to code / data / strings
				LPVOID pData = (BYTE*)pCode + codesize;
				LPVOID pString1 = (BYTE*)pData + datasize;
				LPVOID pString2 = (BYTE*)pString1 + stringsize1;

				// we must provide the address of the SetEnvironmentVariableW function
				THRDDATA data;
				data.SetEnvironmentVariableWAddr = (LPVOID)GetProcAddress(hKernel, "SetEnvironmentVariableW");
				data.lpName = (wchar_t*)pString1;
				data.lpValue = (wchar_t*)pString2;

				// copy code and data to remote process
				if (WriteProcessMemory(hProc, pCode, &ThreadProc, codesize, NULL) &&
					WriteProcessMemory(hProc, pData, &data, datasize, NULL) &&
					WriteProcessMemory(hProc, pString1, szEnvVar, stringsize1, NULL) &&
					WriteProcessMemory(hProc, pString2, szEnvValue, stringsize2, NULL))
				{
					// execute code remotely
					HANDLE hThread;
					if ((hThread = CreateRemoteThread(hProc, NULL, NULL, (LPTHREAD_START_ROUTINE)pCode,
						pData, NULL, NULL)) != NULL)
					{
						DWORD dwExitCode;
						WaitForSingleObject(hThread, 10000) == WAIT_OBJECT_0 &&
							GetExitCodeThread(hThread, &dwExitCode) &&
							dwExitCode != 0;
						CloseHandle(hThread);
					}
				}

				VirtualFreeEx(hProc, pCode, 0, MEM_RELEASE);
			}

			CloseHandle(hProc);
		}
		else
			return 1;
	}
	else
		return 1;

	return 0;
}

Open in new window


compile it.
then run it FROM A CMD SHELL, passing name and value as parameters. e.g. SetEnv.exe MYVAR MYVALUE
upon completion, examine the shell's environment with the command SET: you will see MYVAR=MYVALUE.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
lomo74, thanks for the solution.  I have not had a chance to test it yet, but I will in next day or two.

Thanks

BTW, I see that  <stdio.h>  is included twice,  why does that not create a problem ?

You won'T be able to set persistent environment variables on Windows without doint that in the registry, I am afraid.
@jkr:
you didn't get the point.
sidwelle does NOT want to set persistent variables (that survive across system restarts).
sidwelle wants to set variables that propagate from his process to the parent shell (CMD.EXE).
the registry is useless in this scenario, because the shell won't read those variables until next restart.
I would take a simple solution as Rahul_Gade suggested: I have modified the putenv() and getenv() function which is deprecated. This will get the value back to your console in the same session.
char *pValue;
   size_t len;
   _putenv("Myvar=Myval");
   errno_t err = _dupenv_s( &pValue, &len, "Myvar" );
   if ( err ) return -1;
   printf( "Myvar = %s\n", pValue );
   free( pValue );
   system("pause");

Open in new window

@DeepuAbrahamK:
with _putenv you are setting the variable into the PROCESS.
it will NOT propagate to the console.

>>   errno_t err = _dupenv_s( &pValue, &len, "Myvar" );
>>   if ( err ) return -1;
>>   printf( "Myvar = %s\n", pValue );
>>   free( pValue );
>>   system("pause");

the above is meaningless: after setting the env var into the process, you read it back. Obviously, it is set.
Try exit the program; then issue the dos "SET" command: Myvar is NOT defined.
@lomo74, But I could find the Myvar in the environment variable after exiting the app
well, that's not possible by means of your technique IMHO.
that variable must come from elsewhere (e.g. the registry? or a previous SET command?).
please make a test: try to set ANOTHER variable (e.g. MyVar1).
windows is not supposed to work this way, and MSDN is clear about that --
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682009%28v=vs.85%29.aspx
@lomo74, You are spot on ! @others & @sidwelle, Sorry I have just overlooked ! Thanks
>>you didn't get the point.
>>sidwelle does NOT want to set persistent variables

Um, how would you then explain "I am trying to set an Environment variable that will persist after my app has terminated."?

https://www.experts-exchange.com/questions/27424164/C-sytem-SET-windows-environment-variables.html?cid=1573&anchorAnswerId=37058793#a37058793

Please read the *entire* thread.
@jkr
he wrote:
"after my app has terminated"
NOT
"after my app has terminated and the windows session restarted".

here is your solution's behaviour:
- shell running, variable not set. App is being launched
- app running   ->   it sets variable in the registry - variable still not visible in the process.
- app terminated   ->  returning to the shell - variable still not visible in the shell.
- shell closed, session restarted, shell launched   ->   NOW variable is visible.

mine:
- shell running, variable not set. App is being launched
- app running   ->   it sets variable in the running shell.
- app terminated   ->  returning to the shell - NOW variable is visible.

caught it?

Let's wait for sidwelle's comment. He'll decide which solution better suits his needs.
Iomo74, your scenario is correct.  I will find some time to work with it today.
That is what I needed.

That being said, If someone really wanted an all inclusive solution. both solutions should be included.
I keep getting the following error: fatal error C1083: Cannot open include file: 'Psapi.h': No such file or directory

I downloaded the SP6 for VS6, but the error persists. Is there a simple fix for this error ?
you have to download and install the Windows SDK (http://www.microsoft.com/download/en/details.aspx?id=8442), then point your compiler to the "include" and "lib" subdirectories of the SDK installation directory so it can find .h and .lib files
It just got worse:  

I download the file and burned the disk and intalled: Windows SDK for Windows 7 and .Net Framework 4.

In VS6 C++, opened "Tools" > "Options" > "Directories"
Made sure the following directories were included:
C:\PROGRAM FILES\MICROSOFT SDKS\WINDOWS\V7.0A\INCLUDE
C:\PROGRAM FILES\MICROSOFT SDKS\WINDOWS\V7.0A\LIB

Now I get over a hundred errors:
c:\program files\microsoft sdks\windows\v7.0a\include\psapi.h(85) : error C2065: '__out_bcount' : undeclared identifier
c:\program files\microsoft sdks\windows\v7.0a\include\psapi.h(85) : error C2065: 'cb' : undeclared identifier
... SetTest.exe - 102 error(s), 22 warning(s)

What went wrong ?

what the heck... I'm sorry, I overlooked it... you have VS6: the latest SDK compatible with VS6 is the Windows 2003 Platform SDK (Feb 2003 edition) only available on CD by Microsoft.
remove those dirs from VS options...
ANYWAY -
I just deleted some #include statements and the program compiles fine :-P
leave only the following and try again:
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
and, oh -
remove also that
#pragma comment(lib, "Psapi.lib")
I wondered why you wanted the SDK loaded.  Followed your instructions and removed the 'includes'

and the dir references.  Now when I try to compile I get the following two errors:
But I don't know enough about  how the API works to troubleshoot.

C:\Projects\C++\TEST06\SetTest\SetTest.cpp(203) : error C2664: 'WriteProcessMemory' : cannot convert parameter 3 from 'const char *' to 'void *'
        Conversion loses qualifiers
C:\Projects\C++\TEST06\SetTest\SetTest.cpp(204) : error C2664: 'WriteProcessMemory' : cannot convert parameter 3 from 'const char *' to 'void *'
        Conversion loses qualifiers
Error executing cl.exe.
>> I wondered why you wanted the SDK loaded
Experts are not infallible. I just made a mistake, and I already apologized.

now to your issue:
add a (void*) typecast in front of szEnvVar and szEnvValue
...
WriteProcessMemory(hProc, pString1, (void *)szEnvVar, stringsize1, NULL) &&
(pString2 == NULL || WriteProcessMemory(hProc, pString2, (void *)szEnvValue, stringsize2, NULL)))
{
...
I did not mean to insult, I appreciate all the work.

I added the typecast and that resolved the two errors listed, but another error showed when I tried to compile:
LINK : fatal error LNK1104: cannot open file "psapi.lib"
But I don't see psapi anywhere in the code ?

I included everything that I have so far.

// SetTest.cpp : Defines the entry point for the console application.
//

//#include "stdafx.h"

//int main(int argc, char* argv[])
//{
//	printf("Hello World!\n");
//	return 0;
//}


// SetParentEnv.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//#include <SetupAPI.h>
//#include <stdlib.h>
//#include <stdio.h>
//#include <Psapi.h>
//#include "Psapi.h"
#include <tlhelp32.h>
#include <tchar.h>

//#pragma comment(lib, "Psapi.lib")

BOOL WINAPI GetParentPID(PROCESSENTRY32& procentry)
{
	OSVERSIONINFO  osver;
	HINSTANCE      hInstLib;
	HANDLE         hSnapShot;
	BOOL           bContinue;

	// ToolHelp Function Pointers.
	HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD);
	BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32);
	BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32);

	// Check to see if were running under Windows95 or
	// Windows NT.
	osver.dwOSVersionInfoSize = sizeof(osver);
	if (!GetVersionEx(&osver))
		return FALSE;

	if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
		return FALSE;

	hInstLib = GetModuleHandle(_T("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");
#ifdef _UNICODE
	lpfProcess32First =
		(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
		GetProcAddress(hInstLib, "Process32FirstW");
	lpfProcess32Next =
		(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
		GetProcAddress(hInstLib, "Process32NextW");
#else
	lpfProcess32First =
		(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
		GetProcAddress(hInstLib, "Process32First");
	lpfProcess32Next =
		(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
		GetProcAddress(hInstLib, "Process32Next");
#endif
	if (lpfProcess32Next == NULL ||
		lpfProcess32First == NULL ||
		lpfCreateToolhelp32Snapshot == NULL)
	{
		return FALSE;
	}

	// Get a handle to a Toolhelp snapshot of the systems
	// processes.
	hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapShot == INVALID_HANDLE_VALUE)
		return FALSE;

	// Get the first process' information.
	ZeroMemory(&procentry, sizeof(PROCESSENTRY32));
	procentry.dwSize = sizeof(PROCESSENTRY32);
	bContinue = lpfProcess32First(hSnapShot, &procentry);
	DWORD pid = 0;
	// While there are processes, keep looping.
	DWORD crtpid = GetCurrentProcessId();
	while (bContinue)
	{
		if (crtpid == procentry.th32ProcessID)
			pid = procentry.th32ParentProcessID;

		procentry.dwSize = sizeof(PROCESSENTRY32);
		bContinue = !pid && lpfProcess32Next(hSnapShot, &procentry);
	}

	return pid
		? TRUE
		: FALSE;
}

typedef struct _DATA
{
	LPVOID SetEnvironmentVariableAddr;
	TCHAR *lpName;
	TCHAR *lpValue;
} THRDDATA, *LPTHRDDATA;

static DWORD __declspec(naked) WINAPI ThreadProc(LPTHRDDATA pData)
{
	__asm
	{
		//standard prolog
		PUSH		EBP
		MOV		EBP, ESP

		//move pData in EBX
		PUSH		EBX
		MOV		EBX, DWORD PTR [EBP+0x08]

		//call BOOL bRet = pData->SetEnvironmentVariableAddr(pData->lpName, pData->lpValue)
		PUSH		DWORD PTR [EBX+0x08]
		PUSH		DWORD PTR [EBX+0x04]
		CALL		DWORD PTR [EBX]

		//return bRet ? 0 : 1
		NEG		EAX
		SBB		EAX, EAX
		INC		EAX

		POP		EBX

		//standard epilog
		POP		EBP
		RET		0x0004
	}
}

BOOL WINAPI SetParentEnvironmentVariable(
	const TCHAR *szEnvVar,
	const TCHAR *szEnvValue)
{
	if (szEnvVar == NULL)
		return FALSE;

	PROCESSENTRY32 selfprocentry;
	BOOL bRet = FALSE;

	if (GetParentPID(selfprocentry))
	{
		HANDLE hProc = NULL;
		if ((hProc = OpenProcess(PROCESS_CREATE_THREAD |
			PROCESS_VM_OPERATION |
			PROCESS_VM_READ |
			PROCESS_VM_WRITE |
			PROCESS_QUERY_INFORMATION, FALSE, selfprocentry.th32ParentProcessID)) != NULL)
		{
			size_t len1 = _tcslen(szEnvVar) + 1;
			size_t len2 = szEnvValue == NULL
				? 0
				: _tcslen(szEnvValue) + 1;

			HMODULE hKernel = GetModuleHandle(_T("kernel32.dll"));

			size_t codesize = 32;
			size_t datasize = sizeof(THRDDATA);
			size_t stringsize1 = len1 * sizeof(TCHAR);
			size_t stringsize2 = len2 * sizeof(TCHAR);

			LPVOID pCode = (LPVOID)VirtualAllocEx(hProc, NULL, codesize + datasize + stringsize1 + stringsize2,
				MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

			if (pCode != NULL)
			{
				LPVOID pData = (BYTE*)pCode + codesize;
				LPVOID pString1 = (BYTE*)pData + datasize;
				LPVOID pString2 = szEnvValue == NULL
					? NULL
					: (BYTE*)pString1 + stringsize1;
				
				THRDDATA data;
#ifdef _UNICODE
				data.SetEnvironmentVariableAddr = (LPVOID)GetProcAddress(hKernel, "SetEnvironmentVariableW");
#else
				data.SetEnvironmentVariableAddr = (LPVOID)GetProcAddress(hKernel, "SetEnvironmentVariableA");
#endif
				data.lpName = (TCHAR*)pString1;
				data.lpValue = (TCHAR*)pString2;

				if (WriteProcessMemory(hProc, pCode, &ThreadProc, codesize, NULL) &&
					WriteProcessMemory(hProc, pData, &data, datasize, NULL) &&
					WriteProcessMemory(hProc, pString1, (void *)szEnvVar, stringsize1, NULL) &&
					(pString2 == NULL || WriteProcessMemory(hProc, pString2, (void *)szEnvValue, stringsize2, NULL)))
				{
					HANDLE hThread;
					if ((hThread = CreateRemoteThread(hProc, NULL, NULL, (LPTHREAD_START_ROUTINE)pCode,
						pData, NULL, NULL)) != NULL)
					{
						DWORD dwExitCode;
						bRet = WaitForSingleObject(hThread, 10000) == WAIT_OBJECT_0 &&
							GetExitCodeThread(hThread, &dwExitCode) &&
							dwExitCode == 0;
						CloseHandle(hThread);
					}
				}

				VirtualFreeEx(hProc, pCode, 0, MEM_RELEASE);
			}

			CloseHandle(hProc);
		}
	}

	return bRet;
}

int _tmain(int argc, TCHAR **argv)
{
	if (argc < 2)
		return 1;

	TCHAR *szEnvVar = argv[1];
	TCHAR *szEnvValue = argc > 2
		? argv[2]
		: NULL;

	//set an environment variable in the PARENT process (typically CMD.EXE)
	return SetParentEnvironmentVariable(szEnvVar, szEnvValue)
		? 0
		: 1;
} 

//Toggle HighlightingOpen in New Window

Open in new window

look at the project settings; maybe psapi.lib is listed among the "Object/library modules" in the "Link" tab?
Excellent code.  

I tried to assign more than 500 pts, I think the answer deserves it.
No one else even came close.

Thanks for the help.