Install service and add registry entry under Vista+ - c#

I have a program that runs as a desktop application, but can also be installed as a windows service. The installation is done by using an "install as service" button on the GUI. The event handler for this button looks like this:
ProcessStartInfo psi = new ProcessStartInfo("sc", "description " +
this.ServiceName + " \"" +
((AssemblyDescriptionAttribute)attributes[0]).Description + "\"")
psi.CreateNoWindow = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
psi.Verb = "runas";
Process.Start(psi).WaitForExit();
What I would like to do now is to add some additional registry entries under say:
HKLM\SOFTWARE\MyCompany\Services
which also needs elevated privileges. But if I use the same procedure as described above with the command "REG ADD" the UAC dialog would appear more than once - and I don't want that.
So whats the best way to install a windows service and add a registry entry under HKLM with only having the UAC prompt once?

Create a small exe or a batch file that does both things (the sc and the reg update). Launch that instead of sc.

As for installing service.
You can try topshelf. It is easy to use and very powerful.

I ended up calling myself with admin priviliges and a special parameter myprogram.exe -i and then branch into a special method which uses the ServiceInstaller class to install the program as a service (instead of calling sc).

Related

C# start Child Process (in CMD window) as Administrator with custom environment variables

I have a program which wraps around some Windows SDK executables and opens them in a separate CMD window.
Process process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/C signtool.exe [args] & pause";
process.StartInfo.Verb = "runas";
process.Start();
Right now, I have the Windows SDK folder added to my system's Path environment variable. Is there a way to programmatically add the Windows SDK folder to the Path environment variable of the user OR run the process with the SDK folder added to the Path variable of that particular CMD window?
This is the folder I need added to each CMD window's Path variable:
C:\Program Files (x86)\Windows Kits\10\bin\10.0.16299.0\x86
This sub-process must run as administrator. It does not need to receive the output of the child process.
Use a ProcessStartInfo and its Environment property instance to set this up.
var startInfo = new ProcessStartInfo();
var defaultPath = startInfo.Environment["PATH"];
var newPath = "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.16299.0\\x86" + ";" + defaultPath;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/c set > D:\\env.txt";
startInfo.Verb = "runas";
startInfo.Environment["PATH"] = newPath;
startInfo.UseShellExecute = false; // required to use Environment variables
Process.Start(startInfo);
There are a number of hurdles to overcome here.
As you've discovered, the Environment and EnvironmentVariables properties of ProcessStartInfo cannot be used when UseShellExecute is true (an exception is thrown). But Verb only works when UseShellExecute is false (the Verb is silently ignored). This comes down to the differences between CreateProcess (the core Win32 function) and ShellExecute/ShellExecuteEx (the shell function).
As another commenter suggested, you might try setting the environment in the parent process and then starting the child process. However, elevated processes don't inherit the environment from a non-elevated parent process.
I would be willing to bet that there is a way to do what you want using a correct series of Win32 calls to get an elevated token and then calling something like CreateProcessAsUser. I am also willing to bet it'll be a little error-prone and ugly in C# because of the necessary struct marshaling. So instead of trying to figure that out for you, I'll offer another suggestion:
Just programmatically write a batch script that sets the environment and invokes signtool.exe. You can then invoke that batch script using the runas verb as you're currently doing.

Error (Possible Permissions Issue) When using Process.Start in C#

I'm trying to start an application called snort from a C# application, using System.Diagnostics.Process, and I need to capture its output. To achieve this, I've used the code below.
When I try to run this, I get an error which is related to loading the configuration file. I get this same error if I try to manually run the exe from a CMD without administrator privileges, so I think this is a permissions issue for the forked process, but I'm not entirely sure of that. However, I have tried the following, to no avail:
I have added to the C# application manifest (the C# application is definitely running as administrator).
I have tried using a username and password with Process.StartInfo
However, the error still remains. Also, for clarity: The process is started, I am receiving output in My OutputHandler method, etc -- the issue is with the forked exe, it is having a problem reading the specified configuration file.
The sample code is as follows:
var process = new Process();
process.StartInfo.FileName = #"C:\Snort\bin\snort.exe";
process.StartInfo.Arguments = #"-A console -i2 -c C:\Snort\etc\snort.conf -l C:\Snort\log\ -K ascii";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
This does start the required process, but as I mention that process (snort) then outputs an error when trying to read the configuration file (the same error I get if I try to manually run the same process from a CMD without admin rights--hence why I think the issue might be permissions-based).
Can anyone suggest anything else I might try to get around this. I do need to capture output, so (if I understand it correctly) the 'runas' verb doesn't help.
Thanks for your time.
I explored a few option and tried to continue on the path of permission and changed the application manifest file permission and replaced the standard permission file with:
requestedExecutionLevel level="requireAdministrator" uiAccess="false"
This did not change anything.
I am using windows 8.1 and when I visited the properties of the snort.exe file I changed the compatibility and run the application compatible with win 7 and ticked the box to run as administrator, it is now working fine.
snort.exe properties
Many Thanks to everybody

C# cmd prompt cannot see telnet.exe

I used C# with a console program to create a new cmd process, did not redirect stdin or stdout, so I could type into the command line from here.
(I was having trouble using telnet from there, so this step was just an investigation.)
Able to type into the window and receive output.
When I switched to c:Windows\system32, typing dir te*.exe shows nothing.
In another command prompt I created directly, I see the file (telnet.exe).
Any suggestions about what is wrong?
{
ProcessStartInfo startInfo = new ProcessStartInfo(#"cmd.exe");
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.WindowStyle = ProcessWindowStyle.Normal;
startInfo.CreateNoWindow = false;
startInfo.Arguments = host;
using (Process p = new Process())
{
p.StartInfo = startInfo;
p.Start();
}
}
Since Windows 7, I believe, you have to install Telnet as a Windows Feature.
Here you have a guide to enable Telnet on Win 7, but it's applicable to Win 8.1 as well as Windows 10.
Just in case you can't read the site, the steps are:
Go to Control Panel -> Programs -> Turn Windows Features on or off -> Scroll down until you find the Telnet Client option
Based on the above article, looked at project build properties.
Platform target was set to x86.
Changing to "Any CPU" at least allows me to see the program!
BTW I have looked for the answer for several days before posting this, but in the margin in related - "C# New process created cannot access certain files" gave me the info - after I created this question
Thanks, heuristics!
This is a really devious one. When you are using windows explorer or opening a command prompt directly, you are starting a 64-bit process. When you are starting the "cmd.exe" with Process.Start(), you will get the same version as the process that's starting it. In your case, you are creating a 32-bit process, so you get the 32-bit version of the command prompt. If you change your project to create target x64, you will see the files!
Why is this so? Because, depending on whether you are accessing System32 through a 32-bit or 64-bit app, you will actually be accessing different System32 folders! For more on this, follow this link:
https://superuser.com/questions/330941/some-files-in-system32-not-accessible-outside-explorer-on-windows-7

Suppress UAC Account Settings window while installing SQL Server 2008 Service pack silently

I have two account's on my PC, an admin account an user account. User account has admin privileges to install new programs. I usually work with my user account. When I want to install a SQL Server service pack 3 for SOL Server 2008, the UAC window prompts me to click yes or no to continue installation.
I don't want that to happen. I need no interruption during my installation.
How can I suppress that UAC message box ?
I am calling a .BAT file from my C# program. This is the command line:
start /WAIT C:\Temp\SQLSP3.exe /quiet
/IAcceptSQLServerLicenseTerms /Action=Patch /AllInstances
Following is the UAC prompt. Please help me in supressing this.
This is the C# code to elevate the BAT file execution with Admin Credentials.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = windrive + #"temp\SQLSP3.BAT";
p.StartInfo.Arguments = DateTime.Now.ToShortDateString().ToString().Replace('/', '-') + ".db";
p.StartInfo.UserName = "Admin";
SecureString adminpassword = new SecureString();
adminpassword = ConvertToSecureString(Password);
p.StartInfo.Password = adminpassword;
try
{
p.Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.InnerException);
Console.ReadLine();
}
This following command has disabled the UAC Prompt before installation of SQL SP3 pack.
C:\Windows\System32\cmd.exe /k %windir%\System32\reg.exe ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
C:\Windows\System32\cmd.exe /k %windir%\System32\reg.exe ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v ConsentPromptBehaviorAdmin /t REG_DWORD /d 0 /f
I posted a somewhat granular (but ugly) solution here:
http://stackoverflow.com/questions/5344021/bypass-uac-in-vbscript/34445992#34445992
Basically you need to have a task scheduler point to the install '.exe' file. The install '.exe' file would need to always have the same name and be in the same directory. My implementation fires the event from vbscript but there is no reason why you can't do it from any '.net' language. You would need to change the 'WSH' in the taskscheduler event filter to the appropriate string if you don't stick with firing the event from vbscript.
It only works if you can kick off an application from the task scheduler. I have it running on two Windows 7 laptops. It is an administrative solution. You need administrator privilege to implement it. I use it for powershell and for my UPS power backup application. I suspect I'll find other uses.

SC.EXE - Calling from C# - Access Denied

I have a batch file I wrote (proof of concept) to install a sample service I also created. It contains the following command line:
sc create serviceTest binPath= C:\Sandbox\ServiceTest\ServiceTest.exe DisplayName= "Service Test"
When I right click and select 'Run as Administrator' everything runs as expected. Running the batch file without doing this gives 'Access Denied'. This is proof that the command works.
I have also tried running the sc command via Process.Start():
const string sc = #"sc";
const string arguments = #"create serviceTest binpath= {0} DisplayName= ""Service Test""";
FileInfo fi = new FileInfo(this.txtServiceLocation.Text);
if (fi.Exists)
{
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName = sc,
Arguments = string.Format(arguments, fi.FullName),
UserName = "admin",
Password = "secret".ToSecureString(),
Domain = "MYDOMAIN",
WorkingDirectory = fi.DirectoryName,
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(psi);
}
else
MessageBox.Show("The service exe could not be found.");
I need to be able to do this programatically. What do I need to do to get this to work?
CLARIFICATION: I need to be able to do this without a user being prompted. This code will be running under a custom tfs build process.
In Windows 7 by default, runs most applications with least privilege access (non-admin) control. As your application is trying to modify the system, it needs to be elevated to Admin privilege in order to run successfully. If you want to make this app to run in admin privilege permanently, please follow the instructions as in the link
http://www.groovypost.com/howto/microsoft/automatically-run-any-application-as-admin-in-windows-7/
You need to add a UAC manifest file to your assembly in order to force UAC to run the process as an administrator. For a reference to other solutions, you can see UAC need for console application.
While I never got this to work I can still use impersonation via code and also ensure that the tfs build account has appropriate access. This is what I had to do.

Categories

Resources