Define in C++ new environment variable and use it in bat file - c++

It is very simple, I want to define a new environment variable in C++ using SetEnvironmentVariable, and then call a bat file that use it. This is my c++ code;
PROCESS_INFORMATION processInformation = { 0 };
STARTUPINFO startupInfo = { 0 };
SetEnvironmentVariable("GSDebbugingDir", "Hello");
BOOL result = CreateProcess(NULL,
"dummy.bat"),
NULL,
NULL,
FALSE,
CREATE_NO_WINDOW,
NULL,
NULL,
&startupInfo,
&processInformation);
And my dummy.bat file is :
#echo off
echo GSDebbugingDir = %GSDebbugingDir%
if defined GSDebbugingDir mkdir c:\temp\dummy
pause
But this didn't out the value of GSDebbugingDir variable. What is the problem?

CreateProcess documentation says:
To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
Which makes sense because a .bat file by itself is not an executable module by itself.
But, contrary to what the doc says the following code worked for me with UseApplicationName NOT #defined, i.e. with "cmd.exe" placed into commandLine
Also, to see the output of the bat file you probably want to remove that CREATE_NO_WINDOW flag
//#define UseApplicationName
#ifdef UseApplicationName
TCHAR commandLine[] = _T(" /c dummy.bat");
#else
TCHAR commandLine[] = _T("cmd.exe /c dummy.bat");
#endif
BOOL result = CreateProcess(
#ifdef UseApplicationName
_T("cmd.exe"),
#else
NULL,
#endif
commandLine,
NULL,
NULL,
FALSE,
0, //CREATE_NO_WINDOW,
NULL,
NULL,
&startupInfo,
&processInformation);
BTW pay attention that
The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string).

Related

trying to delete a file in Temp dir using CreateProcessA

Hello I am trying to delete a file using following code:
CreateProcessA(NULL, (LPSTR)"del /f C:\\Users\\samee\\AppData\\Local\\Temp\\tempFile.txt", 0, 0, true, CREATE_NO_WINDOW, 0, 0, &si, &pi);
however this is not working. What am doing wrong here?
del is not an executable that you can run directly. It is a built-in command of the cmd.exe shell. So, you would need to run cmd.exe instead, using its /C or /K parameter to execute shell commands, eg:
char cmdLine[] = "cmd.exe /C del /f C:\\Users\\samee\\AppData\\Local\\Temp\\tempFile.txt";
CreateProcessA(NULL, cmdLine, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
But why do that? The Win32 API has a DeleteFileA() function, you should use that instead, eg:
DeleteFileA("C:\\Users\\samee\\AppData\\Local\\Temp\\tempFile.txt");
Note that the /f parameter of del allows for deleting a read-only file. If DeleteFileA() fails because the file is read-only, then simply remove the read-only flag, eg:
char fileName[] = "C:\\Users\\samee\\AppData\\Local\\Temp\\tempFile.txt";
DWORD attr = GetFileAttributesA(fileName);
if ((attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_READONLY))
SetFileAttributesA(fileName, attr & ~FILE_ATTRIBUTE_READONLY);
DeleteFileA(fileName);

CreateProcess api failing with error code 122 on windows 10

I am using CreateProcess api to start a batch file. The Code works fine on windows 7 but it is failing on Windows 10.
Below is the snippet of code:
CString param; //it holds the very long string of command line arguments
wstring excFile = L"C:\\program files\\BatchFile.bat";
wstring csExcuPath = L"C:\\program files";
wstring exeWithParam = excFile + _T(" ");
exeWithParam = exeWithParam.append(param);
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
TCHAR lpExeWithParam[8191];
_tcscpy_s(lpExeWithParam, exeWithParam.c_str());
BOOL bStatus = CreateProcess(NULL, lpExeWithParam, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, csExcuPath.c_str(), &si, &pi);
DWORD err;
if (!bStatus)
{
err = GetLastError();
}
With the above code, it is invoking a batch file which will start an executable with given parameters. This code is not working only Windows 10 in our product.
GetLastError is returning error code 122 which code for error "The data area passed to a system call is too small." How to figure out what is causing this error and how it can be resolved?
However, when using the same code in a sample test application is not giving any error and passing.
Any clue/hint why is causing it to fail on Windows 10.
You need to execute cmd.exe with the .bat file as a parameter, don't try to execute the .bat directly.
Also, you don't need lpExeWithParam, you can pass exeWithParam directly to CreateProcess().
Try something more like this instead:
CString param; //it holds the very long string of command line arguments
...
wstring excFile = L"C:\\program files\\BatchFile.bat";
wstring csExcuPath = L"C:\\program files";
wstring exeWithParam = L"cmd.exe /c \"" + excFile + L"\" ";
exeWithParam.append(param);
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = {};
BOOL bStatus = CreateProcessW(NULL, &exeWithParam[0]/*or exeWithParam.data() in C++17*/, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB, NULL, csExcuPath.c_str(), &si, &pi);
if (!bStatus)
{
DWORD err = GetLastError();
...
}
else
{
...
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
Error 122 equates to ERROR_INSUFFICIENT_BUFFER and I think the clue here is "it holds the very long string of command line arguments".
Just how long is it? The limit may be lower on Windows 10 - I recommend you experiment (binary chop).
Also, the documentation for CreateProcess states that you must launch cmd.exe explicitly to run a batch file, so I guess you should do what it says.
I think to run a batch file you must set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file

using the CreateProcess function

I'm trying to create something similar to a cmd with Microsoft Visual Studio Express 2013 for Windows Desktop in c++ and one of my function should start a process like open skype by typing "skype.exe". I searched in the internet and found the CreateProcess function that should do the work for me. when I created a function that receives a class value that I created called Line (the name of the class but it doesn't really metter) and used the CreateProcess function in the way that is shown bellow I have to type in my cmd "start skype.exe" but I want it to work like in the regular cmd by writing only "skype.exe", how can I do it?
(the l.parameter is just a string that contains the word skype)
void execute(Line l){
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
LPSTR s = const_cast<char *>(l.parameter.c_str());
if (!CreateProcess(NULL, s, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
printf("CreateProcess failed (%d).\n", GetLastError());
return;
}
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);}
First thing is that:
LPSTR s = const_cast<char *>(l.parameter.c_str());
is bad idea, CreateFile accepts for lpCommandLine non const buffer for a reason - it might modify it:
The system adds a terminating null character to the command-line string to separate the file name from the arguments. This divides the original string into two strings for internal processing.
so you should pass an array, for example:
TCHAR szCmd[MAX_PATH] = {0};
then to your question, if "start skype.exe" works for you and you want to enter only skype.exe at the command line - then why not concatenate strings? for example:
_tcscat(szCmd, _T("start "));
_tcscat(szCmd, parameter.c_str());
and pass szCmd to CreateProcess
the question is whether you use UNICODE build, if yes then make sure parameter is std::wstring, otherwise if you use non-UNICODE build (and it looks like thats true) then std::string is fine.
start is not an executable, it is a feature of cmd.exe, so to invoke start skype.exe via CreateProcess(), you would have to specify cmd.exe as the command and /C start skype.exe as its parameter.
Line l;
line.parameter = "cmd.exe /C start skype.exe";
execute(l);
But that is overkill in this situation, as start is not actually needed, despite what you claim. It is perfectly valid and preferable to invoke skype.exe directly as the command.
However, you have to provide the full path to skype.exe (same if you were to invoke start), otherwise CreateProcess() won't be able to find it, as Skype does not register its .exe file path in the App Paths key of the Registry, or the path to its Phone subfolder (where skype.exe resides) on the system's %PATH% environment variable.
For example:
Line l;
line.parameter = "C:\\Program Files (x86)\\Skype\\Phone\\Skype.exe";
execute(l);
Fortunately, Skype does store the full path to skype.exe in the Registry, specifically in the following key:
HKEY_CURRENT_USER\Software\Skype\Phone
It is stored in a REG_SZ value named "SkypePath".
std::string GetSkypePath()
{
std::string sPath;
HKEY hKey;
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Skype\\Phone", 0, KEY_QUERY_VALUE, &hKey) == 0)
{
char szPath[MAX_PATH+1] = {0};
DWORD dwPathLen = MAX_PATH;
if (RegQueryValueExA(hKey, "SkypePath", NULL, NULL, (LPBYTE)szPath, &dwPathLen) == 0)
sPath = szPath;
RegCloseKey(hKey);
}
return sPath;
}

pass filename from existing Process to new Process MFC c++

I'm trying to figure out how to pass filename from within an existing executable to a newly generated executable of same type & then the new exe load said file name. Following is something I'm working on but I'm bit lost really.
CString cstrExePathLoc;
GetModuleFileName(NULL, cstrExePathLoc.GetBuffer(MAX_PATH), MAX_PATH);
wchar_t szCommandLine[1024] = _T("C:\\Users\\Home\\Desktop\\testfile.tmp");
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
::ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
CreateProcess(
cstrExePathLoc, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
&startupInfo, &processInfo
);
EDIT: this still doesn't open the file. A new ExeApp is started but no file is loaded. No errors generated at all.
I've searched net but no examples I've come across clearly explain how to do this. Any help would be appreciated. Thanks.
EDIT: simple solution here that's worked thanks to Robson Filho Colodeti below.
CString cstrExeFilePathAndFilePath2Open = cstrExePathLoc;
cstrExeFilePathAndFilePath2Open += L" \"";
cstrExeFilePathAndFilePath2Open += cstrFilePath2Open;
cstrExeFilePathAndFilePath2Open += L"\"";
CreateProcess(csExePath, cstrExeFilePathAndFilePath2Open.GetBuffer(0), NULL, NULL, TRUE, NULL, NULL, NULL, &sui, &pi);
Opening the other program
Using CreateProcess
you are in the right way, you can use the CreateProcess method.
BOOL fSuccess;
CString csDir = L"c:\your\working\directory\";
CString csParameters = L"parameter1 parameter2 parameter3 /parameter4=value";
CString csCommand = L"c:\folder\of\the\executable\executable.exe";
csCommand+= L" ";
csCommand+= csParameters;
// Create the child process.
fSuccess = CreateProcess(NULL, csCommand.GetBuffer(0), NULL, NULL, TRUE, 0, NULL,
csDir, &startupInfo, &processInfo);
Using ShellExecute
an easier way is to use the ShellExecute method because the create process method is a more "advanced" way to call a process since it gives you a lot of possibilities to control the results etc...
Reading the parameters inside the other program
then you will have to read these parameters from the other executable: check this thread

why does ::CreateProcess(path,cmd,...) fail with error "File not found"?

I am trying to have a C++ program call an already made C# program to run in the background.
STARTUPINFO info = {sizeof(info)};
PROCESS_INFORMATION processinfo;
DWORD error1 = GetLastError();
bool x = ::CreateProcess((LPCWSTR)"C:\Convert_Shrink.exe", GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
DWORD error = GetLastError();
error1 is 0 before CreateProcess
error is 2 after CreateProcess
error 2:
ERROR_FILE_NOT_FOUND 2 (0x2) The system cannot find the file specified.
I've changed it to C:\ \ incase they were checking for escape sequences but I still get error 2 and I'm not sure why.
You can:
Use CreateProcessA to match your ANSI file path:
bool x = ::CreateProcessA("C:\\Convert_Shrink.exe", GetCommandLineA(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
* Provide a file path which matches the string format required by your Unicode settings:
bool x = ::CreateProcess(_T("C:\\Convert_Shrink.exe"), GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
or
Use CreateProcessW so you can pass a Unicode filepath (supports extended characters):
bool x = ::CreateProcessW(L"C\\\Convert_Shrink.exe", GetCommandLineW(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
(as #dolphy noted, the argument has to be a writable string)
Provide a file path which matches the string format required by your Unicode settings:
#if UNICODE
std::wstring exename =
#else
const char* exename =
#endif
_T("C:\\Convert_Shrink.exe");
bool x = ::CreateProcess(&exename[0], GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
or
Use CreateProcessW so you can pass a Unicode filepath (supports extended characters):
wchar_t exename[] = L"C:\\Convert_Shrink.exe";
bool x = ::CreateProcessW(exename, GetCommandLineW(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
Just for the record. CreateProcessAsUser calls SearchPath internally. SearchPath uses the File System Redirector https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187%28v=vs.85%29.aspx
So, if you are running a 32 bit app under WOW64 and you ask for a process using an exe in system32 dir e.g. "c:\windows\system32\myapp.exe", CreateProcessAsUser will look in syswow64 instead e.g."c:\windows\syswow64\myapp.exe". If your exe is not there you'll get a "file not found error".
I just looked up GetCommandLine(), and MSDN states that it gets the command line for the current process. MSDN entry for CreateProcess() states that the second argument is the command line command that you want to be executed, if I'm reading it correctly. So you are essentially telling CreateProcess() to run another instance of the C++ program, not the C# program.
(edit)
Actually, upon closer inspection, the CreateProcess() documentation does not seem to clearly explain what will happen if you supply both the first and second arguments. It says that the first specifies the module and the second specifies the command line. What's the diff?
Sorry for the inconclusive answer, I would convert this answer into a couple of comments on your question if I could.
Have you tried casting the string to LPCTSTR instead:
bool x = ::CreateProcess((LPCTSTR)"C:\Convert_Shrink.exe", GetCommandLine(), NULL, NULL, false, 0,NULL,NULL, &info, &processinfo);
From Microsoft:
The Unicode version of this function, CreateProcessW, can modify the contents
of this string. Therefore, this parameter cannot be a pointer to read-only
memory (such as a const variable or a literal string). If this parameter is a
constant string, the function may cause an access violation.