I'm trying to hide command window when process starts. I launch McAfee virus scan using this ProcessStartInfo config:
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.FileName = args[0];
...
procInfo.RedirectStandardOutput = true;
procInfo.WindowStyle = ProcessWindowStyle.Hidden;
procInfo.CreateNoWindow = false;
procInfo.UseShellExecute = false;
process.StartInfo = procInfo;
Then, I try:
process.Start();
process.CloseMainWindow();
And also:
process.Start();
while (process.MainWindowHandle == IntPtr.Zero)
{
process.Refresh();
}
var handle = process.MainWindowHandle;
ShowWindow(handle, 0) //ShowWindow from user32.dll
But in both cases command window appears and before it is displayed completely, it desappears (just like maximize window and immediately minimize it). I have also played with all possible ProcessStartInfo properties combinations. I need no window is displayed in any moment. How could I achieve that? Thanks in advance!
For the Code Blocks as shown in your question, the behavior is normal.
Let's have a look at them...
//[...]
procInfo.CreateNoWindow = false; //That's not what you meant
procInfo.UseShellExecute = false;
process.Start(); //Will show up the console window
process.CloseMainWindow(); //Will close the main window (probably also the whole application)
Between process.Start(); and process.CloseMainWindow(); is a short time frame, where the application is visible.
Like described by #BugFinder, it should be enough to edit the CreateNoWindow Value from false to true.
Check if that works.
Your second try is getting you further away:
process.Start();
while (process.MainWindowHandle == IntPtr.Zero)
{
process.Refresh(); //Let's do this as long as there is no window...
}
var handle = process.MainWindowHandle; //This occurs, when there is *already* a window.
ShowWindow(handle, 0) //ShowWindow from user32.dll
The idea of using Shell Functions is basically not bad, but this code is doing the same like the code above (and is also unmanaged).
Let's try a different way and take the C# Sample from PInvoke.Net: (not more managed than ShowWindow()...)
public static void StartupNotepad()
{
const uint NORMAL_PRIORITY_CLASS = 0x0020;
const uint STARTF_USESHOWWINDOW = 0x1; //IMPORTANT for you
bool retValue;
string Application = Environment.GetEnvironmentVariable("windir") + #"\Notepad.exe";
string CommandLine = #" c:\boot.ini"; //Funny
PROCESS_INFORMATION pInfo = new PROCESS_INFORMATION();
STARTUPINFO sInfo = new STARTUPINFO();
sInfo.wShowWindow = 0; //IMPORTANT for you - See http://pinvoke.net/default.aspx/Enums/ShowWindowCommand.html
sInfo.dwFlags = STARTF_USESHOWWINDOW; //IMPORTANT for you
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
retValue = CreateProcess(Application,CommandLine,
ref pSec,ref tSec,false,NORMAL_PRIORITY_CLASS,
IntPtr.Zero,null,ref sInfo,out pInfo);
Console.WriteLine("Process ID (PID): " + pInfo.dwProcessId);
Console.WriteLine("Process Handle : " + pInfo.hProcess);
}
This example demonstrates the use of CreateProcess (MSDN Link at the bottom) in a very static manner.
I suggest you to write a more dynamic function and play around with it. The PROCESS_INFORMATION structure allows you to manipulate the newly created process later in your application. If your problem still persists using the CreateProcess() Call, it's to check whether the started application does some tricky stuff on itself. And last but not least whether the problem occurs not only on your computer...
Here are some more references:
SO: WindowStyle.Hidden vs. CreateNoWindow = True
SO: How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child
SO: winapi: CreateProcess but hide the process' window?
MSDN: ProcessStartInfo.CreateNoWindow Property
MSDN: CreateProcess() Function
MSDN: STARTUPINFO Structure
EDIT:
Probably a workaround, but if your 3rd party App is designed to show a Window and tries to ignore your requests, STARTUPINFO also has dwX and dwY, which sets the Window's position if dwFlags specifies STARTF_USEPOSITION. Same for dwXSize and dwYSize.
If all else fails, you could give it a try and pass positions and sizes, which make the proces appear invisible. Cheers.
Related
How would I go about starting a new process without it being the child of the calling process.
Example:
Main Program (Caller.exe)
process.start("file.exe")
Image:
If the spawning process (parent) ends before the spawned process (child) does, then the parent-child chain is broken. To make use of this, you'd have to use an intermediate stub-process like so:
Caller.exe → Stub.exe → File.exe.
Here Stub.exe is simple launcher program that ends just after starting File.exe.
If you start a process, then you'll be its parent.
Maybe you could try to start your process from cmd.exe instead, so cmd.exe will be the parent.
Process proc = Process.Start(new ProcessStartInfo { Arguments = "/C explorer", FileName = "cmd", WindowStyle = ProcessWindowStyle.Hidden });
I have been trying to start a updater process which deletes the files of the calling process and replaces them with new ones. By setting UseShellExecute = true, I was able to circumvent the spawned process from exiting when the calling process exited.
This is inside a .Net Core 3.0 application using WPF.
var startInfo = new ProcessStartInfo("Updater.exe");
startInfo.UseShellExecute = true;
Process.Start(startInfo);
Environment.Exit(0);
This runs new process without parent:
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = #"cmd";
psi.Arguments = "/C start notepad.exe";
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process.Start(psi);
The documentation of Process.Start(string fileName) says
a new process that’s started alongside already running instances
of the same process will be independent
and it says
Starting a process by specifying its file name is similar to
typing the information in the Run dialog box of the Windows Start menu
which to me seems consistent with independent processes.
So according to the documentation, Process.Start should do what you desire.
System.Diagnostics.Process.Start() method calls kernel32!CreateProcess() under the hood. When creating a process with kernel32!CreateProcess() you can specify a different parent by using a process attribute. Here is a function written in C++ that does just that - although I'm not sure how .Net supports such features.
bool CreateProcessWithParent(DWORD parentId, PWSTR commandline) {
auto hProcess = ::OpenProcess(PROCESS_CREATE_PROCESS, FALSE, parentId);
if (!hProcess)
return false;
SIZE_T size;
//
// call InitializeProcThreadAttributeList twice
// first, get required size
//
::InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
//
// now allocate a buffer with the required size and call again
//
auto buffer = std::make_unique<BYTE[]>(size);
auto attributes = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(buffer.get());
::InitializeProcThreadAttributeList(attributes, 1, 0, &size);
//
// add the parent attribute
//
::UpdateProcThreadAttribute(attributes, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hProcess, sizeof(hProcess), nullptr, nullptr);
STARTUPINFOEX si = { sizeof(si) };
//
// set the attribute list
//
si.lpAttributeList = attributes;
PROCESS_INFORMATION pi;
//
// create the process
//
BOOL created = ::CreateProcess(nullptr, commandline, nullptr, nullptr,
FALSE, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr,
(STARTUPINFO*)&si, &pi);
//
// cleanup
//
::CloseHandle(hProcess);
::DeleteProcThreadAttributeList(attributes);
return created;
}
Source code taken from https://scorpiosoftware.net/2021/01/10/parent-process-vs-creator-process/
Here is the code that I'm now using. I thought that it may be useful to someone. It accepts one argument. The argument is a base64 encoded string that decodes to the path of the file that you would like to run.
Module Module1
Sub Main()
Dim CommandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String) = My.Application.CommandLineArgs
If CommandLineArgs.Count = 1 Then
Try
Dim path As String = FromBase64(CommandLineArgs(0))
Diagnostics.Process.Start(path)
Catch
End Try
End
End If
End Sub
Function FromBase64(ByVal base64 As String) As String
Dim b As Byte() = Convert.FromBase64String(base64)
Return System.Text.Encoding.UTF8.GetString(b)
End Function
End Module
I have a winforms application where I run a process (code shown below) in a loop. Suppose I have 10 items, I get 10 command windows where the actions are run. So, I wanted to know if there is a way where I can run the process 10 times but have just one command window open where all my actions are run.
Process p = new Process();
try
{
var pInfo = new ProcessStartInfo(executable, args);
pInfo.UseShellExecute = false;
pInfo.RedirectStandardOutput = true;
pInfo.RedirectStandardError = true;
pInfo.WorkingDirectory = workingDir;
p.StartInfo = pInfo;
p.EnableRaisingEvents = true;
SubscribeEvents(p);
p.Start();
p.WaitForExit();
}
I guess if you are doing winforms, you actually already probably have a main window open.
You can use this to hide your other windows:
pInfo.CreateNoWindow = true;
I am trying to open different files with their associated applications like this:
ProcessPath = #"C:\Users\Public\Pictures\Sample Pictures\Tulips.jpg";
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = ProcessPath;
processStartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(ProcessPath);
process = new Process();
process.StartInfo = processStartInfo;
process.EnableRaisingEvents = true;
process.Exited += Process_Exited;
process.Start();
Please can you help me get hold of the Process object that represents the application opened because after the call to process.Start(); most of the fields inside process have threw exceptions.
Thanks.
I have managed to find a solution:
using the Windows API function
[DllImport("shell32.dll")]
static extern int FindExecutable(string file, string directory, [Out] StringBuilder lpResult);
to return the associated application path for a given file. I can then use the original code i posted to execute that associated application with the original file passed through as an argument!
How would I go about starting a new process without it being the child of the calling process.
Example:
Main Program (Caller.exe)
process.start("file.exe")
Image:
If the spawning process (parent) ends before the spawned process (child) does, then the parent-child chain is broken. To make use of this, you'd have to use an intermediate stub-process like so:
Caller.exe → Stub.exe → File.exe.
Here Stub.exe is simple launcher program that ends just after starting File.exe.
If you start a process, then you'll be its parent.
Maybe you could try to start your process from cmd.exe instead, so cmd.exe will be the parent.
Process proc = Process.Start(new ProcessStartInfo { Arguments = "/C explorer", FileName = "cmd", WindowStyle = ProcessWindowStyle.Hidden });
I have been trying to start a updater process which deletes the files of the calling process and replaces them with new ones. By setting UseShellExecute = true, I was able to circumvent the spawned process from exiting when the calling process exited.
This is inside a .Net Core 3.0 application using WPF.
var startInfo = new ProcessStartInfo("Updater.exe");
startInfo.UseShellExecute = true;
Process.Start(startInfo);
Environment.Exit(0);
This runs new process without parent:
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = #"cmd";
psi.Arguments = "/C start notepad.exe";
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
System.Diagnostics.Process.Start(psi);
The documentation of Process.Start(string fileName) says
a new process that’s started alongside already running instances
of the same process will be independent
and it says
Starting a process by specifying its file name is similar to
typing the information in the Run dialog box of the Windows Start menu
which to me seems consistent with independent processes.
So according to the documentation, Process.Start should do what you desire.
System.Diagnostics.Process.Start() method calls kernel32!CreateProcess() under the hood. When creating a process with kernel32!CreateProcess() you can specify a different parent by using a process attribute. Here is a function written in C++ that does just that - although I'm not sure how .Net supports such features.
bool CreateProcessWithParent(DWORD parentId, PWSTR commandline) {
auto hProcess = ::OpenProcess(PROCESS_CREATE_PROCESS, FALSE, parentId);
if (!hProcess)
return false;
SIZE_T size;
//
// call InitializeProcThreadAttributeList twice
// first, get required size
//
::InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
//
// now allocate a buffer with the required size and call again
//
auto buffer = std::make_unique<BYTE[]>(size);
auto attributes = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(buffer.get());
::InitializeProcThreadAttributeList(attributes, 1, 0, &size);
//
// add the parent attribute
//
::UpdateProcThreadAttribute(attributes, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hProcess, sizeof(hProcess), nullptr, nullptr);
STARTUPINFOEX si = { sizeof(si) };
//
// set the attribute list
//
si.lpAttributeList = attributes;
PROCESS_INFORMATION pi;
//
// create the process
//
BOOL created = ::CreateProcess(nullptr, commandline, nullptr, nullptr,
FALSE, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr,
(STARTUPINFO*)&si, &pi);
//
// cleanup
//
::CloseHandle(hProcess);
::DeleteProcThreadAttributeList(attributes);
return created;
}
Source code taken from https://scorpiosoftware.net/2021/01/10/parent-process-vs-creator-process/
Here is the code that I'm now using. I thought that it may be useful to someone. It accepts one argument. The argument is a base64 encoded string that decodes to the path of the file that you would like to run.
Module Module1
Sub Main()
Dim CommandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String) = My.Application.CommandLineArgs
If CommandLineArgs.Count = 1 Then
Try
Dim path As String = FromBase64(CommandLineArgs(0))
Diagnostics.Process.Start(path)
Catch
End Try
End
End If
End Sub
Function FromBase64(ByVal base64 As String) As String
Dim b As Byte() = Convert.FromBase64String(base64)
Return System.Text.Encoding.UTF8.GetString(b)
End Function
End Module
can anyone tell me how to spawn another console application from a Winforms app, but (A) not show the console window on the screen, and (B) still obtain the standard output of the application? Currently I have something like the following:
Process SomeProgram = new Process();
SomeProgram.StartInfo.FileName = #"c:\foo.exe";
SomeProgram.StartInfo.Arguments = "bar";
SomeProgram.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
SomeProgram.StartInfo.UseShellExecute = false;
SomeProgram.StartInfo.RedirectStandardOutput = true;
SomeProgram.Start();
SomeProgram.WaitForExit();
string SomeProgramOutput = SomeProgram.StandardOutput.ReadToEnd();
If I set RedirectStandardOutput to false, then the console app is hidden as expected, but I cannot get the standard output text. However, as soon as I set the RedirectStandardOutput to true, the window stops being hidden, although I am able to get the program's output.
So, I know how to make the console app run hidden, and I know how to get the program's output, but how do I get it to do both?
Many TIA
You are missing the CreateNoWindow property which has to be set to true in your case.
I think it will help you:
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = #"C:\Users\Vitor\ConsoleApplication1.exe";
pProcess.StartInfo.Arguments = "olaa"; //argument
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
pProcess.StartInfo.CreateNoWindow = true; //not diplay a windows
pProcess.Start();
string output = pProcess.StandardOutput.ReadToEnd(); //The output result
pProcess.WaitForExit();