.c_str() doesn't behave as expected - c++

I want to create a process to read out some serial port. However the user should be able to change the path where the program is located in the future. This is why I would like to include the variable BasePathFile, which is set to the default value durin the initialization of the class:
const std::string BP = "C:\\aerospec_developement\\";
...
BasePathFile = BP;
...
Can someone explain why //1 doesn't work, but //2 is fine
void AerospecGUI::ReadPressure()
{
//1 const char *Args = ("C:\\Windows\\System32\\cmd.exe /C powershell /C "+ BasePathFile+"sourcecode\\pressure.ps1 -comport COM4 -baud 1200 -parity None -data 8 -stopbits one").c_str();
//2 const char *Args = "C:\\Windows\\System32\\cmd.exe /C powershell /C C:\\aerospec_developement\\sourcecode\\pressure.ps1 -comport COM4 -baud 1200 -parity None -data 8 -stopbits one";
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
memset(&StartupInfo, 0, sizeof(StartupInfo));
memset(&ProcessInfo, 0, sizeof(ProcessInfo));
StartupInfo.cb = sizeof(StartupInfo);
wchar_t wargs[1000];
mbstowcs(wargs, Args, strlen(Args)+1);//Plus null
LPWSTR argsptr = wargs;
bool result = CreateProcess(NULL, argsptr, NULL, NULL, FALSE, NULL, NULL, NULL,
&StartupInfo, &ProcessInfo);
...
Further, the author wrote a very similar line in another function. But this one worked.
bool AerospecGUI::FTP(std::string command, std::string file)
{
// This function executes a batch script with the given parameters. The batch script
// generates a .ftp file which contains the commands to perform the action given by "command"
// (get, put, delete).
const char *Args = ("C:\\Windows\\System32\\cmd.exe /C "+BasePathFile +"FTP\\FileTransfer.bat " + ServerURL + " root password " + command + " " + BasePathFile + "FTP\\ " + file +" " + BasePathFile + "FTP\\" + file.substr(0,file.size()-4)+".ftp").c_str();
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
memset(&StartupInfo, 0, sizeof(StartupInfo));
memset(&ProcessInfo, 0, sizeof(ProcessInfo));
StartupInfo.cb = sizeof(StartupInfo);
wchar_t wargs[1000];
mbstowcs(wargs, Args, strlen(Args)+1);//Plus null
LPWSTR argsptr = wargs;
...

1 and the original author code are wrong: c_str() returns a pointer to a C-like string, but it's a member function of the std::string class (see docs). Dereferencing its pointer after the std::string object has expired is undefined behavior (may or may not work).
2 works fine instead
const char *Args = "C:\\... one";
since it's a string literal and its lifetime spans for the entire program execution.

For 1 you create a temporary std::string object, and gets a pointer to its internal string. Once the temporary object is destructed the string ceases to exist and the pointer will not be pointing anywhere valid.
For 2 you have an actual constant string literal, who has a lifetime of the program and will never cease to exist.
That is seems to be working for your second example is just a fluke. What you have with the stray invalid pointer is undefined behavior and sometimes those might seem to work just fine.

Related

Error "<url> is not recognized as an internal or external command, operable program or batch file."

My code should take arguments, put "+" inbetween them, and search google chrome with this, however I get the error (Command line Argument= "Stack Overflow Site"):
http://www.google.com/search?q=Stack+Overflow+Site'C:\Program' is not
recognized as an internal or external command, operable program or
batch file.
Also in my program I get this error:
error C4996: 'strcpy': This function or variable may be unsafe.
Consider using strcpy_s instead. To disable deprecation, use
_CRT_SECURE_NO_WARNINGS. See online help for details. c:\users\user\documents\visual studio
2013\projects\project1\project1\main.cpp
I have been ignoring this, because I thought it was just a warning, but I'm not sure if it is relevant.
My code:
#include <Windows.h>
#include <string>
#include <iostream>
using namespace std;
int main(int argc, char** argv){
//Loop through arguments and put a "+" between them.
string out = "";
for (int i = 1; i < argc; ++i) {
if (i != 1){
out += "+";
}
out += argv[i];
}
string newout = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \"http://www.google.com/search?q=" + out + "\"";
// set the size of the structures
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
//set y to newout and convert
char *y = new char[newout.length() + 1];
strcpy(y, newout.c_str());
//Run Google Chrome with argument
CreateProcessA("C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", // the path
y, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
delete[] y;
cin.ignore();
}
I suspect you are not getting exactly the same error since you
stopped deleting the commandline buffer before using it, but
some different, equally unexpected result.
If you will read the documentation of CreateProcess
you will learn that the parameters:
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
can be supplied in one of three ways, as illustrated:-
Way 1
lpApplicationName = "\path\to\executable"
lpCommandLine = "args for the executable"
which results in a process initiated with the command:
\path\to\executable args for the executable
Way 2
lpApplicationName = NULL
lpCommandLine = "\path\to\executable args for the executable"
which results in the same process as Way 1
Way 3
lpApplicationName = "\path\to\executable"
lpCommandLine = NULL
which results in a process initiated with the command \path\to\executable.
You are not using Way 1 or Way 2 or Way 3, but:
lpApplicationName = "\path\to\executable"
lpCommandLine = "\path\to\executable args for the executable"
which in your case results in a process initiated with the command:
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \
C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \
http://www.google.com/search?q=Stack+Overflow+Site"
in which the arguments passed to Chrome are:
C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe \
http://www.google.com/search?q=Stack+Overflow+Site"
Of course this does not have the anticipated outcome.
Should you wish to correct this by adopting Way 2, bear particularly
in mind what the documentation says about lpCommandLine:
...
If lpApplicationName is NULL, the first white space–delimited token of the command line specifies the module name.
...

How to run program from hidden folder WinAPI

I'm try to run program with this code:
PROCESS_INFORMATION ProcInfo = { 0 };
STARTUPINFO StartInfo = { 0 };
StartInfo.cb = sizeof(StartInfo);
if (!::CreateProcessW(NULL, (LPWSTR)wszPathToFile, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &StartInfo, &ProcInfo)) {
return GetLastError();
}
But I get error message: The system cannot find the path specified.
wszPathToFile - path to file (example: "C:\test\test.exe /retest"). Folder "test" is hidden
How to fix it?
That the folder is hidden is not relevant. That has no impact here.
As discussed in the comments, the fact that you are casting the lpCommandLine argument indicates that szPathToFile is not the correct type. It must be a pointer to a modifiable array of wide characters. If it was then you could omit the cast and the compiler would accept szPathToFile directly.
Most likely szPathToFile is actually a pointer to an array of ANSI encoded 8 bit char.

Run several copies of some program from c++ program

What I need to do is to run (for example) 6 copies (independent processes) of example.exe (what is also my program) with different command line arguments from my Main program. This copies should work at the same time. I'm using code like this:
const int NumberOfProcesses= 6;
STARTUPINFO si[NumberOfProcesses];
PROCESS_INFORMATION pi[NumberOfProcesses];
srand((unsigned)time(NULL));
for(int i=0;i<NumberOfProcesses;i++)
{
char fname[MAX_PATH];
strncpy(fname,"\"",1);
fname[1] = '\0';
strcat(fname,"d:\\test\\example.exe");
strcat(fname,"\"");
int id = i;
strcat(fname," ");
strcat(fname,(std::to_string(id)).c_str());
int count = (rand()%1000) + 1;
strcat(fname," ");
strcat(fname,(std::to_string(count)).c_str());
int lb = 13;
strcat(fname," ");
strcat(fname,(std::to_string(lb)).c_str());
int ub = 666;
strcat(fname," ");
strcat(fname,(std::to_string(ub)).c_str());
printf(fname);
cout<<"\n";
//Here in fname I have correct command, that runs properly
bool t = false;
t=CreateProcess( NULL, // No module name (use command line)
(LPSTR)fname, // Command line CharToLPWSTR(fname2)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si[i], // Pointer to STARTUPINFO structure
&pi[i] ); // Pointer to PROCESS_INFORMATION structure
}
So if NumberOfProcesses==0 it will run "d:\test\example.exe" 1 2 3 4 from fname. But if NumberOfProcesses==6 (or something else) zero iteration will complete properly, but others will return false. Sometimes 4th iteration runs properly.
I think this is because when zero iteration run "d:\test\example.exe" 1 2 3 4 the example.exe is busy and can not be run one more time. So I changed code to this:
bool t = false;
getchar();
for(int i=0;i<5;i++)
{
t=CreateProcess( NULL, // No module name (use command line)
(LPSTR)fname, // Command line CharToLPWSTR(fname2)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si[i], // Pointer to STARTUPINFO structure
&pi[i] ); // Pointer to PROCESS_INFORMATION structure
if(t) break;
}
So I got some delay between starting example.exe - and it helped. All 6 copies starts and finishes, but they don't run parallel (I have output from example.exe).
But this is not the way I want my program work.
How can I avoid this problem?
Thanks.
UPD.
According to Werner Henze's answer I've just add couple of lines (to initialize STURTUPINFO) into loop
const int NumberOfProcesses= 6;
STARTUPINFO si[NumberOfProcesses];
PROCESS_INFORMATION pi[NumberOfProcesses];
for(int i=0;i<NumberOfProcesses;i++)
{
///Next two lines is important
ZeroMemory( &si[i], sizeof(si[i]) );
si[i].cb = sizeof(si);
/*Some actions*/
t=CreateProcess( NULL, // No module name (use command line)
(LPSTR)fname, // Command line CharToLPWSTR(fname2)
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si[i], // Pointer to STARTUPINFO structure
&pi[i] ); // Pointer to PROCESS_INFORMATION structure
}
And it is work fine now.
Thanks again.
In the cases where CreateProcess returns FALSE you should check GetLastError(). In the code that you posted you are not initializing the si array. STARUPINFO is an input paramter to CreateProcess, so you must initialize it before passing it to CreateProcess.
I guess example.exe is running so fast that they are terminated before you come to look at them. If you printf a timing value like GetTickCount() after each CreateProcess, you will see that all CreateProcess calls occur very fast.

How do I pass an std::string environment block to CreateProcess?

I'm currently trying to use CreateProcess with the Path, Arguments and Environment Variables. My variables are stored in strings.
In the below example filePath and cmdArgs work fine, but I cannot get the envVars to work.
std::string filePath = "C:\\test\\DummyApp.exe";
std::string cmdArgs = "Arg1 Arg2 Arg3";
std::string envVars = "first=test\0second=jam\0"; // One
//LPTSTR testStr = "first=test\0second=jam\0"; // Two
CreateProcess(
LPTSTR(filePath.c_str()), //path and application name
LPTSTR(cmdArgs.c_str()), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance
0, // Creation flags
LPTSTR(envVars.c_str()), // environment block
//testStr //this line works
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
When I run this code the error that comes back is "error 87: The parameter is incorrect".
What I don't understand is that if I comment out the line labeled "one" and replace it with the line labeled "two" (and make the matching swap in the function call) then it works correctly.
The constructor of std::string you used will copy "first=test\0second=jam\0" until first \0 (C-style string).
To pass all the string use another constructor:
std::string envVars("first=test\0second=jam\0", 22);
^^^^^^^^^^^^^^^^^^^^^^^^ ^
|
22 characters -------+

C++ Get Program files dir, append extra path and execute

I'm trying to write a few simple lines of code that will get the 'Program Files' dir path on both XP and Vista/7 (on vista/7 I need the path to the x86 folder), add some extra path to an application and execute it.
This is what I have so far, but it's not executing the external program, not giving an error as well..
wchar_t localAppData[MAX_PATH];
STARTUPINFO sInfo;
PROCESS_INFORMATION pInfo;
SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, 0, NULL, localAppData);
std::wstringstream ss;
ss << localAppData << L"/MyApp/MyExe.exe";
LPCWSTR str = ss.str().c_str();
CreateProcess(str, NULL,NULL, NULL,FALSE,NULL,NULL,NULL,&sInfo,&pInfo);
return str;
I've updated my code to this according to hmjd's suggestion:
wchar_t localAppData[MAX_PATH];
STARTUPINFO sInfo = { sizeof(STARTUPINFO), NULL, L"winsta0\\default" };
PROCESS_INFORMATION pInfo;
SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, 0, NULL, localAppData);
std::wstringstream ss;
ss << localAppData << L"/PacificPoker/bin/888poker.exe";
std::wstring ss_str = ss.str();
wchar_t* path = new wchar_t[ss_str.length() + 1]();
std::copy(ss_str.begin(), ss_str.end(), path);
CreateProcess(path, NULL,NULL, NULL,FALSE,NULL,NULL,NULL,&sInfo,&pInfo);
delete[] path;
return GetLastError();
Still getting '3' for GetLastError, but I can confirm that C:\Program Files\MyApp\MyExe.exe exists..
A few problems with the code:
sInfo is not initialised, you at least need to set the cb member
STARTUPINFO sInfo = { sizeof(STARTUPINFO), // 'cb'
NULL, // 'lpReserved'
L"winsta0\\default" };// 'lpDesktop'
See STARTUPINFO for more details.
the first argument to CreateProcess() should be non-const, but is being passed c_str() which would be a const if it was not a dangling pointer. The ss.str() method returns a std::string, and the c_str() is returning a pointer into that std::string but it is a temporary object and is destroyed at the end of the expression (the ;), making str a dangling pointer. Change to:
std::wstring ss_str = ss.str();
wchar_t* path = new wchar_t[ss_str.length() + 1]();
std::copy(ss_str.begin(), ss_str.end(), path);
CreateProcess(path, ...);
delete[] path;
Check return values of all your functions and query GetLastError() to determine failure reason.
You've probably tried this, but does localAppData get a valid string if you set the CSIDL to CSIDL_PROGRAM_FILES instead of CSIDL_PROGRAM_FILESX86?
If you're testing under XP, I don't know what CSIDL_PROGRAM_FILESX86 will return.