デスクトップを整理するアプリを作った(実装説明)
環境
Windows11 Pro 24H2
Visual Studio Pro2022(64bit) Version 17.13.0
アプリ説明
デスクトップ上にある、アイコンを分ける為の枠を表示するアプリです
枠の上部には任意の文言をかけたり、枠自体の色も変えれるので、
アイコンの塊毎に意味を持たせることが出来て、デスクトップ上のアイコンが迷子になるのを防ぎます
Boothで販売しているので、興味あれば購入下さい
アプリの説明は以下
技術要素
作るにあたって頑張った点です
・タスクトレイに常駐させる
・フォームを枠だけ残して透過とする
・フォームを絶えず最背面とする
・スタートアップに登録する
・ショートカットからの起動時の設定の読み込み
タスクトレイへの常駐
formデザインの画面で、ツールボックス内にある、「NotifyIcon」をダブルクリックしてください
デザイン画面の下側に「NotifyIcon」が表示されます
プロパティウィンドウにて、「Icon」に、タスクトレイで表示されるアイコンを指定してください
これでタスクトレイに指定のアイコンが表示されますが、Formも表示されます
起動時には非表示にしたいので、Formのプロパティを変更しましょう
プロパティ | 値 |
---|---|
Icon | アイコンファイルを選択下さい |
WindowsState | Minimized |
フォームを枠だけ残して透過とする
デフォルトのフォームの表示では、タイトルなど不要な部分があるので、それらを消しちゃいます
また、枠を表示しているだけなので、タスクバーにアイコンが表示されるのは違和感があるので、これを辞めます
これらは、Fromのコンストラクタに記載します
this.FormBorderStyle = FormBorderStyle.None; // フォームの上部を無くす
this.ShowInTaskbar = false; // タスクバーにアイコンを表示しない
あとは、FormのPaintイベントに記述します
まずは、外周の角を丸くします
pathを作成して、詳細を追加する前にstartします
int radius = 10;// 角の半径
System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
path.StartFigure();
後はpathを追加していきます、まず左上の円弧です
path.AddArc(new Rectangle(0, 0, radius, radius), 180, 90);
という感じで、pathにそれぞれの角を追加していきます
AddArcの引数には、第1引数にRectangleを渡します
Rectangleは名前の通り、円の情報ですが、
AddArcの第2,3引数が度数となっており、
Rectanbleで指定した円の180度から90度を有効としています
ここではは、左上側だけを追加していますが、残り3か所分も追加しましょう
path.AddArc(new Rectangle(this.Width - radius, 0, radius, radius), 270, 90);
path.AddArc(new Rectangle(this.Width - radius, this.Height - radius, radius, radius), 0, 90);
path.AddArc(new Rectangle(0, this.Height - radius, radius, radius), 90, 90);
右上、右下、左下の順で追加していきます
AddArcの最後は、Closeが必要です
path.CloseFigure();
次にFormの中を切り抜くために同じ様に設定してきます
GraphicsPath innerPath = new GraphicsPath();
innerPath.StartFigure();
innerPath.AddLine(lineWidth, lineWidth, this.Width - lineWidth, lineWidth);
innerPath.AddArc(new Rectangle(this.Width - lineWidth - diameter, this.Height - lineWidth - diameter, diameter, diameter), 0, 90);
innerPath.AddArc(new Rectangle(lineWidth, this.Height - lineWidth - diameter, diameter, diameter), 90, 90);
innerPath.CloseFigure();
path.AddPath(innerPath,false);
フォームを絶えず最背面とする
ここが一番手間取った処理です
Formのプロパティでは、最前面にすることは出来ても、絶えず最背面には出来ません
クリックすると、前面に出てきちゃいます
今回の枠としては、前面に出てきて欲しくないので、絶えず最背面としています
Win32APIを使用します
コードの先頭に以下の宣言を記述下さい
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
「Program Manager」内の「Program Manager」の子ウィンドウはすでにいくつかあるのですが、
この中の「FolderView'SysListView32」の子ウィンドウとすることで最背面としています
この「Program Manager」内の「FolderView'SysListView32」をどうやって探したかというと、
Spy++というアプリでWindowハンドルを知る事が出来ます
Spy++はVisualStudioのメニューから「ツール」「Spy++」で起動します
実装としては、「FolderView'SysListView32」は、「Progman」の中にあるので、まずはこのProgmanを探して、
その中から、SysListView32を探し、ウィンドウハンドルを取得します
IntPtr shellWindow = FindWindow("Progman", null);
IntPtr desktopWindow = IntPtr.Zero;
if (shellWindow != IntPtr.Zero){
IntPtr shellDllDefView = FindWindowEx(shellWindow, IntPtr.Zero, "SHELLDLL_DefView", null);
if (shellDllDefView != IntPtr.Zero){
desktopWindow = FindWindowEx(shellDllDefView, IntPtr.Zero, "SysListView32", "FolderView");
}
}
取得したウィンドウハンドルを用いて、枠であるFormを子ウィンドウに設定します
SetParent(this.Handle, desktopWindow);
これで、ずっと最背面に固定されます
スタートアップに登録する
設定ウィンドウ内に、「スタートアップに登録」というチェックボックスを用意しました
ここにチェックを入れると、スタートアップに登録されて、次回からPC起動時に勝手にソフトが立ち上がります
このスタートアップへの登録は、レジストリへ登録する事で実現します
Microsoft.Win32.RegistryKey regkey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
@"Software\Microsoft\Windows\CurrentVersion\Run", true);
regkey.SetValue(Application.ProductName, Application.ExecutablePath);
regkey.Close();
ショートカットからの起動時の設定の読み込み
インストーラを作成して、動作確認を行うと、設定ファイルを読み出せずに、起動毎に初期化される現象が起こりました。これはショートカットからの起動時に、カレントフォルダが変わってしまい、設定ファイルを読み出せない、といった事が原因でした
string exePath = Path.GetDirectoryName(Application.ExecutablePath);
Directory.SetCurrentDirectory(exePath);
自分で作成したアプリをインストールして使用するといった事があまりなかったので、
簡単な事なんですが、結構悩みました
Discussion