I have a homebrew Win32 script interpreter called Wintasks which has worked perfectly for 15 years. It dispatches win32 executables using CreateProcess(). It still works fine on Win7-32, but under Win7-64 if any command line arguments are used when Wintasks is invoked the CreateProcess fails with error 998 (memory access violation). Remove the command line arguments from the Wintasks run and all is well.
Wintasks gets its first parameter (the task script to run) from the Winmain lpszCmdLine parameter, and having only that present works. Putting even a blank after the task script filename causes the problem. Parameters can be accessed successfully in all circumstances referencing __argv[].
When run from MSVC5 (as said, it is an old program) under debug everything works, so I cannot solve the problem that way, it is just the Release executable which gives the problem. Running exactly the same copy of Wintasks using exactly the same task script file from a Win32 VM shows no problem.
I would be grateful for any inspiration. I append the code from Wintasks which runs the CreateProcess.
DWORD WinExecX(LPSTR cmdline, int winstate)
{
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
BOOL CreateProcessStatus;
DWORD retval;
DWORD fdwCreate = 0; /* flags for CreateProcess */
memset(&StartupInfo,0,sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags=STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow=winstate;
openerr=0;
CreateProcessStatus = CreateProcess( NULL, /*(LPTSTR)name,*/
cmdline,
NULL,
NULL,
FALSE, /* changed 2000-10-18 */
fdwCreate,
NULL, /*envblk,*/
NULL,
&StartupInfo,
&ProcessInformation
);
if(!CreateProcessStatus)
{
openerr=GetLastError();
return 0;
}
elevel=retval = (DWORD)ProcessInformation.hProcess;
CloseHandle(ProcessInformation.hThread);
return retval;
}
Related
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);
After a successful call to CreateProcess, I am trying to get the path of the created process using GetModuleFileNameEx (lpApplicationName and lpCommandLine parameters can vary or be null so they aren't reliable in this case).
The problem is that GetModuleFileNameEx fails with error 6 (ERROR_INVALID_HANDLE), leaving its buffer with invalid data. I cannot understand the reason, since CreateProcess succeeds and process handle should have been saved correctly in pi.hProcess.
Hope you can shed some light, thanks in advance!
EDIT: An update: I noticed that removing the CREATE_SUSPENDED removes this problem too, but I need that flag set. How can I do?
// Defining GetModuleFileNameExA function
typedef DWORD (WINAPI *fGetModuleFileNameExA)
(
HANDLE hProcess,
HMODULE hModule,
LPSTR lpFilename,
DWORD nSize
);
//Load dinamically DLL function on program startup:
fGetModuleFileNameExA _GetModuleFileNameExA = (fGetModuleFileNameExA) GetProcAddress( LoadLibraryA("Psapi.dll"), "GetModuleFileNameExA");
// **** OTHER UNRELATED CODE HERE ****
PROCESS_INFORMATION pi;
//This call succeeds
if (!CreateProcessW( ApplicationName,
CommandLine,
NewProcess.lpProcessAttributes,
NewProcess.lpThreadAttributes,
NewProcess.bInheritHandles,
CREATE_SUSPENDED | CREATE_NEW_CONSOLE,
NULL,
CurrentDirectory,
&NewProcess.bufStartupInfo,
&pi)
) MessageBoxA(0, "Error creating process", "", 0);
char ProcessPath[MAX_PATH];
//Problem here: call fails with error 6
if (!_GetModuleFileNameExA(pi.hProcess, NULL, ProcessPath, MAX_PATH)) {GetLastError();}
//Invalid data is displayed
MessageBoxA(0, ProcessPath, "GetModuleFileNameEx",0);
From the CreateProcess documentation on MSDN:
Note that the function returns before the process has finished initialization. If a required DLL cannot be located or fails to initialize, the process is terminated. To get the termination status of a process, call GetExitCodeProcess.
...
The calling thread can use the WaitForInputIdle function to wait until the new process has finished its initialization and is waiting for user input with no input pending. This can be useful for synchronization between parent and child processes, because CreateProcess returns without waiting for the new process to finish its initialization. For example, the creating process would use WaitForInputIdle before trying to find a window associated with the new process.
Similar question
I have two application. I will just call them A and B. I have access to the code of application A.
I want to launch the applicaiton B and open a file in that application from A. I am using
CreateProcess method to acheive it. But the application B is not getting opened completely.
I am not sure whether there are any common dlls or other dependencies between these two applications.
The code that I use to open the applicaiton B form A is:
std::wstring appBPath(L"C:\\B.exe");
std::wstring filePath(L"C:\\file.pdf");
std::wstring cmdLineCommand = L"\"" + appBPath+ L"\" \"" + filePath + L"\"";
STARTUPINFO startupInfo;
PROCESS_INFORMATION processInfo;
// set the size of the structures
SecureZeroMemory( &startupInfo, sizeof(startupInfo) );
startupInfo.cb = sizeof(startupInfo);
SecureZeroMemory( &processInfo, sizeof(processInfo) );
CreateProcess( NULL, // No module name (use command line)
&cmdLineCommand[0], // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // New process has a new console
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&startupInfo, // Pointer to STARTUPINFO structure
&processInfo ); // Pointer to PROCESS_INFORMATION structure
The exe file starts to load but it gets stuck during the initialisation. But, the application B doesn't terminate.
It goes to some inconsistent state. This issue is happens only only for application A that's installed from it's installer.
It doesn't occur when I run the release or debug build. For the release and debug build we are using VC10. But for installer
build we use VC12. I am not sure on the compiler optimization that are there in place for the installer build.
The CreateProcess function returns success.
Thread state of the application B from process explorer:
I need to restart the program that im working on after an update has been downloaded except im running into some issues.
If i use CreateProcess nothing happens, if i use ShellExecute i get an 0xC0150002 error and if i use ShellExecute with the command "runas" it works fine. I can start the command prompt fine using CreateProcess and ShellExecute just not the same exe again and dont want to use runas as this will elevate the exe.
Any Ideas?
Windows 7, visual studio 2008 c++
alt text http://lodle.net/shell_error.jpg
CreateProcess:
char exePath[255];
GetModuleFileName(NULL, exePath, 255);
size_t exePathLen = strlen(exePath);
for (size_t x=exePathLen; x>0; x--)
{
if (exePath[x] == '\\')
break;
else
exePath[x] = '\0';
}
char name[255];
GetModuleFileName(NULL, name, 255);
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartupInfo = {0};
BOOL res = CreateProcess(name, "-wait", NULL, NULL, false, 0, NULL, exePath, &StartupInfo, &ProcInfo );
ShellExecute:
char exePath[255];
GetModuleFileName(NULL, exePath, 255);
size_t exePathLen = strlen(exePath);
for (size_t x=exePathLen; x>0; x--)
{
if (exePath[x] == '\\')
break;
else
exePath[x] = '\0';
}
char name[255];
GetModuleFileName(NULL, name, 255);
INT_PTR r = (INT_PTR)ShellExecute(NULL, "runas", name, "-wait", exePath, SW_SHOW);
CreateProcess() is an arcane beast. I remember unfondly my first frustrations with it. You should look at the Microsoft CreateProcess Example and the CreateProcess Page. (those links likely have a short lifetime, Googling CreateProcess should work just as well).
I can see 3 problems in your code.
StartupInfo must have "cb" set to the structure size:
STARTUPINFO StartupInfo = {0};
StartupInfo.cb = sizeof(StartupInfo);
The second argument requires both the command and the arguments to form the command line. Your program will see "-wait" as argv[0] and ignore it or pay it no mind.
char command[512];
sprintf(command, "%s -wait", name);
BOOL res = CreateProcess(name, command, // and as you had before
You don't look at GetLastError() if CreateProcess() fails (by returning a zero). It may have helped you but I suspect it would just say "invalid argument" or somesuch. Hey, there's only 10 of them to check, don't be lazy :-)
Another bug I committed is not closing the hProcess and/or hThread handles return in PROCESS_INFORMATION when I was done. I did do hProcess, but not hThread.
Looks like a manifest or registry question judging from the error code. If you can't get the actual error message string for more details, you might try:
moving every possible manifest file (Microsoft.VC80.CRT.manifest and the like) into your exe's directory, to ensure accessibility
cleanly and completely uninstall/wipe out old versions of DLL you may have installer newer versions of (I suggest: uninstall EVERY version, clean the registry with a sweep-clean tool such as Norton's, reinstall the new stuff from scratch)
What happens if you run the process using system()? It gives you less control, but you'll be running it from the same context you're running in. Also, Try monitoring the launch of your second process using ProcMon, it might give you the hint you need about the source of the failure.
Ok worked it all out in the end.
The first time my exe ran it used the default paths and as such loaded vld (a leak detector dll) from the default path. However in the exe i modified the dll path to be the bin folder ([app]\bin) when i restarted the exe using CreateProcess it picked up on a different vld dll (this was my mistake) that had incorrect side by side linkage and it was only after looking at event viewer that i worked it out.
Thanks for all your help.