Impossible de lancer le clavier à l'écran (osk.exe) à partir D'un processus 32 bits sur Win7 x64

90% du temps, je suis incapable de lancer osk.exe d'un processus de 32 bits sur Win7 x64. À l'origine, le code n'utilisait que:

Process.Launch("osk.exe");

qui ne fonctionnera pas sur x64 à cause de la virtualisation du répertoire. Pas un problème que je pensais, je vais juste désactiver la virtualisation, lancer l'application, et l'activer à nouveau, ce que je pensée était la bonne façon de faire les choses. J'ai aussi ajouté du code pour faire remonter le clavier s'il a été minimisé (ce qui fonctionne bien) - le code (dans un sample WPF app) se présente maintenant comme suit:

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));
            }
        }
    }
}

Mais ce code, la plupart du temps, déclenche l'exception suivante sur osk.Start():

La procédure spécifiée est introuvable au Système.Diagnostic.Processus.StartWithShellExecuteEx (ProcessStartInfo startInfo)

j'ai essayé de mettre du fil long.Les commandes de sommeil autour de l'osk.Ligne de départ, juste pour s'assurer que ce n'était pas une condition de course, mais le même problème persiste. Quelqu'un peut tache où je fais quelque chose de mal, ou fournir une solution alternative pour cela? semble pour bien lancer le bloc-notes, il ne joue pas avec le clavier à l'écran.

15
demandé sur minarmahmud 2010-05-28 17:19:16

7 réponses

Je n'ai pas d'explication très solide pour le message d'erreur exact que vous recevez. Mais désactiver la redirection va gâcher le framework .NET. Par défaut, les Processus.Start() P/invoque la fonction API ShellExecuteEx () pour démarrer le processus. Cette fonction vit dans shell32.dll, une DLL qui pourrait avoir à être chargé si ce n'était pas fait auparavant. Vous aurez le mauvais quand vous désactiverez la redirection.

une solution de contournement pour cela est de définir Processstinfo.Useshellexécute à faux. Vous n'avez pas besoin de lui ici.

de toute évidence, désactiver la redirection est une approche risquée avec des effets secondaires que vous ne pouvez pas vraiment prédire. Il y a beaucoup de DLLs qui se chargent de la demande. Un très petit assistant EXE que vous compilez avec plateforme Target = N'importe quel CPU peut résoudre votre problème.

5
répondu Hans Passant 2010-05-28 14:51:34

une application 32 bits tournant sur un système d'exploitation 64 bits devrait démarrer la version 64 bits d'osk.EXE. Ci-dessous vous voyez un code copié écrit en C# pour démarrer le clavier correct à l'écran.

    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);
    }
7
répondu Rudy Meijer 2015-01-31 16:31:55

il se passe certaines choses sous le capot qui vous obligent à démarrer osk.exe d'un fil MTA. La raison semble en être qu'un appel à Wow64DisableWow64FsRedirection affecte seulement le thread courant. Toutefois, sous certaines conditions,Process.Start va créer le nouveau processus à partir d'un thread séparé, par exemple quand UseShellExecute est défini à false et aussi lorsqu'il est appelé à partir d'un thread STA tel qu'il semble.

le code ci-dessous vérifie l'état de l'appartement et s'assure ensuite de démarrer l'écran Clavier à partir d'un fil 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);
    }
}
2
répondu Dirk Vollmar 2010-05-28 14:50:09

Ce fil semble contenir de vraies solutions: http://www.dreamincode.net/forums/topic/174949-open-on-screen-keyboard-in-c%23/

spécifiquement, Je l'ai essayé sur Win7 x64 et WinXP 32 bit jusqu'à présent, et cela fonctionne.

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);
}
1
répondu O'Rooney 2013-10-17 19:34:44

méthode maladroite:

exécutez ce fichier batch sur le côté (commencé à partir de l'Explorateur 64 bits):

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

Créer un fichier oskstart.tmp quand vous en avez besoin du clavier

0
répondu archimedes 2015-02-23 13:24:15

Cambiar las propiedades de la aplicación. COMPILAR-Desmarcar check preferencia 32 bits.

Changement d'application propertys Compila - Décochez la case "MEILLEUR 32 BITS" (ou similaire)

Vous pouvez lire ceci:

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

ou faire d'autres applications avec ces options et le déjeuner(run) de votre application principale.

0
répondu R.Alonso 2016-10-29 20:49:39

pour ceux qui font face "ne pouvait pas démarrer clavier à l'écran.", changez la cible plate-forme de votre projet à N'importe quel CPU.

-1
répondu Marcio 2013-06-08 15:46:50