I want to uninstall a software by using my code, I have already tried wmic approach to perform uninstallation but it can't able to find my Software in the system. Is it possible to uninstall without using msi file or any setup file. I found this code but it doesn't work---
public string GetUninstallCommandFor(string productDisplayName)
{
RegistryKey localMachine = Registry.LocalMachine;
string productsRoot = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products";
RegistryKey products = localMachine.OpenSubKey(productsRoot);
string[] productFolders = products.GetSubKeyNames();
foreach (string p in productFolders)
{
RegistryKey installProperties = products.OpenSubKey(p + #"\InstallProperties");
if (installProperties != null)
{
string displayName = (string)installProperties.GetValue("DisplayName");
if ((displayName != null) && (displayName.Contains(productDisplayName)))
{
string uninstallCommand = (string)installProperties.GetValue("UninstallString");
return uninstallCommand;
}
}
}
return "";
}
The most reliable way would be to programmatically execute the following shell command:
msiexec.exe /x {PRODUCT-GUID}
If you made the original MSI you will have access to your PRODUCT-GUID, and that is all you need. No need for the actual MSI file as Windows stashes a copy of this away for exactly this purpose.
Just FYI:
Windows ® Installer. V 5.0.14393.0
msiexec /Option <Required Parameter> [Optional Parameter]
Install Options
</package | /i> <Product.msi>
Installs or configures a product
/a <Product.msi>
Administrative install - Installs a product on the network
/j<u|m> <Product.msi> [/t <Transform List>] [/g <Language ID>]
Advertises a product - m to all users, u to current user
</uninstall | /x> <Product.msi | ProductCode>
Uninstalls the product
Display Options
/quiet
Quiet mode, no user interaction
/passive
Unattended mode - progress bar only
/q[n|b|r|f]
Sets user interface level
n - No UI
b - Basic UI
r - Reduced UI
f - Full UI (default)
/help
Help information
Restart Options
/norestart
Do not restart after the installation is complete
/promptrestart
Prompts the user for restart if necessary
/forcerestart
Always restart the computer after installation
Logging Options
/l[i|w|e|a|r|u|c|m|o|p|v|x|+|!|*] <LogFile>
i - Status messages
w - Nonfatal warnings
e - All error messages
a - Start up of actions
r - Action-specific records
u - User requests
c - Initial UI parameters
m - Out-of-memory or fatal exit information
o - Out-of-disk-space messages
p - Terminal properties
v - Verbose output
x - Extra debugging information
+ - Append to existing log file
! - Flush each line to the log
* - Log all information, except for v and x options
/log <LogFile>
Equivalent of /l* <LogFile>
Update Options
/update <Update1.msp>[;Update2.msp]
Applies update(s)
/uninstall <PatchCodeGuid>[;Update2.msp] /package <Product.msi | ProductCode>
Remove update(s) for a product
Repair Options
/f[p|e|c|m|s|o|d|a|u|v] <Product.msi | ProductCode>
Repairs a product
p - only if file is missing
o - if file is missing or an older version is installed (default)
e - if file is missing or an equal or older version is installed
d - if file is missing or a different version is installed
c - if file is missing or checksum does not match the calculated value
a - forces all files to be reinstalled
u - all required user-specific registry entries (default)
m - all required computer-specific registry entries (default)
s - all existing shortcuts (default)
v - runs from source and recaches local package
Setting Public Properties
[PROPERTY=PropertyValue]
try this
We get a ManagementObject property by using the following format:
The full block of code to list installed applications:
using System.Management
private List<string> ListPrograms()
{
List<string> programs = new List<string>();
try
{
ManagementObjectSearcher mos =
new ManagementObjectSearcher("SELECT * FROM Win32_Product");
foreach (ManagementObject mo in mos.Get())
{
try
{
//more properties:
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa394378(v=vs.85).aspx
programs.Add(mo["Name"].ToString());
}
catch (Exception ex)
{
//this program may not have a name property
}
}
return programs;
}
catch (Exception ex)
{
return programs;
}
}
Now that we have a list of installed applications we should be able to pass the [Name] property to our uninstall method.
we now need to Invoke the Win32_Product method to “Uninstall”
Here is the entire block to uninstall an application, I'll get in detail after you take a look.
private bool UninstallProgram(string ProgramName)
{
try
{
ManagementObjectSearcher mos = new ManagementObjectSearcher(
"SELECT * FROM Win32_Product WHERE Name = '" + ProgramName + "'");
foreach (ManagementObject mo in mos.Get())
{
try
{
if (mo["Name"].ToString() == ProgramName)
{
object hr = mo.InvokeMethod("Uninstall", null);
return (bool)hr;
}
}
catch (Exception ex)
{
//this program may not have a name property, so an exception will be thrown
}
}
//was not found...
return false;
}
catch (Exception ex)
{
return false;
}
}
It's an old question and maybe too late to answer but here is my code anyway
public static void UninstallApplication(string uninstallString)
{
if (string.IsNullOrEmpty(uninstallString))
{
throw new ArgumentNullException(nameof(uninstallString));
}
ProcessStartInfo startInfo = new ProcessStartInfo();
int indexofexe = uninstallString.IndexOf(".exe");
//Check for executable existence
if (indexofexe > 0)
{
uninstallString = uninstallString.Replace(#"""", string.Empty);
//Get exe path
string uninstallerPath = uninstallString.Substring(0, indexofexe + 4);
startInfo.FileName = uninstallerPath;
//Check for arguments
if (uninstallerPath.Length != uninstallString.Length)
{
string args = uninstallString.Substring(uninstallerPath.Length);
if (!string.IsNullOrEmpty(args))
{
/*If not set to false You will get InvalidOperationException :
*The Process object must have the UseShellExecute property set to false in order to use environment variables.*/
startInfo.UseShellExecute = false;
startInfo.Arguments = args;
}
}
}
//Not tested
else
{
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/c " + uninstallString;
}
//Start the process
Process.Start(startInfo).WaitForExit();
}
This worked on most of my applications
Related
In Windows default settings, the pagefile size is set to Automatically manage paging file size for all drives.
This project is quite special. It is not maintained by operators. These computers are only provided for internal personnel to handle business or learn business processes. These computers have no fixed users, they are placed in public areas. When they have problems, the person on duty will reinstall the system using the fixed ghost file and execute the script to optimize the system with one click. These devices are old, and there are some thin terminals with 4G memory and 64G hard disk. Before long, new devices will replace them and have new management solutions. As a temporary transitional scheme before replacing the new scheme, I think providing a simple "optimization" program will be the simplest way to deal with it at present.
I want to change the page file size to 20% - 50% of the physical memory through C# .net-6.0 like (Test Computer's Physical memory is 32 GB)
I checked some information and it seems that wmic can meet my needs.
wmic COMPUTERSYSTEM set AutomaticManagedPagefile=false
wmic PAGEFILESET where name ="C:\\pagefile.sys" set InitialSize=1638,MaximumSize=4095
I tested these two lines of commands on Windows10 2019 LTSC, and they worked well.But when I use code to execute,I have some problems.Here is my code:
internal static void ExecuteCmd(string command)
{
try
{
Process process = new();
ProcessStartInfo startInfo = new()
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
CreateNoWindow = true,
FileName = "cmd.exe",
Arguments = "/c " + command,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
};
process.StartInfo = startInfo;
process.Start();
}
catch(Exception e)
{
LogHelper.Error(e.Message);
}
}
internal static void ChangeSystemPageFileSize()
{
string? TotalPhysicalMemory = "0";
string? InitialSize = "0";
string? MaximumSize = "0";
try
{
ManagementObjectSearcher Search = new ManagementObjectSearcher();
Search.Query = new ObjectQuery("Select * From Win32_ComputerSystem");
foreach (ManagementObject obj in Search.Get().Cast<ManagementObject>())
{
TotalPhysicalMemory = $"{Math.Round(Convert.ToDouble(obj["TotalPhysicalMemory"]) / (1024 * 1024))}";
if (!string.IsNullOrWhiteSpace(TotalPhysicalMemory))
{
break;
}
}
InitialSize = Math.Floor(int.Parse(TotalPhysicalMemory) * 0.2).ToString().Trim();
MaximumSize = Math.Floor(int.Parse(TotalPhysicalMemory) * 0.5).ToString().Trim();
CommandHelper.ExecuteCmd("wmic COMPUTERSYSTEM set AutomaticManagedPagefile=false");
CommandHelper.ExecuteCmd("wmic PAGEFILESET where name =\"C:\\\\pagefile.sys\" set InitialSize=" + InitialSize +",MaximumSize=" + MaximumSize);
}
catch (Exception e)
{
LogHelper.Error(e.Message);
}
I have obtained the correct physical memory size, but only the first command executed takes effect.When computer restart,I get windows settings like .
Thanks #ProgrammingLlama #user9938
I updated the page file size setting through WMI. There may be a little problem:
InitialSize = (UInt32)Math.Floor(TotalPhysicalMemory * 0.2);
MaximumSize = (UInt32)Math.Floor(TotalPhysicalMemory * 0.5);
ManagementObject ComputerSystem = new($#"ROOT\CIMV2:Win32_ComputerSystem.Name='{Environment.MachineName}'");
ComputerSystem["AutomaticManagedPagefile"] = false;
ComputerSystem.Put();
ManagementObject PageFileSetting = new($#"ROOT\CIMV2:Win32_PageFileSetting");
PageFileSetting.SetPropertyValue("Name", "C:\\PAGEFILE.SYS");
PageFileSetting.SetPropertyValue("InitialSize", InitialSize);
PageFileSetting.SetPropertyValue("MaximumSize", MaximumSize);
PageFileSetting["Name"] = "C:\\PAGEFILE.SYS";
PageFileSetting["InitialSize"] = InitialSize;
PageFileSetting["MaximumSize"] = MaximumSize;
PageFileSetting.Put();
Neither the SetPropertyValue method nor the Put method can update the value of InitialSize MaximumSize Name
Maybe someone can give me some advice?
The following shows how to use ManagementObject to change the settings for the Windows page file (on the OS partition).
Note: The code below has had limited testing. It's recommended to conduct your own testing to ensure that the code functions as desired. While the code seems to set the PageFile sizes for the OS drive, there may be alternative (more desirable) methods of setting these values. Additionally, prior to running the code below ensure that you Create a System Restore Point. Also, ensure that you've performed a backup of your computer.
According to Win32_ComputerSystem class:
TotalPhysicalMemory
Total size of physical memory. Be aware that, under some
circumstances, this property may not return an accurate value for the
physical memory. For example, it is not accurate if the BIOS is using
some of the physical memory. For an accurate value, use the Capacity
property in Win32_PhysicalMemory instead.
Download/install NuGet package: System.Management
Add Application Manifest File:
In VS menu, click Project
Select Add New Item...
Select Application Manifest File (name: app.manifest)
Click Add
Open Solution Explorer:
In VS menu, click View
Select Solution Explorer
Modify requestedExecutionLevel:
In Solution Explorer, right-click app.manifest and select Open
Change From:
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
Change To:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
It appears that the data for PagingFiles can be modified by the following:
Add the following using directives
using System.Management;
using Microsoft.Win32;
using System.Diagnostics;
Code:
public UInt64 GetTotalMemoryInMB()
{
UInt64 totalPhysicalMemoryInBytes = 0;
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select Capacity from Win32_PhysicalMemory"))
{
foreach (ManagementObject obj in searcher.Get())
{
if (obj == null)
continue;
totalPhysicalMemoryInBytes += obj["Capacity"] is null ? 0 : Convert.ToUInt64(obj["Capacity"]);
}
}
return totalPhysicalMemoryInBytes / 1024 / 1024;
}
public string SetPageFileWmi(uint initialSizePercentage, uint maximumSizePercentage)
{
StringBuilder sb = new StringBuilder();
//get Windows folder
string? winDir = Environment.GetEnvironmentVariable("windir");
//get drive letter that OS is installed on
string winDriveLetter = winDir?.Substring(0, 2) is null ? String.Empty : winDir.Substring(0, 2); //ex: C:
//get total physical memory
UInt64 totalPhysicalMemory = GetTotalMemoryInMB();
sb.AppendLine($"Total Physical Memory: {totalPhysicalMemory}");
//calculate initial size
UInt32 initialSize = Convert.ToUInt32(Math.Floor(totalPhysicalMemory * (initialSizePercentage / 100.0))); //ex: 20% / 100.0 = 0.2
initialSize = initialSize < 16 ? 16 : initialSize; //initial size needs to be >= 16 MB
//calculate maximum size
UInt32 maximumSize = Convert.ToUInt32(Math.Floor(totalPhysicalMemory * (maximumSizePercentage / 100.0))); //ex: 50% / 100.0 = 0.5
using (ManagementObject obj = new ManagementObject($#"ROOT\CIMV2:Win32_ComputerSystem.Name='{Environment.MachineName}'"))
{
//set value
obj.SetPropertyValue("AutomaticManagedPagefile", false);
//commit
obj.Put(new PutOptions() { Type = PutType.UpdateOnly });
}
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select Name from Win32_PageFile"))
{
foreach (ManagementObject obj in searcher.Get())
{
string? pfName = obj["Name"]?.ToString();
Debug.WriteLine($"pfName: '{pfName}'");
//only get the page file for the OS drive
if (!String.IsNullOrEmpty(pfName) && pfName.ToLower().StartsWith(winDriveLetter.ToLower()))
{
sb.AppendLine($"Name: {pfName}");
sb.AppendLine($"InitialSize: {initialSize}");
sb.AppendLine($"MaximumSize: {maximumSize}");
sb.AppendLine("");
using (ManagementObject obj2 = new ManagementObject($#"ROOT\CIMV2:Win32_PageFileSetting.Name='{pfName}'"))
{
//set value
//obj2.SetPropertyValue("Name", pfName);
obj2.SetPropertyValue("InitialSize", initialSize);
obj2.SetPropertyValue("MaximumSize", maximumSize);
//commit
obj2.Put(new PutOptions() { Type = PutType.UpdateOrCreate });
}
}
}
}
return sb.ToString();
}
Usage
string result = SetPageFileWmi(20, 50);
According to Changing the Location of the Pagefile, the registry location is HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\.
Update:
Here's a method to reset to the default setting:
public static string SetPageFileDefaultWmi()
{
using (ManagementObject obj = new ManagementObject($#"ROOT\CIMV2:Win32_ComputerSystem.Name='{Environment.MachineName}'"))
{
obj.SetPropertyValue("AutomaticManagedPagefile", true);
obj.Put(new PutOptions() { Type = PutType.UpdateOnly });
}
return "Paging file set to default.";
}
Resources:
How to automatically set the minimum and maximum paging file size?
Put Options Class
PutOptions Type Property
PutType Enum
Introduction To Page Files
Changing the Location of the Pagefile
Ternary Operator
Additional Resources:
RegistryKey Class
RegistryKey.OpenBaseKey
RegistryKey.OpenSubKey
RegistryKey.GetValue
RegistryKey.SetValue
I want to move several files that names are saved in an ObservableCollection<String> _collection with this method:
string firstFolderThatContainsEveryFile = "...\Folder\Files";
string secondFolderArchiv = "...\Folder\Files\Archiv";
foreach (var item in _collection)
{
string firstFolder = System.IO.Path.Combine(firstFolderThatContainsEveryFile, item);
string secondFolder = System.IO.Path.Combine(secondFolderArchiv, item);
File.Move(firstFolder, secondFolder);
}
This works at the first time, but if i load new files into firstFolderThatContainsEveryFile and try to use my move method i get an exception:
File already in Use by other process
This are the steps:
I open the programm -> use the move method -> success -> close the programm -> fill the folder with new files -> open the programm -> use the move method -> exception!
How can i get the processname or processID to close the process before i use my move method, or is there even a better way to get around this?
To figure out which process are using your file, with the solution proposed by this, you can use a tool Handle of Microsoft and this code C# to invoke the tool.
public void ViewProcess(string filePath)
{
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = filePath + " /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern))
{
try{
Console.WriteLine(match.Value); // this is the process ID using the file
}
catch(Exception ex)
{
}
}
}
If the file is using by the others program, you should figure why they use it, if by your program, so recheck your code to understand why.
Attempting to pull the automatic update settings from the registry of a remote server. For some reason, it's returning a 0 even though a manual check of the key is 1-4. What am I overlooking? Snippet below:
ManagementScope msAutoUpdateReg = new ManagementScope(#"\\" + remoteServer + #"\root\DEFAULT:StdRegProv", connection);
msAutoUpdateReg.Connect();
ManagementClass ci = new ManagementClass(msAutoUpdateReg, new ManagementPath(#"DEFAULT:StdRegProv"), new ObjectGetOptions());
ManagementBaseObject inParams = ci.GetMethodParameters("GetDWORDValue");
inParams["hDefKey"] = 0x80000002; //HKLM
inParams["sSubKeyName"] = #"Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update";
inParams["sValueName"] = "AUOptions";
ManagementBaseObject outParams = ci.InvokeMethod("GetDWORDValue", inParams, null);
UInt32 auValue = (UInt32)outParams["uValue"];
if (auValue.ToString() != "0")
{
if (auValue == 1)
{
string currentSetting = "Keep my computer up to date has been disabled in Automatic Updates.";
}
if (auValue == 2)
{
string currentSetting = "Notify of download and installation.";
}
if (auValue == 3)
{
string currentSetting = "Automatically download and notify of installation.";
}
if (auValue == 4)
{
string currentSetting = "Automatically download and scheduled installation.";
}
}
else
{
string currentSetting = "Unknown";
}
I guess a process of elimination might help here...
1) Is this happening on just one server or are you getting this on all servers? How about on your own local machine? Is it a Windows version thing? For example it seems my Windows 10 box doesn't show the SubKey name you are looking for.
2) Do you also get zero if you change the sValueName to "foo"? Is a value of 0 representing an error?
3) Can you put a watch on outParams and check to see what values have been returned?
4) Are you being blocked by UAC, firewall or other permission issues? Can you execute other WMI commands against this server without any problems? Do you need to Run As Administrator to get this to work?
5) Are you getting an other exceptions or return values? I'm guessing you've posted just a portion of the code here so is this code inside a try/catch block?
Sorry if this sounds either vague or simplistic but I think you may need to look at what does work and what doesn't to see if you can identify a pattern.
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);
}
}
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}...");
}