I am building a program that would open Sublime text and after a period of time would close the application itself . I can't figure out how to close the application using the existing code .
This is what I have so far :
STARTUPINFO siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
if (CreateProcess(L"C:\\Program Files\\Sublime Text 2\\sublime_text.exe",
L" source.cpp",
NULL,
NULL,
FALSE,
CREATE_DEFAULT_ERROR_MODE,
NULL,
NULL,
&siStartupInfo,
&piProcessInfo) == FALSE)
WaitForSingleObject(piProcessInfo.hProcess, INFINITE);
::CloseHandle(piProcessInfo.hThread);
::CloseHandle(piProcessInfo.hProcess);
First, you are calling WaitForSingleObject() and CloseHandle() if CreateProcess() fails, which is useless. Don't call those functions unless it succeeds instead.
Second, you are calling the Unicode version of CreateProcess(), which has a caveat that your code is not handling. Per the CreateProcess() documentation:
lpCommandLine [in, out, optional]
The command line to be executed. The maximum length of this string is 32,768 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.
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.
Third, if you want to terminate the process after a timeout, you could use TerminateProcess(), but that is brute force and should be avoided when possible. Sublime has a UI, so the preferred solution is to ask the UI to close itself down and then wait for it to do so, as documented on MSDN:
How To Terminate an Application "Cleanly" in Win32.
If you absolutely must shut down a process, follow these steps:
Post a WM_CLOSE to all Top-Level windows owned by the process that you want to shut down. Many Windows applications respond to this message by shutting down.
NOTE: A console application's response to WM_CLOSE depends on whether or not it has installed a control handler.
Use EnumWindows() to find the handles to your target windows. In your callback function, check to see if the windows' process ID matches the process you want to shut down. You can do this by calling GetWindowThreadProcessId(). Once you have established a match, use PostMessage() or SendMessageTimeout() to post the WM_CLOSE message to the window.
Use WaitForSingleObject() to wait for the handle of the process. Make sure you wait with a timeout value, because there are many situations in which the WM_CLOSE will not shut down the application. Remember to make the timeout long enough (either with WaitForSingleObject(), or with SendMessageTimeout()) so that a user can respond to any dialog boxes that were created in response to the WM_CLOSE message.
If the return value is WAIT_OBJECT_0, then the application closed itself down cleanly. If the return value is WAIT_TIMEOUT, then you must use TerminateProcess() to shutdown the application.
NOTE: If you are getting a return value from WaitForSingleObject() other then WAIT_OBJECT_0 or WAIT_TIMEOUT, use GetLastError() to determine the cause.
By following these steps, you give the application the best possible chance to shutdown cleanly (aside from IPC or user-intervention).
With that said, try something more like this:
BOOL CALLBACK SendWMCloseMsg(HWND hwnd, LPARAM lParam)
{
DWORD dwProcessId = 0;
GetWindowThreadProcessId(hwnd, &dwProcessId);
if (dwProcessId == lParam)
SendMessageTimeout(hwnd, WM_CLOSE, 0, 0, SMTO_ABORTIFHUNG, 30000, NULL);
return TRUE;
}
...
STARTUPINFO siStartupInfo;
PROCESS_INFORMATION piProcessInfo;
memset(&siStartupInfo, 0, sizeof(siStartupInfo));
memset(&piProcessInfo, 0, sizeof(piProcessInfo));
siStartupInfo.cb = sizeof(siStartupInfo);
WCHAR szFilename[] = L"C:\\full path to\\source.cpp";
if (CreateProcess(L"C:\\Program Files\\Sublime Text 2\\sublime_text.exe",
szFileName,
NULL,
NULL,
FALSE,
CREATE_DEFAULT_ERROR_MODE,
NULL,
NULL,
&siStartupInfo,
&piProcessInfo))
{
CloseHandle(piProcessInfo.hThread);
WaitForInputIdle(piProcessInfo.hProcess, INFINITE);
if (WaitForSingleObject(piProcessInfo.hProcess, SomeTimeoutHere) == WAIT_TIMEOUT)
{
EnumWindows(&SendWMCloseMsg, piProcessInfo.dwProcessId);
if (WaitForSingleObject(piProcessInfo.hProcess, AnotherTimeoutHere) == WAIT_TIMEOUT)
{
// application did not close in a timely manner, do something...
// in this example, just kill it. In a real world
// app, you should ask the user what to do...
TerminateProcess(piProcessInfo.hProcess, 0);
}
}
CloseHandle(piProcessInfo.hProcess);
}
Related
The Command CreateProcess With the command WaitForSingleObject
can to open an image?
If Yes How can I open the image?
I tried to open but i don't know Where to put the path to open
if (CreateProcess(NULL, "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Paint.lnk", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
If you just want to open an existing image using defualt app then use ShellExectue API. For example:
ShellExecuteW(NULL, L"open", L"Z:\\cat.PNG", NULL, NULL, SW_SHOW);
You could also open image with mspaint using the same API:
ShellExecuteW(NULL, L"open", L"C:\\Windows\\system32\\mspaint.exe", L"Z:\\cat.PNG", NULL, SW_SHOW);
ShellExecuteEx will let you wait for finishing process.
You can do the same using CreateProcess. As #DavidHeffernan pointed out the second parameter of CreateProcess should point to writable memory else it will raise access violation. To make it clear I will just omit the first parameter. Example:
STARTUPINFOW process_startup_info{ 0 };
process_startup_info.cb = sizeof(process_startup_info); // setup size of strcture in bytes
PROCESS_INFORMATION process_info{ 0 };
wchar_t commandline_args[] = L"\"C:\\Windows\\system32\\mspaint.exe\" Z:\\cat.PNG";
if (CreateProcessW(NULL, commandline_args, NULL, NULL, TRUE, 0, NULL, NULL, &process_startup_info, &process_info))
{
//WaitForSingleObject(process_info.hProcess, INFINITE); // uncomment to wait till process finish
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
}
I strongly recommend to read this CodeProject article (A newbie's elementary guide to spawning processes).
The CreateProcess function can only start .exe and .bat files. Use ShellExecute[Ex] if you want to launch files the same way the shell/Explorer does.
A process must have a PE EXE file as its main file and if you ask CreateProcess to start something else it is just going to fail. ShellExecute will look up the file extension in the registry to find the correct executable (or COM handler) to execute and this often ends up calling CreateProcess on your behalf with the file you specified as a command line parameter.
Using ShellExecuteEx with the SEE_MASK_NOCLOSEPROCESS flag might might give you a process handle you can wait on but you also have to be prepared for hProcess being NULL. This can happen if the registered application for the file type is already running and DDE or COM was used to open the file in this existing application instance...
So, curious problem, I'm trying to create a process, and then resume it, mostly exploring the Windows API. I've noticed that if I do this:
system("C:\\Windows\\System32\\calc.exe");
It will open a calculator exe, however if I try to do the same thing using CreateProcessA, I get this:
STARTUPINFO starting_info;
PROCESS_INFORMATION process_info;
// let's try and make a process
if (!CreateProcessA(NULL, "C:\\Windows\\System32\\calc.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &starting_info, &process_info)) {
return;
}
// resume thread
NtResumeThread(process_info.hThread, NULL);
This for some reason throws an error of 0xc0000142 most of the times when it "creates" the process, else it just fails.
What's going on?
See the following MSDN sample code for creating a process:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx
You need to zero out the si and pi structs, also set
si.cb = sizeof(si);
In the end, close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
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've coded two processes using C++. One is the GUI process that is called by my console app using CreateProcess API. I need to pass text from the GUI app (child) to the console app (parent.) The amount of text could be arbitrary -- from a few lines to KBs of text.
What is the easiest way to do it?
PS. I have access to the source code of both processes.
Console application can create a WinAPI window (non-visible), such that it can receive messages (idea taken from AllocateHWND function in Delphi).
Another solution is to use named pipes.
Another solution is to send data locally via TCP/IP.
If these strings are only a debug ones, consider using OutputDebugString function from WinAPI and capturing them with a program like SysInternals' DbgView.
If the GUI application is truly graphical only, you don't really use the standard output stream (i.e. std::cout). This can the be reused for output to your console application.
First you need to create an anonymous pipe using CreatePipe:
HANDLE hPipeRead;
HANDLE hPipeWrite;
CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0);
Now you have to handles that can be used as a normal file handle; One to read from and the other to write to. The write-handle should be set as the standard output for the new process you create:
STARTUPINFO startupInfo = { 0 };
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdOutput = hPipeWrite; // Standard output of the new process
// is set to the write end of the pipe
CreateProcess(
lpApplicationName,
lpCommandLine,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&startupInfo, // Use our startup information
&processInfo);
Now whenever the child process needs to write to the parent, it only have to use standard output:
std::cout << "In child process, are you getting this parent?";
The parent uses ReadFile to read from the read-end of the pipe:
char buffer[256];
DWORD bytesRead = 0;
ReadFile(hPipeRead, buffer, sizeof(buffer), &bytesRead, NULL);
Note: I haven't done WIN32 programming in some time, so might be wrong on some details. But should hopefully be enough to get you started.
There are of course many other ways if Inter Process Communications (IPC), including (but not limited to) sockets, files, shared memory, etc.
The easy way is probably to make the child actually a console application, even though it also creates windows.
In that case, you can have your parent spawn the child using _popen, and the child can just write the output to its normal stdout/std::cout. _popen returns a FILE *, so the parent can read the child's output about like it'd normally read a file (well, normally for C anyway).
Various methods can be used, some of them were given above. Which one is simplest depends on you task.
I can also suggest you filemapping technics which is widely used in IPC, and for ex. dll are implemented using filemapping.
It allows mutliply processes to share the same resources simultaniously, access is random not consequntial.
Here are main steps of implementation:
1. Process A creates a file;
2. Process A creates a named system object mappedfile to the file (mappedfile allocates memory);
3. Process A creates a system object a viewOfMapped file(this allows to map some area of the process A to the pages in the main memory which were allocated by mappedFile);
4. Process B creates the named system object mappedfile(name should be similar to the one of process A used), viewOfMapped file;
5. By pointers returned by viewOfMapped processes can share the same memory.
Example:
Process A:
/* 1. file creation */
hFile = CreateFile(0, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
/* 2. Create file mapping */
wchar_t lpName[] = L"fileMappingObject0";
HANDLE hfileMappingObject;
hfileMappingObject = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024, lpName);
/* 3. Create MappedFileViewOfFile */
void* p = (MapViewOfFile(hfileMappingObject, FILE_MAP_ALL_ACCESS, 0, 0, 0));
Process B:
/* 2. Create file mapping */
wchar_t lpName[] = L"fileMappingObject0";
HANDLE hfileMappingObject;
hfileMappingObject = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 1024, lpName);
/* 3. Create MappedFileViewOfFile */
void* p = (MapViewOfFile(hfileMappingObject, FILE_MAP_ALL_ACCESS, 0, 0, 0));
This method is rather simple and also powerfull.
I am using CreateProcess() to run an external console application in Windows from my GUI application. I would like to somehow gather the output to know whether there were errors. Now I know I have to do something with hStdOutput, but I fail to understand what. I am new to c++ and an inexperienced programmer and I actually don't know what to do with a handle or how to light a pipe.
How do I get the output to some kind of variable (or file)?
This is what I have a the moment:
void email::run(string path,string cmd){
WCHAR * ppath=new(nothrow) WCHAR[path.length()*2];
memset(ppath,' ',path.length()*2);
WCHAR * pcmd= new(nothrow) WCHAR[cmd.length()*2];
memset(pcmd,' ',cmd.length()*2);
string tempstr;
ToWCHAR(path,ppath); //creates WCHAR from my std::string
ToWCHAR(cmd,pcmd);
STARTUPINFO info={sizeof(info)};
info.dwFlags = STARTF_USESHOWWINDOW; //hide process
PROCESS_INFORMATION processInfo;
if (CreateProcess(ppath,pcmd, NULL, NULL, FALSE, 0, NULL, NULL, &info, &processInfo))
{
::WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
delete[](ppath);
delete[](pcmd);
}
This code probably makes any decent programmer scream, but (I shouldn't even say it:) It works ;-)
The Question: How do I use hStdOutput to read the output to a file (for instance)?
Microsoft has an example in its knowledge base that demonstrates how to capture the output of a child console process. The basic principle is that the parent process creates pipes (one per standard handle to redirect) and passes the handles to CreateProcess.
The child process does not need to be modified for this to work, which is important if you do not have control over the child's source.
More information: How to spawn console processes with redirected standard handles