Windows 10 1709 中日语输入法的奇怪行为

Strange behaviour with Japanese IME in Windows 10 1709

我在 Windows 10 Creators 更新中遇到问题,当我尝试使用 IME 向我的应用程序输入内容时,第一个字符被忽略;即,如果我使用 IME 通过键入 K 和 A 来输入日文平假名字符“か”,我最终只会得到“あ”,而 K 丢失了。这只发生在第一个字符上。但完全相同的应用程序在 Windows 7~8.

中正常工作

详情如下:

该应用程序是 Container/Server 类型的 MFC MDI 应用程序。它的工作非常简单明了。如果打开了一个文档,那么当触发 WM_KEYDOWN 时,动态创建一个 CEdit 框并将按下的键输入编辑框。如果编辑框已经存在,则无需重新创建。只需将输入附加到编辑框的内容即可。

我创建了 2 个示例 MFC MDI 项目(例如 MDI_sample1 和 MDI_Sample2)。保持默认的 cpp 和 h 文件不变,只是添加了一个新的 class(例如 CwEdit),该 class 将 CEdit class 子class 到 MDI_Sample1 和 MDI_Sample2 项目。现在,在 MDI_Sample1 中,我打开 *View.cpp,并添加 WindowProc 覆盖。在此函数中,我检查 WM_KEYDOWN 消息,并在 WM_KEYDOWN 除了 VK_BACK、VK_ENTER、VK_TAB 上,我使用 CwEdit 动态创建一个编辑框class,然后使用我作为 WindowProc 函数的参数获得的当前 wParam 和 lParam SendMessage a WM_KEYDOWN。 运行 程序,我创建一个文件然后按'k'键。将在文档中创建一个编辑框。如果未使用 IME,字符 'k' 也将被输入到这个新创建的编辑框中。接下来,我按 'a',字符 'a' 被附加到编辑框中的 'k'。到目前为止一切顺利。

接下来,我再次创建一个新文档。这次,我将 windows IME 激活为日语并输入 'k'。同样,将创建一个编辑框,它将显示带有波浪下划线的 'k'。我输入 'a' 并正确显示日文字符“か”。再次,预期和正确。

我将此 exe 文件复制到 windows 10 1709 机器并 运行 它。同样,我重复上述相同的步骤来输入字符 'k'。在没有激活 IME 的情况下,将创建框并在其中输入 'k'。接下来我按 'a',编辑框将正确显示为 'ka'。接下来,我创建一个新文档。这次,我将 windows IME 激活为日语并输入 'k'。同样,将创建一个编辑框,但它是空的。我输入 'a',它现在显示日文字符“あ”。这种行为发生在所有角色身上。当 IME 处于活动状态时,将不会显示用于创建编辑框的第一个按键。但是一旦创建了编辑框,一切正常。

我把整个代码复制到MDI_Sample2。但是有一点变化。这一次,在视图中,我覆盖了 PreTranslateMessage 并执行了与之前在 WindowProc 中完成的完全相同的过程。并删除 WindowProc 覆盖。这个 MDI_Sample2 在 Windows 7 和 Windows 10 1709 上都运行良好,即使日语输入法处于活动状态。

两个项目的*View.cpp代码如下:

MDI_Sample1View.cpp


BOOL MDI_Sample1View::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // TODO: Add your specialized code here and/or call the base class
    if(message == WM_CHAR)
    {
        int wp = static_cast<int>(wParam);
        // All printable ascii characters
        if (wp >= 0x32 && wp <= 0x255)
        {
            EnableEdit();
            M_pEdit->SendMessage(message, wParam, lParam);
            return TRUE;
        }
    }
    else if(message == WM_KEYDOWN)
    {
        if (wParam == VK_ESCAPE)
        {
            if(M_pEdit &&
                GetFocus() == M_pEdit)
            {
                DisableEdit();
                return TRUE;
            }
        }
        EnableEdit();
    }
    return CView::WindowProc(message, wParam, lParam);
}

MDI_Sample2View.cpp


BOOL MDI_Sample2View::PreTranslateMessage(MSG* pMsg)
{
    // TODO: Add your specialized code here and/or call the base class
    if(pMsg->message == WM_CHAR)
    {
        int wp = static_cast<int>(pMsg->wParam);
        // All printable ascii characters
        if (wp >= 0x32 && wp <= 0x255)
        {
            EnableEdit();
            M_pEdit->SendMessage(pMsg->message, pMsg->wParam, pMsg->lParam);
            return TRUE;
        }
    }
    else if(pMsg->message == WM_KEYDOWN)
    {
        if (pMsg->wParam == VK_ESCAPE)
        {
            if(M_pEdit &&
                GetFocus() == M_pEdit)
            {
                DisableEdit();
                return TRUE;
            }
        }
        EnableEdit();
    }
    return CView::PreTranslateMessage(pMsg);
}

所有其他文件与我创建新项目时visual studio创建的文件相同。 CwEdit.cpp class 有两个函数,即创建编辑框的 Create 和下面给出的 OnKeyDown:

void CwSpEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if(nChar == VK_ESCAPE)
    {
        SetWindowText(_T(""));
        return;
    }
    CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}

两个项目的其余部分完全相同。那么,这里发生了什么?为什么 WindowProc 忽略第一个字符而 PreTranslateMessage 工作正常?

如何解决这个问题?我需要让它像以前一样与 WindowProc 一起工作。

更新:

有关此问题的一些其他详细信息。 例如,我尝试输入日语单词“さくら”。使用英文字母,这将拼写为 'sakura'。现在,我 运行 应用程序,select Microsoft IME for japanese hiragana input and type 'sakura'。在 Creators 更新之前 windows 10,这将按如下方式工作。 's' 击键将生成编辑框。在此之后,它还将调用 IME 组合 window,它现在将显示带有波浪下划线的 's'。以下击键 'a' 会将 IME window 中的 's' 更新为日文字符“さ”。下一次击键 'k' 将更新 IME window 以显示带有波浪下划线等的 k 的 'さk'。这是预期的正确行为。

在Windows10 1709中,它的工作原理是:'s'的第一次击键会产生编辑框。但是没有 IME 组合 window 出现。即使在调试 运行 期间也不会显示任何错误或警告消息。下一个击键 'a' 现在将调用 IME 组合 window 与 'a' 的日语等价物,即字符 'あ'。意思是,最后,我得到'あくら',英文字母是'akura'。第一个's'丢了

当我使用 'WindowsProc' 处理编辑框创建时会发生这种情况。在这种情况下,它将正常工作,直到您将 OS 更新为 Windows 10 1709。另一方面,如果我在 'PreTranslateMessage' 中创建编辑框,即使在 Windows 10 1709。在 Windows 10 1709 中处理 'WindowsProc' 的方式发生了什么变化以及如何解决它?

我终于明白了。 IME 行为似乎在 Windows 10 1709 及更高版本中发生了变化。 我将用一个例子来解释不同的行为:

案例 1:在 Windows 10 1709 之前 -> 打开记事本。将 IME 设置为日语平假名并按 'k' 键。你会看到一个'k with a wavy underline'。您需要一个或多个字符才能将此 'k' 组合成正确的平假名。在您提供更多输入或按 Esc 取消输入之前,'k' 将保留为未确认的 IME 输入。保持原样,没有任何额外的输入,只需单击其他地方(如您的桌面),这样记事本就会失去焦点。您会注意到 taskbar/languagebar 中的 IME 指示符已更改。您可能还会看到 windows 自己的 IME 组合 window(windows 7 中的小黑字)弹出,其中包含您的 'k'。现在回到记事本,您会发现未确认的 'k' 仍然悬而未决,等待您提供进一步的输入或取消它。简而言之,当焦点改变时,未确认的输入法字符串仍然保持未确认状态。

案例 2:Windows10 1709 以后: -> 重复上述步骤。在这里你可以注意到差异。一旦焦点改变,IME 组合就会停止。因此,作为 UNCONFIRMED IME 字符串的 'k' 被丢弃。

在问题中给出的示例中,WindProc 和 PreTranslateMessage 发生的情况是,对于 WindProc,在 IME keyPress 上,View 处于焦点状态并接收 KeyPress 消息。它处理它并将其传递给它的子级,根据我们的代码,创建一个新的编辑控件。现在,随着编辑控件的创建,它获得了焦点。根据新行为,当焦点从视图更改为控件时,IME 组合将停止。由于这发生在第一次 IME 按键时,我们得到了一个仍处于未确认状态的字符。作为未确认的 IME 字符,此字符将被丢弃。

使用 PreTranslateMessage,我们收到按键消息并继续创建编辑控件。创建时,编辑控件获得焦点,而我们的视图失去焦点。这会生成 KillFocus 消息,但由于我们仍在前面的 KeyPress 消息处理过程中,因此 KillFocus 消息未被处理。它正在等待前面的消息处理完成。现在,当我们 return 创建控件后返回时,我们将按键传递给新创建的编辑框。因此,最终接收到按键以及随后未确认的 IME 字符的是编辑框。因此,根据我们的示例 'k' 按键,编辑框而不是视图收到未经确认的 'k'。下一个按键自然会被编辑框接收到,因为它现在处于焦点之下,所以这第二个输入被添加到未确认的 'k' 并且合成像往常一样执行。

此行为不仅限于需要多次按键的字符。即使像 'a' 这样的单个按键字符也以相同的方式工作,因为即使这些字符在我们按下 Enter 键或 select 组合候选之一等之前仍未确认。