I want to use CreateProcess() to execute an ADB command that launches an application/activity. It works using the simpler system(command) function, but I want to eliminate the creation of the command line window by system(). Below is what I have right now. I have tried using different CreateProcess()es, like CreateProcessW and CreateProcessA, but to no avail.
char prog[] = "C:\\Program Files\\Android\\sdk\\platform-tools\\platform-tools\\adb.exe";
char args[] = "adb shell am start -a android.intent.action.MAIN -n com.example.dmiller.myapplication/.Blankscreen";
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcess((LPCWSTR)prog, (LPWSTR)args, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
I got this partially from this answer How to use createprocess to execute adb program in PATH? but when the command should be executed in my program, nothing happens (when running system(cmd.c_str()), the appropriate app is launched on the attached device). Could anyone provide some help? Alternative methods are welcome as well.
UPDATE: I have applied some things from the below post to try to provide a better question. Code has been updated to my latest version.
CreateProcess doesn't pass command line arguments
Type-casting a char array to have type LPCWSTR doesn't make it so. The former is an array of one-byte characters. The latter is (a pointer to) an array of two-byte characters. The type cast tells the compiler that the one is really the other, and the compiler trusts you. It does not perform a conversion.
To fix this, declare prog and args to have type WCHAR or wchar_t instead of char, and use the L prefix on the literals:
WCHAR prog[] = L"...";
WCHAR args[] = L"...";
Then you can remove the LPCWSTR type casts because they won't be necessary anymore.
As usual, whenever you call an API function, it's wise to check the return value and look for any error codes. The documentation advises that you should check whether the function returns zero. If it does, call GetLastError to find out what the OS thinks the problem was.
Related
So, on Windows, it seems execl() is broken in different ways across different versions of windows. I've spent a lot of time narrowing it down and debugging, and it doesn't really make sense, and I can only think there's something wrong with Microsoft's implementation of execl() (actually execlp() for me).
The only Windows version execlp() seems to work correctly on is 7.
On Windows 10, it works fine, until I compile with -mwindows in MinGW.
Then it just makes my program terminate with a zero exit code.
On Windows XP, it interprets spaces in argument parameters as separate arguments, despite the actual number of arguments being clearly specified by the nature of the function's prototype...
So, looks like I'll have to use some Windows native function and wrap it in "#ifdef WIN32"s.
What I really need is execl() (execlp isn't necessary) like behavior on Windows, in that it replaces the current process image with a new one, and keeps network descriptors open like execl() will.
I'm really at a loss as to what good options are, and while CreateProcess seems somewhat able to do some of it, I can't find enough info on what I'm trying to do.
Any help is appreciated. Thanks!
Unfortunately, you will need a completely different code path on windows, because in the win32 subsystem, creating a process is coupled together with loading and running a new image in a single call: CreateProcess().
In a typical posix scenario, you would fork() your new process, set up things like e.g. file descriptors and then exec*() the new binary. To achieve something similar in Windows, you must rely on the possibilities you get from CreateProcess(). For open files (or sockets), win32 uses "handles" and these can be marked inheritable, for example I do the following for a pipe:
HANDLE pin, pout;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = 1;
if (!CreatePipe(&pin, &pout, &sa, 0))
{
fprintf(stderr, "Error creating pipe: %lu\n", GetLastError());
return;
}
This way, the pipe's handles are inheritable. Then, when calling CreateProcess(), by passing 1 (or TRUE) for bInheritHandles, the new process inherits all handles marked this way. In this example, I do the following:
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.hStdInput = nul;
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
si.dwFlags |= STARTF_USESTDHANDLES;
// simple example, my production code looks different, e.g. quoting the command:
char cmdline[1024];
strcpy(cmdline, exename);
strcat(cmdline, " ");
snprintf(cmdline + strlen(cmdline), 1024 - strlen(cmdline), "%" PRIxPTR, (uintptr_t)pout);
// [...]
PROCESS_INFORMATION pi;
CreateProcess(0, cmdline, 0, 0, 1, 0, 0, 0, &si, &pi);
In the child, the pipe could be used like that:
uintptr_t testPipeHandleValue;
if (sscanf(argv[1], "%" SCNxPTR, &testPipeHandleValue) != 1)
{
exit(EXIT_FAILURE);
}
int testPipeFd = _open_osfhandle(
(intptr_t)testPipeHandleValue, _O_APPEND | _O_WRONLY);
FILE *testPipe = _fdopen(testPipeFd, "a");
setvbuf(testPipe, 0, _IONBF, 0);
Of course this will look different for a network socket, but I hope the general idea helps.
I need help with my simple program, which tries to create a new process running Speech recognition.
When I open cmd and type in the command C:\Windows\Speech\Common\sapisvr.exe -SpeechUX then the speech recognition would successfully start. It will start even when running through system(C:\\Windows\\...) which basically just mimics cmd.
However, when creating the new process with CreateProcess() as below, the function fails. If I put whole path and argument into second parameter CreateProcess(NULL, TEXT("C:\\Windows...\\sapisvr.exe -SpeechUX"), ...), then I get a runtime exception: Access violation writing location
#include <windows.h>
int main()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(
TEXT("C:\\Windows\\Speech\\Common\\sapisvr.exe"), //Module name
TEXT(" -SpeechUX"), //command line params
NULL, //Process attributes
NULL, //Thread attributes
FALSE, //Handle inheritance
0, //No creation flags
NULL, //Use parent's environment
NULL, //Use parent's starting directory
&si, //Pointer to STARTUPINFO structure
&pi )) //Pointer to PROCESS_INFORMATION structure
{
printf("error creating process\n");
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
First I tried to test CreateProcess function with running notepad with an argument to open an existing file. When I put path to notepad.exe to first parameter and name of the file to the command line parameter, it didn't recognise it and opened a new file instead.
This whole applies as well to trying to run msconfig.exe from my program, which doesn't take any parameters, so I guess the problem is somewhere else, I just have no idea where.
I searched the web and none of the answers actually worked for me. I'm working in Visual Studio 2015 on Windows 8.1.
Thanks for help.
The CreateProcess function has a second argument as an LPTSTR. For the CreateProcessW version of this function, this must be a writeable buffer, not a string literal. Thus your program's behavior is undefined. Since you are getting an access violation writing to a location when calling CreateProcess, we will assume that CreateProcess is being mapped to CreateProcessW.
At the link posted, here is the quote:
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.
So the fix is simply define an array, not a literal:
TCHAR commandParam[] = TEXT(" -SpeechUX");
if (!CreateProcess(TEXT("C:\\Windows\\Speech\\Common\\sapisvr.exe"),
commandParam,
...
}
or if passing NULL as the first argument:
TCHAR commandParam[] = TEXT("C:\\Windows\\Speech\\Common\\sapisvr.exe");
//...
if (!CreateProcess(NULL, commandParam, ...
Also, if CreateProcess returns an error, you should call GetLastError and optionally FormatMessage, to get the error that occurred, and not simply output that there is an error.
When I call CreateProcess in Windows, the new process doesn't seem to inherit the console of the calling process. I made a test program that runs "ruby xtest", xtest being a script that writes "hello" to standard output. I ran this test program from Emacs, and get no output. I also tried the following code calling GetStdHandle, but again, no output. Then I tried passing CREATE_NEW_CONSOLE in dwCreationFlags to CreateProcess, which made a whole new window with the Ruby output. Finally, I made a simple fork/exec
test program and compiled it using Cygwin's GCC. This program worked: the Ruby output showed up in Emacs as expected. I tried to decipher the Cygwin source code in http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/spawn.cc?rev=1.268&content-type=text/x-cvsweb-markup&cvsroot=src but failed. So, how do you make the new process inherit the console of the parent process such that the output from the child shows up as expected?
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess(0, "ruby xtest", 0, 0, 1, 0, 0, 0, &si, &pi)) die("CreateProcess");
I know, this thread is rather old, however, I just ran into the same problem.
Just as for the TS, the console handle was inherited and working fine under Cygwin, but not on a Windows console. Instead, the output on stdout was neither shown, nor any error was reported. Inherited Pipe handles worked still fine.
I took me some time to identify the (now obvious) problem: CreateProcess() was called with CREATE_NO_WINDOW. Dropping this flag, console output is fine. (Though, according to the code of the TS, they never set this flag in the first place.)
Hope this might be helpful for people who also stumble across this thread, like myself.
According to Microsoft documentation, lpCommandLine (2. parameter):
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.
When I stopped using a constant here it worked for me. I didn't need the STARTF_USESTDHANDLES and GetStdHandle thing.
This code from a console prg runs and outputs another console exe in the same console:
FillChar(SI, SizeOf(SI), 0);
SI.cb:=SizeOf(SI);
FillChar(PI, SizeOf(PI), 0);
if CreateProcess(nil, CmdLineVar, nil, nil, False, 0, nil, nil, SI, PI) then ...
I've done this by passing in pipes for hStdInput, hStdOutput, and hStdError and manually routing data from the hStdOutput and hStdError pipes to the console.
Not sure if debeige ever solved this, but I needed the same thing, but starting up another thread to listen to stdout output, just to put it on stdout seemed nuts to me.
The following works for me, and is slightly different than what he originally posted. I thought at first it wouldn't work if you don't set si.cb, but when I commented that in mine, it still worked, so... YMMV.
STARTUPINFO siStartInfo;
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdInput = g_hChildStd_IN_Rd; // my outgoing pipe
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(
NULL,
szCmdline,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&siStartInfo,
&piProcInfo);
I'm trying to create a function which opens a PDF in firefox separate from the main process. I believe I am having trouble with the parameters for createProcess... any help is greatly appreciated
EDIT: the batch file is being created, I have tested it several times, and to explain a bit:
The batch file is because I really don't know what I am doing, I am a student in computer science and this is a side project to help me at my job. I work at a law office and file the mail electronically as it comes in. I wanted to make a simple program that would loop through the scans directory, display the scan and prompt the user for information about the document. Therefore I need to be able to build a file path dynamically. Originally I was using "system" to open firefox and display the document. After a bit of trying I got it to work with a batch file. I then learned that system is a blocking command and that I would need to start a separate thread. This is where I ran into createprocess. I simply continued to use the batch file from my old system idea... And the more I think about it I can't remember which professor suggested the batch file or why...
void openPDF(char scansQueue[][MAX_NAME], int index)
{
// build bat file
fstream outfile;
outfile.open("C:\\firefox.bat");
if(outfile.good())cout<<"outfile good!!!!"<<endl;
outfile<<"\"C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe\" \"C:\\Scans\\" <<scansQueue[index]<<"\"";
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if(!CreateProcess(NULL, L"C:\\firefox", NULL, NULL, false, 0, NULL, NULL, &si, &pi))cout<<"PROCESS FAILED TO EXECUTE!!!";
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
There are several problems with this code. A few has already been pointed out in the comments (closing potentially invalid handles on failure, possibility that the batch file can't be created, and the rather questionable command line). Here's a few more issues.
First, you can't run a batch file this way.
The documentation for CreateProcess clearly states:
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.
Second, you are passing a string literal for lpCommandLine, something that's also explicitly outlawed by the documentation:
lpCommandLine [in, out, optional]
...
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.
Finally, why are you creating a temporary batch file to run a single command? You could easily have written the CreateProcess call to start Firefox directly.
What's the easiest way to duplicate the current process, to spawn another instance in Windows? I know Linux has fork(), but all I need is to run main in the same process again, probably using CreateProcess() with the correct arguments.
As #DavidHeffernan commented:
STARTUPINFO si;
::memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
::CreateProcess(NULL, ::GetCommandLine(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
Cygwin implements fork() within its managed environment, but even that is an intricate square dance in getting the child to catch up with the parent to accurately replicate POSIX behavior.
It seems like you don't need to emulate fork(), but fork()/exec(). For that, gathering the environment variables, program parameters and passing them to CreateProcess() should be enough. There are options to copy the file descriptors to the child too. See CreateProcess's documentation.