I have a windows service that runs under a logon say "UserA". The job of the windows service is to start a new process on a timer elapsed event.
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Task.Factory.StartNew(() => StartNewProcess());
}
private void Initialize()
{
newProcess = new Process();
newProcess.StartInfo.FileName = "Test.exe";
newProcess.StartInfo.Arguments = "sessionId...";
newProcess.StartInfo.CreateNoWindow = false;
newProcess.StartInfo.ErrorDialog = false;
newProcess.StartInfo.RedirectStandardError = true;
newProcess.StartInfo.RedirectStandardInput = true;
newProcess.StartInfo.UseShellExecute = false;
newProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
}
private void StartNewProcess()
{
newProcess.Start();
}
On task manager I see that both the windows service and the new process have a "User Name" as "UserA".
But the problem is Windows service is able to access "C:\QueryResult" but the new process is not able to access "C:\QueryResult"
I am using File.Copy("C:\QueryResult", "D:\blahblah") on both the process
Has the security context changed in the new process?
Try automatically elevating the application permissions like so:
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Windows.Forms;
public static class Program
{
[STAThread]
public static void Main()
{
if (!IsAdmin() && IsWindowsVistaOrHigher())
RestartElevated();
else
DoStuff();
}
private static Boolean IsAdmin()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
if (identity != null)
return (new WindowsPrincipal(identity)).IsInRole(WindowsBuiltInRole.Administrator);
return false;
}
private static Boolean IsWindowsVistaOrHigher()
{
OperatingSystem os = Environment.OSVersion;
return ((os.Platform == PlatformID.Win32NT) && (os.Version.Major >= 6));
}
private static void RestartElevated()
{
String[] argumentsArray = Environment.GetCommandLineArgs();
String argumentsLine = String.Empty;
for (Int32 i = 1; i < argumentsArray.Length; ++i)
argumentsLine += "\"" + argumentsArray[i] + "\" ";
ProcessStartInfo info = new ProcessStartInfo();
info.Arguments = argumentsLine.TrimEnd();
info.CreateNoWindow = false;
info.ErrorDialog = false;
info.FileName = Application.ExecutablePath;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
info.Verb = "runas";
info.WindowStyle = ProcessWindowStyle.Hidden;
info.WorkingDirectory = Environment.CurrentDirectory;
try
{
Process.Start(info);
}
catch { return; }
Application.Exit();
}
}
Related
class TcpIpCommands
{
public string Check()
{
Process p = new Process();
p.StartInfo.FileName = #"C:\Windows\syswow64\netsh.exe";
p.StartInfo.Arguments = "int tcp show global";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(737);
p.Start();
p.WaitForExit();
return p.StandardOutput.ReadToEnd();
}
public string Command(string FileName, string Arguments)
{
ProcessStartInfo p = new ProcessStartInfo();
p.FileName = FileName;
p.Arguments = Arguments;
p.UseShellExecute = false;
p.CreateNoWindow = true;
p.RedirectStandardOutput = true;
p.StandardOutputEncoding = Encoding.GetEncoding(737);
using (Process process = Process.Start(p))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
return result;
}
}
}
public string Reseting()
{
Process p = new Process();
p.StartInfo.FileName = #"C:\Windows\syswow64\netsh.exe";
p.StartInfo.Arguments = #"interface tcp reset";
p.StartInfo.Verb = "runas";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(737);
p.Start();
p.WaitForExit();
return p.StandardOutput.ReadToEnd();
}
}
I have an problem with Command Method and Reseting Method. If i try to execute them I receive in RichTextBox the message
set global command failed on ipv4 the parameter is incorrect
But If i run netsh interface tcp show global or hit the Check Method they info me that the status are change.
here are the buttons
private void BtnCheck_Click(object sender, EventArgs e)
{
TcpIpCommands tic = new TcpIpCommands();
richTextBox1.Text = tic.Check();
}
private void btnChimney_Click(object sender, EventArgs e)
{
TcpIpCommands tic = new TcpIpCommands();
richTextBox1.Text = tic.Command(#"C:\Windows\system32\netsh.exe", "interface tcp set global chimney=enabled");
}
private void BtnReset_Click(object sender, EventArgs e)
{
TcpIpCommands tic = new TcpIpCommands();
richTextBox1.Text = tic.Reseting();
}
I cant understand why RichTextBox shows that message but the process execute succefully.
A note. I have try to run the commands with
StartInfo.Verb = "runas";
and both from
C:\Windows\System32\netsh.exe
and
C:\Windows\syswow64\netsh.exe
and i get the same error "set global command failed on ipv4 the parameter is incorrect" but the code execute and the parameter change.
i am trying the below code
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = new StreamWriter(p.StandardInput.BaseStream))
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine(#"dir");
}
}
using (StreamWriter sw = new StreamWriter(p.StandardInput.BaseStream))
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine(#"dir");
}
}
the first sw.writeline is going ok, but the second i discovered that the process hasexeited is true.
why the process exited , and how to keep it running to execute further commands?
Put both write commands in the same using block and omit the second using block. The using-block disposes the StreamWriter after you leave the block. Disposing the StreamWriter closes the underlying stream and causes cmd to exit.
i solved the problem like that
static Queue<string> CmdQueue = new Queue<string>();
static Thread ThreadCMD = new Thread(new ThreadStart(CMDThread));
private static void CMDThread()
{
bool Active = true; // used to indicate the status of the session
ProcessStartInfo psiOpt = new ProcessStartInfo();
psiOpt.FileName = "cmd.exe";
psiOpt.WindowStyle = ProcessWindowStyle.Hidden;
psiOpt.RedirectStandardOutput = true;
psiOpt.RedirectStandardInput = true;
psiOpt.RedirectStandardError = true;
psiOpt.UseShellExecute = false;
psiOpt.CreateNoWindow = true;
Process procCommand = Process.Start(psiOpt);
procCommand.OutputDataReceived += ProcCommand_OutputDataReceived;
procCommand.ErrorDataReceived += ProcCommand_ErrorDataReceived;
procCommand.BeginOutputReadLine();
procCommand.BeginErrorReadLine();
using (StreamWriter sw = procCommand.StandardInput)
{
while (Active)
{
while (CmdQueue.Count==0) { }
string cmd = CmdQueue.Dequeue();
if (cmd != "cmdexit")
{
sw.WriteLine(cmd);
}
else
{
Active = false;
}
}
}
procCommand.OutputDataReceived -= ProcCommand_OutputDataReceived;
procCommand.ErrorDataReceived -= ProcCommand_ErrorDataReceived;
procCommand.Close();
}
private static void ProcCommand_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
PushCMDResult(Encoding.ASCII.GetBytes(e.Data));
}
private static void ProcCommand_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
PushCMDResult(Encoding.ASCII.GetBytes(e.Data));
}
public void StartCMD()
{
ThreadCMD.Start();
}
public void Execute(string cmd)
{
CmdQueue.Enqueue(cmd);
}
i can start cmd.exe session , and execute as many as needed of commands using execute to enqueue the cmd string in Queue which will be written to the streamwriter .
i used async reading for output and error as well.
I want to perform some command lines to display the result after each input.
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("ftp");
//output
sw.WriteLine("open ftp.server.com");
//output
sw.WriteLine("username");
//output
sw.WriteLine("password");
//output
}
}
Help me to understand how to make the output result after each sw.WriteLine(...)?
Updated
It is not working with ftp. Why?
Initialization:
Test test = new Test();
test.start();
Console.ReadKey();
Class Test:
class Test
{
static StringBuilder StdOutput = new StringBuilder();
Process p = null;
Queue<string> cmdQueue = new Queue<string>();
public void start(){
cmdQueue = new Queue<string>();
cmdQueue.Enqueue("cd c:\\");
cmdQueue.Enqueue("dir");
cmdQueue.Enqueue("ftp");
cmdQueue.Enqueue("open us1.hostedftp.com");
cmdQueue.Enqueue("z3r9#ya.ru");
cmdQueue.Enqueue("123456");
cmdQueue.Enqueue("dir");
setupProcess();
startProcess();
}
private void setupProcess()
{
p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
StdOutput = new StringBuilder();
p.StartInfo = info;
}
private async void startProcess()
{
p.Start();
p.BeginOutputReadLine();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
while (cmdQueue.Count > 0)
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd != "")
{
await sw.WriteLineAsync(cmd);
Thread.Sleep(100);
//System.Console.WriteLine(StdOutput);
}
else
{
break;
}
}
Console.WriteLine(StdOutput);
}
p.WaitForExit();
}
}
private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
StdOutput.Append(Environment.NewLine + outLine.Data);
//System.Console.WriteLine(Environment.NewLine + outLine.Data);
}
}
}
I assume that you are actually asking about how to catch outputs from all the commands you want to have executed in the (one) process.
Here is a version of a solution I came up with a long time ago, when I was a rookie here..
The trick is to collect the output as is comes along by listening to events the Process will trigger whenever output gets created: OutputDataReceived and ErrorDataReceived. We need to run things async for this to work, so it will look a little more complicated than the usual examples, which only have one process executing one command..:
First a few variables:
Queue<string> cmdQueue = new Queue<string>();
static StringBuilder StdOutput = new StringBuilder();
static StringBuilder ErrOutput = new StringBuilder();
Process p = null;
Task processTask = null;
bool processIsRunning = false;
Here is a button click event that starts processing all commands from a multiline TextBox. Output gets collected in the two StringBuilders; when the queue is empty, I wait a little longer..:
private void button1_Click(object sender, EventArgs e)
{
cmdQueue = new Queue<string>(tb_commands.Lines.ToList());
setupProcess();
startProcessTask();
while (cmdQueue.Count > 0) Thread.Sleep(100);
Thread.Sleep(500);
tb_out.AppendText(StdOutput + "\r\n" + ErrOutput + "\r\n");
}
Here is the routine that set up the Process. Here we register two events that will notify us when there are lines in the output streams..:
private void setupProcess()
{
p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataHandler);
StdOutput = new StringBuilder();
ErrOutput = new StringBuilder();
p.StartInfo = info;
}
After the setup we can start a Task that will start our Process asynchonously..:
private void startProcessTask()
{
var task = Task.Factory.StartNew(() => startProcess());
processTask = task;
}
..and finally here is the async method that after starting the Process and beginning with the asynchronous read operations on the redirected streams, keeps feeding it all lines from the command queue.
private async void startProcess()
{
try { p.Start(); processIsRunning = true; } catch
{
ErrOutput.Append("\r\nError starting cmd process.");
processIsRunning = false;
}
p.BeginOutputReadLine();
p.BeginErrorReadLine();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
do
{
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd != "") await sw.WriteLineAsync(cmd);
} catch { }
} while (processIsRunning);
try { p.WaitForExit(); } catch { ErrOutput.Append("WaitForExit Error.\r\n"); }
}
}
The last pieces are the two events we have registered for reading the output from the two streams and adding them to the StringBuilders:
private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
StdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private static void ErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
ErrOutput.Append(Environment.NewLine + outLine.Data);
}
}
Note that this works fine for all sorts of commands you can feed into the process, including FTP. Here I change my codepage, show the images I have before, log in to an FTP server, call up the help page, cd and dir, download an image, close the connection and check the images I have now..:
One Caveat: There must be something wrong in the way I wrote this, as VS keeps complaining about a System.InvalidOperationException and the exe file hogs ~10% cpu. Helping me out would be very much appreciated..
I need get an enter command for my C# process.
my C# code work for test batch file, not for the fortran console application
I read this post: C# Process Call, Interact with Standard Input and Standard Output
but it does not work for me.
Anyone can give me some tips?
my sample batch file (test.bat):
note: this batch file simulate my another application.
#echo off
cls
dir
echo "please input enter key"
pause
tree
my C# code:
private Process _process = null;
private bool _bEnterCR = false;
private void Begin_Click(object sender, RoutedEventArgs e)
{
this.tbOutput.Text = "";
_bEnterCR = false;
if (null != _process)
{
_process.Dispose();
}
string strPathName = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
// Use ProcessStartInfo class
_process = new Process();
_process.StartInfo.CreateNoWindow = true;
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.FileName = strPathName + "\\test.bat";
_process.StartInfo.WorkingDirectory = strPathName + "\\Output\\";
_process.StartInfo.RedirectStandardError = true;
_process.StartInfo.RedirectStandardInput = true;
_process.StartInfo.RedirectStandardOutput = true;
//_process.EnableRaisingEvents = true;
_process.OutputDataReceived += new DataReceivedEventHandler(OnOutputDataReceived);
_process.ErrorDataReceived += new DataReceivedEventHandler(OnOutputDataReceived);
//_process.Exited += new EventHandler(OnProcessExited);
_process.Start();
_process.BeginOutputReadLine();
_process.BeginErrorReadLine();
}
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (String.IsNullOrEmpty(e.Data) == false)
{
if (e.Data.Contains("please input enter key") && _bEnterCR == false)
{
Debug.WriteLine("Pause Found, Entering <CR> command");
// work for batch file, not for console application
_process.StandardInput.Write(#"\r\n");
_bEnterCR = true;
}
new Thread(() =>
{
this.Dispatcher.Invoke(new Action(() =>
{
tbOutput.AppendText(e.Data + Environment.NewLine);
}));
}).Start();
}
}
I've got a problem where I need to enable a card that has been disabled already and a searcher on WMI NetworkAdapter does not return the object.
I can think of a possible way to do this but I've been unable to get it to work, thats is to create a managementObject using this as the constructor name. but this just throws exceptions
{\\.\root\CIMV2:Win32_NetworkAdapter.NetConnectionID='Wireless Network Connection'}
The other way was to shell a netsh and enable the device, which is kind of ugly, or to use shell32/dll "Enable" to do the same, again, both passing just the name. Ive been getting the name from a registry scan on HKLM\SYSTEM\CurrentControlSet\Network and looking for MediaType=2 to get a string list of wireless devices. All is good if I run the application while the adapter is enabled as I can get the networkObject for the wireless device but it all falls over if the application starts while the wireless device is disabled.
Thanks
Edit : This is the code that I would love to work but no go :(
using System;
using System.Management;
class Sample
{
public static int Main(string[] args)
{
ManagementObject mObj = new ManagementObject("\\\\.\\root\\CIMV2:Win32_NetworkAdapter.NetConnectionID=\"Wireless Network Connection\"");
mObj.InvokeMethod("Enable", null);
return 0;
}
}
This method essentially is using C# to leverage the WMI and Win32_NetworkAdapter Class. It should have methods built in for:
Enable
Disable
So you can execute your command on a Selected interface.
You can achieve that in this manner:
SelectQuery query = new SelectQuery("Win32_NetworkAdapter", "NetConnectionStatus=2");
ManagementObjectSearcher search = new ManagementObjectSearcher(query);
foreach(ManagementObject result in search.Get())
{
NetworkAdapter adapter = new NetworkAdapter(result);
// Identify the adapter you wish to disable here.
// In particular, check the AdapterType and
// Description properties.
// Here, we're selecting the LAN adapters.
if (adapter.AdapterType.Equals("Ethernet 802.3"))
{
adapter.Disable();
}
}
There is a blog that actually outlines such a task; it defines how to create a Wrapper around the WMI Class.
Another solution may be to also use the ControlService (advapi32).
[DllImport("advapi32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ControlService(IntPtr hService, SERVICE_CONTROL dwControl, ref SERVICE_STATUS lpServiceStatus);
Hopefully that one of those ways help..
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Security.Principal;
namespace WifiRouter
{
public partial class Form1 : Form
{
bool connect = false;
public Form1()
{
InitializeComponent();
}
public static bool IsAdmin()
{
WindowsIdentity id = WindowsIdentity.GetCurrent();
WindowsPrincipal p = new WindowsPrincipal(id);
return p.IsInRole(WindowsBuiltInRole.Administrator);
}
public void RestartElevated()
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.CreateNoWindow = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = System.Windows.Forms.Application.ExecutablePath;
startInfo.Verb = "runas";
try
{
Process p = Process.Start(startInfo);
}
catch
{
}
System.Windows.Forms.Application.Exit();
}
private void button1_Click(object sender, EventArgs e)
{
string ssid = textBox1.Text, key = textBox2.Text;
if (!connect)
{
if (String.IsNullOrEmpty(textBox1.Text))
{
MessageBox.Show("SSID cannot be left blank !",
"Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
if (textBox2.Text == null || textBox2.Text == "")
{
MessageBox.Show("Key value cannot be left blank !",
"Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
if (key.Length >= 6)
{
Hotspot(ssid, key, true);
textBox1.Enabled = false;
textBox2.Enabled = false;
button1.Text = "Stop";
connect = true;
}
else
{
MessageBox.Show("Key should be more then or Equal to 6 Characters !",
"Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}
else
{
Hotspot(null, null, false);
textBox1.Enabled = true;
textBox2.Enabled = true;
button1.Text = "Start";
connect = false;
}
}
private void Hotspot(string ssid, string key,bool status)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe");
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
Process process = Process.Start(processStartInfo);
if (process != null)
{
if (status)
{
process.StandardInput.WriteLine("netsh wlan set hostednetwork mode=allow ssid=" + ssid + " key=" + key);
process.StandardInput.WriteLine("netsh wlan start hosted network");
process.StandardInput.Close();
}
else
{
process.StandardInput.WriteLine("netsh wlan stop hostednetwork");
process.StandardInput.Close();
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
if (!IsAdmin())
{
RestartElevated();
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Hotspot(null, null, false);
Application.Exit();
}
}
}