I am trying to run a batch file using C#
The batch file for the test purposes contains
msg * Test
It works if I run it manually.
Then I use the following code to run this .bat file
filePath = full path to batch file
var startInfo = new ProcessStartInfo
{
Arguments = "/C \"" + filePath + "\"",
FileName = "cmd.exe",
UseShellExecute = true
};
Process p = Process.Start(startInfo);
and it does not work ->
cannot find msg
What I am doing wrong?
P.S. the batch file should not be changed.
Try this way:
batchfile:
set "msg=%SystemRoot%\System32\msg.exe"
if not exist "%msg%" set "msg=%SystemRoot%\Sysnative\msg.exe"
"%msg%" * Hello
code:
string sFile = <full path to batch file>;
Process.Start("cmd.exe", "/c " + sFile);
Probably need some authorization, you may try the following code:
static void ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.Domain = "domain"; // Your own domain
processInfo.UserName = "userName"; // Your own user name
System.Security.SecureString s = new System.Security.SecureString();
s.AppendChar('p'); // Your own password
s.AppendChar('a');
s.AppendChar('s');
s.AppendChar('s');
s.AppendChar('w');
s.AppendChar('o');
s.AppendChar('r');
s.AppendChar('d');
processInfo.Password = s;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
// Warning: This approach can lead to deadlocks, see Edit #2
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" :
output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" :
error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
static void Main()
{
ExecuteCommand(#"C:\displayMsg.bat");
}
You may check your domain in Control Panel >> User Account >> Manage User Accounts
Source of reference: source
The problem is the location of the file (msg.exe) in the different OS versions (32bit/64bit)
I suppose it helps How can I execute msg.exe by C# in windows?
Edited:
It works fine -
class Program
{
static void Main(string[] args)
{
int ExitCode;
try
{
var returnedMsgPath = string.Empty;
if (LocateMsgExe(out returnedMsgPath))
{
var startInfo = new ProcessStartInfo()
{
FileName = returnedMsgPath,
Arguments = #"* /v Hello",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true
};
var p = Process.Start(startInfo);
p.WaitForExit();
// *** Read the streams ***
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
ExitCode = p.ExitCode;
MessageBox.Show("output >>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
p.Close();
}
else
{
MessageBox.Show("Not found");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public static bool LocateMsgExe(out string returnedMsgPath)
{
returnedMsgPath = null;
string[] msgPaths = new string[] { Environment.ExpandEnvironmentVariables(#"%windir%\system32\msg.exe"),
Environment.ExpandEnvironmentVariables(#"%windir%\sysnative\msg.exe") };
foreach (string msgPath in msgPaths)
{
if (File.Exists(msgPath))
{
returnedMsgPath = msgPath;
return true;
}
}
return false;
}
}
Current attempt at WTSEnumerateProcesses:
[DllImport("wtsapi32.dll", SetLastError = true)]
static extern Int32 WTSEnumerateProcesses(
IntPtr serverHandle, // Handle to a terminal server.
Int32 reserved, // must be 0
Int32 version, // must be 1
ref IntPtr ppProcessInfo, // pointer to array of WTS_PROCESS_INFO
ref Int32 pCount // pointer to number of processes
);
public struct WTS_PROCESS_INFO
{
public int SessionID;
public int ProcessID;
// This is spointer to a string...
public IntPtr ProcessName;
public IntPtr userSid;
}
public static void ListProcs(String ServerName)
{
IntPtr serverHandle = IntPtr.Zero;
List<string> resultList = new List<string>();
serverHandle = OpenServer(ServerName);
IntPtr ProcessInfoPtr = IntPtr.Zero;
Int32 processCount = 0;
Int32 retVal = WTSEnumerateProcesses(serverHandle, 0, 1, ref ProcessInfoPtr, ref processCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_PROCESS_INFO));
Int32 currentProcess = (int)ProcessInfoPtr;
uint bytes = 0;
if (retVal != 0)
{
WTS_PROCESS_INFO pi = (WTS_PROCESS_INFO)Marshal.PtrToStructure((System.IntPtr)currentProcess, typeof(WTS_PROCESS_INFO));
currentProcess += dataSize;
for (int i = 0; i < processCount; i++)
{
MessageBox.Show(pi.ProcessID.ToString());
}
WTSFreeMemory(ProcessInfoPtr);
}
}
I am obviously missing something pretty crucial here, as my listProcs method just returns the same ID over and over again. I need to read up on C APIs and work out what WTSEnumeratEProcesses is actually doing, and how I can query these processes.
Possible solution example code (top answer)
I am creating a self-help IT app for my organisation, where users have the ability to log off their own session as well as display all active processes and select one to terminate.
Users have no problem logging off, but I am having an issue when enumerating processes. Due to the fact that I am using a log in name and password to query active processes, the CMD window is displayed briefly every time this occurs. I can't find any solution to this in the documentation, and was hoping someone could point me in the right direction.
The code is below:
using System.Drawing;
using System;
using System.ComponentModel;
using System.Security;
using System.Diagnostics;
using System.DirectoryServices;
using System.Collections.Generic;
using System.Windows.Forms;
namespace ITHelp
{
class funcs
{
///////////////////////////////////////////////////// GET SERVERS
public static List<string> get_Servers()
{
// Get servers using AD directory searcher
List<string> serverList = new List<string>();
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
string domainContext = rootDSE.Properties["defaultNamingContext"].Value as string;
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://OU=XA76-2012,OU=Servers,OU=XenApp,dc=MYDOMAINNAME1,dc=co,dc=uk");
using (DirectorySearcher searcher = new DirectorySearcher(
searchRoot,
"(&(objectClass=computer)(!(cn=*MASTER*)))",
new string[] { "cn" },
SearchScope.Subtree))
{
foreach (SearchResult result in searcher.FindAll())
{
foreach (string server in result.Properties["cn"])
{
serverList.Add(server);
}
}
}
return serverList;
}
///////////////////////////////////////////////////// GET SESSION
public static string[] get_Session(List<string> servers, string name)
{
string[] sessionDetails = new string[3];
// Iterate through serverList to find the correct connection - then add this to the sessionDetails array
string current = "";
for (int i = 0; i < servers.Count; i++)
{
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/c QUERY SESSION " + name + " /SERVER:" + servers[i] + ".MYDOMAINNAME1.co.uk ")
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process getsess = Process.Start(startInfo);
getsess.OutputDataReceived += (x, y) => current += y.Data;
getsess.BeginOutputReadLine();
getsess.WaitForExit();
if (current.Length != 0)
{
// Session ID
// Better to use this as an identifer than session name, as this is always available
sessionDetails[0] = current.Substring(119, 4);
// Server Name
sessionDetails[1] = servers[i] + ".MYDOMAINNAME1.co.uk";
// Session Name (ica-)
// This is only available if the session is not disconnected
//sessionDetails[2] = current.Substring(76, 11);
// Removed this as it is not used - BUT COULD BE HELPFUL FOR CHECKING SESSION EXISTENCE/DETAILS
break;
}
}
return sessionDetails;
}
///////////////////////////////////////////////////// GET PROCESSES
public static Dictionary<string, string> getProc(string server, string sessID)
{
var ss = new SecureString();
ss.AppendChar('M');
ss.AppendChar('y');
ss.AppendChar('p');
ss.AppendChar('a');
ss.AppendChar('s');
ss.AppendChar('s');
ss.AppendChar('w');
ss.AppendChar('o');
ss.AppendChar('r');
ss.AppendChar('d');
ss.MakeReadOnly();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/C tasklist /S " + server + " /FI \"SESSION eq " + sessID + "\" /FO CSV /NH")
{
WindowStyle = ProcessWindowStyle.Minimized,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
WorkingDirectory = #"C:\windows\system32",
Verb = "runas",
Domain = "MYDOMAINNAME1",
UserName = "XATest",
Password = ss
};
List<string> procList = new List<string>();
Process proc = Process.Start(startInfo);
proc.OutputDataReceived += (x, y) => procList.Add(y.Data);
proc.BeginOutputReadLine();
proc.WaitForExit();
// Create a new ditionary ...
Dictionary<string, string> procDict = new Dictionary<string, string>();
for (int i = 0; i < procList.Count - 1; i++)
{
if (procDict.ContainsKey(procList[i].Split(',')[0].Trim('"')))
{
// Do nothing
}
else
{
procDict.Add(procList[i].Split(',')[0].Trim('"'), procList[i].Split(',')[1].Trim('"'));
}
}
return procDict;
}
///////////////////////////////////////////////////// RESET SESSION
public static void reset_Session(string sessID, string servName, string name)
{
// Ensure the sesion exists
if (sessID != null)
{
// Log session off
logoff_Session(sessID, servName);
// While isLoggedIn returns true, wait 1 second (checks 50 times)
for (int i = 0; i < 50; i++)
{
if (isLoggedIn(name, servName) == true)
{
System.Threading.Thread.Sleep(1000);
}
else
{
break;
}
}
// Wait here to prevent starting a session while still logged in
System.Threading.Thread.Sleep(3000);
}
// Finally, start the session (Outlook)
start_Session(name);
}
///////////////////////////////////////////////////// LOGOFF SESSION
public static void logoff_Session(string sessID, string servName)
{
Process logoff = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C LOGOFF " + sessID + " /SERVER:" + servName;
logoff.StartInfo = startInfo;
logoff.Start();
}
///////////////////////////////////////////////////// START SESSION
public static void start_Session(string name)
{
// Start Outlook
Process.Start("C:\\Users\\" + name + "\\AppData\\Roaming\\Citrix\\SelfService\\Test_Outlook2013.exe");
}
///////////////////////////////////////////////////// IS LOGGED IN
private static bool isLoggedIn(string name, string server)
{
string current = " ";
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/c QUERY SESSION " + name + " /SERVER:" + server)
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Process logcheck = Process.Start(startInfo);
logcheck.OutputDataReceived += (x, y) => current += y.Data;
logcheck.BeginOutputReadLine();
logcheck.WaitForExit();
if (current.Contains(userName()))
{
return true;
}
else
{
return false;
}
}
///////////////////////////////////////////////////// USERNAME
public static string userName()
{
// Get userName
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
userName = userName.Remove(0, 8);
return userName;
}
///////////////////////////////////////////////////// KILL PROCESS
public static void killProc(string server, string procid)
{
var ss = new SecureString();
ss.AppendChar('M');
ss.AppendChar('y');
ss.AppendChar('p');
ss.AppendChar('a');
ss.AppendChar('s');
ss.AppendChar('s');
ss.AppendChar('w');
ss.AppendChar('o');
ss.AppendChar('r');
ss.AppendChar('d');
ss.MakeReadOnly();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd", "/C taskkill /S " + server + " /PID " + procid + " /F")
{
WorkingDirectory = #"C:\windows\system32",
Verb = "runas",
Domain = "MYDOMAINNAME1",
UserName = "XATest",
Password = ss,
WindowStyle = ProcessWindowStyle.Minimized,
UseShellExecute = false,
CreateNoWindow = true
};
Process proc = Process.Start(startInfo);
proc.WaitForExit();
}
///////////////////////////////////////////////////// KILL BUSYLIGHT
public static void killBL()
{
foreach (KeyValuePair<string, string> entry in Program.proclist)
{
if (entry.Key == "Busylight.exe")
{
killProc(Program.servName, entry.Value);
System.Threading.Thread.Sleep(3000);
Process.Start("C:\\Users\\" + Program.name + "\\AppData\\Roaming\\Citrix\\SelfService\\Test_Busylight.exe");
return;
}
// Start BUSYLIGHT - the above method should close the application instantly
}
}
///////////////////////////////////////////////////// KILL LYNC
public static void killLync()
{
foreach (KeyValuePair<string, string> entry in Program.proclist)
{
if (entry.Key == "lync.exe")
{
killProc(Program.servName, entry.Value);
Process.Start("C:\\Users\\" + Program.name + "\\AppData\\Roaming\\Citrix\\SelfService\\Test_SkypeforBusiness.exe");
System.Threading.Thread.Sleep(3000); /////////////////////////////////////////////////////////
return;
}
}
}
///////////////////////////////////////////////////// CHECK RUNNING
public static bool checkRunning(string procName)
{
var ss = new SecureString();
ss.AppendChar('M');
ss.AppendChar('y');
ss.AppendChar('p');
ss.AppendChar('a');
ss.AppendChar('s');
ss.AppendChar('s');
ss.AppendChar('w');
ss.AppendChar('o');
ss.AppendChar('r');
ss.AppendChar('d');
ss.MakeReadOnly();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C tasklist /S " + Program.servName + " /FI \"SESSION eq " + Program.sessID + "\" /FO CSV /NH";
startInfo.WorkingDirectory = #"C:\windows\system32";
startInfo.Verb = "runas";
startInfo.Domain = "MYDOMAINNAME1";
startInfo.UserName = "XATest";
startInfo.Password = ss;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
string strCheck = " ";
Process proc = Process.Start(startInfo);
proc.OutputDataReceived += (x, y) => strCheck += y.Data;
proc.BeginOutputReadLine();
proc.WaitForExit();
if (strCheck.Contains(procName))
{
return true;
}
else
{
return false;
}
}
}
}
Any suggestions or feedback on this much appreciated!
Many thanks
The Remote Desktop Services APIs can certainly do all the things you want. However I'm not sure whether non-admin users are allowed to manipulate their own sessions on other machines.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa383464%28v=vs.85%29.aspx
WTSOpenServer to get a handle to a particular server.
WTSEnumerateProcesses to get a list of processes.
WTSEnumerateSessions to get a list of sessions.
WTSLogoffSession to logoff a specific session.
WTSTerminateProcess to kill a specific process.
Here's some example code using the APIs to enumerate sessions. This is using the constant WTS_CURRENT_SESSION to open the current server, but you can use WTSOpenServer to talk to some other remote server. This is code I hacked out of a live app so it won't compile as-is.
If you program C# long enough you will come across APIs that just don't exist in C# and you have to pinvoke the C versions of the API. I suggest you have a look at http://pinvoke.net if you want help learning how to pinvoke C APIs.
public const int WTS_CURRENT_SESSION = -1;
[StructLayout(LayoutKind.Sequential)]
public struct WTS_SESSION_INFO
{
public Int32 SessionID;
public IntPtr pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
[DllImport("wtsapi32.dll")]
public static extern bool WTSEnumerateSessions(
IntPtr hServer,
Int32 Reserved,
Int32 Version,
ref IntPtr ppSessionInfo,
ref Int32 pCount);
[DllImport("wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr pMemory);
IntPtr pSessions = IntPtr.Zero;
int count = 0;
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessions, ref count))
{
unsafe
{
WTS_SESSION_INFO* pHead = (WTS_SESSION_INFO*)pSessions.ToPointer();
for (int i = 0; i < count; ++i)
{
WTS_SESSION_INFO* pCurrent = (pHead + i);
var session = new Session(pCurrent->SessionID, pCurrent->State);
_activeSessions[pCurrent->SessionID] = session;
session.Id, session.IsConnected, session.IsLoggedOn, session.User.UserName);
}
}
WTSFreeMemory(pSessions);
}
From MSDN site on ProcessStartInfo.CreateNoWindow Property :
Remarks
If the UseShellExecute property is true or the UserName and Password
properties are not null, the CreateNoWindow property value is ignored
and a new window is created.
There is no workaround or resolution mentioned, and I have been unable to find one anywhere.
I have had to resort to me application briefly displaying CMD windows when running certain processes (The CreateNoWindow property works when not using UserName and Password).
Found another solution to this.
As Donovan mentioned, this can be done using WTSEnumerateProcesses.
However, if someone wanted to list remote processes (for a specific session) without marshalling c++ methods, you could also use qprocess:
qprocess /id:10 /server:servername
This lists all processes running on that session.
For more details see here
net application that use the process class for execute a exe with parameter. My Problem is that it works a few day good but now I dont can execute this exe with the parameter -.-
If I try it manuell with a terminal than it works but if I do it with C# Code than I get a message that I dont can create the file.
*** ERROR *** Cannot create qrun.inf file
Here is my c# code:
string cmd = Server.MapPath(#"~/exe/lstc_qrun.exe -s server01 -R");
string output = ExecuteCommand(cmd);
//Output = "*** ERROR *** Cannot create qrun.inf file"
here is the ExecuteCommand Method:
public static string ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
try
{
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
using (process = Process.Start(processInfo))
{
string output = process.StandardOutput.ReadToEnd();
exitCode = process.ExitCode;
return output;
}
}
catch (Exception ex)
{
return "error: " + ex.Message;
}
}
What can I do ..I work on my local machine :(
Check this reference of Process.Start() :
public static Process Start(
string fileName,
string arguments,
string userName,
SecureString password,
string domain
)
or you can just fill the ProcessStartInfo members :
processInfo.Username = 'Username' ;
processInfo.Password = 'Password' ;
processInfo.Domain = 'MyDomain' ;
I'm currently using setup.dll to install a filter driver in a programmatic way.
the following is my code:
protected bool INFSetup(string path_to_inf, bool Install)
{
string exe = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "rundll32.exe");
if (File.Exists(exe) && File.Exists(path_to_inf))
{
try
{
Process proc = new Process();
proc.EnableRaisingEvents = true;
string FileName = exe;
string Arguments = #"SETUPAPI.DLL,InstallHinfSection " + (Install ? "DefaultInstall" : "DefaultUninstall") + " 128 " + path_to_inf;
Debug.Writeline("Executing: '" + FileName + "' with arguments: " + Arguments);
ProcessStartInfo StartInfo = new ProcessStartInfo(FileName, Arguments);
StartInfo.CreateNoWindow = true;
StartInfo.UseShellExecute = false;
proc.StartInfo = StartInfo;
if (proc.Start())
{
if (proc.WaitForExit(10000))
{
return (proc.ExitCode == 0);
}
else
{
Debug.Writeline("INFSetup: proc.WaitForExit() returned false");
}
}
else
{
Debug.Writeline("INFSetup: proc.Start() returned false");
}
}
catch (Exception e)
{
Debug.Writeline("Caught Execption while installing INF: " + e.ToString());
return false;
}
}
return false;
}
although the code works fine, I was wondering if there is a way to do the same with native Win32 calls?
it would be great if anyone has a sample c# code?
Thanks
Henry
As the rundll command line suggests InstallHinfSection is also exported from SETUPAPI.DLL and is thus p/invokable. An MSFT bod posted a p/invoke signature here:
using System.Runtime.InteropServices;
[DllImport("Setupapi.dll", EntryPoint="InstallHinfSection", CallingConvention=CallingConvention.StdCall)]
public static extern void InstallHinfSection(
[In] IntPtr hwnd,
[In] IntPtr ModuleHandle,
[In, MarshalAs(UnmanagedType.LPWStr)] string CmdLineBuffer,
int nCmdShow);
InstallHinfSection(IntPtr.Zero, IntPtr.Zero, "my path", 0);
I am writing a module which will be executing any kind of shell commands related to Active Directory and other shell commands on a particular domain controller.
Some of command are working but some of commands are not working properly.
Here is the code
public static void ExecuteShellCommand(string _FileToExecute, string _CommandLine, ref string _outputMessage, ref string _errorMessage)
{
System.Diagnostics.Process _Process = null;
try
{
_Process = new System.Diagnostics.Process();
string _CMDProcess = string.Format(System.Globalization.CultureInfo.InvariantCulture, #"{0}\cmd.exe", new object[] { Environment.SystemDirectory });
string _Arguments = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", new object[] { _FileToExecute });
_Arguments = string.Format(" /C \"{0}\"", _Arguments);
Console.WriteLine("---aruguments quering : cmd.exe" + _Arguments);
System.Diagnostics.ProcessStartInfo _ProcessStartInfo = new System.Diagnostics.ProcessStartInfo(_CMDProcess, _Arguments);
_ProcessStartInfo.CreateNoWindow = true;
_ProcessStartInfo.UseShellExecute = false;
_ProcessStartInfo.RedirectStandardOutput = true;
_ProcessStartInfo.RedirectStandardInput = true;
_ProcessStartInfo.RedirectStandardError = true;
_Process.StartInfo = _ProcessStartInfo;
//_ProcessStartInfo.Domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name;
_Process.Start();
_errorMessage = _Process.StandardError.ReadToEnd();
_Process.WaitForExit();
_outputMessage = _Process.StandardOutput.ReadToEnd();
_Process.WaitForExit();
}
catch (Exception _Exception)
{
Console.WriteLine("Exception caught in process: {0}", _Exception.ToString());
}
finally
{
_Process.Close();
_Process.Dispose();
_Process = null;
}
}
CommandExecutionEngine.ExecuteShellCommand("nltest", "/logon_query /server:india.cobra.net", ref output, ref error);
Console.WriteLine("output for dir : " + output + " error : " + error);
Commands:
repadmin /showrepl
dcdiag
dcdiag /s:<dcname
command nltest executing but not returning any result. Where the other mentioned commands giving error is not recognized as internal or external command. Where if I execute command directly from console its working fine.
Here I am invoking a process under the context of domain administrator account so that I will not be have any permission issues.
Please suggest.
Possibly since UseShellExecute = false, the application location is not being found. Use the full path.