[Windows/WPF] タスクマネージャー上で、親ウインドウの「バックグラウンドプロセス」として表示されるようなプロセスを作ってみる
もくじ
やりたいこと
タスクマネージャーを見ていると、
一つの「アプリ」のプロセスと、その下に「バックグラウンドプロセス」という、アプリとは違うProcessIDを持ったプロセスがいる奴(例えばChrome)と、
※一番下の「アプリ」になってる奴が親プロセスか??
一つの「アプリ」のプロセスの下に、PIDの表示無しでいくつか名前(おそらくウインドウのタイトル)が出ている奴(例えばpowerpoint)がいる。
その二種類でなにが違うのか?と考えたときに、たぶん前者は「①ウインドウを持ってないプロセスを親のアプリが起動した」ケースで、後者は「②ウインドウを親のアプリが生成した」ケースじゃないか?と想像。
①:親アプリが、ウインドウのないexeをProcess.Start("exeパス")
で起動するイメージ。
②:親アプリが、var w = new Window();
みたいなことをするイメージ。
本当にそうなのか、実験してみる。
実験環境
Windows 10 home
バージョン 2004(19041.1165)
.Net Framework4.7.2
①のサンプルコード
①の実験が思いのほかややこしかった。手順をまとめておく。
ソリューション/プロジェクト作成
-
WPFアプリ(.NET Framework)でソリューションを作成
(メインのプロジェクトの名前はMyTestAppにした)
-
バックグラウンドプロセスとして動くexe用のプロジェクトとしてWPFアプリ(.NET Framework)を追加する。(プロジェクトの名前はWindowlesAppにした)
※下記のような感じになった
バックグラウンドプロセスとして動くための設定
-
WindowlessAppプロジェクトのApp.xamlのビルドアクションをPageにする
-
WindowlessAppのApp.xamlの先頭の
<Window>
にあるStartupUri="MainWindow.xaml"
を削除
-
WindowlessAppのApp.xaml.cs に '[STAThread]' をつけてMainメソッドを作る
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()を呼ぶことにした。
<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>
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使用率、メモリ使用率などのリソース情報を個別に持つ様子。
②のサンプルコード
②は簡単で、例えば下記のようにすればよい。
private void Button_Click(object sender, RoutedEventArgs e)
{
var a = new Window() { Title = "実験ウインドウ"};
a.Show();
}
ボタンをおしたときに、Windowを出すようにした。
上のコードを実行すると、下記のようになる。
同じプロセスなので、CPU使用率とかメモリ使用率は1個だけの扱いになる様子。
結果と備考
結果としては、最初に予想した通りだった。
②は普段からよく使うやり方だが、①のバックグラウンドプロセスはあまり使ったことがなかった。
chromeでもバックグラウンドプロセスを使っているので、たぶん一般的なやり方なんだろうと思う。(見えないところで、何かをかんしさせたりするのに使えるかも?)
備考として、
WindowlessAppの方のexeだけを起動してみると、「WindowlesApp.exe」は、タスクマネージャーの「プロセス」タブには出てこないが、「詳細」タブの方には出てくる様子。
そこら辺の出る出ないの違いはよく分からない...
参考
バックグラウンドプロセスを作るときの、Mainメソッドの作り方の参考にさせて頂いた。
Discussion