【C#】サイズと表示位置を指定してスクリーンキーボードを起動する
概要
ひょんなことからスクリーンキーボード(osk.exe)を起動するプログラムを作ることになりました。タイトルにあるとおり、キーボードのサイズと表示位置を制御したいとのこと。またWindowsAPIかよ(^ω^)
環境
Windows10
Visual Studio Community 2017
スクリーンキーボードを起動
まずはキーボードを起動してみましょう。
static void Main(string[] args) { using (Process process = new Process()) { process.StartInfo.FileName = "osk.exe"; process.Start(); } Console.ReadKey(); }
アラートが。
スクリーン キーボードを起動できません。
stackoverflowによると、Any CPUビルドだとダメっぽい。プロジェクトプロパティのビルドの設定で、32ビットを優先のチェックボックスを外すか、x64ビルドとすれば動くとのことだったのでプラットフォームターゲットをx64でビルドすることで起動することを確認しました。
サイズと表示位置を指定する
SetWindowPos
というWindowsAPIを使えば実現できそうだったのですが、引数のウィンドウハンドルがprocess.MainWindowHandle
では取れない!取得するまでにやたら時間がかかるので、タイムアウトを設けておいて取得できるまでひたすら頑張る。遅ぇぇぇ
while (0 >= (int)process.MainWindowHandle) { if (IsTimeout(startTime, timeSpan)) { Console.WriteLine("Timeout"); Environment.Exit(0); } System.Threading.Thread.Sleep(1000); Console.WriteLine("..."); process.Refresh(); }
これでサイズと表示位置を制御できると思ったら早かった。うまく動かないんです。Marshal.GetLastWin32Error()
でエラーコードを取得してエラーメッセージを見てみたらアクセス拒否!管理者権限で実行しないとダメでした。くぅー、そんなん書いてある?見つけられなかったよ。。。
苦労したけど、なんとか動いた全部入りコードがこちら。
using System; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace OnScreenKeyboard { class Program { private class User32 { internal const UInt32 WM_SYSCOMMAND = 0x112; internal const UInt32 SC_RESTORE = 0xf120; [DllImport("user32.dll", CharSet = CharSet.Auto)] internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int uFlags); internal const int SWP_NOSIZE = 0x0001; internal const int SWP_NOMOVE = 0x0002; internal const int SWP_NOZORDER = 0x0004; internal const int SWP_SHOWWINDOW = 0x0040; internal const int SWP_ASYNCWINDOWPOS = 0x4000; internal const int HWND_TOP = 0; internal const int HWND_BOTTOM = 1; internal const int HWND_TOPMOST = -1; internal const int HWND_NOTOPMOST = -2; } private class Kernel32 { [DllImport("kernel32.dll")] internal static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr Arguments); internal const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; internal static string GetMessage(int errorCode) { var message = new StringBuilder(255); FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)errorCode, 0, message, message.Capacity, IntPtr.Zero ); return message.ToString(); } } static void Main(string[] args) { var onScreenKeyboad = "osk.exe"; IntPtr windowHandle = IntPtr.Zero; string processName = System.IO.Path.GetFileNameWithoutExtension(onScreenKeyboad); var query = from process in Process.GetProcesses() where process.ProcessName == processName select process; var keyboardProcess = query.FirstOrDefault(); if (keyboardProcess == null) { using (Process process = new Process()) { process.StartInfo.FileName = onScreenKeyboad; process.Start(); process.WaitForInputIdle(); var startTime = DateTime.Now; var timeSpan = new TimeSpan(0, 0, 10); while (0 >= (int)process.MainWindowHandle) { if (IsTimeout(startTime, timeSpan)) { Console.WriteLine("Timeout"); Environment.Exit(0); } System.Threading.Thread.Sleep(1000); Console.WriteLine("..."); process.Refresh(); } windowHandle = process.MainWindowHandle; var result = User32.SetWindowPos(windowHandle, 0, 0, 500, 1000, 300, User32.SWP_NOZORDER | User32.SWP_SHOWWINDOW); if (!result) { var errorCode = Marshal.GetLastWin32Error(); var message = Kernel32.GetMessage(errorCode); Console.WriteLine(message); } } } else { windowHandle = keyboardProcess.MainWindowHandle; User32.SendMessage(windowHandle, User32.WM_SYSCOMMAND, new IntPtr(User32.SC_RESTORE), new IntPtr(0)); Console.WriteLine("ReOpen"); } Console.WriteLine("Standby"); Console.ReadKey(); } static bool IsTimeout(DateTime startTime, TimeSpan timeSpan) { if (DateTime.Now - startTime > timeSpan) { return true; } return false; } } }
頭で考えるな、感じるんだ!