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

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.

Related

.c_str() doesn't behave as expected

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.

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.

Error 12006 in WinHttpCrackUrl

I'm trying to build an address from a variable. So I can pass it to WinHttpOpenRequest.
char *uNameAddr = (char*) ExeBaseAddress + 0x34F01C;
printf("%s \n", uNameAddr);
string url = "http://xxxx.xxxx.com/xxxx/?u=";
string username = uNameAddr;
string combine = url + username;
cout << combine << endl;
//http://xxxx.xxxx.com/xxxx/?u=MyUsername <--
URL_COMPONENTS urlComp;
LPCWSTR pwszUrl1 = (LPCWSTR)combine.c_str();
DWORD dwUrlLen = 0;
Then I have to pass it here:
hRequest = WinHttpOpenRequest( hConnect, L"GET", urlComp.lpszUrlPath,
NULL, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
0);
urlComp.lpszUrlPath should be http://xxxx.xxxx.com/xxxx/?u=MyUsername
Any advice? My application crashes when it gets to process that part.
ERROR
12006 ERROR_INTERNET_UNRECOGNIZED_SCHEME
The URL scheme could not be recognized or is not supported.
LPCWSTR pwszUrl1 = (LPCWSTR)combine.c_str();
std::string::c_str returns const char *. LPCWSTR is const wchar_t *.
Casting to LPCWSTR is lying to the compiler and yourself, what combine.c_str() returns is not a pointer to a wide-character string.
You'll likely have better success with std::wstring, which represents wide-character strings.
Consider reading Unicode in the Windows API for more information.

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++ chars stored in the LPCSTR .. broken?

LPCSTR dllPath = ExePath().append("\\").append(DEF_INJECT_DLL).c_str();
DWORD dwBufSize = (DWORD)(strlen(dllPath) + 1) * sizeof(LPCSTR);
/* test */
char tbuf[1024]= {0,};
sprintf_s(tbuf, "dllPath : %s\r\ndwBufSize : %d", dllPath, dwBufSize);
MessageBoxA(NULL, tbuf, "TEST", MB_OK);
part of the code to inject my dll.
ExePath() is a function to get AbsolutePath of std::string data type using GetModuleFileNameA API and so on.
DEF_INJECT_DLL is defined by #define "MyDll.dll"
But when I run this code, it shows me broken strings....
And, when I change the MessageBoxA to this:
MessageBoxA(NULL,
ExePath().append("\\").append(DEF_INJECT_DLL).c_str(),
"TEST",
MB_OK);
it shows properly?
Also, I tried in this way:
MessageBoxA(NULL,dllPath, "TEST", MB_OK);
but it shows to me like first screenshot.
What is the problem?
The problem is in this line:
LPCSTR dllPath = ExePath().append("\\").append(DEF_INJECT_DLL).c_str();
here you call ExePath(), which returns a std::string instance, modify it, and finally call c_str() to get the raw data buffer.
However, the return value is a temporary object. After that line, the returned std::string is deleted, and will clean its memory. Therefore, the address where dllPath points to is no longer valid!
You could store the return value in a local instance, e.g.
std::string str = ExePath().append("\\").append(DEF_INJECT_DLL);
LPCSTR dllPath = str.c_str();