I am creating a UI for our IT Department that shadow a user's session. First, you have to get the session ID using the following command.
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/K" + "qwinsta /server:" + ComputerName + " " + Username);
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = true;
Process = Process.Start(startInfo);
And this is what you get out.
SESSIONNAME USERNAME ID STATE TYPE DEVICE
console mlynch 8 Active
As of right now, this opens the command prompt for this command with the computer name and username you put into a couple of text boxes. You then take the session ID and input it into a 3rd text box and click the connect button. I am trying to bypass this step. Input the computer and username, click connect and it goes. I need to find a way to get the session ID and save it to a string, then reference that string in the next line of code.
if (MouseControlCheck.Checked == true)
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc.exe /shadow:" + SessionID + " /v " + ComputerName + " /control");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
else
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc.exe /shadow:" + SessionID + " /v " + ComputerName);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
How do I read the session ID to a string?
TLDR: I want to skip over console and mlynch and just get to the number under ID. This is not going to be used to get the local machine ID, I want the ID of a computer on the network.
Based on this post, c# how to determine session id of specific logged in windows user, might be easier to just use windows api to get the user session info. This removes the need to call cmd and parse the output. I modified it slightly to get a specific user
class Program
{
static void Main(string[] args)
{
// this code gets the users from localhost -
// can change this to a remote hostname on the network
Console.Write(UserLogins.GetUser("COMPUTER_NAME", "USER_NAME"));
Console.ReadKey();
}
public class UserLogins
{
[DllImport("wtsapi32.dll")]
static extern IntPtr WTSOpenServer([MarshalAs(UnmanagedType.LPStr)] String pServerName);
[DllImport("wtsapi32.dll")]
static extern void WTSCloseServer(IntPtr hServer);
[DllImport("wtsapi32.dll")]
static extern Int32 WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] Int32 Reserved,
[MarshalAs(UnmanagedType.U4)] Int32 Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)] ref Int32 pCount);
[DllImport("wtsapi32.dll")]
static extern void WTSFreeMemory(IntPtr pMemory);
[DllImport("Wtsapi32.dll")]
static extern bool WTSQuerySessionInformation(
System.IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned);
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public Int32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public String pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
public enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType
}
public enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
public static IntPtr OpenServer(String Name)
{
IntPtr server = WTSOpenServer(Name);
return server;
}
public static void CloseServer(IntPtr ServerHandle)
{
WTSCloseServer(ServerHandle);
}
public static UserInfo GetUser(string server, string username)
{
return GetUsers(server).FirstOrDefault(c => c.User.Equals(username, StringComparison.InvariantCultureIgnoreCase));
}
public static IEnumerable<UserInfo> GetUsers(String ServerName)
{
IntPtr serverHandle = IntPtr.Zero;
List<String> resultList = new List<string>();
serverHandle = OpenServer(ServerName);
try
{
IntPtr SessionInfoPtr = IntPtr.Zero;
IntPtr userPtr = IntPtr.Zero;
IntPtr domainPtr = IntPtr.Zero;
Int32 sessionCount = 0;
Int32 retVal = WTSEnumerateSessions(serverHandle, 0, 1, ref SessionInfoPtr, ref sessionCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int32 currentSession = (int)SessionInfoPtr;
uint bytes = 0;
if (retVal != 0)
{
for (int i = 0; i < sessionCount; i++)
{
WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)currentSession, typeof(WTS_SESSION_INFO));
currentSession += dataSize;
WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSUserName, out userPtr, out bytes);
WTSQuerySessionInformation(serverHandle, si.SessionID, WTS_INFO_CLASS.WTSDomainName, out domainPtr, out bytes);
yield return new UserInfo
{
Domain = Marshal.PtrToStringAnsi(domainPtr),
User = Marshal.PtrToStringAnsi(userPtr),
SessionID = si.SessionID
};
WTSFreeMemory(userPtr);
WTSFreeMemory(domainPtr);
}
WTSFreeMemory(SessionInfoPtr);
}
}
finally
{
CloseServer(serverHandle);
}
}
}
public class UserInfo
{
public string Domain { get; set; }
public string User { get; set; }
public int SessionID { get; set; }
public override string ToString()
{
return string.Format("{0}\\{1}: {2}", Domain, User, SessionID);
}
}
}
From MS site
You need to add RedirectStandardOutput = true and then Read Standardoutput.
string[] allLines;
using (Process process = new Process())
{
process.StartInfo.FileName = "ipconfig.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
// Synchronously read the standard output of the spawned process.
StreamReader reader = process.StandardOutput;
string output = reader.ReadToEnd();
// Convert all text to string array
allLines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
// Write the redirected output to this application's window.
Console.WriteLine(output);
process.WaitForExit();
}
An alternative solution to your problem would be to use WMI to get the user information (even remotely), avoiding output redirection altogether:
using Microsoft.Management.Infrastructure;
string Namespace = #"root\cimv2";
string OSQuery = "SELECT * FROM Win32_OperatingSystem";
CimSession mySession = CimSession.Create("Computer_B");
IEnumerable<CimInstance> queryInstance = mySession.QueryInstances(Namespace, "WQL", OSQuery);
I figured out what I had to do.
Process GetSessionID = new Process();
GetSessionID.StartInfo.FileName = "CMD.exe";
GetSessionID.StartInfo.Arguments = "/C" + "for /f \"skip=1 tokens=3\" %1 in ('query user " + Username + "/server:" + ComputerName + "') do #echo %1";
GetSessionID.StartInfo.RedirectStandardOutput = true;
GetSessionID.StartInfo.UseShellExecute = false;
GetSessionID.StartInfo.CreateNoWindow = true;
GetSessionID.Start();
SessionIDOutput = GetSessionID.StandardOutput.ReadToEnd();
GetSessionID.WaitForExit();
DoAllTheThingsTextBox.Text = SessionIDOutput;
if (GetSessionID.HasExited == true)
{
var digitArray = DoAllTheThingsTextBox.Text.Where(Char.IsDigit).ToArray();
SessionID = new String(digitArray);
if (MouseControlCheck.Checked == true)
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc /shadow:" + SessionID + " /v " + ComputerName + " /control");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
else
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc /shadow:" + SessionID + " /v " + ComputerName);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
}
Related
I want my Process does not cross 70% of CPU usage. And i found solution in here:
How can I programmatically limit my program's CPU usage to below 70%? . Here is the class I am trying to use from that link:
public class ProcessManager
{
[Flags]
public enum ThreadAccess : int
{
TERMINATE = (0x0001),
SUSPEND_RESUME = (0x0002),
GET_CONTEXT = (0x0008),
SET_CONTEXT = (0x0010),
SET_INFORMATION = (0x0020),
QUERY_INFORMATION = (0x0040),
SET_THREAD_TOKEN = (0x0080),
IMPERSONATE = (0x0100),
DIRECT_IMPERSONATION = (0x0200)
}
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int CloseHandle(IntPtr hThread);
public static void ThrottleProcess(int processId, double limit)
{
var process = Process.GetProcessById(processId);
var processName = process.ProcessName;
var p = new PerformanceCounter("Process", "% Processor Time", processName);
while (true)
{
var interval = 100;
Thread.Sleep(interval);
var currentUsage = p.NextValue() / Environment.ProcessorCount;
if (currentUsage < limit) continue; // Infinant loop ?
var suspensionTime = (currentUsage-limit) / currentUsage * interval;
SuspendProcess(processId);
Thread.Sleep((int)suspensionTime);
ResumeProcess(processId);
}
}
private static void SuspendProcess(int pid)
{
var process = Process.GetProcessById(pid);
if (process.ProcessName == string.Empty)
return;
foreach (ProcessThread pT in process.Threads)
{
IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
if (pOpenThread == IntPtr.Zero)
{
continue;
}
SuspendThread(pOpenThread);
CloseHandle(pOpenThread);
}
}
private static void ResumeProcess(int pid)
{
var process = Process.GetProcessById(pid);
if (process.ProcessName == string.Empty)
return;
foreach (ProcessThread pT in process.Threads)
{
IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
if (pOpenThread == IntPtr.Zero)
{
continue;
}
var suspendCount = 0;
do
{
suspendCount = ResumeThread(pOpenThread);
} while (suspendCount > 0);
CloseHandle(pOpenThread);
}
}
}
The author of this code under his post left the comment that says, use ThrottleProcess after Process.Start(). I did it, but it seems like right after Process has started, it gets inside ThrottleProcess and get stucks inside While loop. And i can't figure out what to do with it, maybe it should run method asynchronously? Like Process should run independently of Throttle isn't it?
Here's my Process method:
private string startProcess(string fileName, string args)
{
// Create Process manager
var ProcessManager = new ProcessManager();
string result = "";
Process p;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = fileName;
psi.Arguments = args;
psi.WorkingDirectory = "...\\TempFolder";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
p = Process.Start(psi);
// After it gots here, process get stuck inside while loop
ProcessManager.ThrottleProcess(p.Id , 1);
try
{
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
if (p.ExitCode != 0)
throw new Exception("Program returned with error code " + p.ExitCode);
result = output.ToString();
}
catch (Exception ex)
{
result = ex.ToString();
}
finally
{
p.Close();
p.Dispose();
}
return result;
}
Turns out you can set affinity for a process. Affinity it is the quantity of cores that your process will use. The only thing you need, is just to add for your Process method this string:
Process p = new Process();
p.ProcessorAffinity = (IntPtr)1; // or any number (your cores)
It decreased CPU overload to minimum for my case.
I need to export a registry key from the HKLM hive to a file. This is a key which I've successfully created just a few lines back in the same program. I am using a Process object to have my C# program execute the shell command:
cmd.exe /c regedit.exe /e C:\temp\CDPRegExport.txt HKEY_LOCAL_MACHINE\SOFTWARE\NPTMigration
If I execute my program, the file C:\temp\CDPRegExport.txt is not created. However, if I run the command above directly (in this same administrative console window) it works fine!
I tried adding an application manifest to ensure that my .NET program is running as an administrator.
I tried invoking reg.exe export instead of regedit.exe /e but the result is the same (I'm guessing the same DLL is ultimately being used by these 2 programs).
Here's the main Registry Export method:
static bool RegistryExport(string regKey, string destFile)
{
Cmd cmd = new Cmd()
{
CreateNoWindow = true
};
cmd.Exec(#"regedit.exe", #"/e", destFile, regKey);
Console.WriteLine("Standard Out:\r\n" + cmd.StandardOut);
Console.WriteLine("Standard Error:\r\n" + cmd.StandardErr);
if (!File.Exists(destFile))
{
AppContext.log.Critical(#"Registry export file ({0}) not found!", destFile);
return false;
}
return true;
}
...And here's Cmd.Exe():
public void Exec(string command, params string[] Parameters)
{
string fullyQualifiedCommand = #"/c " + command + GetParameters(Parameters);
Console.WriteLine(fullyQualifiedCommand);
try
{
psi = new ProcessStartInfo(#"cmd", fullyQualifiedCommand)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
};
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
using (Process process = new Process())
{
process.StartInfo = psi;
process.Start();
if (RedirectIOStreams)
{
StandardOut = process.StandardOutput.ReadToEnd();
StandardErr = process.StandardError.ReadToEnd();
}
process.WaitForExit();
}
NormalExit = true;
}
catch (Exception ex)
{
StandardOut = string.Empty;
StandardErr = ex.ToString();
}
}
private static string GetParameters(string[] Parameters)
{
string expression = string.Empty;
if (Parameters.Length == 0)
return string.Empty;
for (int index=0; index<Parameters.Length; index++)
{
if (Parameters[index].Contains(" "))
expression += " \"" + Parameters[index] + "\"";
else
expression += " " + Parameters[index];
}
return expression;
}
When the program uses regedit.exe both standard out and standard error are simply blank.
When it uses reg.exe export however, standard error shows:
"ERROR: The system was unable to find the specified registry key or value."
Again, this is odd because if I invoke the exact same reg.exe or regedit.exe syntax directly via the command window, it works fine!
If 32-bit process on 64-bit OS, you must disable the WOW64 file system redirection
This test works for me (Windows 10, VS 2015 ) =>
bool bWow64 = false;
IsWow64Process(Process.GetCurrentProcess().Handle, out bWow64);
if (bWow64)
{
IntPtr OldValue = IntPtr.Zero;
bool bRet = Wow64DisableWow64FsRedirection(out OldValue);
}
string sKey = #"HKEY_LOCAL_MACHINE\SOFTWARE\NPTMigration";
string sFile = #"C:\temp\CDPRegExport.txt";
using (Process process = new Process())
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.WindowStyle = ProcessWindowStyle.Hidden;
psi.FileName = "reg";
psi.Arguments = "export " + "" + sKey + "" + " " + "" + sFile + "";
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
process.StartInfo = psi;
process.Start();
using (StreamReader reader = process.StandardOutput)
{
string sResult = reader.ReadToEnd();
Console.Write(sResult);
}
}
with declarations :
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool IsWow64Process(IntPtr hProcess, out bool Wow64Process);
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool Wow64DisableWow64FsRedirection(out IntPtr OldValue);
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
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'm trying to check wether a directory exist on not or a local network. After some research on stackoverflow and MSDN, I develop my code by using impersonate method. The problem is it's not working very well, The Directory.exists() method always return False Here you have my code (it's nearly the same as the one from MSDN):
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
class Environment
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_LOGON_INTERACTIVE = 2;
private void m_SendAlertes()
{
SafeTokenHandle safeTokenHandle;
string v_pathToDir = "\\192.168.1.199\Clients SiteInternet";
if (!LogonUser("RKalculateur", "SERVEUR2",
"riskedge", LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle))
{
int ret = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(ret);
}
using (safeTokenHandle)
{
using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
{
using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
if (Directory.Exists(#v_pathToDir))
{
// Proceed code here
}
}
}
}
}
}
Here you have a picture of the rights for this directory :
It's probably an issue connected to user permissions.
From MSDN:
If you do not have at a minimum read-only permission to the
directory, the Exists method will return false.
If you're using local account and not domain account, using Directory.Exists() method is problematic.
I had similar problem in the past: I had to check if a net share existed in my network and there was no domain. Your way didn't work for me. In the end, I gave up on Directory.Exists() method and ended up using NET USE command ( http://www.cezeo.com/tips-and-tricks/net-use-command/ )
bool exists = false;
string output = "";
string error = "";
System.Diagnostics.Process process = new System.Diagnostics.Process();
process = new System.Diagnostics.Process();
ExecuteShellCommand(process, "NET USE", "\""+ #path + "\" "+
this.password+ " /USER:"+machinename+"\\"+username + " /PERSISTENT:NO",
ref output, ref error);
Console.WriteLine("\r\n\t__________________________"+
"\r\n\tOutput:" + output.Trim().Replace("\r", " ") +
"\r\n\tError: " + error.Trim().Replace("\r"," "));
if (output.Length>0 && error.Length==0)
{
exists = true;
}
process = new System.Diagnostics.Process();
ExecuteShellCommand(process, "NET USE", " /DELETE " + #path,
ref output, ref error);
....
public void ExecuteShellCommand(System.Diagnostics.Process process, string fileToExecute,
string command, ref string output, ref string error)
{
try
{
string CMD = string.Format(System.Globalization.CultureInfo.InvariantCulture, #"{0}\cmd.exe", new object[] { Environment.SystemDirectory });
string args = string.Format(System.Globalization.CultureInfo.InvariantCulture, "/C {0}", new object[] { fileToExecute });
if (command != null && command.Length > 0)
{
args += string.Format(System.Globalization.CultureInfo.InvariantCulture, " {0}", new object[] { command, System.Globalization.CultureInfo.InvariantCulture });
}
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(CMD, args);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardError = true;
process.StartInfo = startInfo;
process.Start();
// timeout
process.WaitForExit(10 * 1000);
output = process.StandardOutput.ReadToEnd();
error = process.StandardError.ReadToEnd();
}
catch (Win32Exception e32)
{
Console.WriteLine("Win32 Exception caught in process: {0}", e32.ToString());
}
catch (Exception e
{
Console.WriteLine("Exception caught in process: {0}", e.ToString());
}
finally
{
// close process and do cleanup
process.Close();
process.Dispose();
process = null;
}
}
I know it's a hack but it worked for me and it's a possibility. (Although you may need to set up a proper net share)