90%の確率で、_osk.exe
_の32ビットプロセスから_Win7 x64
_を起動できません。元々、コードは次のものを使用していました。
_Process.Launch("osk.exe");
_
これは、ディレクトリの仮想化のためにx64では機能しません。私が思った問題ではありません。仮想化を無効にしてアプリを起動し、再度有効にします。これが正しい方法でしたthought。また、キーボードが最小化されている場合にキーボードを元に戻すためのコードをいくつか追加しました(これは正常に機能します)-コード(サンプルWPFアプリ内)は次のようになります:
_using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;using System.Diagnostics;
using System.Runtime.InteropServices;
namespace KeyboardTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
[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";
public MainWindow()
{
InitializeComponent();
}
private void KeyboardButton_Click(object sender, RoutedEventArgs 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);
}
// 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));
}
}
}
}
_
ただし、このコードは、ほとんどの場合、osk.Start()
で次の例外をスローします。
指定されたプロシージャがSystem.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)で見つかりませんでした
競合状態ではないことを確認するために、osk.Start行の周りに長いThread.Sleepコマンドを配置しようとしましたが、同じ問題が解決しません。誰かが私が何か間違っているところを見つけたり、これに対する代替ソリューションを提供したりできますか? らしいメモ帳を正常に起動すると、オンスクリーンキーボードでボールを再生できなくなります。
私はあなたが受け取っている正確なエラーメッセージについて非常にしっかりした説明を持っていません。ただし、リダイレクトを無効にすると、.NETFrameworkが台無しになります。デフォルトでは、Process.Start()P/ShellExecuteEx()API関数を呼び出してプロセスを開始します。この関数はShell32.dllにあります。DLLこれまでにロードしていなかった場合はロードする必要があるかもしれません。リダイレクトを無効にすると、間違った関数になります。
その回避策は、ProcessStartInfo.UseShellExecuteをfalseに設定することです。ここでは必要ありません。
明らかに、リダイレクトを無効にすることは、実際には予測できない副作用を伴う危険なアプローチです。 lotsのDLLがあり、デマンドロードされます。 Platform Target =任意のCPUでコンパイルする非常に小さなヘルパーEXEで、問題を解決できます。
64ビットオペレーティングシステムで実行されている32ビットアプリケーションは、64ビットバージョンのosk.exeを起動する必要があります。以下に、スクリーンキーボードで正しく起動するためにC#で記述されたコードを切り取ったものを示します。
private static void ShowKeyboard()
{
var path64 = @"C:\Windows\winsxs\AMD64_Microsoft-windows-osk_31bf3856ad364e35_6.1.7600.16385_none_06b1c513739fb828\osk.exe";
var path32 = @"C:\windows\system32\osk.exe";
var path = (Environment.Is64BitOperatingSystem) ? path64 : path32;
Process.Start(path);
}
MTAスレッドからosk.exeを起動する必要がある特定のことが内部で行われています。その理由は、 Wow64DisableWow64FsRedirection
現在のスレッドにのみ影響します。ただし、特定の条件下では、Process.Start
は、別のスレッドから新しいプロセスを作成します。 UseShellExecute
がfalseに設定されている場合、およびSTAスレッドから呼び出されている場合。
以下のコードは、アパートの状態をチェックしてから、MTAスレッドからオンスクリーンキーボードを起動するようにします。
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
class Program
{
[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);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd,
UInt32 Msg,
IntPtr wParam,
IntPtr lParam);
private const UInt32 WM_SYSCOMMAND = 0x112;
private const UInt32 SC_RESTORE = 0xf120;
private const string OnScreenKeyboardExe = "osk.exe";
[STAThread]
static void Main(string[] args)
{
Process[] p = Process.GetProcessesByName(
Path.GetFileNameWithoutExtension(OnScreenKeyboardExe));
if (p.Length == 0)
{
// we must start osk from an MTA thread
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
ThreadStart start = new ThreadStart(StartOsk);
Thread thread = new Thread(start);
thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
thread.Join();
}
else
{
StartOsk();
}
}
else
{
// there might be a race condition if the process terminated
// meanwhile -> proper exception handling should be added
//
SendMessage(p[0].MainWindowHandle,
WM_SYSCOMMAND, new IntPtr(SC_RESTORE), new IntPtr(0));
}
}
static void StartOsk()
{
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);
}
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = OnScreenKeyboardExe;
// We must use ShellExecute to start osk from the current thread
// with psi.UseShellExecute = false the CreateProcessWithLogon API
// would be used which handles process creation on a separate thread
// where the above call to Wow64DisableWow64FsRedirection would not
// have any effect.
//
psi.UseShellExecute = true;
Process.Start(psi);
// Re-enable directory virtualisation if it was disabled.
if (System.Environment.Is64BitOperatingSystem)
if (sucessfullyDisabledWow64Redirect)
Wow64RevertWow64FsRedirection(ptr);
}
}
不器用な方法:
このバッチファイルを側面で実行します(64ビットエクスプローラーから開始):
:lab0 TIMEOUT/T 1> nul 存在する場合oskstart.tmpgoto lab2 goto lab0 :lab2 del oskstart.tmp osk goto lab0
キーボードが必要なときにファイルoskstart.tmpを作成します
これは私のコードです
var path64 = Path.Combine(Directory.GetDirectories(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "winsxs"), "AMD64_Microsoft-windows-osk_*")[0], "osk.exe");
var path32 = @"C:\windows\system32\osk.exe";
var path = (Environment.Is64BitOperatingSystem) ? path64 : path32;
if(File.Exists(path))
{
Process.Start(path);
}