Process.Start(ProcessStartInfo) doesn't send command line to screensavers (.scr files) - c#

I am currently writing an app to start the screensaver on Windows 10 and show the screen instead of a black background. So that Bubbles and relatives can act like in older OS version.
Here is my full code:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
public class DrawOverMyScreen {
public static void Main(string[] CommandLine) {
switch (CommandLine[0]) {
case "/c":
DialogResult Answer = MessageBox.Show("What do you want to do?\n\n - Press \"Yes\" to configure the screensaver\n - Press \"No\" to change the screensaver\n - Press \"Cancel\" to do nothing", "DrawOverMyScreen Configuration", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);
switch (Answer) {
case DialogResult.Yes:
Screensaver("/c");
break;
case DialogResult.No:
throw new NotImplementedException();
break;
default:
break;
}
break;
default:
Screensaver("/s");
break;
}
}
public static void Screensaver(string CommandLine) {
RegistryKey Settings = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\DrawOverMyScreen");
if (Settings != null) {
string ScreensaverLocation = Settings.GetValue("Screensaver", string.Empty).ToString();
if (!string.IsNullOrEmpty(ScreensaverLocation) && File.Exists(ScreensaverLocation)) {
Process Screensaver = Process.Start(new ProcessStartInfo(ScreensaverLocation, CommandLine));
Screensaver.WaitForExit();
}
}
}
}
Notice the Screensaver method. It uses Process.Start(new ProcessStartInfo(ScreensaverLocation, CommandLine)); to start the screensaver. But whenever I do Screensaver("/c"); to run the screensaver's config utility, I only get the normal screensaver view (The one you get when idle after a certain time). Using the run prompt like this: C:\Windows\SysWOW64\SCREEN~1.SCR /c also gives the same result, but command line prompt actually opens the config utility.
Why won't it work, and how can I make it so it works?

Just from what you have provided, I can't tell you why it won't work. I do not have a screensaver to test that with (that I know of). But I'm able to do all four of these with Notepad opening a text file:
Separate ProcessStartInfo
ProcessStartInfo procInfo = new ProcessStartInfo("notepad.exe", "c:\\test.txt");
Process proc = Process.Start(procInfo);
proc.WaitForExit();
Separate ProcessStartInfo with Properties
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.Arguments = "c:\\test.txt";
procInfo.FileName = "notepad.exe";
Process proc = Process.Start(procInfo);
proc.WaitForExit();
Inline ProcessStartInfo
Process proc = Process.Start(new ProcessStartInfo("notepad.exe", "c:\\test.txt"));
proc.WaitForExit();
No PSI, Just Process
Process proc = Process.Start("notepad.exe", "c:\\test.txt");
proc.WaitForExit();
You may want to go with the first one so that you can breakpoint on the "Process proc..." line and examine the properties of procInfo. The Arguments property should show the 2nd value (in my case, c:\\test.txt), and the FileName property should be the path to what you are executing (mine is notepad.exe).
EDIT: I added the separate one with properties so you can really see explicit setting.
CONFIGURE A SCREENSAVER
I have worked out an example using the 3D Text screensaver:
string scrPath = #"C:\Windows\System32\ssText3d.scr";
ProcessStartInfo procInfo = new ProcessStartInfo();
procInfo.FileName = scrPath;
procInfo.Verb = "config";
procInfo.UseShellExecute = false;
Process proc = Process.Start(procInfo);
proc.WaitForExit();
I didn't use the Arguments. Instead, I used the Verb. This requires UseShellExecute to be set to false. I got the expected configuration dialog instead of the screensaver running.
More About Verbs
This is where the verbs are for screen savers.
You can also define custom verbs: Register an Application to Handle Arbitrary File Types

Related

How to create a new command prompt window and redirecting user input?

For a game server application I already have a console where some runtime information is displayed. Still, I would like to have another one where the admin can input commands (e.g. in case of emergency) while the resulting effects of these inputs is still displayed in the main console window.
There already are similar questions on stackoverflow concerning this topic, however, applying the answers didn't result in what I was hoping for. I have troubles understanding why on the one hand, I seemingly have to set UseShellExecute = true; to actually get a new window while this make it impossible to RedirectStandardInput = true; and vice-versa. However, having input and output via a new process but in the same console prompt works fine, except for the visual clutter (while you write, output is appended to your wirtten but not sent input which is quite uncomfortable).
So, is it still possible to use a separate command prompt for admin input (I guess so), or do I have to set up another form of inter-process communication and create a separate program (with Main-function and all)?
Here is my current code concerning the process generation. Note, that it is embedded in a less streamlined context if you should be wondering about the overall composition:
bool canExecute = false;
Process consoleProcess;
ProcessStartInfo startInfo = new ProcessStartInfo();
OperatingSystem os = Environment.OSVersion;
switch (os.Platform)
{
case PlatformID.MacOSX:
canExecute = false;
break;
case PlatformID.Unix:
canExecute = true;
startInfo.FileName = "/bin/bash";
break;
case PlatformID.Win32NT:
canExecute = true;
startInfo.FileName = "cmd.exe";
break;
case PlatformID.Win32S:
canExecute = true;
startInfo.FileName = "cmd.exe";
break;
case PlatformID.Win32Windows:
canExecute = true;
startInfo.FileName = "cmd.exe";
break;
case PlatformID.WinCE:
canExecute = true;
startInfo.FileName = "cmd.exe";
break;
case PlatformID.Xbox:
canExecute = false;
break;
}
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
consoleProcess = new Process();
consoleProcess.StartInfo = startInfo;
consoleProcess.Start();
if (canExecute)
{
using (StreamWriter sw = consoleProcess.StandardInput)
{
String line;
while ((line = Console.ReadLine()) != null)
{
// do something useful with the user input
}
}
}
Thank you in advance!
You can't do this with just the built-in .NET Process class. It doesn't support the right options.
The issue is that, by default, a new Windows console process always inherits the already-allocated console of its parent process. When you use UseShellExecute = true; (the default for Process), this causes the Process class to (of course) use the ShellExecuteEx() method. Since the new process is created through Windows Shell instead of your process, there's no console to inherit and so the process gets its own. But if you create the process directly, you get the default console-inheritance behavior.
But of course, since you want to redirect standard I/O, you can't use UseShellExecute = true;. You have to set it to false.
The only way around this is to call CreateProcess() yourself directly, via p/invoke, so that you can pass the flag you need and which the Process class doesn't offer a way to control. The flag in question is CREATE_NEW_CONSOLE. Passed to the function call, it tells the CreateProcess() function to create a separate console for the new process.
Please see MSDN's Creation of a Console for more information on this behavior and how to adjust it to suit your needs.
Naturally, this opens a whole new can of worms, as you will no longer have the direct convenience from the Process class to aid with redirection of I/O. You might find it easier in the long run to just write a thin non-console proxy program to run the actual program. That way, you can start the non-console proxy, which of course will not inherit your current console, and then have it start the program you actually want to run. Doing it that way is not terribly elegant (not the least of which because, the proxy not being a console program, you won't be able to easily redirect I/O via stdio), but it's reasonably simple and keeps you in the managed-code world, with easier-to-use APIs.
Please find below an example of a bidirectional proxy (compiled as a "Windows Application", not a "Console Application"), with a parent process and a child process. The child process simply echoes to the console whatever is typed in. The parent process writes to the proxy whatever is typed in. The proxy sends to the child whatever it receives from the parent, and sends to the parent whatever it receives from the child. All processes treat an empty line input as the termination condition.
For your own purposes, you would likely use a one-way pipe (i.e. PipeDirection.In for the proxy and PipeDirection.Out for the parent process), and have the proxy redirect only StandardInput. That way, all output will still appear in the child process's window. (The bidirectional example is more for proof-of-concept...obviously if both input and output are directed, there's not much point in forcing the child process into its own window :) ).
Proxy: (ConsoleProxy.exe)
class Program
{
static void Main(string[] args)
{
NamedPipeClientStream pipe = new NamedPipeClientStream(".", args[1],
PipeDirection.InOut, PipeOptions.Asynchronous);
pipe.Connect();
Process process = new Process();
process.StartInfo.FileName = args[0];
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
using (TextReader reader = new StreamReader(pipe))
using (TextWriter writer = new StreamWriter(pipe))
{
Task readerTask = ConsumeReader(process.StandardOutput, writer);
string line;
do
{
line = reader.ReadLine();
if (line != "")
{
line = "proxied write: " + line;
}
process.StandardInput.WriteLine(line);
process.StandardInput.Flush();
} while (line != "");
readerTask.Wait();
}
}
static async Task ConsumeReader(TextReader reader, TextWriter writer)
{
char[] rgch = new char[1024];
int cch;
while ((cch = await reader.ReadAsync(rgch, 0, rgch.Length)) > 0)
{
writer.Write("proxied read: ");
writer.Write(rgch, 0, cch);
writer.Flush();
}
}
}
Child process: (ConsoleApplication1.exe)
class Program
{
static void Main(string[] args)
{
Console.Title = "ConsoleApplication1";
string line;
while ((line = PromptLine("Enter text: ")) != "")
{
Console.WriteLine(" Text entered: \"" + line + "\"");
}
}
static string PromptLine(string prompt)
{
Console.Write(prompt);
return Console.ReadLine();
}
}
Parent process:
class Program
{
static void Main(string[] args)
{
NamedPipeServerStream pipe = new NamedPipeServerStream("ConsoleProxyPipe",
PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
Console.Title = "Main Process";
Process process = new Process();
process.StartInfo.FileName = "ConsoleProxy.exe";
process.StartInfo.Arguments = "ConsoleApplication1.exe ConsoleProxyPipe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.Start();
pipe.WaitForConnection();
using (TextReader reader = new StreamReader(pipe))
using (TextWriter writer = new StreamWriter(pipe))
{
Task readerTask = ConsumeReader(reader);
string line;
do
{
line = Console.ReadLine();
writer.WriteLine(line);
writer.Flush();
} while (line != "");
readerTask.Wait();
}
}
static async Task ConsumeReader(TextReader reader)
{
char[] rgch = new char[1024];
int cch;
while ((cch = await reader.ReadAsync(rgch, 0, rgch.Length)) > 0)
{
Console.Write(rgch, 0, cch);
}
}
}

Embedding a CMD terminal in a C# Winforms application

What I intend to do is build an application which, among other things, will have a command line embedded in it just like some IDEs do (something I find extremely useful).
This is the code that I have so far, do note that it's a Winforms project:
public partial class Form1 : Form
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
p.StartInfo = info;
p.Start();
}
private void button1_Click(object sender, EventArgs e) {
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
textBox2.Text = p.StandardOutput.ReadToEnd();
textBox3.Text = p.StandardError.ReadToEnd();
p.WaitForExit();
}
}
}
As you can see there are 3 textboxes and one button:
textbox1 is for entering the command
textbox2 is for stdout
textbox3 is for stderr
On to my problem:
I can only input one command because after executing it, my CMD window vanishes. I know it dies off because I've set info.CreateNoWindow = false; and it indeed vanishes and if I try to enter another command I get an exception.
How would I go on about keeping my CMD window 'alive' so that I can use it as much as I please? In short I want to truly mimic CMD behavior.
Feel free to ask for more information if something is not clear.
Extra info/What I tried:
I've tried adding info.Attributes = "/K"; since I know that /K should keep the CMD alive. I've also read that p.WaitForExit(); should keep the CMD alive, but from what I figured this is only for the purpose of reading the output. Needless to say, I do not need that since I'm already redirecting its output. Neither of these solutions work but it is entirely possible that I'm using them the wrong way.
I need that process alive so I can easily navigate using cd and executing a sequence of commands when needed, such as when accessing ftp or mysql. I know I can work around these two examples with parameters, but not for every application. In short, spawning a new process every time is not something I want. I want that CMD interface to be up at all times.
The cmd process dies after
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
But I cannot pinpoint why.
What CMD console provides is an interface to execute predefined functions (in System32 or in %PATH%). Process class also have same capabilities ,what you can do is as the user enters command text and presses return key in textbox2 (which can be multi-lined, black-background, white text) you can pass the command text to Process p = new Process();and append the result so it looks like single cmd session. Now before passing the whole command text we need to separate arguments (if any) which is text appearing after first space. Example:
SHUTDOWN /S /T 10
where Shutdown will be filename and /S /T 10 will be arguments.
Before executing set default directory of ProcessStartInfo:-
_processStartInfo.WorkingDirectory = #"%Path%";
Otherwise default will be System32 folder.

Execute command in current terminal, without opening up a new terminal.

I have a c# application currently running from the Terminal. It opens up a new terminal to execute a certain command then closes that terminal. I'm wondering if I can just execute this command in the terminal that is currently opened, instead of opening a new one. My code to execute the command is as follows.
Process proc = new System.Diagnostics.Process();
proc.StartInfo.WorkingDirectory = #"MyDirectory";
proc.StartInfo.FileName = #"/usr/bash";
proc.StartInfo.Arguments = command;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
How would I rewrite this without opening a new terminal?
As far as I know, no - but you can use the code
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
to make it so that the window does not pop up. Then in your normal terminal you can use print to update the user with a message like "Running Script..."
You can directly use libc system vs. the Diagnostics.Process and the redirection pipes (which just wrap the libc functions):
using System;
using System.Runtime.InteropServices;
namespace ShellMe
{
class MainClass
{
[DllImport ("libc")]
private static extern int system (string exec);
public static void Main (string[] args)
{
system("YourCommand2Run"); // blocking, you are in a shell so same rules apply
system("YourCommand2Run &"); // non-blocking
}
}
}

externally access processing via cmd

As I aksed in another post, I am trying to automate running processing ide from c#. Finally I found the way to run the processing sketch via cmd, with setting the installed processing folder in the path of evironment variable.
I find it works with inputting command directly in cmd.exe, but when I want to do the same thing through some c# code in Visual Studio, it doesn't run the .pde file.
Here is the code,
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Runprocessing
{
static void Main()
{
Process process = new Process();
ProcessStartInfo stinfo = new ProcessStartInfo();
stinfo.FileName = "cmd.exe";
stinfo.Arguments = "/c"+"processing-java --run --sketch=D:\\pw --output=D:\\pw\\output";
stinfo.CreateNoWindow = true;
stinfo.UseShellExecute = false;
process = Process.Start(stinfo);
process.WaitForExit();
process.Close();
process.Dispose();
}
}
}
My question is, how should I properly use processing-java to activate the sketch. because here I am stating
stinfo.FileName = "cmd.exe";
stinfo.Arguments = "/c"+"processing-java --run --sketch=D:\\pw --output=D:\\pw\\output";
Is this the right way to use processing-java in cmd?

Log to text file from Process.Start

I am starting a process using Process.Start(ProcessStartInfo). It currently brings up a console window and the output of the process is displayed there until the process completes, in which case the console window closes automatically.
The process outputs a lot of text, so I do not just want to redirect this output to a string, like examples I have found so far.
How can I get the text of the console output to go into a text log file?
ProcessStartInfo myPSI = new ProcessStartInfo();
myPSI.FileName = myFileName;
myPSI.Arguments = myArgs;
myPSI.CreateNoWindow = false;
myPSI.UseShellExecute = false;
myPSI.WindowStyle = ProcessWindowStyle.Hidden;
try
{
using (Process exeProcess = Process.Start(myPSI))
{
exeProcess.WaitForExit();
}
}
catch
{
}
You need to use output redirection. See here: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
You can redirect the output to whatever you want... for example a stream... you can even process the output in a separate thread if you want to - for source code and details see http://www.codeproject.com/KB/threads/ReadProcessStdoutStderr.aspx

Categories