Impossible de lancer le clavier à l’écran (osk.exe) à partir d’un processus 32 bits sous Win7 x64

90% du temps, je ne osk.exe pas à lancer osk.exe partir d’un processus 32 bits sous Win7 x64 . A l’origine, le code utilisait simplement:

 Process.Launch("osk.exe"); 

Ce qui ne fonctionnera pas sur x64 à cause de la virtualisation des répertoires. Pas un problème, je pensais, je vais simplement désactiver la virtualisation, lancer l’application et la réactiver, ce qui, à mon avis, était la bonne façon de procéder. J’ai également ajouté du code pour restaurer le clavier s’il a été réduit (ce qui fonctionne très bien). Le code (dans un exemple d’application WPF) se présente désormais 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 { ///  /// Interaction logic for MainWindow.xaml ///  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 ssortingng OnScreenKeyboadApplication = "osk.exe"; public MainWindow() { InitializeComponent(); } private void KeyboardButton_Click(object sender, RoutedEventArgs e) { // Get the name of the On screen keyboard ssortingng 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, lève l’exception suivante sur osk.Start() :

La procédure spécifiée est introuvable sur System.Diagnostics.Process.StartWithShellExecuteEx (ProcessStartInfo startInfo).

J’ai essayé de placer de longues commandes Thread.Sleep autour de la ligne osk.Start, histoire de vérifier que ce n’était pas une situation de concurrence critique, mais le même problème persiste. Est-ce que n’importe qui peut repérer où je fais quelque chose de mal ou fournir une solution alternative à cela? Il semble bien fonctionner en lançant le Bloc-notes, il ne veut tout simplement pas jouer au ballon avec le clavier à l’écran.

Je n’ai pas d’explication très solide pour le message d’erreur exact que vous recevez. Mais la désactivation de la redirection va perturber le framework .NET. Par défaut, Process.Start () P / Invoque la fonction API ShellExecuteEx () pour démarrer le processus. Cette fonction réside dans shell32.dll, une DLL qui pourrait devoir être chargée si cela n’était pas déjà fait. Vous obtiendrez le mauvais lorsque vous désactivez la redirection.

Une solution consiste à définir ProcessStartInfo.UseShellExecute sur false. Vous n’en avez pas besoin ici.

De toute évidence, la désactivation de la redirection est une approche risquée avec des effets secondaires imprévisibles. De nombreuses DLL sont chargées à la demande. Un très petit EXE auxiliaire que vous comstackz avec Platform Target = N’importe quel processeur peut résoudre votre problème.

Une application 32 bits s’exécutant sur un système d’exploitation 64 bits devrait démarrer la version 64 bits de osk.exe. Ci-dessous, vous voyez un code extrait 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); } 

Certaines choses se passent sous le capot qui vous obligent à démarrer osk.exe à partir d’un thread MTA. La raison semble être qu’un appel à Wow64DisableWow64FsRedirection affecte uniquement le thread actuel. Cependant, sous certaines conditions, Process.Start créera le nouveau processus à partir d’un thread séparé, par exemple lorsque UseShellExecute est défini sur false et également lorsqu’il est appelé à partir d’un thread STA, comme il semble.

Le code ci-dessous vérifie l’état de l’appartement et assure ensuite le démarrage du clavier à l’écran à partir d’un thread 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 ssortingng OnScreenKeyboardExe = "osk.exe"; [STAThread] static void Main(ssortingng[] 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); } } 

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

Plus précisément, je l’ai essayé jusqu’à présent sur Win7 x64 et WinXP 32 bits, et cela fonctionne.

 static void StartOSK() { ssortingng windir = Environment.GetEnvironmentVariable("WINDIR"); ssortingng 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); } 

Méthode maladroite:

Exécutez ce fichier de commandes sur le côté (démarré à partir de l’explorateur 64 bits):

 : lab0
 TIMEOUT / T 1> nul
 s'il existe oskstart.tmp aller à lab2
 goto lab0
 : lab2
 del oskstart.tmp
 osk
 goto lab0

Créez le fichier oskstart.tmp lorsque vous avez besoin du clavier

En savoir plus sur les applications. COMPILAR – Desmarcar check preferencia 32 bits.

Changer les propriétés de l’application Compila – Décochez la case ‘BEST 32 BITS’ (ou similaire)

Vous pouvez lire ceci:

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

ou faire une autre application avec ces options et déjeuner (exécuter) à partir de votre application principale.

Pour ceux qui font face à «Impossible de démarrer le clavier à l’écran», modifiez la cible de la plate-forme de votre projet en Tout processeur.