🗿

[Windows/WPF] タスクマネージャー上で、親ウインドウの「バックグラウンドプロセス」として表示されるようなプロセスを作ってみる

2021/08/22に公開

もくじ
https://qiita.com/tera1707/items/4fda73d86eded283ec4f

やりたいこと

タスクマネージャーを見ていると、

一つの「アプリ」のプロセスと、その下に「バックグラウンドプロセス」という、アプリとは違うProcessIDを持ったプロセスがいる奴(例えばChrome)と、

※一番下の「アプリ」になってる奴が親プロセスか??

一つの「アプリ」のプロセスの下に、PIDの表示無しでいくつか名前(おそらくウインドウのタイトル)が出ている奴(例えばpowerpoint)がいる。

その二種類でなにが違うのか?と考えたときに、たぶん前者は「①ウインドウを持ってないプロセスを親のアプリが起動した」ケースで、後者は「②ウインドウを親のアプリが生成した」ケースじゃないか?と想像。

①:親アプリが、ウインドウのないexeをProcess.Start("exeパス")で起動するイメージ。
②:親アプリが、var w = new Window();みたいなことをするイメージ。

本当にそうなのか、実験してみる。

実験環境

Windows 10 home
バージョン 2004(19041.1165)
.Net Framework4.7.2

①のサンプルコード

①の実験が思いのほかややこしかった。手順をまとめておく。

ソリューション/プロジェクト作成

  1. WPFアプリ(.NET Framework)でソリューションを作成
    (メインのプロジェクトの名前はMyTestAppにした)

  2. バックグラウンドプロセスとして動くexe用のプロジェクトとしてWPFアプリ(.NET Framework)を追加する。(プロジェクトの名前はWindowlesAppにした)
    ※下記のような感じになった

バックグラウンドプロセスとして動くための設定

  1. WindowlessAppプロジェクトのApp.xamlのビルドアクションをPageにする

  2. WindowlessAppのApp.xamlの先頭の<Window>にあるStartupUri="MainWindow.xaml"を削除

  3. WindowlessAppのApp.xaml.cs に '[STAThread]' をつけてMainメソッドを作る

App.xaml.cs
using System;
using System.Windows;

namespace WindowlessApp
{
    public partial class App : Application
    {
        [STAThread]
        public static void Main()
        {
            App app = new App();
            app.InitializeComponent();
            app.Run();
        }
    }
}

これでWindowlesApp.exeがバックグラウンドプロセスとして動くための設定終了。
これをビルドすると、バックグラウンドプロセスとして動く「WindowlessApp.exe」ができるはず。

※あと必要に応じて、不要になったMainWindow.xamlの削除等行う。

親アプリの設定

バックグラウンドプロセス(WindowlessApp.exe)を呼びたい個所に、下記を書く。

var selfPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
var paintPath = selfPath + @"\WindowlessApp.exe";
var p = Process.Start(paintPath);

親アプリ側は、Process.Start()でWindowlessApp.exeを呼ぶだけ。

上のコードの前提として、今回は実験として、親アプリ(MyTestApp.exe)と同じフォルダにWindowlessApp.exeができる前提なので、できたものを手動でコピーするか、MyTestAPPのプロジェクトの参照にWindowlessAppのプロジェクトを入れて、「ローカルにコピー」をtrueにしておくこと。

今回は、MyTestApp側のMainWindowにボタンを追加して、そこのClick時に上のProcess.Start()を呼ぶことにした。

MainWindow.xaml
<Window x:Class="MyTestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyTestApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Click="Button_Click"/>
    </Grid>
</Window>
MainWindow.xaml.cs
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows;

namespace MyTestApp
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var selfPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            var paintPath = selfPath + @"\WindowlessApp.exe";
            var p = Process.Start(paintPath);
        }
    }
}

①の出来上がり

上記を実行すると、一つの「アプリ」のプロセスと、その下に「バックグラウンドプロセス」という、アプリとは違うProcessIDを持ったプロセスがいる、という状態が出来上がる。

※上のサンプルのままだと、WindowlessAppが終わるすべを持っていないので、親アプリを終了しても背後で動作し続けてしまう様子。終わらせる仕組み実装必要。

※プロセスが異なる(プロセスIDが異なる)と、個別にCPU使用率、メモリ使用率などのリソース情報を個別に持つ様子。

②のサンプルコード

②は簡単で、例えば下記のようにすればよい。

MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
    var a = new Window() { Title = "実験ウインドウ"};
    a.Show();
}

ボタンをおしたときに、Windowを出すようにした。
上のコードを実行すると、下記のようになる。

同じプロセスなので、CPU使用率とかメモリ使用率は1個だけの扱いになる様子。

結果と備考

結果としては、最初に予想した通りだった。
②は普段からよく使うやり方だが、①のバックグラウンドプロセスはあまり使ったことがなかった。

chromeでもバックグラウンドプロセスを使っているので、たぶん一般的なやり方なんだろうと思う。(見えないところで、何かをかんしさせたりするのに使えるかも?)

備考として、
WindowlessAppの方のexeだけを起動してみると、「WindowlesApp.exe」は、タスクマネージャーの「プロセス」タブには出てこないが、「詳細」タブの方には出てくる様子。

そこら辺の出る出ないの違いはよく分からない...

参考

バックグラウンドプロセスを作るときの、Mainメソッドの作り方の参考にさせて頂いた。
https://atmarkit.itmedia.co.jp/ait/articles/1511/04/news027.html

Discussion