?
Solved

C++, sytem SET windows environment variables

Posted on 2011-10-31
33
Medium Priority
?
1,526 Views
Last Modified: 2012-05-12
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

0
Comment
Question by:sidwelle
  • 12
  • 9
  • 5
  • +4
33 Comments
 
LVL 46

Expert Comment

by:Kent Olsen
ID: 37058460
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
0
 
LVL 86

Expert Comment

by:jkr
ID: 37058573
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
0
 

Author Comment

by:sidwelle
ID: 37058627
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 ?
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 7

Expert Comment

by:Rahul_Gade
ID: 37058682
Check out if this works for you :

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

Open in new window


-Rahul
0
 
LVL 86

Expert Comment

by:jkr
ID: 37058683
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

0
 
LVL 46

Expert Comment

by:Kent Olsen
ID: 37058694
Hi sid,

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


Kent
0
 

Author Comment

by:sidwelle
ID: 37058793
I am trying to set an Environment variable that will persist after my app has terminated.
0
 
LVL 86

Expert Comment

by:jkr
ID: 37058802
>>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.
0
 
LVL 11

Expert Comment

by:cup
ID: 37059905
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.
0
 
LVL 8

Assisted Solution

by:lomo74
lomo74 earned 2000 total points
ID: 37061503
@jkr:
The registry way won't solve the problem. The shell will read the registry the next time it is run (or the session restarted, I can't remember exactly).
sidwelle wants the env variable to propagate to its running shell upon program termination.

@sidwelle:
I read here:
<<Altering the environment variables of a child process during process creation is the only way one process can directly change the environment variables of another process. A process can never directly change the environment variables of another process that is not a child of that process.>>

So... apparently it's not possible.
But we can use a mix of this and code injection to perform the task.
Hold on, I'm working at it --
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37061711
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.
0
 
LVL 8

Accepted Solution

by:
lomo74 earned 2000 total points
ID: 37062013
sorry, my last post contained some little mistakes.
the following is neated up and works with either MBCS or Unicode.
 
// 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 <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, szEnvVar, stringsize1, NULL) &&
					(pString2 == NULL || WriteProcessMemory(hProc, pString2, 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;
}

Open in new window

0
 

Author Comment

by:sidwelle
ID: 37067516
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 ?

0
 
LVL 86

Expert Comment

by:jkr
ID: 37067531
You won'T be able to set persistent environment variables on Windows without doint that in the registry, I am afraid.
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37068237
@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.
0
 
LVL 11

Expert Comment

by:Deepu Abraham
ID: 37068655
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

0
 
LVL 8

Expert Comment

by:lomo74
ID: 37068750
@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.
0
 
LVL 11

Expert Comment

by:Deepu Abraham
ID: 37069058
@lomo74, But I could find the Myvar in the environment variable after exiting the app
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37069248
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
0
 
LVL 11

Expert Comment

by:Deepu Abraham
ID: 37069856
@lomo74, You are spot on ! @others & @sidwelle, Sorry I have just overlooked ! Thanks
0
 
LVL 86

Expert Comment

by:jkr
ID: 37070536
>>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."?

http://www.experts-exchange.com/Programming/Languages/CPP/Q_27424164.html?cid=1573#a37058793

Please read the *entire* thread.
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37070617
@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.
0
 

Author Comment

by:sidwelle
ID: 37070992
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.
0
 

Author Comment

by:sidwelle
ID: 37098130
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 ?
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37098720
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
0
 

Author Comment

by:sidwelle
ID: 37104645
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 ?

0
 
LVL 8

Expert Comment

by:lomo74
ID: 37105355
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>
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37105381
and, oh -
remove also that
#pragma comment(lib, "Psapi.lib")
0
 

Author Comment

by:sidwelle
ID: 37111854
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.
0
 
LVL 8

Expert Comment

by:lomo74
ID: 37112292
>> 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)))
{
...
0
 

Author Comment

by:sidwelle
ID: 37118486
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

0
 
LVL 8

Expert Comment

by:lomo74
ID: 37118981
look at the project settings; maybe psapi.lib is listed among the "Object/library modules" in the "Link" tab?
0
 

Author Closing Comment

by:sidwelle
ID: 37128166
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.
0

Featured Post

How to Use the Help Bell

Need to boost the visibility of your question for solutions? Use the Experts Exchange Help Bell to confirm priority levels and contact subject-matter experts for question attention.  Check out this how-to article for more information.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this post we will learn how to make Android Gesture Tutorial and give different functionality whenever a user Touch or Scroll android screen.
If you are a mobile app developer and especially develop hybrid mobile apps then these 4 mistakes you must avoid for hybrid app development to be the more genuine app developer.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
In this fourth video of the Xpdf series, we discuss and demonstrate the PDFinfo utility, which retrieves the contents of a PDF's Info Dictionary, as well as some other information, including the page count. We show how to isolate the page count in a…

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question