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