I am trying to use the TerminateProcess to terminate an app launched by ShellExecuteEX like this:
SHELLEXECUTEINFO ExecuteInfo;
ExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI; /* Odd but true */
ExecuteInfo.hwnd = NULL;
ExecuteInfo.cbSize = sizeof(ExecuteInfo);
ExecuteInfo.lpVerb = NULL;
ExecuteInfo.lpFile = "http://www.microsoft.com";
ExecuteInfo.lpParameters = "";
ExecuteInfo.lpDirectory = NULL;
ExecuteInfo.nShow = SW_SHOW;;
ShellExecuteEx(&ExecuteInfo);
//WaitForSingleObject(ExecuteInfo.hProcess, 0);
Sleep(4000);
TerminateProcess(ExecuteInfo.hProcess, 0);
IE gets opened but it never closes. Am I doing something wrong?
According to MSDN, fMask must be set to SEE_MASK_NOCLOSEPROCESS for .hProcess to get set. I would add a test to see if it is NULL. As a side note I've always had better luck using CreateProcess.
Edit:
This is how you do it using CreateProcess:
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
CreateProcess( NULL,
"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.google.com/",
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi );
Sleep(4000);
TerminateProcess(pi.hProcess, 0);
You should add error-checking and could query the path of the default browser using: AssocQueryString like this:
AssocQueryString(0,ASSOCSTR_EXECUTABLE,"http","open", szExe, &cchExe);
You can get more information by checking the returned hProcess (e.g., in a debugger). Also make sure you have the SEE_MASK_NOCLOSEPROCESS flag set.
But my psychic powers say: Opening an IE document doesn't necessarily create a new process. And, if it does, it may not be the one you think it is: The process you may have created may have spawned yet another process to actually host the document. Raymond Chen mentions that about halfway down this blog post.
Related
The saga continues...
I've searched the web, i've searched on StackOverflow, i found many hope giving answers/solutions, but somehow they have all failed (up)on me (including the ones related to ShellExecute(Ex) ).
How to hide a (flashing) CMD window (incl. arguments) using CreateProcess??
I basically want to call/execute a set of conditional/native cmd.exe commands (i.e. FOR /F, and ||), but also an external command FIND(STR).exe. And this, without showing a (flashing) CMD window.
But even hiding something as simple as "cmd.exe /C ECHO ...flashing window is bad..." seems impossible to do.
The code i've tried (including many variations related to the dwFlags and wShowWindow flags
#include <windows.h>
int main()
{
char cmdline[] = "cmd.exe /c ECHO ...flashing window is bad...";
PROCESS_INFORMATION pi;
STARTUPINFO si;
// memset(&si,0,sizeof(STARTUPINFO));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
// si.dwFlags = STARTF_USESTDHANDLES;
// si.dwFlags = CREATE_NO_WINDOW;
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
// si.wShowWindow = CREATE_NO_WINDOW;
CreateProcess(NULL, (LPSTR) cmdline, NULL, NULL, 0, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
// ExitProcess;
return 0;
}
I don't want to rely on external programs i.e. .vbs (Windows Scripting Host) or shortcut tricks, but simply a standalone compiled .exe.
Is this (really) too much to ask, or am i doing it (completely) wrong?
Thanks...
Update: You also seem to confuse CreateProcess flags (its dwCreationFlags argument) with the member of STARTUPINFO structure. These are different flags, CREATE_NO_WINDOW should not be in STARTUPINFO.
You have to pass the CREATE_NO_WINDOW flag, then the console window won't show. Originally I've answered that you have to redirect the standard handles which is not correct (but still highly recommanded).
Set STARTF_USESTDHANDLES and fill in appropriate handles. If you are interested in the output of the process, create pipes, otherwise you can just open nul an pass that.
Try Using ProcessBuilder. Here is an example of some code that I have that seems to work just fine. In my code below, the shellScript is a StringBuilder that I am dynamically creating that contains the command and it's parameters that I want to execute.
String[] scriptArray = shellScript.toString().split(" ");
ProcessBuilder builder = new ProcessBuilder(scriptArray);
File outputFile = new File("/logs/AgentOutputLog.txt");
File errorFile = new File("/logs/AgentErrorLog.txt");
builder.redirectOutput(outputFile);
builder.redirectError(errorFile);
Process process = builder.start();
int errCode = process.waitFor();
//errCode = 0 means online
if(errCode == 0){
success = true;
break;
//errCode = 1 means offline
} else if (errCode == 1){
success = false;
break;
}
I'm trying to use the following code to run a user-mode process from my service application (running as a local system.)
The requirement for the user-mode process is to run without elevatation, but to have UIAccess="true" in its manifest to be able to display top-most windows correctly under Windows 8.
So I do this (from my service) to run my user-mode process:
//NOTE: Error checking is omitted for readability
//'dwSessionID' = user session ID to run user-mode process in
//'pUserProcPath' = L"C:\\Program Files (x86)\\Company\\Software\\user_process.exe"
HANDLE hToken = NULL;
WTSQueryUserToken(dwSessionID, &hToken);
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
LPVOID pEnvBlock = NULL;
CreateEnvironmentBlock(&pEnvBlock, hToken2, FALSE);
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = L"winsta0\\default";
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
//ImpersonateLoggedOnUser(hToken2); //Not necessary as suggested below
PVOID OldRedir;
Wow64DisableWow64FsRedirection(&OldRedir);
BOOL bSuccess = CreateProcessAsUser(
hToken2, // client's access token
pUserProcPath, // file to execute
NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT, // creation flags
pEnvBlock, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
int nOSError = ::GetLastError();
Wow64RevertWow64FsRedirection(OldRedir);
//RevertToSelf(); //Not necessary as suggested below
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
DestroyEnvironmentBlock(pEnvBlock);
CloseHandle(hToken2);
CloseHandle(hToken);
This runs fine, if UIAccess="false" in the manifest for the user_process.exe.
But if I do the following:
Enable UIAccess="true" for the user_process.exe:
Sign it with my code-signing certificate and
Place it in the "C:\Program Files (x86)\Company\Software" folder.
What I get is that CreateProcessAsUser fails with the error ERROR_ELEVATION_REQUIRED.
Can someone suggest how to make it work?
PS. I tried adjusting my service's privileges to enable SE_DEBUG_NAME and SE_TCB_NAME, but neither helped.
PS2. If I simply double-click my user_process.exe process compiled with UIAccess="true", code-signed, and placed in the C:\Program Files (x86)\Company\Software folder, it starts & runs just fine (not elevated.).
I found the answer. Here it is for whoever runs into it as well:
Add this after DuplicateTokenEx call:
HANDLE hToken2;
DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2);
//ONLY if requiring UIAccess!
DWORD dwUIAccess = 1;
::SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess));
I am trying to start GUI application using my service. I developed the service on VS2012 and running on windows 7. But CreateProcessAsUser function doesn't start application even though it returns successfully. following is my code:
PHANDLE hToken = NULL;
WTSQueryUserToken (WTSGetActiveConsoleSessionId (), hToken) ;
if( !CreateProcessAsUser( hToken,
NULL, // No module name (use command line)
pPath, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
what could be the issue?
Thanks,
KM.
After retrieving the user token from WTSQueryUserToken(), call DuplicateTokenEx() to convert it into a primary token, and pass that token to CreateProcessAsUser(). You also need to specify the "winsta0\default" (use: "winsta0\\default") desktop via the STARTUPINFO structure. You should also call CreateEnvironmentBlock() using the same token, and pass that environment to CreateProcessAsUser() as well.
There is not enough information to be sure of my answer but that kind of error often happen when the structures are not correctly initialized.
PHANDLE hToken = NULL;
WTSQueryUserToken (WTSGetActiveConsoleSessionId (), hToken) ;
//be sure that the handle is correct ! (can be the issue)
if (!hToken) printf("Token error.\n");
//init here !
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if( !CreateProcessAsUser( hToken,
NULL, // No module name (use command line)
pPath, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
)
I faced a similar issue while using WTSQueryUserToken in windows 7, but the same function worked in windows 10.
So I took the explorer.exe token, called the DuplicateTokenEx function.
Set the corresponding values for startupinfo structure
si.lpDesktop = "winsta0\\default";
si.wShowWindow = SW_SHOWNORMAL;
si.dwFlags = STARTF_USESHOWWINDOW;
and called createprocessasuser
I tried using CreateProcess to run a simple command like hg > test.txt. I tried running the string as a whole (as opposed to separating it into an application name and its parameters). Why does CreateProcess(0, "notepad.exe test.txt", ...) work but CreateProcess(0, "hg > test.txt", ...) does not?
The code below creates a console-less process with stdout and stderr redirected to the specified file.
#include <windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h = CreateFile(_T("out.log"),
FILE_APPEND_DATA,
FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL ret = FALSE;
DWORD flags = CREATE_NO_WINDOW;
ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = NULL;
si.hStdError = h;
si.hStdOutput = h;
TCHAR cmd[]= TEXT("Test.exe 30");
ret = CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi);
if ( ret )
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
return -1;
}
You can't use stdout redirection in the command line passed to CreateProcess. To redirect stdout you need to specify a file handle for the output in the STARTUPINFO structure.
You are also making another, more subtle, mistake. The second parameter, lpCommandLine must point to writeable memory because CreateProcess overwrites the buffer. If you happen to be using the ANSI version of the function then you will get away with this, but not for the Unicode version.
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.
Microsoft has an example how to redirect the standard output:
http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx.
CreateProcess() launches processes, it is not a command line itnerpreter. It doesn't know what ">" is and won't do the stream redirection for you. You need to open the file test.txt yourself and pass the handle to it to CreateProcess inside the STARTUPINFO structure:
CreateProcess
STARTUPINFO
you should run process cmd.exe with params "/c command line".
This will redirect the output to a file or to organize a pipeline through CreateProcess.
if I am using CreateProcess() multiple times, is okay to share the PROCESS_INFORMATION and STARTUPINFO variables? Or is it really bad practice? I've read quite a bit of documentation, but I can't find any examples about handling CreateProcess() calls more than once.
As an example, say I have the fake function below:
int SampleClass::sampleFn1(){
//Variables
STARTUPINFOW siStartInfo;
PROCESS_INFORMATION piProcInfo;
memset(&siStartInfo, 0, sizeof(siStartInfo));
memset(&piProcInfo, 0, sizeof(piProcInfo));
siStartInfo.cb = sizeof(siStartInfo);
//let us assume cmdPath = cmd.exe directory, and cmdTxtPtr has correct text
if(!CreateProcess(cmdPath, cmdTxtPtr, NULL, NULL, false, 0,
NULL, NULL, &siStartInfo, &piProcInfo)){
return 1; //failed at step 1
}
if(!CreateProcess(cmdPath,_T("/C ant debug"),NULL,NULL,false,0,NULL,
(LPCTSTR)directory,&siStartInfo,&piProcInfo)){
return 2; //failed at debug
}
WaitForSingleObject(piProcInfo.hProcess,10000);
result = GetExitCodeProcess(piProcInfo.hProcess,&exitCode);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return 0;//finished
}
A similar function happens to work in my program, but I'd like to make it as safe as possible.
Or... Should I do something like the code below instead:
int SampleClass::sampleFn2(){
//Variables
STARTUPINFOW siStartInfo;
PROCESS_INFORMATION piProcInfo;
memset(&siStartInfo, 0, sizeof(siStartInfo));
memset(&piProcInfo, 0, sizeof(piProcInfo));
siStartInfo.cb = sizeof(siStartInfo);
//let us assume cmdPath = cmd.exe directory, and cmdTxtPtr has correct text
if(!CreateProcess(cmdPath, cmdTxtPtr, NULL, NULL, false,
0, NULL, NULL, &siStartInfo, &piProcInfo)){
return 1; //failed at update project
}
WaitForSingleObject(piProcInfo.hProcess,10000);
result = GetExitCodeProcess(piProcInfo.hProcess,&exitCode);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
memset(&siStartInfo, 0, sizeof(siStartInfo));
memset(&piProcInfo, 0, sizeof(piProcInfo));
siStartInfo.cb = sizeof(siStartInfo);
if(!CreateProcess(cmdPath,_T("/C ant debug"),NULL,NULL,
false,0,NULL,(LPCTSTR)directory,&siStartInfo,&piProcInfo)){
return 2; //failed at debug
}
WaitForSingleObject(piProcInfo.hProcess,10000);
result = GetExitCodeProcess(piProcInfo.hProcess,&exitCode);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return 0;//finished
}
Or do they both being handled poorly? Thank you.
AFAIK the CreateProcess function writes into both the STARTUPINFO and PROCESSINFO structure, so unless you don't care about any of that info, which I think you should, you could do the second example you give.
By doing doing memset with 0, you reset all data in the structs to 0.
I'm not sure that this is very good practice, but maybe someone else can give more insight.