こんにちは、たびとです。
古い話ですが、Windows XP 時代に .NET Framework による WinForm を散々作っていましたが、 Win32 API を使わないとできないことが多々あったので、よく使っていました。
この Win32 API を .NET 6 で使うことができるのだろうか、というのが今回のテーマです。
Windows タスクバーを右クリックしたときに表示される「タスク バーの設定」から 「タスク バーを自動的に隠す」のチェックボックスを有効・無効に切り替えるアプリを作って検証してみます。
この記事の対象者
Win32 API を .NET 6 で実装する
Win32 API を使うことだけを考えると、.NET Framework を選択した方がいいと思います。 しかし、サーバアプリを ASP.NET を .NET 6 で作成した場合、 クライアントアプリを .NET Framework 4.8 で作るのはどうなのかなと思ってしまいます。 例えば、共通ライブラリとか作る場合、.NET 6 に統一した方がメンテナンスが楽になります。
タスクバー制御の Win32 API
「タスクバーを自動的に隠す」を実装するには、Win32 API の FindWindow と SHAppBarMessage を使います。 詳細は、以下を参照してください。
この Win32 API を C# で実装するには、DllImport
属性を使って定義するだけです。
それに加えて、Win32 API に指定する引数や構造体を必要に応じて定義します。
ソースコード
Visual Studio 2022 の 新規プロジェクトの Console アプリで .NET 6 を選択し、 適当な名前で作成します。 Program.cs のソースに以下のコードをコピペして完了です。
using System.Drawing; using System.Runtime.InteropServices; // ウィンドウハンドルを取得する [DllImport("user32.dll", CharSet = CharSet.Unicode)] extern static IntPtr FindWindow( [MarshalAs(UnmanagedType.LPWStr), In] string lpClassName, [MarshalAs(UnmanagedType.LPWStr), In] string? lpWindowName); // appbar メッセージをシステムに送信する [DllImport("shell32.dll", CharSet = CharSet.Unicode)] extern static UIntPtr SHAppBarMessage(UInt32 dwMessage, ref AppBarData pData); const UInt32 ABM_GETSTATE = 0x00000004; //Windows タスクバーの自動表示と常時表示の状態を取得する const UInt32 ABM_SETSTATE = 0x0000000a; //Windows タスクバーの自動表示と常時表示の状態を設定する const uint ABS_AUTOHIDE = 0x01; //自動的に隠す const uint ABS_ALWAYSONTOP = 0x02; //常に表示する // Windows(OS)判定 if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.Error.WriteLine("Windows で実行してください。"); return; } // タスバーの状態を取得する if (ABS_AUTOHIDE == GetTaskbarStatus()) { Console.WriteLine("Auto hide -> Always on top"); SetTaskbarStatus(ABS_ALWAYSONTOP); } else { Console.WriteLine("Always on top -> Auto hide."); SetTaskbarStatus(ABS_AUTOHIDE); } // タスクバーの状態を取得する uint GetTaskbarStatus() { var data = new AppBarData { hWnd = FindWindow("System_TrayWnd", null) }; data.cbSize = (UInt32)Marshal.SizeOf(data); return (uint)SHAppBarMessage(ABM_GETSTATE, ref data); } // タスクバーの状態を変更する uint SetTaskbarStatus(uint option) { var data = new AppBarData { hWnd = FindWindow("System_TrayWnd", null), lParam = (Int32)option }; data.cbSize = (UInt32)Marshal.SizeOf(data); return (uint)SHAppBarMessage(ABM_SETSTATE, ref data); } // APPBARDATA 構造体 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct AppBarData { public UInt32 cbSize; public IntPtr hWnd; public UInt32 uCallbackMessage; public UInt32 uEdge; public Rectangle rc; public Int32 lParam; }
ポイント
Win32 API を使うときに、マーシャリングを意識する必要があります。 詳細は、以下のページを参考にしてください。
ここで、SHAppBarMessage の引数 struct AppBarData
に [MarshalAs(UnmanagedType.LPStruct)]
を付けると、
エラーになったので外してあります。
Rectangle
を使っているため、アンマネージドへの変換に失敗しているのかもしれません。
自前で Rect 構造体を用意するといいのかもしれないですけど、今回はここまでにしておきます。
実行結果
ソースコードを実行するたびに、「タスクバーを自動的に隠す」設定の有効・無効が交互に切り替わります。 以下、Windows 11 で実行した結果を張り付けています。 たまに、タスクバーが自動的に隠れない場合があります。そんなときは、一度スタートメニューをクリックすると隠れると思います。
WSL2 Ubuntu で実行するとどうなるのか
WSL2 Ubuntu にソースコードをコピーしてビルドすると、 Win32 API でエラーになるかと思いきや、成功してしまいました。
$ dotnet build Microsoft (R) Build Engine version 17.0.1+b177f8fa7 for .NET Copyright (C) Microsoft Corporation. All rights reserved. Determining projects to restore... All projects are up-to-date for restore. Taskbar -> /home/tabito/Taskbar/Taskbar/bin/Debug/net6.0/Taskbar.dll Build succeeded. 0 Warning(s) 0 Error(s) Time Elapsed 00:00:00.73
実行すると、Windows の OS 判定を入れているので、期待通りに終了します。
$ dotnet run Windows で実行してください。
まとめ
今回は、Win32 API を .NET 6 で実装するために簡単なアプリを作成しました。 .NET Framework の頃と同じ感覚で実装できるようなので、過去資産がある場合は有効活用ができると思います。 ただし、昔はマーシャリングを厳密に書いていないソースが多いので、そこを注意した方がいいかもしれません。
また、今回のように WSL2 Ubuntu 上で Win32 API を指定したソースコードは、ビルドも実行もできてしまいます。 .NET 6 に移植する場合、他のプラットフォームで動作することも注意する必要があります。
では、皆さん、よい旅を。