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

Ste*_*ins 17 c# wpf 64-bit exception on-screen-keyboard

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

Process.Launch("osk.exe");
Run Code Online (Sandbox Code Playgroud)

由于目录虚拟化,这对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
{
    /// <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));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,大多数情况下,此代码会抛出以下异常osk.Start():

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

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

小智 10

在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);
    }
Run Code Online (Sandbox Code Playgroud)

  • 这种方法对我有用,但amd64_microsoft ....路径的命名对我来说有点不同.我必须使用模式匹配搜索目录:`Path.Combine(Directory.GetDirectories(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows),"winsxs"),"amd64_microsoft-windows-osk _*")[0] ,"osk.exe")` (6认同)

Han*_*ant 5

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

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

显然,禁用重定向是一种冒险的方法,会产生您无法预测的副作用.有很多 DLL可以加载需求.使用Platform Target = Any CPU编译的一个非常小的帮助程序EXE可以解决您的问题.

  • 将UseShellExec设置为false意味着它崩溃了"执行没有shellexec需要提升"的行 (2认同)