代码之家  ›  专栏  ›  技术社区  ›  Steven Robbins

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

  •  17
  • Steven Robbins  · 技术社区  · 14 年前

    90%的时间我无法启动 osk.exe 从32位进程开始 Win7 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行周围放置long-thread.sleep命令,只是为了确保它不是竞争条件,但同样的问题仍然存在。有没有人能发现我做错了什么,或者提供一个替代的解决方案?它 似乎 为了启动记事本,它不能用屏幕上的键盘玩球。

    6 回复  |  直到 8 年前
        1
  •  5
  •   Hans Passant    14 年前

    对于您收到的确切错误信息,我没有非常可靠的解释。但是禁用重定向会破坏.NET框架。默认情况下,process.start()p/调用shellexecuteex()api函数来启动进程。此函数位于shell32.dll中,如果以前没有加载,则可能需要加载该dll。当你禁用重定向时,你会得到错误的。

    解决方法是将processStartInfo.useShellExecute设置为false。你不需要在这里。

    很明显,禁用重定向是一种危险的方法,其副作用您无法真正预测。有 太多了 加载请求的DLL。用platform target=any cpu编译的非常小的helper exe可以解决您的问题。

        2
  •  9
  •   Rudy Meijer    9 年前

    在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);
        }
    
        3
  •  2
  •   Dirk Vollmar    14 年前

    某些事情在幕后进行,需要从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);
        }
    }
    
        4
  •  0
  •   archimedes    9 年前

    笨拙的方法:

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

    :lab0
    TIMEOUT /T 1 >nul
    if exist oskstart.tmp goto lab2
    goto lab0
    :lab2
    del oskstart.tmp
    osk
    goto lab0
    

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

        5
  •  -1
  •   R.Alonso    8 年前

    坎比亚拉阿普利卡奇村(Cambiar las Propiedades de la Aplicaci)N. compilar-Desmarcar检查首选项32位。

    更改应用程序属性 compila-取消选中“最佳32位”(或类似)

    您可以阅读:

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

    或使用此选项执行其他应用程序,并从主应用程序中选择午餐(跑步)。

        6
  •  -2
  •   Marcio    11 年前

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