私は見ましたが、簡単な質問が何であるかを見つけることができませんでした:
Windowsサービスは、それが開始されたServiceNameをどのように決定できますか?
インストールがレジストリをハックしてコマンドライン引数を追加できることはわかっていますが、論理的にはは不要である必要があるため、この質問です。
レジストリハックよりもきれいに単一のバイナリの複数のコピーを実行したいと思っています。
編集:
これはC#で書かれています。私のアプリMain()エントリポイントは、コマンドライン引数に応じて、さまざまな処理を実行します。
現在、インストール手順では、サービス名とスレッド数がレジストリのImagePathに追加されるため、アプリはServiceNameを判別できます。
差出人: https://connect.Microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=387024
これはWMIソリューションです。 ServiceBase.ServiceMainCallback()をオーバーライドすることもできますが、これは私にとってはうまくいくようです...
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プロパティは、サービスのコンパイル時の名前を提供します。サービスのインストール時に別の名前を指定した場合、ServiceName属性は正しい名前を与えません。したがって、以下のコードを使用して、サービスのサービス名を取得する必要がありました。
これは、NVRAMの方法の(LINQを使用しない)代替手段です。
/**
* 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";
}
最初に ServiceBase.Run() を呼び出さずに、main()の最初の行としてWindowsサービスの名前を誤って取得しようとしました。名前を取得する前に、ServiceBase.Run()を使用して実行可能ファイルをサービスとして登録する必要があります。
参照: http://msdn.Microsoft.com/en-us/library/hde9d63a.aspx#Y32
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"];
すべてのサービス実行可能ファイルが実装する必要があるServiceMain()エントリポイントは、最初の入力引数としてServiceNameを受け取ります。
.NETを使用してサービスを記述している場合、ServiceMain()エントリポイントは.NETによって実装されます。 ServiceNameは、ServiceProcess.ServiceBase.ServiceNameプロパティを使用してサービスがインストールされるときに割り当てられます。動的なServiceName値をサポートするように.NETサービスをカスタマイズしようとしている場合、実行時に実際のServiceNameにアクセスする方法がわかりません。
This.ServiceNameの何が問題になっていますか(service.cs内にいる場合)?
例:
protected override void OnStart(string[] args)
{
Logger.Info($"{this.ServiceName} started on {Environment.MachineName}...");
}
より良い解決策を探すことで、私はこれを試しました:
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 );
サービス名と設定ファイルをbinpathに渡しました。サービスはSC.exeを使用してインストールされました(installutilは使用しません!)
サービスでは、コマンドライン引数を取得できます
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;
}
Service.Run()を完了する前にサービスの場所を知る必要がある鶏と卵の問題がありました(サービスはクライアントまたはサーバーのインストールの一部である可能性があり、インストーラーはそれらに適切な名前を付け、それがオンになっていたかを検出する必要がありました起動)
私は名前を取得するためにレジストリに依存していました。
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;
}