Windows 10 1709中日语IME的奇怪行为

PRi*_*Ktd 5 c++ mfc ime windows-10

我在Windows 10 Creators更新中遇到一个问题,当我尝试使用IME向应用程序中输入内容时,第一个字符将被忽略。即,如果我使用IME输入日语平假名字符“?” 通过输入K&A,我最终只会得到'?' 随着K的丢失。这仅发生在第一个字符上。但是完全相同的应用程序可以在Windows 7〜8中正常工作。

详细信息如下:

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

我创建了2个示例MFC MDI项目(例如MDI_sample1&MDI_Sample2)。保持默认的cpp&h文件不变,只需向MDI_Sample1和MDI_Sample2项目添加一个新类(例如CwEdit),该类将CEdit类子类化。现在,在MDI_Sample1中,打开* View.cpp,并添加WindowProc覆盖。在此函数中,我检查WM_KEYDOWN消息,并在WM_KEYDOWN上(除了VK_BACK,VK_ENTER,VK_TAB之外),使用CwEdit类动态创建一个编辑框,然后使用我获得的当前wParam和lParam向SendMessage发送一个WM_KEYDOWN。 WindowProc函数。运行程序,创建一个文档,然后按“ 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替代。即使日语IME处于活动状态,此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);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

我创建新项目时,所有其他文件都与Visual Studio创建的文件相同。CwEdit.cpp类具有2个功能,即创建以创建编辑框,以及一个OnKeyDown,如下所示:

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

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

我该如何解决这个问题?我需要使其与WindowProc一起使用。

更新:

有关此问题的一些其他详细信息。例如,我尝试输入日语单词“ ???”。使用英文字母,这将被拼写为“ sakura”。现在,我运行该应用程序,为日语平假名输入选择Microsoft IME,然后键入“ sakura”。在Creators更新之前的Windows 10之前,这将按以下方式工作。“ s”键将生成编辑框。此后,它还将调用IME合成窗口,该窗口现在将显示带有波浪下划线的“ s”。以下击键“ a”会将IME窗口中的“ s”更新为日语字符“?”。下一个击键“ k”将更新IME窗口以显示“?k”,其中k带有波浪下划线,依此类推。这是预期的正确行为。

在Windows 10 1709中,它的工作方式是:第一个“ s”击键将生成编辑框。但是没有输入法合成窗口。即使在调试运行期间也不会显示错误或警告消息。现在,下一个击键“ a”将调用IME合成窗口,其日语等效字符为“ a”,即字符“?”。最终,我得到的意思是“ ???” 在英文字母中是“ akura”。第一个“ s”丢失。

当我使用“ WindowsProc”处理编辑框创建时,会发生这种情况。在这种情况下,它将正常工作,直到您将操作系统更新到Windows 101709。另一方面,如果我在“ PreTranslateMessage”中创建了编辑框,即使在Windows 10 1709中它也将正常工作。在Windows 10 1709中已处理过,以及如何解决?

PRi*_*Ktd 2

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

情况 1:Windows 10 1709 之前 -> 打开记事本。将 IME 设置为日语平假名,然后按“k”键。您将看到一个带有波浪下划线的“k”。您需要一个或多个字符才能将这个“k”组成正确的平假名。在您提供更多输入或按 Esc 取消输入之前,“k”将保留为未经确认的 IME 输入。保持原样,无需任何其他输入,只需单击其他位置(例如桌面),记事本就会失去焦点。您会注意到任务栏/语言栏中的 IME 指示器已更改。您可能还会看到 Windows 自己的 IME 组合窗口(Windows 7 中的黑色小窗口)弹出,其中包含您的“k”。现在回到记事本,您会发现未经确认的“k”仍然悬在等待您提供进一步的输入或取消它。简而言之,当焦点改变时,未确认的IME字符串仍保持未确认状态。

情况 2:Windows 10 1709 及以上: -> 重复上述步骤。在这里你可以注意到差异。一旦焦点发生变化,IME 组合就会停止。因此,作为未确认 IME 字符串的“k”将被丢弃。

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

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

此行为不仅限于需要多次按键的字符。即使像“a”这样的单个按键字符也会以相同的方式起作用,因为即使这些字符在我们按下 Enter 键或选择组合候选之一等之前也保持未确认状态。