アーカイブ

【.NET】Windowsサービスの実行ファイルパスを取得する【C#】【WMI】

Windowsサービスの情報を外部のアプリケーションから取得する方法のメモ。
目次
  1. なんの記事?
  2. サービス名 / 表示名は簡単に取得可能
  3. 実行ファイルパスを取得するには?
    1. 方法①:Win32APIを使用する
    2. 方法②:WMIを使用する ※オススメ
    3. 動作結果
  4. おわりに
  5. 参考

なんの記事?

上記のようなWindowsサービスの実行ファイルパスを取得する方法を紹介します。

サービスアプリケーション自身からではなく、外部のアプリケーションから取得します。

サービス名 / 表示名は簡単に取得可能

コンピューターにインストールされたサービスの一覧の列挙は.NETの名前空間「System.ServiceProcess」のクラス「ServiceController」で提供される機能で簡単に行うことが可能となっています。

DOBON.NET様の記事

ローカルコンピュータのすべてのサービス(またはデバイスドライバサービスのみ)を取得する – .NET Tips (VB.NET,C#…)

しかし、この方法では、サービスに関する情報はサービス名と表示名しか取得ができないようです。

実行ファイルパスを取得するには?

方法①:Win32APIを使用する

1つ目は、Win32APIを使用する方法です。

Win32APIとは?
アプリケーションからOS(Windows)の機能を操作するために用意されたAPI。主にアンマネージDLL(.NETアセンブリではない)として提供されるので、C#などから利用する場合はDllImport属性を付けて関数を宣言するなど下準備が必要。


◯サンプルコード

private const string ADVAPI32 = "advapi32.dll";
[DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, int dwDesiredAccess);

[DllImport(ADVAPI32, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseServiceHandle(IntPtr handle);

[DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, int dwDesiredAccess);

[DllImport(ADVAPI32, CharSet = CharSet.Unicode, SetLastError = true)]
private static extern Boolean QueryServiceConfig(IntPtr hService, IntPtr intPtrQueryConfig, UInt32 cbBufSize, out UInt32 pcbBytesNeeded);

private const int SERVICE_QUERY_CONFIG = 0x0001;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct QUERY_SERVICE_CONFIG
{
    internal int dwServiceType;
    internal int dwStartType;
    internal int dwErrorControl;
    internal string lpBinaryPathName;
    internal string lpLoadOrderGroup;
    internal int dwTagId;
    internal string lpDependencies;
    internal string lpServiceStartName;
    internal string lpDisplayName;
}

public string GetExePath_Win32(string name)
{
    string exePath = "";

    IntPtr scManager = OpenSCManager(null, null, SERVICE_QUERY_CONFIG);
    if (scManager == IntPtr.Zero)
    {
        return exePath;
    }

    IntPtr service = OpenService(scManager, name, SERVICE_QUERY_CONFIG);
    if (service == IntPtr.Zero)
    {
        return exePath;
    }

    UInt32 cbBufSize = 0;
    QueryServiceConfig(service, IntPtr.Zero, 0, out cbBufSize);
    IntPtr intPtrQueryConfig = Marshal.AllocHGlobal((int)cbBufSize);
    QueryServiceConfig(service, intPtrQueryConfig, cbBufSize, out cbBufSize);
    QUERY_SERVICE_CONFIG qsc = (QUERY_SERVICE_CONFIG)Marshal.PtrToStructure(intPtrQueryConfig, typeof(QUERY_SERVICE_CONFIG));
    exePath = qsc.lpBinaryPathName;

    CloseServiceHandle(service);
    CloseServiceHandle(scManager);

    return exePath;
}

中々長いですね…手軽に使える感じはありません。

実行ファイルパスなどのサービスの構成情報を取得するには、

  • コンピューターのサービスコントロールマネージャーに接続(OpenSCManager)
  • サービスを開く(OpenService)
  • サービスの情報を取得(QueryServiceConfig)

の3ステップが必要になります。
すべでWin32APIの関数なので、それぞれDLLImportでの宣言と関連する型定義が必要になります。

動作要件

今回使用するAPIの動作要件は以下

OSクライアント:Windows XP ~
サーバー:Windows Server 2003 ~
.NETCore1.0~
.NET Framework1.1~

Win32APIは歴史の古いものですが、途中で追加になったAPIもありAPIごとに要件が異なります。
念のため確認しましょう。(令和の時代にほぼほぼ問題ならないかな?)

方法②:WMIを使用する ※オススメ

2つ目は、WMIを使用する方法です。

WMIとは?
「WBEM」というネットワーク上のデバイスを集中管理するためのシステム管理の業界標準技術仕様のWindowsの実装。「WQL(WMIクエリ)」というDBMSのSQLに似た言語でWindowsコンピューターのハードウェア/ソフトウェアのデータを取得・操作することができます。

WMIは様々なプログラミング言語で使用可能で、.NETでも機能が提供されています。

Microsoft.Management.Infrastructure 名前空間

◯サンプルコード

public string GetExePath_WMI_New(string name)
{
    string exePath = "";

    using (CimInstance services = new CimInstance("Win32_Service", @"root\cimv2"))
    {
        services.CimInstanceProperties.Add(CimProperty.Create("Name", name, CimFlags.Key));
        using (CimSession mySession = CimSession.Create("localhost"))
        using (CimInstance searchInstance = mySession.GetInstance(@"root\cimv2", services)) {
            exePath = searchInstance.CimInstanceProperties["PathName"].Value.ToString();
        } 
    }
      
    return exePath;
}

上記記述の場合、GetInstanceで対象が存在しない場合例外がスローされるようです。


◯サンプルコード(クエリ版)

WMIクエリ(WQL)を使用することも可能です。

public string GetExePath_WMI_Query(string name)
{
    string exePath = "";

    using (CimSession mySession = CimSession.Create("localhost"))
    {
        IEnumerable<CimInstance> queryInstance = mySession.QueryInstances(@"root\cimv2", "WQL", $"SELECT * FROM Win32_Service WHERE Name = '{name}'");
        CimInstance cimInstance = queryInstance.FirstOrDefault();
        if(cimInstance != null)
        {
            exePath = cimInstance.CimInstanceProperties["PathName"].Value.ToString();
        }             
    }
       
    return exePath;
}

System.Management 名前空間 ※現在非推奨

前述の「Microsoft.Management.Infrastructure」 の他、「System.Management」にも機能が提供されています。しかし、現在では非推奨であり「Microsoft.Management.Infrastructure」 を使用するように推奨されています。

◯サンプルコード

public string GetExePath_WMI(string name)
{
    string exePath = "";

    ConnectionOptions option = new ConnectionOptions();
    ManagementScope scope = new ManagementScope("\\root\\cimv2", option);
    ObjectQuery query = new ObjectQuery($"SELECT * FROM Win32_Service WHERE Name = '{name}'");
    using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
    using (ManagementObjectCollection services = searcher.Get())
    {
        ManagementObjectCollection.ManagementObjectEnumerator enumerator = services.GetEnumerator();
        if (enumerator.MoveNext())
        {
            ManagementBaseObject service = enumerator.Current;
            exePath = service["PathName"].ToString();
            service.Dispose();
        }
    }

    return exePath;
}

動作要件

今回取得対象のWin32_Serviceクラスの要件は以下

OSクライアント:Windows Vista 以降
サーバー:Windows Server 2008 以降
.NETCore1.0~
.NET Framework4.5.1~
※System.Managementは1.1~

アクセス対象のWMIクラスによって要件が異なります。
使用する際は要確認。

動作結果

簡単なフォームを作って、前述の処理で取得したファイルパスと処理にかかった時間を表示させてみました。

無事、①②とも同じ結果が得られました!

おわりに

今回はローカルコンピューターから情報を取得しましたが、①②もリモートコンピューターからも取得可能なようです。認証やファイアウォールなどのハードルはあるとは思いますが…

参考

プロフィール
筆者:UMAイカ

IT企業に勤務しています。
当ブログは商品レビューや生活の知恵、プログラミング、PCTipsなどについてお役立ち情報を発信します。趣味などの雑記も少し。
【マイブーム】:ウイスキー/自炊
【最近のひとこと】:転職したい・・・

- SNS -
アーカイブ