C# 无法在Win7 x64上从32位进程启动屏幕键盘(osk.exe)
90%的时间我无法在C# 无法在Win7 x64上从32位进程启动屏幕键盘(osk.exe),c#,wpf,exception,64-bit,on-screen-keyboard,C#,Wpf,Exception,64 Bit,On Screen Keyboard,90%的时间我无法在Win7 x64上从32位进程启动osk.exe。最初代码只是使用: Process.Launch("osk.exe"); 由于目录虚拟化,它在x64上不起作用。我想这不是问题,我只是禁用虚拟化,启动应用程序,然后再次启用它,我认为这是正确的方法。我还添加了一些代码,以便在键盘被最小化时重新启动(这很好)——代码(在示例WPF应用程序中)现在如下所示: using System; using System.Collections.Generic; using System.
Win7 x64
上从32位进程启动osk.exe
。最初代码只是使用:
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));
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Documents;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;使用系统诊断;
使用System.Runtime.InteropServices;
命名空间键盘测试
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
[DllImport(“kernel32.dll”,SetLastError=true)]
私有静态外部bool Wow64DisableWow64FsRedirection(参考IntPtr ptr);
[DllImport(“kernel32.dll”,SetLastError=true)]
公共静态外部bool Wow64RevertWow64FsRedirection(IntPtr ptr);
私有const UInt32 WM_SYSCOMMAND=0x112;
专用const UInt32 SC_RESTORE=0xf120;
[DllImport(“user32.dll”,CharSet=CharSet.Auto)]
静态外部IntPtr发送消息(IntPtr hWnd、UInt32消息、IntPtr wParam、IntPtr LPRAM);
ScreenkeyboadApplication=“osk.exe”上的私有字符串;
公共主窗口()
{
初始化组件();
}
私有无效键盘按钮\单击(对象发送器,路由目标e)
{
//获取屏幕键盘的名称
string processName=System.IO.Path.GetFileName WithOutExtension(在ScreenkeyBoadApplication上);
//检查应用程序是否未运行
var query=来自process.getprocesss()中的进程
其中process.ProcessName==ProcessName
选择流程;
var keyboardProcess=query.FirstOrDefault();
//如果它不存在,就启动它
if(keyboardProcess==null)
{
IntPtr ptr=新的IntPtr();
bool successfullydisabledw64redirect=false;
//如果我们在x64上,请禁用x64目录虚拟化,
//否则键盘启动将失败。
if(System.Environment.is64位操作系统)
{
successfullydisabledw64redirect=wow64 disablewow64fsredirection(参考ptr);
}
//osk.exe在windows/system文件夹中。因此,我们可以直接调用它,而无需路径
使用(Process osk=new Process())
{
osk.StartInfo.FileName=OnScreenKeyboadApplication;
osk.Start();
osk.WaitForInputIdle(2000年);
}
//如果目录虚拟化已禁用,请重新启用它。
if(System.Environment.is64位操作系统)
如果(成功禁用WOW64重定向)
Wow64RevertWow64FsRedirection(ptr);
}
其他的
{
//如果键盘已经在运行,请将其置于前端
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行中加入长线程.Sleep命令,只是为了确保这不是一个竞争条件,但同样的问题仍然存在。有人能指出我做错了什么,或者提供一个替代解决方案吗?启动记事本时,它似乎工作得很好,只是无法与屏幕键盘配合。某些事情需要您从MTA线程启动osk.exe。原因似乎是对的调用只影响当前线程。但是,在某些情况下,
Process.Start
将从单独的线程创建新进程,例如,当UseShellExecute
设置为false时,以及从STA线程调用时
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);
}
下面的代码检查单元状态,然后确保从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);
}
}
对于您收到的确切错误消息,我没有非常可靠的解释。但是禁用重定向将使.NET框架陷入混乱。默认情况下,Process.Start()P/调用ShellExecuteEx()API函数来启动进程。此函数位于shell32.dll中,如果以前没有加载该dll,则可能必须加载该dll。当你禁用重定向时,你会得到错误的结果 解决方法是将ProcessStartInfo.UseShellExecute设置为false。你在这里不需要它
显然,禁用重定向是一种危险的方法,其副作用你无法真正预测。有很多DLL可以按需加载。使用平台目标编译的非常小的helper EXE=任何CPU都可以解决您的问题。对于那些面临“无法在屏幕键盘上启动”问题的人,请将项目的平台目标更改为任何CPU。在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);
}
笨拙的方法:
R
var path64 = Path.Combine(Directory.GetDirectories(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "winsxs"), "amd64_microsoft-windows-osk_*")[0], "osk.exe");
var path32 = @"C:\windows\system32\osk.exe";
var path = (Environment.Is64BitOperatingSystem) ? path64 : path32;
if(File.Exists(path))
{
Process.Start(path);
}