I have a program that only requires elevation to Admin on very rare occasions so I do not want to set-up my manifest to require permanent elevation.
How can I Programmatically request elevation only when I need it?
I am using C#
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool hasAdministrativeRight = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!hasAdministrativeRight)
{
RunElevated(Application.ExecutablePath);
this.Close();
Application.Exit();
}
private static bool RunElevated(string fileName)
{
//MessageBox.Show("Run: " + fileName);
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.Verb = "runas";
processInfo.FileName = fileName;
try
{
Process.Start(processInfo);
return true;
}
catch (Win32Exception)
{
// Do nothing. Probably the user canceled the UAC window
}
return false;
}
Related
i am trying to call and collect the data returned by the CMD command query user.
Calling this via cmd from the Windows-startbar gives me a normal result.
Calling this via this c# function give 0 output.
public void callQueryUser()
{
ProcessStartInfo psi = new ProcessStartInfo("cmd.exe");
Process p = Process.Start(psi);
string cmd = string.Format(#"/c query user");
psi.Arguments = cmd;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.WaitForExit();
string result = p.StandardOutput.ReadToEnd();
MessageBox.Show(result);
}
I checked and the Window says command cant befound... I also check if they are both the same cmd.exe and thats also true. It seems like calling the cmd.exe via C# makes somewhat of a differences.
Anyone any idea what i could check next ?
It's not necessary to use cmd to retrieve the information you want using Process. However, if your OS is 64-bit, your program is running as 32-bit, and you're trying to access %windir%\System32\query.exe, you need to use %windir%\Sysnative\query.exe instead.
Try the following:
Option 1:
public void callQueryUser()
{
string queryPath = string.Empty;
//use 'Sysnative' to access 64-bit files (in System32) if program is running as 32-bit process
//use 'SysWow64' to access 32-bit files on 64-bit OS
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "Sysnative", "query.exe");
else
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "query.exe");
Debug.WriteLine("queryPath: " + queryPath);
// create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(queryPath);
startInfo.Arguments = "user"; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
string result = e.Data;
MessageBox.Show(result);
}
};
p.Start(); //start
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
//waits until the process is finished before continuing
p.WaitForExit();
}
}
Option 2:
public void callQueryUser()
{
string queryPath = string.Empty;
//environment variable windir has the same value as SystemRoot
//use 'Sysnative' to access 64-bit files (in System32) if program is running as 32-bit process
//use 'SysWow64' to access 32-bit files on 64-bit OS
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess)
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "Sysnative", "query.exe");
else
queryPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "query.exe");
Debug.WriteLine("queryPath: " + queryPath);
// create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(queryPath);
startInfo.Arguments = "user"; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
p.Start(); //start
//waits until the process is finished before continuing
p.WaitForExit();
string result = p.StandardOutput.ReadToEnd();
MessageBox.Show(result);
}
}
Resources:
Accessing files from System32 directory using 32 bit application on 64 bit machine
Process Class
ProcessStartInfo Class
Environment.GetEnvironmentVariable Method
So I'm trying to run an .exe on my windows server that requires a user with specific access rights to run it. Luckily I have those rights on the server and can run the executable just fine manually.
However when I want to run it from my code, which is a .net core console API application I encounter a problem saying: 'The handle is invalid'.
Here is the method where Im trying to achieve this:
public void UpdateDataSets()
{
try
{
Process processStart = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo(#"PathToExecutable.exe");
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.Arguments = $#"MyArguments";
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.UserName = "MyUserName";
startInfo.Domain = "MyDomain";
startInfo.Password = new NetworkCredential("", "MyUserPassword").SecurePassword;
processStart.StartInfo = startInfo;
string textToRead;
using(Process process = Process.Start(startInfo))
{
textToRead = process.StandardOutput.ReadToEnd();
process.WaitForExit(20000); //time limit because maybe infinite, I dont know?
}
File.WriteAllText(#"StandardOutput.txt", textToRead);
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace.ToString());
}
}
I first tried with startInfo.Verb = "runas" and with startInfo.LoadUserProfile = true before hard-coding my active directory credentials, but I just got different errors there.
What am I doing wrong here?
I am working on a program that needs to extract a WinRar SFX automatically. Is there anyway I can programmatically click a install button once the exe is started with a Process? Here is the code that I have so far.
public bool Extract()
{
try
{
Process process = new Process();
process.StartInfo.WorkingDirectory = FilePath;
process.StartInfo.FileName = FilePath + fileName;
process.Start();
process.WaitForExit();
}
catch (Exception)
{
return false;
}
return true;
}
You could make the sfx silent with winrar.
Here is a link
I am making a Windows Form Program where I can be able to connect to an FTP Server to download data to show it to the user. I have to make the app in a way that the user can also connect through a gateway. I made the next function to log on through a gateway:
private Boolean addRoute(string ip, string gw)
{
string arg = String.Format("ADD {0} MASK 255.255.255.255 {1}", ip, gw);
return startProcess("route.exe", arg, 10000, true);
}
private Boolean startProcess(String fileName, String arguments, int timeout, bool admin)
{
try
{
Process process = new Process();
process.StartInfo.FileName = fileName;
if (System.Environment.OSVersion.Version.Major >= 6)
{
//if (admin) { process.StartInfo.UserName = "admin"; }
//process.StartInfo.UserName = "admin";
}
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = arguments;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();
if (timeout > 0)
{
if (process.WaitForExit(timeout))
{
return true;
}
else
{
return false;
}
}
else
{
process.WaitForExit();
return true;
}
}
catch (Exception e)
{
Global.LogMessageToFile(e.Message);
return false;
}
}
This code works perfect with my PC but when I test it in other PC's with Windows 7 doesn't work anymore.
I thought there was a problem with the UAC permissions so I did the solution adapted to the second answer of the link Selectively disabling UAC for specific programs on Windows Programatically but it doesn't seem to be the problem.
Do you have any other ideas about how can I accomplish this with the code of my program?
EDIT
I have been blocked to post comments, so I will answer that I have no exceptions, and what I need is connect to a lower level, from one network to another, through a gateway. That's why I have adopted this solution.
So I have a windows service running as Local System.
this windows service then starts off a WCF service.
From my machine there is no problem and works fine.
From a test console application, on the target machine, it works fine
From a windows service, on the target machine, it does not work. Nor does it throw an exception...
I am really stuck on this. :(
Could this be permissions?
m_tknCancelToken = new CancellationTokenSource();
/**************************************************************************************/
/*** Create and start the task ***/
/**************************************************************************************/
m_tskService = Task.Factory.StartNew((object o) =>
{
RunService();
},
m_tknCancelToken);
/**************************************************************************************/
/*** Set the handler when the task is cancelled or faulted ***/
/**************************************************************************************/
m_tskService.ContinueWith(
TaskEndedHandler,
TaskContinuationOptions.OnlyOnFaulted);
m_tskService.ContinueWith(
TaskEndedHandler,
TaskContinuationOptions.OnlyOnCanceled);
and then to catch the errors.
private void TaskEndedHandler(Task tskTask)
{
Log.Log(String.Format("{0} has ended", ServiceName), "WHS010CI");
if (tskTask.Exception != null)
{
Log.LogEx(tskTask.Exception, "WHS0103E");
if (tskTask.Exception.InnerExceptions != null)
{
foreach (Exception ex in tskTask.Exception.InnerExceptions)
{
Log.LogEx(ex, "WHS0104E");
}
}
}
if(tskTask.IsCanceled)
{
Log.Log(String.Format("[{0}] has been cancelled", ServiceName), "WHS0104W");
}
}
As usual it was a stupid mistake.
In my console application I was binding an SSL certificate to a port, this was removed as the powers that be did not want this in production code, which is understandable. So I removed it to then have a seperate batch file or otherwise which has to be manually run... however this is what I forgot to do. :(
for those that are interested, below is the code from my test app.
process = new Process();
process.StartInfo = BindCertToPort(port, certificate);
process.Start();
method:
private static ProcessStartInfo BindCertToPort(int iPort, X509Certificate2 certificate, bool bRemove = false)
{
string strAction = null;
string strExtraArguments = null;
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86), "netsh.exe");
if (bRemove)
{
strAction = "delete";
}
else
{
strAction = "add";
strExtraArguments = string.Format(" certhash={0} appid={{{1}}}", certificate.Thumbprint, Guid.NewGuid());
}
startInfo.Arguments = string.Format("http {0} sslcert ipport=0.0.0.0:{1}{2}", strAction, iPort, strExtraArguments);
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
return startInfo;
}