本文介绍了无法从 Win7 x64 上的 32 位进程启动屏幕键盘 (osk.exe)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

90% of the time I am unable to launch osk.exe from a 32bit process on Win7 x64. Originally the code was just using:

Process.Launch("osk.exe");

由于目录虚拟化,这在 x64 上不起作用.我认为这不是问题,我只是禁用虚拟化,启动应用程序,然后再次启用它,我认为是正确的做事方式.我还添加了一些代码,如果键盘已被最小化(工作正常),则可以将键盘恢复 - 代码(在示例 WPF 应用程序中)现在如下所示:

Which won't work on x64 because of the directory virtualization. Not a problem I thought, I'll just disable virtualization, launch the app, and enable it again, which I thought was the correct way to do things. I also added some code to bring the keyboard back up if it has been minimized (which works fine) - the code (in a sample WPF app) now looks as follows:

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() 上抛出以下异常:

But this code, most of the time, throws the following exception on osk.Start():

找不到指定的程序在 System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)

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

I've tried putting long Thread.Sleep commands in around the osk.Start line, just to make sure it wasn't a race condition, but the same problem persists. Can anyone spot where I'm doing something wrong, or provide an alternative solution for this? It seems to work fine launching Notepad, it just won't play ball with the onscreen keyboard.

推荐答案

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

I don't have a very solid explanation for the exact error message you are getting. But disabling redirection is going to mess up the .NET framework. By default, Process.Start() P/Invokes the ShellExecuteEx() API function to start the process. This function lives in shell32.dll, a DLL that might have to be loaded if that wasn't previously done. You'll get the wrong one when you disable redirection.

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

A workaround for that is to set ProcessStartInfo.UseShellExecute to false. You don't need it here.

显然,禁用重定向是一种冒险的方法,其副作用是您无法真正预测的.有很多 DLL 可以按需加载.您使用 Platform Target = Any CPU 编译的非常小的辅助 EXE 可以解决您的问题.

Clearly, disabling redirection is a risky approach with side-effects you cannot really predict. There are lots of DLLs that get demand-loaded. A very small helper EXE that you compile with Platform Target = Any CPU can solve your problem.

这篇关于无法从 Win7 x64 上的 32 位进程启动屏幕键盘 (osk.exe)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 21:41