Невозможно запустить экранную клавиатуру (osk.exe) из 32-разрядного процесса на Win7 x64
В 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 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)
Я пробовал добавлять длинные команды Thread.Sleep в строку osk.Start, чтобы убедиться, что это не гонка, но та же проблема сохраняется. Может ли кто-нибудь определить, где я делаю что-то неправильно, или предоставить альтернативное решение для этого? Кажется, что он отлично работает с Notepad, он просто не будет играть в мяч с экранной клавиатурой.
Ответы
Ответ 1
У меня нет очень четкого объяснения точного сообщения об ошибке, которое вы получаете. Но отключение перенаправления испортит платформу .NET. По умолчанию Process.Start() P/вызывает функцию API ShellExecuteEx() для запуска процесса. Эта функция находится в shell32.dll, DLL, которая может быть загружена, если это не было сделано ранее. При отключении перенаправления вы получите неправильное.
Обходной путь для этого - установить для параметра ProcessStartInfo.UseShellExecute значение false. Здесь вам это не нужно.
Очевидно, что отключение перенаправления - это рискованный подход с побочными эффектами, которые вы не можете предсказать. Существует множество DLL, которые загружаются по требованию. Очень маленький вспомогательный EXE, который вы компилируете с помощью Platform Target = Any CPU, может решить вашу проблему.
Ответ 2
32-разрядное приложение, работающее в 64-разрядной операционной системе, должно запустить 64-разрядную версию osk.exe.
Ниже вы видите код, отрезанный на С#, чтобы запустить правильную экранную клавиатуру.
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
Некоторые вещи происходят под капотом, которые требуют, чтобы вы запускали osk.exe из потока MTA. Причина в том, что вызов 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
В этой ветке есть некоторые реальные решения:
http://www.dreamincode.net/forums/topic/174949-open-on-screen-keyboard-in-c%23/
В частности, я уже пробовал это на Win7 x64 и WinXP 32 бит, и он работает.
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);
}
Ответ 5
Неуклюжий метод:
Запустите этот командный файл сбоку (запущен из 64-разрядного проводника):
:lab0
TIMEOUT /T 1 >nul
if exist oskstart.tmp goto lab2
goto lab0
:lab2
del oskstart.tmp
osk
goto lab0
Создайте файл oskstart.tmp, если вам нужна клавиатура
Ответ 6
Cambiar las propiedades de la aplicación.
COMPILAR - Desmarcar проверяет предпочтение 32 бита.
Изменить свойства приложения
Compila - Снимите флажок "BEST 32 BITS" (или аналогичный)
Вы можете прочитать это:
http://blog.anthonybaker.me/2012/08/spawning-windows-on-screen-keyboard-osk.html
или сделать другое приложение с этими параметрами и обедать (запустить) из основного приложения.
Ответ 7
Для тех, кто сталкивается с "Не удалось запустить экранную клавиатуру", измените целевой объект платформы на любой процессор.