无法在Win7 x64上从32位进程启动屏幕键盘(osk.exe)

90%的时间我无法从Win7 x64上的32位进程启动osk.exe 。 最初代码只是使用:

 Process.Launch("osk.exe"); 

由于目录虚拟化,这对x64无效。 不是我想的问题,我只是禁用虚拟化,启动应用程序,然后再次启用它,我认为这是正确的做事方式。 我还添加了一些代码,如果它已经被最小化(这可以正常工作)使键盘恢复 – 代码(在示例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 { ///  /// Interaction logic for MainWindow.xaml ///  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()上抛出以下exception:

无法在System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)中找到指定的过程

我已经尝试在osk.Start行周围放置长的Thread.Sleep命令,只是为了确保它不是竞争条件,但同样的问题仍然存在。 任何人都可以发现我做错了什么,或者为此提供替代解决方案吗? 它似乎工作正常启动记事本,它只是不会与屏幕键盘玩球。

对于您收到的确切错误消息,我没有非常可靠的解释。 但禁用重定向会破坏.NET框架。 默认情况下,Process.Start()P /调用ShellExecuteEx()API函数来启动进程。 此函数位于shell32.dll中,如果之前未执行过,则可能需要加载该DLL。 禁用重定向时,您将得到错误的一个。

解决方法是将ProcessStartInfo.UseShellExecute设置为false。 你在这里不需要它。

显然,禁用重定向是一种风险方法,具有您无法预测的副作用。 有很多 DLL可以加载需求。 使用Platform Target = Any 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); } } 

这个post似乎包含一些真正的解决方案: http : //www.dreamincode.net/forums/topic/174949-open-on-screen-keyboard-in-c%23/

具体来说,到目前为止,我已经在Win7 x64和WinXP 32位上尝试过它,并且它可以工作。

 static void StartOSK() { string windir = Environment.GetEnvironmentVariable("WINDIR"); string osk = null; if (osk == null) { osk = Path.Combine(Path.Combine(windir, "sysnative"), "osk.exe"); if (!File.Exists(osk)) { osk = null; } } if (osk == null) { osk = Path.Combine(Path.Combine(windir, "system32"), "osk.exe"); if (!File.Exists(osk)) { osk = null; } } if (osk == null) { osk = "osk.exe"; } Process.Start(osk); } 

笨拙的方法:

在侧面运行此批处理文件(从64位资源管理器启动):

 :lab0
 TIMEOUT / T 1> nul
如果存在oskstart.tmp goto lab2
转到lab0
 :实验2
 del oskstart.tmp
 OSK
转到lab0

需要键盘时创建文件oskstart.tmp

Cambiar las propiedades delaaplicación。 COMPILAR – Desmarcar检查首选32位。

更改app propertys Compila – 取消选中’BEST 32 BITS’(或类似)

你可以读到这个:

http://blog.anthonybaker.me/2012/08/spawning-windows-on-screen-keyboard-osk.html

或使用此选项进行其他应用程序并从主应用程序中进行午餐(运行)。

对于那些面临“无法启动屏幕键盘”的人,请将项目的平台目标更改为任何CPU。