64ビットWindowsで.NET FrameworkのProcess.Startからソフトウェアキーボードが起動しない
Windows7の64ビットOSでC#のプログラムからProcess.Startでソフトウェアキーボード(osk.exe)を起動させようとしたところ、エラーメッセージが表示されてしまい、大ハマリしました。
解決策を見つけたので、覚書として残しておきます。
64ビットWindowsの場合、フォルダやレジストリのリダイレクトが働くため、アプリケーションが正常に動作しないことがあります。
対策として、一時的にリダイレクションを無効化し、起動してから元に戻します。
単純なアプリケーションである、メモ帳(notepad.exe)などは、対策をしなくても問題なく起動するようです。
参考文献
Unable to launch onscreen keyboard (osk.exe) from a 32-bit process on Win7 x64
(2013年7月15日修正)
参考文献のまま起動すると、osk.WaitForInputIdle(2000);の行で
「WaitForInputIdle に失敗しました。プロセスがグラフィック インターフェイスを含んでいない可能性があります。」
が発生するので、コメントアウトしています。
終了はリダイレクション関係なしにKillすれば大丈夫でした。
ほとんどコピーですが、ソースを残しておきます。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ScreenKeyboardBootSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//参考文献
//Unable to launch onscreen keyboard (osk.exe) from a 32-bit process on Win7 x64
//http://stackoverflow.com/questions/2929255/unable-to-launch-onscreen-keyboard-osk-exe-from-a-32-bit-process-on-win7-x64
[DllImport(“kernel32.dll", SetLastError = true)]
private static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
[DllImport(“kernel32.dll", SetLastError = true)]
public static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr);
private const UInt32 WM_SYSCOMMAND = 0x112;
private const UInt32 SC_RESTORE = 0xf120;
[DllImport(“user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private string OnScreenKeyboadApplication = “osk.exe";
//起動
private void button1_Click(object sender, EventArgs e)
{
// Get the name of the On screen keyboard
string processName = System.IO.Path.GetFileNameWithoutExtension(OnScreenKeyboadApplication);
// Check whether the application is not running
var query = from process in Process.GetProcesses()
where process.ProcessName == processName
select process;
var keyboardProcess = query.FirstOrDefault();
// launch it if it doesn’t exist
if (keyboardProcess == null)
{
IntPtr ptr = new IntPtr(); ;
bool sucessfullyDisabledWow64Redirect = false;
// Disable x64 directory virtualization if we’re on x64,
// otherwise keyboard launch will fail.
if (System.Environment.Is64BitOperatingSystem)
{
sucessfullyDisabledWow64Redirect = Wow64DisableWow64FsRedirection(ref ptr);
}
// osk.exe is in windows/system folder. So we can directky call it without path
using (Process osk = new Process())
{
osk.StartInfo.FileName = OnScreenKeyboadApplication;
osk.Start();
//osk.WaitForInputIdle(2000);//起動してすぐはWaitForInputIdleで異常が発生するため、コメントアウト (異常内容:WaitForInputIdle に失敗しました。プロセスがグラフィック インターフェイスを含んでいない可能性があります。)
}
// Re-enable directory virtualisation if it was disabled.
if (System.Environment.Is64BitOperatingSystem)
if (sucessfullyDisabledWow64Redirect)
Wow64RevertWow64FsRedirection(ptr);
}
else
{
// Bring keyboard to the front if it’s already running
var windowHandle = keyboardProcess.MainWindowHandle;
SendMessage(windowHandle, WM_SYSCOMMAND, new IntPtr(SC_RESTORE), new IntPtr(0));
}
}
//終了
private void button2_Click(object sender, EventArgs e)
{
// Get the name of the On screen keyboard
string processName = System.IO.Path.GetFileNameWithoutExtension(OnScreenKeyboadApplication);
// Check whether the application is not running
var query = from process in Process.GetProcesses()
where process.ProcessName == processName
select process;
var keyboardProcess = query.FirstOrDefault();
// launch it if it doesn’t exist
if (keyboardProcess != null)
{
//Kill process.
keyboardProcess.Kill();
keyboardProcess.WaitForExit();
keyboardProcess.Close();
keyboardProcess.Dispose();
}
}
}
}