Insert (dynamic) command string in ShellExecute function in C++ - c++

Using C++ (on Windows 10), I'm trying to execute a command in cmd.exe that execute a python file that takes in another file (in csv format).
What I would like to do is the same thing as if I were typing on the command line something like this:
python3 .\plotCSV.py .\filetoplot.csv
Or in better mode:
python3 C:\...\Documents\plotCSV.py C:\...\Documents\filetoplot.csv
For this I'm using ShellExecute like this:
ShellExecute(NULL, "open", "C:\\Windows\\System32\\cmd.exe", "/c \"python3 C:\...\Documents\plotCSV.py C:\...\Documents\filetoplot.csv\"", NULL, SW_SHOWDEFAULT);
And for the csv file selected (filetoplot.csv for example) this works. Except that, for what I need, the name of the csv file is generate and changes every time in my C++ program and is saved in a variable file_name.c_str(). So, if I use this in ShellExecute I have:
ShellExecute(NULL, "open", "C:\\Windows\\System32\\cmd.exe", "/c \"python3 C:\...\Documents\plotCSV.py C:\...\Documents\file_name.c_str()\"", NULL, SW_SHOWDEFAULT);
But unfortunately (obviously) it doesn't work because there really isn't a csv file renamed "file_name.c_str()".
I found also the function ShellExecuteEx and wanting to repeat the same procedure I think the function should be used like this:
SHELLEXECUTEINFO info = {0};
info.cbSize = sizeof(SHELLEXECUTEINFO);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.hwnd = NULL;
info.lpVerb = NULL;
info.lpFile = "cmd.exe";
info.lpParameters = ("python3 C:\...\Documents\plotCSV.py C:\...\Documents\file_name.c_str()");
info.lpDirectory = NULL;
info.nShow = SW_SHOW;
info.hInstApp = NULL;
ShellExecuteEx(&info);
But even here it doesn't work (I probably misinterpret how the function works).
Hoping I have explained myself well, I kindly ask for your advice on how to proceed in this regard.
Thank you very much

You are trying to write code inside a string literal.
That is not possible in C++!
You need to first create your dynamic parameter string, then pass it to a function.
std::string has an overloaded + operator that supports string literals (const char *).
std::string param1 = "/c \"python3 C:\\...\\Documents\\plotCSV.py C:\\...\\Documents\\" + file_name + '\"';
ShellExecute(NULL, "open", "C:\\Windows\\System32\\cmd.exe", param1.c_str(), NULL, SW_SHOWDEFAULT);

Related

CreateProcess() works in cmd but if executed in cygwin

i'm working on a c++ project on windows, and i want to use cygwin not as a target environment, but just as a shell for replacing cmd, my project is actually compiled for windows with mingw.
My program for now just execute a CreateProcess, the problem is that if i call it from the cmd it works, if i execute the program from cygwin it does nothing while still the CreateProcess() is returning true.
That's the code:
int exec_module(const string& name, HANDLE stdIn, HANDLE stdOut, HANDLE stdErr) {
STARTUPINFO child_sinfo = { sizeof(child_sinfo) };
PROCESS_INFORMATION child_pinfo;
ZeroMemory(&child_sinfo, sizeof(child_sinfo));
child_sinfo.cb = sizeof(child_sinfo);
ZeroMemory(&child_pinfo, sizeof(child_pinfo));
child_sinfo.dwFlags = STARTF_USESTDHANDLES;
child_sinfo.hStdInput = stdIn;
child_sinfo.hStdOutput = stdOut;
child_sinfo.hStdError = stdErr;
return CreateProcess(0, _T((char *) name.c_str()), 0, 0, false,0, 0, 0, &child_sinfo, &child_pinfo);
}
And i call it like this:
if (!exec_module("caccone.exe", GetStdHandle(STD_INPUT_HANDLE), GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE))) {
cout << "Errore durante l'esecuzione del modulo" << endl;
}
I just can't figure out what's the problem.
My Bad here, to work well in cygwin i had to add synchronization code between parent and child, but the cmd shell worked also in a naife way.
Thx Harry Johnston for the point on the _T macro.

C++ Downloading a file to appdata

I'm trying to download a file to
char* appdata = getenv("APPDATA");
lpURLDownloadToFile URLDownloadToFile;
HMODULE hUrlmon = LoadLibrary("URLMON.DLL");
URLDownloadToFile = (lpURLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
URLDownloadToFile(0, "http://example.com/test.zip",appdata+"test.zip", 0, 0);
I don't have an error when I write the path manually,But ı got error when I try to use appdata+"test.zip"
How can I do it. Thanks.
You cannot combine char* pointers by adding them like so, you need to concatenate those strings. You might want to use std:string instead which support operator+=.
std::string appdata(getenv("APPDATA"));
appdata += "test.zip";
HMODULE hUrlmon = LoadLibrary("URLMON.DLL");
URLDownloadToFile = (lpURLDownloadToFile)GetProcAddress(hUrlmon, "URLDownloadToFileA");
URLDownloadToFile(0, "http://example.com/test.zip", appdata.c_str(), 0, 0);

Executing dos commands with Borland C++ and saving output

New to C++.
I have been looking most of the afternoon, does anyone know a simple way to execute DOS commands and save to a variable for a windows forms application?
You can use system("dir"); . This will bring up the command prompt and run the dir command.
Alternatively you can use WinExec.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687393(v=vs.85).aspx
You can make the command to redirect to a text file, and read off of it.
bool execDosCommand(char *command, AnsiString &output)
{
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa,sizeof(SECURITY_ATTRIBUTES));
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle=true;
sa.lpSecurityDescriptor=NULL;
HANDLE ReadPipeHandle;
HANDLE WritePipeHandle; // not used here
if(!CreatePipe(&ReadPipeHandle, &WritePipeHandle, &sa, 0))
return false;
STARTUPINFOA si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb=sizeof(STARTUPINFO);
si.dwFlags=STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow=SW_HIDE;
si.hStdOutput=WritePipeHandle;
si.hStdError=WritePipeHandle;
PROCESS_INFORMATION pi;
ZeroMemory(&pi,sizeof(PROCESS_INFORMATION));
text cmd;
cmd.print("/c %s", command);
char pathbuf[MAX_PATH];
_searchenv("CMD.EXE", "PATH", pathbuf);
if(!CreateProcessA(pathbuf, cmd.t_str(), NULL, NULL, true, 0, NULL, NULL, &si, &pi))
return false;
char Data[1024];
for (;;)
{
DWORD BytesRead;
DWORD TotalBytes;
DWORD BytesLeft;
if(!PeekNamedPipe(ReadPipeHandle,Data,sizeof(Data),&BytesRead, &TotalBytes,&BytesLeft)) return false;
if(BytesRead)
{
if(!ReadFile(ReadPipeHandle,Data,sizeof(Data)-1,&BytesRead,NULL))
return false;
Data[BytesRead]='\0';
output += Data;
}
else
{
if(WaitForSingleObject(pi.hProcess,0)==WAIT_OBJECT_0)
break;
}
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(ReadPipeHandle);
CloseHandle(WritePipeHandle);
return true;
}
for example: execDosCommand("dir C:\", output);
as roymustang mentioned, you can use the system command to execute another command from the system. This could be a batch script for example that pipes the output of the command into a text file. You can then read the text file to actually get the information.
The Problem you have with "returning" the command output is, how does the command output look like? In what data structure would you store it? Most of the time you'll get a bunch of unformatted text, which can't be parsed to easily so there is no real generic way to return the output of an application or script into a C++ data structure.
You might as well want to take a look here:
http://docwiki.embarcadero.com/RADStudio/en/System,_wsystem
Like described above I don't believe there is a way to return the output of an application call to your program, at least none that I ever heard about.
Greets,
Florian

CreateProcess from a process started from a `cmd` file

I'm attempting to launch a process from a different process. The mechanism in which this is achieved is not subject to change. Both the launcher and the original process are located in C:\dir.
I'm starting my launcher from a cmd file. The cmd file itself is located somewhere else, and in order for it to find the launcher executable, I'm setting the PATH variable:
set PATH=C:\dir;%PATH%;
launcher.exe
The launcher starts the child process with the following code:
STARTUPINFO startupInfo;
startupInfo.cb = sizeof (STARTUPINFO);
startupInfo.lpReserved = 0;
startupInfo.lpDesktop = NULL;
startupInfo.lpTitle = NULL;
startupInfo.dwX = 0;
startupInfo.dwY = 0;
startupInfo.dwXSize = 0;
startupInfo.dwYSize = 0;
startupInfo.dwXCountChars = 0;
startupInfo.dwYCountChars = 0;
startupInfo.dwFillAttribute = 0;
startupInfo.dwFlags = _showInForeground ? STARTF_USESHOWWINDOW : 0;
startupInfo.wShowWindow = _showInForeground ? 1 : 0;
startupInfo.cbReserved2 = 0;
startupInfo.lpReserved2 = 0;
PROCESS_INFORMATION processInfo;
BOOL retVal = CreateProcess("child.exe", "", NULL, NULL, FALSE,
_showInForeground ? (CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE) : CREATE_DEFAULT_ERROR_MODE,
NULL, NULL, &startupInfo,&processInfo);
It returns 0 and last error is 2, which is File not found.
If it helps, GetCurrentDirectory returns the directory where the cmd is located, not C:\dir. I'm guessing CreateProcess can't find child.exe because PATH is not available to it.
Any ideas how to get this to work?
EDIT: Some good comments with answers (as comments are sometimes overlooked):
Suggestion: set statupInfo.lpDirectory to "c:\dir"
Answer: can't. I'm starting from the cmd because the directory may change.
According to MSDN, CreateProcess actually does search PATH, but only if lpApplicationName is NULL and the executable is the first token in lpCommandLine.
In other words it should work if you call CreateProcess(NULL, "child.exe", ...
I haven't tried it though, so YMMV and so on.
The launcher process can get its own path using GetModuleFileName() that it can use to create the full path to the 2nd executable. There is no need to change the Path environment variable or change the working directoy.

How do you run external programs with parameters without the cmd window showing up in Windows?

I just asked a question earlier today because I wanted to run an executable file that takes parameters from my C++ code and it wasn't working.
It works now, but I'm still having problems since I thought I was going the right way about this, but it seems like what I want to accomplish can't be done the way I'm approaching it...
This is my corrected code from my other question:
#include <stdlib.h>
#include <conio.h>
int main (){
system("\"\"C:\\Users\\Adam\\Desktop\\pdftotext\" -layout \"C:\\Users\\Adam\\Desktop\\week 4.pdf\"\"");
_getch();
}
which is me running "pdftotext -layout myfile.pdf" as if I was running it from a CMD window.
The thing is, I don't actually want the cmd to show up since I have a GUI interface on top of it and I want to display a nicer progress bar instead of seeing the windows pop-up for every file I need to parse.
I looked around and either I don't understand what I'm reading since I'm relatively new to C++, or I just didn't find what I was looking for. I found that using CreateProcess, I should be able to do this, but after copying some code I found somewhere else, the cmd window pops-up anyway.
I'd like it if someone could give me the name of a function I could use to accomplish something like this or if someone could give some example code for this small case in the code I posted since I'm not sure I understand everything as I should, being new to C++ and all.
Edit: As requested in a comment, the code for CreateProcess that I tried is what I found at this url:
http://www.goffconcepts.com/techarticles/development/cpp/createprocess.html
Which is (with my own parameters that I think should go there):
#include <windows.h>
#include <string>
#include <conio.h>
size_t ExecuteProcess(std::wstring FullPathToExe, std::wstring Parameters, size_t SecondsToWait)
{
size_t iMyCounter = 0, iReturnVal = 0, iPos = 0;
DWORD dwExitCode = 0;
std::wstring sTempStr = L"";
/* - NOTE - You should check here to see if the exe even exists */
/* Add a space to the beginning of the Parameters */
if (Parameters.size() != 0)
{
if (Parameters[0] != L' ')
{
Parameters.insert(0,L" ");
}
}
/* The first parameter needs to be the exe itself */
sTempStr = FullPathToExe;
iPos = sTempStr.find_last_of(L"\\");
sTempStr.erase(0, iPos +1);
Parameters = sTempStr.append(Parameters);
/* CreateProcessW can modify Parameters thus we allocate needed memory */
wchar_t * pwszParam = new wchar_t[Parameters.size() + 1];
if (pwszParam == 0)
{
return 1;
}
const wchar_t* pchrTemp = Parameters.c_str();
wcscpy_s(pwszParam, Parameters.size() + 1, pchrTemp);
/* CreateProcess API initialization */
STARTUPINFOW siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if (CreateProcessW(const_cast<LPCWSTR>(FullPathToExe.c_str()),
pwszParam, 0, 0, false,
CREATE_DEFAULT_ERROR_MODE, 0, 0,
&siStartupInfo, &piProcessInfo) != false)
{
/* Watch the process. */
dwExitCode = WaitForSingleObject(piProcessInfo.hProcess, (SecondsToWait * 1000));
}
else
{
/* CreateProcess failed */
iReturnVal = GetLastError();
}
/* Free memory */
delete[]pwszParam;
pwszParam = 0;
/* Release handles */
CloseHandle(piProcessInfo.hProcess);
CloseHandle(piProcessInfo.hThread);
return iReturnVal;
}
int main(void){
ExecuteProcess(L"C:\\Users\\Adam\\Desktop\\pdftotext", L"-layout \"C:\\Users\\Adam\\Desktop\\week 4.pdf\"", 0);
_getch();
}
I'm a little bit overwhelmed since it uses some things I've never used before, but I think I understand the core (keyword: think). It doesn't solve my problem, though, because the cmd shows up and by retesting it I actually noticed that the cmd doesn't even run the .exe and doesn't give me an error message.
I hope this bit of code helps... I didn't want to look into it further since it seemed like I wasn't even going in the right direction.
Use CreateProcess instead of system.
--EDIT--
the code for CreateProcess that I tried is what I found at this url:
The code is a mess, I'd advise to avoid that url in future.
At the end of "CreateProcess" article is a link named "Creating Processes", which contains simpler example that is easier to read. Use it as a starting point.
After adding the following lines for siStartupInfo, cmd window won't pop up any more with my own test *.exe.
siStartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
siStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
siStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartupInfo.wShowWindow = SW_HIDE;
But I have another problem. As I try to run some other executable, whose command line would be
TEST.exe <input-file> output-file
in a cmd window, WaitForSingleObject() return 258, and GetLastError() return 1813 ("The specified resource type cannot be found in the image file.").
See system() and CreateProcess() / CreateProcessW() for more details.
Any ideas would be highly appreciated!
The only way I found how to execute an external program is:
system("start C:\file path\ file");
The only problem is that the file or directory can't have spaces.