19

90% 的时间我无法osk.exeWin7 x64. 最初的代码只是使用:

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
{
    /// <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 命令,以确保它不是竞争条件,但同样的问题仍然存在。谁能发现我做错了什么,或为此提供替代解决方案?启动记事本似乎可以正常工作,只是无法使用屏幕键盘玩球。

4

6 回答 6

12

在 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);
    }
于 2015-01-31T16:31:55.293 回答
5

对于您收到的确切错误消息,我没有非常可靠的解释。但是禁用重定向会弄乱 .NET 框架。默认情况下,Process.Start() P/Invoke ShellExecuteEx() API 函数来启动进程。该函数位于 shell32.dll 中,如果以前没有加载,则可能必须加载该 DLL。当你禁用重定向时,你会得到错误的。

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

显然,禁用重定向是一种冒险的方法,其副作用是您无法真正预测的。有许多按需加载的 DLL。您使用 Platform Target 编译的一个非常小的帮助程序 EXE = 任何 CPU 都可以解决您的问题。

于 2010-05-28T14:51:34.157 回答
3

某些事情在后台发生,需要您从 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);
    }
}
于 2010-05-28T14:05:42.320 回答
3

这是我的代码

            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);
            }
于 2020-02-27T11:59:16.353 回答
0

笨方法:

在旁边运行这个批处理文件(从 64 位资源管理器开始):

:实验室0
超时/T 1 >nul
如果存在 oskstart.tmp 转到 lab2
转到实验室0
:实验室2
删除 oskstart.tmp
操作系统
转到实验室0

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

于 2015-02-23T13:24:15.040 回答
-1

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

于 2013-06-08T15:46:50.280 回答