使用 C# 和 Win API 移动 On-Screen 键盘 (osk.exe)
Move On-Screen Keyboard (osk.exe) with C# & Win API
我制作了这个 .ps1
小脚本,因为它允许我 运行 C# 而无需使用编译器(至少直接)。我想移动以 cmd /c osk.exe
打开的 "Accessibility On-Screen Keyboard",因为我不能真正使用 TabTip
- Win8+ 上的平移触摸屏键盘。
由于 On-Screen 键盘不像平移键盘那么漂亮,我想 将键盘移动 到所需位置 和调整它的大小。我注意到 OSK 有一个 child window (OSKMainClass
→ DirectUIHWND
),所以我为此努力了,但没有运气。另一方面,单个 window 的相同代码适用于记事本并正确放置和调整它的大小。
我将 Process.Start()
放入 if 中,以便它返回一些反馈,因此我看到它找到了 child window - 很好。 但是,它没有移动它。
当我按下 Alt+Tab
并按住 Alt
时出现了一件有趣的事情 - OSK window 看起来像灰色全屏(metro-like 样式)。我不确定这是否是 parent window 的预期行为。
此外,我认为这是 window 样式的问题,但不,样式几乎相同(除了两个不相关的样式),所以我不知道如何继续。有什么想法吗?
代码:
$CSsource = @"
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Win {
public static class API {
[DllImport("user32.dll")]
static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName
);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(
IntPtr parentHwnd,
IntPtr childAfter,
string className,
string windowTitle
);
[DllImport("user32.dll")]
static extern bool ShowWindow(
IntPtr hWnd,
int nCmdShow
);
[DllImport("user32.dll")]
static extern bool MoveWindow(
IntPtr hWnd,
int X, int Y,
int Width, int Height,
bool Repaint
);
public static void Move(
string wClass, string wName,
string childClass,
int top, int left,
int width, int height
) {
IntPtr hwnd = FindWindow(wClass, wName);
if ((int) hwnd > 0) {
IntPtr subHwnd;
if (childClass != String.Empty) {
subHwnd = FindWindowEx(hwnd, IntPtr.Zero, childClass, null);
} else {
subHwnd = IntPtr.Zero;
}
if ((int) subHwnd > 0) {
MoveWindow(subHwnd, left, top, width, height + 50, true);
Process.Start("cmd"); //feedback from loop, heh
} else {
MoveWindow(hwnd, left, top, width, height + 50, true);
}
}
}
}
}
"@
add-type -TypeDefinition $CSsource
#[Win.API]::Move('OSKMainClass', 'On-Screen Keyboard', 'DirectUIHWND', 50, 50, 200, 100)
#[Win.API]::Move('OSKMainClass', 'Accessibility On-Screen Keyboard', 'DirectUIHWND', 50, 50, 200, 100)
[Win.API]::Move('OSKMainClass', 'Accessibility On-Screen Keyboard', '', 50, 50, 200, 100)
[Win.API]::Move('Notepad', 'Untitled - Notepad', '', 50, 50, 200, 100)
OSK window 样式:
- WS_CAPTION
- WS_VISIBLE
- WS_CLIPSIBLINGS
- WS_CLIPCHILDREN
- WS_SYSMENU
- WS_THICKFRAME
- WS_OVERLAPPED
- WS_MINIMIZEBOX
- WS_EX_LEFT
- WS_EX_LTRREADING
- WS_EX_TOPMOST
- WS_EX_WINDOWEDGE
- WS_EX_APPWINDOW
- WS_EX_LAYERED
- WS_EX_NOACTIVATE
记事本window 样式:
以上+
- WS_RIGHTSCROLLBAR
- WS_ACCEPTFILES
OSK 在其清单中有 UIAccess="true"
,因此它以更高的完整性级别(略高于中等)运行。
要与之互动,您需要:
- 运行 您的应用提升了
或
- 将 UIAccess="true" 放入您的清单中
- 签署 .exe(This blog post 表示您可以在测试期间自行签署)
- 将 .exe 放在 Program Files 文件夹中的某个位置
您也可以尝试禁用 UAC 以确认是您缺少 UIAccess 的问题。
我制作了这个 .ps1
小脚本,因为它允许我 运行 C# 而无需使用编译器(至少直接)。我想移动以 cmd /c osk.exe
打开的 "Accessibility On-Screen Keyboard",因为我不能真正使用 TabTip
- Win8+ 上的平移触摸屏键盘。
由于 On-Screen 键盘不像平移键盘那么漂亮,我想 将键盘移动 到所需位置 和调整它的大小。我注意到 OSK 有一个 child window (OSKMainClass
→ DirectUIHWND
),所以我为此努力了,但没有运气。另一方面,单个 window 的相同代码适用于记事本并正确放置和调整它的大小。
我将 Process.Start()
放入 if 中,以便它返回一些反馈,因此我看到它找到了 child window - 很好。 但是,它没有移动它。
当我按下 Alt+Tab
并按住 Alt
时出现了一件有趣的事情 - OSK window 看起来像灰色全屏(metro-like 样式)。我不确定这是否是 parent window 的预期行为。
此外,我认为这是 window 样式的问题,但不,样式几乎相同(除了两个不相关的样式),所以我不知道如何继续。有什么想法吗?
代码:
$CSsource = @"
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Win {
public static class API {
[DllImport("user32.dll")]
static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName
);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(
IntPtr parentHwnd,
IntPtr childAfter,
string className,
string windowTitle
);
[DllImport("user32.dll")]
static extern bool ShowWindow(
IntPtr hWnd,
int nCmdShow
);
[DllImport("user32.dll")]
static extern bool MoveWindow(
IntPtr hWnd,
int X, int Y,
int Width, int Height,
bool Repaint
);
public static void Move(
string wClass, string wName,
string childClass,
int top, int left,
int width, int height
) {
IntPtr hwnd = FindWindow(wClass, wName);
if ((int) hwnd > 0) {
IntPtr subHwnd;
if (childClass != String.Empty) {
subHwnd = FindWindowEx(hwnd, IntPtr.Zero, childClass, null);
} else {
subHwnd = IntPtr.Zero;
}
if ((int) subHwnd > 0) {
MoveWindow(subHwnd, left, top, width, height + 50, true);
Process.Start("cmd"); //feedback from loop, heh
} else {
MoveWindow(hwnd, left, top, width, height + 50, true);
}
}
}
}
}
"@
add-type -TypeDefinition $CSsource
#[Win.API]::Move('OSKMainClass', 'On-Screen Keyboard', 'DirectUIHWND', 50, 50, 200, 100)
#[Win.API]::Move('OSKMainClass', 'Accessibility On-Screen Keyboard', 'DirectUIHWND', 50, 50, 200, 100)
[Win.API]::Move('OSKMainClass', 'Accessibility On-Screen Keyboard', '', 50, 50, 200, 100)
[Win.API]::Move('Notepad', 'Untitled - Notepad', '', 50, 50, 200, 100)
OSK window 样式:
- WS_CAPTION
- WS_VISIBLE
- WS_CLIPSIBLINGS
- WS_CLIPCHILDREN
- WS_SYSMENU
- WS_THICKFRAME
- WS_OVERLAPPED
- WS_MINIMIZEBOX
- WS_EX_LEFT
- WS_EX_LTRREADING
- WS_EX_TOPMOST
- WS_EX_WINDOWEDGE
- WS_EX_APPWINDOW
- WS_EX_LAYERED
- WS_EX_NOACTIVATE
记事本window 样式:
以上+
- WS_RIGHTSCROLLBAR
- WS_ACCEPTFILES
OSK 在其清单中有 UIAccess="true"
,因此它以更高的完整性级别(略高于中等)运行。
要与之互动,您需要:
- 运行 您的应用提升了
或
- 将 UIAccess="true" 放入您的清单中
- 签署 .exe(This blog post 表示您可以在测试期间自行签署)
- 将 .exe 放在 Program Files 文件夹中的某个位置
您也可以尝试禁用 UAC 以确认是您缺少 UIAccess 的问题。