聚焦可编辑控件时屏幕键盘上的 MFC

2022-01-12 00:00:00 mfc controls onfocus on-screen-keyboard

我想做一件在概念上很简单的事情:对于每个接受键盘输入的控件(CEdit、带有可编辑文本的 CCombobox 等),当控件获得焦点并启用时,使屏幕键盘出现.最好使用可访问性支持(我已经阅读了一些有关 Microsoft 用户界面自动化的资料),而不是直接调用 osk.exe 实用程序.

I want to do a thing conceptually simple as this: For every control that accepts keyboard input(CEdit, CCombobox with Editable text, etc.), when the control is focused and enabled, make the On Screen Keyboard appear. Preferably with accessibility support (I've done some reading about Microsoft User Interface Automation) rather than calling directly the osk.exe utility.

当用户将焦点放在可编辑控件上时,它非常类似于触摸屏智能手机.

A thing very like the touchscreen smartphones when the user puts the focus on an editable control.

更新:如果有一个 Windows 选项可以使屏幕键盘按照我描述的方式运行,那很好!

UPDATE: If there is a Windows option that can make the On Screen Keyboard to behave the way I describe that's just fine!

更新 2:我想要的东西类似于 http://msdn.microsoft.com/en-us/library/windows/apps/hh465404.aspx,但在 Windows 7 中.

UPDATE 2: The thing I want is similar to http://msdn.microsoft.com/en-us/library/windows/apps/hh465404.aspx , but in Windows 7.

我对 IUIAutomation 类做过实验,在聚焦可编辑控件时直接调用 osk.exe,在聚焦不可编辑控件时关闭该窗口.

I had done experiments with the IUIAutomation class, calling directly osk.exe when focusing an editable control, and closing that window when focusing a non-editable one.

但是,我还有三个问题:

But, I still have three problems:

1) 打开 CFileDialog 应用程序时无响应.仅应用程序的另一个模态对话框发生同样的情况(在每个其他模态对话框中一切正常).我发现 CFileDialog 打开了一些后台线程和我唯一有问题的模式.我怀疑这与线程问题有关.

1) When opening a CFileDialog application becomes unresponsive. The same is happening for only another Modal Dialog of the application (in every other Modal Dialog everything works fine). I discovered that CFileDialog opens some background threads and my only problematic modal too. I suspect this has to do with threading issues.

2) 当某个其他控件获得焦点时,然后我直接单击 CBS_DROPDOWN 组合框的下拉按钮,而不是选择它的编辑控件,我看到 OSK 出现和消失.使用 Spy++ 和 UIA Inspect,我怀疑文本编辑在组合框进入删除状态之前被聚焦.

2) When some other control is focused, and thereafter I click directly the DropDown button of a CBS_DROPDOWN combobox, not picking on its edit control, I see a flash of OSK appear and disappear. Using Spy++ and UIA Inspect I got the suspicion of the text edit being focused before the combobox goes to the dropped state.

3) 用SetWindowPos做了一些实验后,MoveWindow

3) After doing some experiments with SetWindowPos, MoveWindow

::MoveWindow(osk_wnd->m_hWnd, LeftOsk, TopOsk, -1, -1, FALSE);

或者

osk_wnd->SetWindowPos(NULL, LeftOsk, TopOsk, -1, -1, SWP_NOSIZE | SWP_NOACTIVATE);

还使用了一些消息处理实验,例如

and using also some message processing experiments like

//osk_wnd is a CWnd* variable that represents OSK main window
osk_wnd->PostMessage(WM_SYSCOMMAND, SC_MOVE + HTCAPTION, MAKELPARAM(point.x, point.y));

或者

POINT       point       = {0};

GetCursorPos(&point);
SendMessage(osk_wnd->m_hWnd, WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x, point.y));

SendMessage(osk_wnd->m_hWnd, WM_NCMOUSEMOVE, HTCAPTION, MAKELPARAM(LeftOsk, TopOsk));

GetCursorPos(&point);
SendMessage(osk_wnd->m_hWnd, WM_NCLBUTTONUP, HTCAPTION, MAKELPARAM(LeftOsk + point.x, TopOsk + point.y));

我无法移动 OSK 窗口.我能移动它很重要;否则,它会出现覆盖焦点控件,并且用户看不到他给控件的输入!旁注:以下用于最小化 OSK 窗口的代码完美运行:

I could not move the OSK window. It is important that I can move it; otherwise, it will appear covering the focused control, and the user can not see the input he is giving to the control! Side note: the following code for unminimizing OSK window worked perfectly:

if(osk_wnd->IsIconic())
    osk_wnd->PostMessage(WM_SYSCOMMAND, SC_RESTORE, NULL);

即使尝试使用此 AutoIt 脚本移动该窗口也无济于事:

Even trying to move that window with this AutoIt script did nothing:

If WinActivate("[CLASS:OSKMainClass]") Then

   If WinWaitActive("[CLASS:OSKMainClass]") Then

      ConsoleWrite("activ" & @CRLF)

      Sleep(500)

      If WinMove("[CLASS:OSKMainClass]", "", 30 ,320,360,123) Then
         ConsoleWrite("move 1" & @CRLF)
      EndIf
   EndIf
EndIf

If WinMove("[CLASS:OSKMainClass]", "", 30 ,320,360,123) Then
   ConsoleWrite("move 2" & @CRLF)
EndIf

我看到了文字

激活

移动 1

移动 2

正在打印,但 OSK 窗口没有移动到任何地方.也许它正在积极拒绝定位指令.

being printed, but the OSK window is not being moved to anywhere. Maybe it is actively rejecting posiotioning instructions.

我尝试了这个 AutoIt 脚本来移动 Tabtip.exe 窗口,但它也失败了.我试过这个脚本来移动 Visual Studio 命令提示符的窗口,它移动了!GRRRRR!

I tried this AutoIt script to move the Tabtip.exe Window and it also fails. I tried this script to move the Visual Studio Command Prompt's Window and it moves! GRRRRR!

更新 3:在我看来,这与用户访问控制和权限有关.如果我禁用用户访问控制或以管理员身份运行应用程序,则 MoveWindow 指令可以完美运行!这同样适用于 AutoIt 试图移动 OSKMainCLASS 窗口!那么,Windows 7 中是否有一些本地或组策略允许我对 OSK 应用程序进行例外处理?

UPDATE 3: Seems to me this thing has to do with User Access Control and Permissions. If I disable User Access Control or run the application as Administrator the MoveWindow instruction works perfectly! The same applies to AutoIt trying to move OSKMainCLASS window! So, is there some Local or group policy in Windows 7 that allow me to make an exception to the OSK application?

4) 为了让 osk.exe 在 64 位操作系统上运行,我尝试了所有不强制禁用 SysWOW64 重定向的方法,但我无法摆脱它.到目前为止,我没有发现任何问题,但也许下面的代码将来会导致问题.

4) For getting the osk.exe running on 64 bit operating system I tried everything that didn't force to disable the SysWOW64 redirection, but I could not escape from it. Till now, I did not see any problems, but maybe the following code will cause problems in the future.

const int sysDirNameSize= 1024;
TCHAR sysDir[sysDirNameSize];
if( !GetSystemDirectory( sysDir,  sysDirNameSize) )
{
    ASSERT(FALSE);
    return;
}

CString osk_path = CString(sysDir) + _T("\osk.exe");
PVOID pOldValue = NULL;
BOOL bRes= Wow64DisableWow64FsRedirection(&pOldValue);
::ShellExecute(NULL, NULL, osk_path, _T("") , sysDir, SW_SHOW);
if(bRes)
    Wow64RevertWow64FsRedirection(pOldValue);

更新 5:似乎我上次尝试执行 osk.exe 进程开始了,但它的窗口没有出现!要将值归因于 osk_wnd,我有一个函数可以遍历 CWnd::GetDesktopWindow() 的所有 GW_CHILD 窗口,以搜索 GetClassName(...) 为OSKMainClass"但没有找到的那个!

UPDATE 5: Seems last times I tried to execute osk.exe the process begins, but its window doesn't appear! To attribute the value to osk_wnd, I have a function that traverses all GW_CHILD windows of the CWnd::GetDesktopWindow() to search the one which GetClassName(...) is "OSKMainClass" and none is found!

推荐答案

再一次,我自己解决了问题:)

One more time, I solved the problem by myself :)

1) 看完http://social.msdn.microsoft.com/Forums/br/windowsaccessibilityandautomation/thread/aee0be4d-2cf5-45e7-8406-2de3e5d0af03 和 http://www.c-plusplus.de/forum/285011-full (德语,但谷歌翻译可以帮助),我决定把相关的代码放在一个单独的线程.打开 CFileDialog 窗口时不再挂起 :)

1) After reading http://social.msdn.microsoft.com/Forums/br/windowsaccessibilityandautomation/thread/aee0be4d-2cf5-45e7-8406-2de3e5d0af03 and http://www.c-plusplus.de/forum/285011-full (German, but Google translator can help), I decided to put the code related on a separate thread. No more hanging when opening CFileDialog windows :)

2) 项目经理说这是小问题:)

2) Project manager said that this is a minor problem :)

3) 在跟项目经理说在执行 osk.exe 时需要传递一个安全令牌后,他回答说这会为一个非常简单的事情在代码中引入很多复杂性,最好应用程序可以以管理员模式运行:)

3) After saying to Project manager that it would be needded to pass a security token when executing osk.exe, he replied that that would introduce much complexity in the code for a very simple thing, and it is preferable that application can run in admin mode :)

4) 直到现在都没有引起问题,就这样吧:)

4) Not causing problems till now, so let it be :)

无论如何,谢谢你的帮助.

Anyway, thanks for help.

相关文章