Rename computer name with .NET - c#

I am trying to rename a computer name from a C# application.
public class ComputerSystem : IComputerSystem
{
private readonly ManagementObject computerSystemObject;
public ComputerSystem()
{
var computerPath = string.Format("Win32_ComputerSystem.Name='{0}'", Environment.MachineName);
computerSystemObject = new ManagementObject(new ManagementPath(computerPath));
}
public bool Rename(string newComputerName)
{
var result = false;
var renameParameters = computerSystemObject.GetMethodParameters("Rename");
renameParameters["Name"] = newComputerName;
var output = computerSystemObject.InvokeMethod("Rename", renameParameters, null);
if (output != null)
{
var returnValue = (uint)Convert.ChangeType(output.Properties["ReturnValue"].Value, typeof(uint));
result = returnValue == 0;
}
return result;
}
}
The WMI call returns error code 1355.
MSDN doesn't mention much about error codes, what does it mean and how can I fix it?

Error code 1355 means ERROR_NO_SUCH_DOMAIN: "The specified domain either does not exist or could not be contacted.".
The documentation for the Rename method states that the name must contain the domain name. For a non-domain-joined machine, try .\NewName instead of just NewName.

It's very difficult to update the PC name using any external methods due to protection of the system. The best way to do so is to use the Windows own utility of WMIC.exe to rename the PC. Just launch the wmic.exe from C# and pass rename command as argument.
exit code 0
>
public void SetMachineName(string newName)
{
// Create a new process
ProcessStartInfo process = new ProcessStartInfo();
// set name of process to "WMIC.exe"
process.FileName = "WMIC.exe";
// pass rename PC command as argument
process.Arguments = "computersystem where caption='" + System.Environment.MachineName + "' rename " + newName;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(process))
{
proc.WaitForExit();
// print the status of command
Console.WriteLine("Exit code = " + proc.ExitCode);
}
}

Related

How to get apps(processes) per user on a terminal server? [duplicate]

I'm trying to get a list of processes currently owned by the current user (Environment.UserName). Unfortunately, the Process class doesn't have any way of getting the UserName of the user owning a process.
How do you get the UserName of the user which is the owner of a process using the Process class so I can compare it to Environment.UserName?
If your solution requires a pinvoke, please provide a code example.
Thanks, your answers put me on the proper path. For those who needs a code sample:
public class App
{
public static void Main(string[] Args)
{
Management.ManagementObjectSearcher Processes = new Management.ManagementObjectSearcher("SELECT * FROM Win32_Process");
foreach (Management.ManagementObject Process in Processes.Get()) {
if (Process["ExecutablePath"] != null) {
string ExecutablePath = Process["ExecutablePath"].ToString();
string[] OwnerInfo = new string[2];
Process.InvokeMethod("GetOwner", (object[]) OwnerInfo);
Console.WriteLine(string.Format("{0}: {1}", IO.Path.GetFileName(ExecutablePath), OwnerInfo[0]));
}
}
Console.ReadLine();
}
}
The CodeProject article How To Get Process Owner ID and Current User SID by Warlib describes how to do this using both WMI and using the Win32 API via PInvoke.
The WMI code is much simpler but is slower to execute. Your question doesn't indicate which would be more appropriate for your scenario.
You will have a hard time getting the username without being an administrator on the computer.
None of the methods with WMI, through the OpenProcess or using the WTSEnumerateProcesses will give you the username unless you are an administrator. Trying to enable SeDebugPrivilege etc does not work either. I have still to see a code that works without being the admin.
If anyone know how to get this WITHOUT being an admin on the machine it is being run, please write how to do it, as I have not found out how to enable that level of access to a service user.
You might look at using System.Management (WMI). With that you can query the Win32_Process tree.
here is the MS link labelled "GetOwner Method of the Win32_Process Class"
Props to Andrew Moore for his answer, I'm merely formatting it because it didn't compile in C# 3.5.
private string GetUserName(string procName)
{
string query = "SELECT * FROM Win32_Process WHERE Name = \'" + procName + "\'";
var procs = new System.Management.ManagementObjectSearcher(query);
foreach (System.Management.ManagementObject p in procs.Get())
{
var path = p["ExecutablePath"];
if (path != null)
{
string executablePath = path.ToString();
string[] ownerInfo = new string[2];
p.InvokeMethod("GetOwner", (object[])ownerInfo);
return ownerInfo[0];
}
}
return null;
}
You'll need to add a reference to System.Management.dll for this to work.
Here's what I ended up using. It works in .NET 3.5:
using System.Linq;
using System.Management;
class Program
{
/// <summary>
/// Adapted from https://www.codeproject.com/Articles/14828/How-To-Get-Process-Owner-ID-and-Current-User-SID
/// </summary>
public static void GetProcessOwnerByProcessId(int processId, out string user, out string domain)
{
user = "???";
domain = "???";
var sq = new ObjectQuery("Select * from Win32_Process Where ProcessID = '" + processId + "'");
var searcher = new ManagementObjectSearcher(sq);
if (searcher.Get().Count != 1)
{
return;
}
var process = searcher.Get().Cast<ManagementObject>().First();
var ownerInfo = new string[2];
process.InvokeMethod("GetOwner", ownerInfo);
if (user != null)
{
user = ownerInfo[0];
}
if (domain != null)
{
domain = ownerInfo[1];
}
}
public static void Main()
{
var processId = System.Diagnostics.Process.GetCurrentProcess().Id;
string user;
string domain;
GetProcessOwnerByProcessId(processId, out user, out domain);
System.Console.WriteLine(domain + "\\" + user);
}
}

How to figure out what IIS Express instance is using a port?

I want to kill a running IIS Instance programmatically that is occupying a specific port, but it seems there is no way to figure out what IIS Instance is using a specific port.
netstat.exe just shows that the process is having the PID 4, but that's the system process. "netsh http show urlacl" does not display the occupied port at all.
The IIS Express Tray program knows this somehow. When I try to start another IIS Express instance while the port is occupied I get the following error:
"Port '40000' is already being used by process 'IIS Express' (process ID '10632').
Anyone got a clue how I can get this information?
It seems like the PID is 4 (System) because the actual listening socket is under a service called http.
I looked at what iisexpresstray.exe was using to provide a list of all running IISExpress applications. Thankfully it's managed .NET code (all in iisexpresstray.dll) that's easily decompiled.
It appears to have at least three different ways of getting the port number for a process:
Reading /port from the command-line arguments (unreliable as we know)
Running netsh http show servicestate view=requestq and parsing the output
Calling Microsoft.Web.RuntimeStatusClient.GetWorkerProcess(pid) and parsing the site URL
Unfortunately, most of the useful stuff in iisexpresstray.dll like the IisExpressHelper class is declared internal (although I imagine there're tools to generate wrappers or copy the assembly and publicize everything).
I opted to use Microsoft.Web.dll. It was in my GAC, though for some reason wasn't appearing in the list of assemblies available to add as a reference in Visual Studio, so I just copied the file out from my GAC. Once I had Microsoft.Web.dll it was just a matter of using this code:
using (var runtimeStatusClient = new RuntimeStatusClient())
{
var workerProcess = runtimeStatusClient.GetWorkerProcess(process.Id);
// Apparently an IISExpress process can run multiple sites/applications?
var apps = workerProcess.RegisteredUrlsInfo.Select(r => r.Split('|')).Select(u => new { SiteName = u[0], PhysicalPath = u[1], Url = u[2] });
// If we just assume one app
return new Uri(apps.FirstOrDefault().Url).Port;
}
You can also call RuntimeClient.GetAllWorkerProcesses to retrieve only actual worker processes.
I looked into RegisteredUrlsInfo (in Microsoft.Web.dll) as well and found that it's using two COM interfaces,
IRsca2_Core (F90F62AB-EE00-4E4F-8EA6-3805B6B25CDD)
IRsca2_WorkerProcess (B1341209-7F09-4ECD-AE5F-3EE40D921870)
Lastly, I read about a version of Microsoft.Web.Administration apparently being able to read IISExpress application info, but information was very scarce, and the one I found on my system wouldn't even let me instantiate ServerManager without admin privileges.
Here is a C# implementation of calling netsh.exe as recommended within the answer by #makhdumi:
Usage:
static public bool TryGetCurrentProcessRegisteredHttpPort(out List<int> ports, out Exception ex)
{
NetshInvoker netsh = new NetshInvoker();
return netsh.TryGetHttpPortUseByProcessId(Process.GetCurrentProcess().Id, out ports, out ex);
}
Implementation:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace YourCompanyName.Server.ServerCommon.Utility
{
/// <summary>
/// Invoke netsh.exe and extract information from its output.
/// Source: #crokusek, https://stackoverflow.com/questions/32196188
/// #GETah, https://stackoverflow.com/a/8274758/538763
/// </summary>
public class NetshInvoker
{
const string NetshHttpShowServiceStateViewRequestqArgs = "http show servicestate view=requestq";
public NetshInvoker()
{
}
/// <summary>
/// Call netsh.exe to determine the http port number used by a given windowsPid (e.g. an IIS Express process)
/// </summary>
/// <param name="windowsPid">For example an IIS Express process</param>
/// <param name="port"></param>
/// <param name="ex"></param>
/// <returns></returns>
public bool TryGetHttpPortUseByProcessId(Int32 windowsPid, out List<Int32> ports, out Exception ex)
{
ports = null;
try
{
if (!TryQueryProcessIdRegisteredUrls(out Dictionary<Int32, List<string>> pidToUrlMap, out ex))
return false;
if (!pidToUrlMap.TryGetValue(windowsPid, out List<string> urls))
{
throw new Exception(String.Format("Unable to locate windowsPid {0} in '{1}' output.",
windowsPid, "netsh " + NetshHttpShowServiceStateViewRequestqArgs));
}
if (!urls.Any())
{
throw new Exception(String.Format("WindowsPid {0} did not reference any URLs in '{1}' output.",
windowsPid, "netsh " + NetshHttpShowServiceStateViewRequestqArgs));
}
ports = urls
.Select(u => new Uri(u).Port)
.ToList();
return true;
}
catch (Exception ex_)
{
ex = ex_;
return false;
}
}
private bool TryQueryProcessIdRegisteredUrls(out Dictionary<Int32, List<string>> pidToUrlMap, out Exception ex)
{
if (!TryExecNetsh(NetshHttpShowServiceStateViewRequestqArgs, out string output, out ex))
{
pidToUrlMap = null;
return false;
}
bool gotRequestQueueName = false;
bool gotPidStart = false;
int currentPid = 0;
bool gotUrlStart = false;
pidToUrlMap = new Dictionary<int, List<string>>();
foreach (string line in output.Split('\n').Select(s => s.Trim()))
{
if (!gotRequestQueueName)
{
gotRequestQueueName = line.StartsWith("Request queue name:");
}
else if (!gotPidStart)
{
gotPidStart = line.StartsWith("Process IDs:");
}
else if (currentPid == 0)
{
Int32.TryParse(line, out currentPid); // just get the first Pid, ignore others.
}
else if (!gotUrlStart)
{
gotUrlStart = line.StartsWith("Registered URLs:");
}
else if (line.ToLowerInvariant().StartsWith("http"))
{
if (!pidToUrlMap.TryGetValue(currentPid, out List<string> urls))
pidToUrlMap[currentPid] = urls = new List<string>();
urls.Add(line);
}
else // reset
{
gotRequestQueueName = false;
gotPidStart = false;
currentPid = 0;
gotUrlStart = false;
}
}
return true;
}
private bool TryExecNetsh(string args, out string output, out Exception exception)
{
output = null;
exception = null;
try
{
// From #GETah, https://stackoverflow.com/a/8274758/538763
Process p = new Process();
p.StartInfo.FileName = "netsh.exe";
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
output = p.StandardOutput.ReadToEnd();
return true;
}
catch (Exception ex)
{
exception = ex;
return false;
}
}
}
}
In my case I just output "Command line" column in Task manager and it's getting obvious, which IISExpress is that:
You can run below command to get the information of the executable and its PID
netstat -a -n -o -b | find "iisexpress.exe"

how to find out the path of the program using c#

I found a snippet of code explaining how to use System.Diagnostics.Process.Start to run an external program in C#. The snippet shows running cmd.exe, which is in the path.
Let's assume that there is some external program (Beyond Compare for example). I don't know if it is installed on the PC. How can I check if this program is installed using C#? If the program is installed, I would like to find the path so that I can launch it.
I found this question, which directed me to this this article.
I've modified the source for readability, and to solve your problem specifically (note that I've guessed the description and executable name of Beyond Compare.)
You can call it like this, from your main:
string path = FindAppPath("Beyond Compare");
if (path == null)
{
Console.WriteLine("Failed to find program path.");
return;
}
path += "BeyondCompare.exe";
if (File.Exists(path))
{
Process beyondCompare = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = path + "BeyondCompare.exe",
Arguments = string.Empty // You may need to specify args.
}
};
beyondCompare.Start();
}
The source for FindAppPath follows:
static string FindAppPath(string appName)
{
// If you don't use contracts, check this and throw ArgumentException
Contract.Requires(!string.IsNullOrEmpty(appName));
const string keyPath =
#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(keyPath))
{
var installed =
(from skName in key.GetSubKeyNames()
let subkey = key.OpenSubKey(skName)
select new
{
name = subkey.GetValue("DisplayName") as string,
path = subkey.GetValue("InstallLocation") as string
}).ToList();
var desired = installed.FindAll(
program => program.name != null &&
program.name.Contains(appName) &&
!String.IsNullOrEmpty(program.path));
return (desired.Count > 0) ? desired[0].path : null;
}
}
Keep in mind that this method returns the first matching path, so don't feed it an appName argument that's too generic (eg. "Microsoft") or you probably won't get what you're looking for.
Well, if you're trying to see if a program exists where you're looking for it (like BeyondCompare.exe), you can just use a call to:
System.IO.File.Exists("path_to_program.exe");
If it returns true, then you know your program exists and you can run it with your process runner code. If it returns false, then you know it's not there and you shouldn't launch your process.
If I'm misunderstanding your question, please let me know and I'll update my answer accordingly.
Thanks. Hope this helps!
Simple logic to do for this.
string filepath = "c:\windows\test.exe";
bool bOk = false;
try
{
bOk = System.IO.File.Exists(filepath);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
if (!bOk)
{
Console.WriteLine("Error: Invalid Path");
}
else
{
Process p = new Process();
p.StartInfo.FileName = filepath;
p.StartInfo.Arguments = "/c dir *.cs";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine("Output:");
Console.WriteLine(output);
}
Are you sure you don't need to make that check. Simply start the program without path (only the filename) and set ProcessStartInfo.UseShellExecute = true.
Windows will look for the app in its list of installed app. If it doesn't find it, Process.Start()will fail. The interesting thing is that you never had to care about where the app is stored.

Automatically starting a java-web-start application from C# and .NET, .WaitForExit(); doesn't work as desired

I'm using a C# application to launch a java web application. I'd like my C# application to know when the user closes out of the java app. I'm using this code:
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
Process.Start(psi).WaitForExit();
This code successfully launches the java program, however, WaitForExit() is called and immediately returns. I believe this is because "javaws" simply starts another process called "javaw" and then "javaws" closes. Is there any way to A) wait for all child processes to finish too, or B) wait for a specific child process?
--
I've discovered this related post: Are javaws exit codes really broken?, though the answer there suggests that 1.6.0_23 has fixed it, I am seeing the exact same behavior on my development machine with Java 1.6.0_23. What I am looking for now is a workaround to make the above code work as expected. I need to start this jnlp file, wait for its execution to complete, and then do some additional code in C# program. The Java application is out of my control, so I can't add the functionality there.
--
For anyone wondering, the final solution looks like this:
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
Process.Start(psi).WaitForExit();
var javaw = Process.GetProcessesByName("javaw");
javaw.Single(ja => ja.StartTime.Equals(javaw.Max(j => j.StartTime))).WaitForExit();
This provides the added benefit of only waiting for the most recently started javaw process, in the event there are other java web apps running on the machine.
You can get desired process from list of running processes and then wait it for exit:
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
// make sure child process is already started
Process.Start(psi).WaitForExit();
foreach (Process p in Process.GetProcessesByName("javaw"))
{
p.WaitForExit();
}
To kill not only javaw, but all of child processes you need to get process ID of javaws and compare it to parent ID of all running processes. Here is complete code:
(extension method comes from here)
class Program
{
static void Main(string[] args)
{
var javaws = File.Exists(#"C:\Program Files\Java\jre6\bin\javaws.exe") ? #"C:\Program Files\Java\jre6\bin\javaws.exe" : #"C:\Program Files (x86)\Java\jre6\bin\javaws.exe";
var psi = new ProcessStartInfo(javaws, String.Format("http://{0}:/appstart.jnlp", hostAddress));
var parentProc = Process.Start(psi);
parentProc.WaitForExit();
foreach (Process p in Process.GetProcesses())
{
if (p.Parent().Id == parentProc.Id)
p.WaitForExit();
}
}
}
public static class ProcessExtensions
{
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
Looking at some documentation, it seems you can pass '-wait' as a parameter to javaws to make it wait until the application exits.
http://download.oracle.com/javase/1.5.0/docs/guide/javaws/developersguide/javaws.html

How can a Windows Service determine its ServiceName?

I've looked and couldn't find what should be a simple question:
How can a Windows Service determine the ServiceName for which it was started?
I know the installation can hack at the registry and add a command line argument, but logically that seems like it should be unnecessary, hence this question.
I'm hoping to run multiple copies of a single binary more cleanly than the registry hack.
Edit:
This is written in C#. My apps Main() entry point does different things, depending on
command line arguments:
Install or Uninstall the service. The command line can provide a non-default
ServiceName and can change the number of worker threads.
Run as a command-line executable (for debugging),
Run as a "Windows Service". Here, it creates an instance of my ServiceBase-derived
class, then calls System.ServiceProcess.ServiceBase.Run(instance);
Currently, the installation step appends the service name and thread count to the ImagePath in the registry so the app can determine it's ServiceName.
From: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
Here is a WMI solution. Overriding the ServiceBase.ServiceMainCallback() might also work, but this seems to work for me...
protected String GetServiceName()
{
// Calling System.ServiceProcess.ServiceBase::ServiceNamea allways returns
// an empty string,
// see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
// So we have to do some more work to find out our service name, this only works if
// the process contains a single service, if there are more than one services hosted
// in the process you will have to do something else
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
String query = "SELECT * FROM Win32_Service where ProcessId = " + processId;
System.Management.ManagementObjectSearcher searcher =
new System.Management.ManagementObjectSearcher(query);
foreach (System.Management.ManagementObject queryObj in searcher.Get()) {
return queryObj["Name"].ToString();
}
throw new Exception("Can not get the ServiceName");
}
ServiceBase.ServiceName property gives the compile-time name of service. If you specify a different name when installing the service, then ServiceName attribute will not give correct name. So, I had to use below code to obtain the service name of my service.
It's an alternative (without using LINQ) to NVRAM's method:
/**
* Returns the service name of currently running windows service.
*/
static String getServiceName()
{
ServiceController[] scServices;
scServices = ServiceController.GetServices();
// Display the list of services currently running on this computer.
int my_pid = System.Diagnostics.Process.GetCurrentProcess().Id;
foreach (ServiceController scTemp in scServices)
{
// Write the service name and the display name
// for each running service.
// Query WMI for additional information about this service.
// Display the start name (LocalSytem, etc) and the service
// description.
ManagementObject wmiService;
wmiService = new ManagementObject("Win32_Service.Name='" + scTemp.ServiceName + "'");
wmiService.Get();
int id = Convert.ToInt32(wmiService["ProcessId"]);
if (id == my_pid)
{
return scTemp.ServiceName;
#if IS_CONSOLE
Console.WriteLine();
Console.WriteLine(" Service : {0}", scTemp.ServiceName);
Console.WriteLine(" Display name: {0}", scTemp.DisplayName);
Console.WriteLine(" Start name: {0}", wmiService["StartName"]);
Console.WriteLine(" Description: {0}", wmiService["Description"]);
Console.WriteLine(" Found.......");
#endif
}
}
return "NotFound";
}
I was incorrectly trying to obtain the name of windows service as first line in main() without first calling ServiceBase.Run(). We must register our executable as service using ServiceBase.Run() before obtaining its name.
Ref.: http://msdn.microsoft.com/en-us/library/hde9d63a.aspx#Y320
Short version with Linq
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Service where ProcessId = " + processId);
ManagementObjectCollection collection = searcher.Get();
var serviceName = (string)collection.Cast<ManagementBaseObject>().First()["Name"];
By searching for a better solution i tried this:
string serviceName = "myDynamicServiceName";
string serviceBin = "path\\to\\Service.exe";
string configFile = "path\\to\\myConfig.xml";
string credentials = "obj= .\\mytestuser password= test";
string scCommand = string.Format( "sc create {0} start= auto binPath= \"\\\"{1}\\\" -ini={2} -sn={3}\" type= own{4}", serviceName, serviceBin, configFile , serviceName ,credentials );
I passed the servicename and an configuration file to the binpath.
The service was installed by using the SC.exe (i don't use the installutil!)
On the service you can get the Commandline-Arguments
protected override void OnStart(string[] args){
string binpath = new System.IO.FileInfo(System.Reflection.Assembly.GetAssembly(this.GetType()).Location).DirectoryName + "\\";
System.IO.StreamWriter sw = new System.IO.StreamWriter( binpath + "test.log");
sw.WriteLine( binpath );
string[] cmdArgs = System.Environment.GetCommandLineArgs();
foreach (string item in cmdArgs) {
sw.WriteLine(item);
}
sw.Flush();
sw.Dispose();
sw = null;
}
I had a chicken-and-egg problem where I needed to know the service location before completing Service.Run() (Service could be part of a client or server installation, installer named them appropriately, and I needed to detect which it was on startup)
I relied on the registry to get me the name.
public String IdentifySelfFromRegistry()
{
String executionPath = Assembly.GetEntryAssembly().Location;
Microsoft.Win32.RegistryKey services = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
#"SYSTEM\CurrentControlSet\services");
if (services != null)
{
foreach(String subkey in services.GetSubKeyNames())
{
if (executionPath.Equals(ServicePathFromServiceKey(services.OpenSubKey(subkey))))
return subkey;
}
}
return String.Empty;
}
protected static String ServicePathFromServiceKey(Microsoft.Win32.RegistryKey serviceKey)
{
if (serviceKey != null)
{
String exec = serviceKey.GetValue(ServicePathEntry) as String;
if (exec != null)
return exec.Trim('\"');
}
return String.Empty;
}
The ServiceMain() entry point that every service executable must implement receives the ServiceName as its first input argument.
If you are writing your service using .NET, the ServiceMain() entry point is implemented by .NET for you. The ServiceName is assigned when the service is installed using the ServiceProcess.ServiceBase.ServiceName property. If you are trying to customize a .NET service to support dynamic ServiceName values, I have no clue how to access the actual ServiceName at runtime.
public static bool IsServiceInstalled(string serviceName)
{
// get list of Windows services
ServiceController[] services = ServiceController.GetServices();
// try to find service name
foreach (ServiceController service in services)
{
if (service.ServiceName == serviceName)
return true;
}
return false;
}
What's wrong with this.ServiceName, if you're inside the service.cs?
i.e.:
protected override void OnStart(string[] args)
{
Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");
}

Categories

Resources