はじめに
この問題は 2022/11/30 (日本時間) リリースの Windows 11 22H2 Moment 2 (KB5020044 / Build 22621.900) で解決しました。
2022/12/14 (日本時間) リリースの KB5021255 / Build 22621.963 を直接インストールした (KB5020044 を経由しない) 場合には〔半角/全角〕問題が発生するという報告がありましたが、当方で Windows 11 22H2 Enterprise 評価版をクリーンインストールして試した限りではこの問題は発生しませんでした。
Windows 11 22H2 において、特定のアプリケーションが〔半角/全角〕キーを押しただけでクラッシュするようです。
具体的には 浮動小数点例外 (Floating-point exception) を使うようになっているアプリケーションがクラッシュします。Delphi および C++Builder 製アプリの多くがこれに該当します。
後述しますが、Delphi / C++Builder 固有の問題ではありません。
当初は認識していませんでしたが、テキストエディタ『Mery』の作者である Kuro さんのツイートでこの問題を知る事となりました。
『TeraPad』でも同様の問題が発生しているようです。
『Clibor』でも同様の問題が発生しているようです。
Delphi で問題を再現するのは簡単で、フォームにエディットボックスを貼ったアプリケーションを用意するだけです。実行して〔半角/全角〕を押すと浮動小数点例外でアプリケーションがクラッシュします。
物理キーボードだけではなく、スクリーンキーボード で〔半角/全角〕キーを押してもアプリケーションはクラッシュします。
〔半角/全角〕キーを押しただけで浮動小数点例外が発生するのは意図された動作だとは思えません。
Windows 10 22H2 ではこの問題は発生しません。
See also:
- Windows 11 22H2 で半角/全角キーを押すと Mery が落ちる問題について (haijin-boys.com)
- Mery (haijin-boys.com)
- TeraPad (tera-net.com)
- Clibor (chigusa-web.com)
回避方法
Windows Update を実施してください。以下にあるのは問題が発生する環境での回避方法です。
いくつか回避方法があるようです。
〔半角/全角〕キーを押さずに…例えばタスクバーの Microsoft IME アイコンから切り替えるとアプリケーションは落ちませんが現実的な回避方法ではありません。
...と思っていたら、Microsoft の回避方法 (2022/11/18 付) は
Workaround: Changing input mode by clicking the IME mode icon in the taskbar should not trigger this issue. This issue occurs only when using keyboard shortcuts.
「タスクバーから切り替えれ!」 ですって。正気ですか?
■ 以前のバージョンの Microsoft IME を使う
[設定]
から [時刻と言語 > 言語と地域 > 日本語 (…) > 言語のオプション > Microsoft IME > 全般]
と辿るか、タスクバーの Microsoft IME アイコンを右クリックして [設定 > 全般]
と辿り、[以前のバージョンの Microsoft IME を使う] をオンにします。
Windows 10 以降、新しい Microsoft IME の挙動がしばしばおかしくなるので、私は [以前のバージョンの Microsoft IME を使う] をオンにして使っており、本件に気付きませんでした。
- KB4564002: 一部の Microsoft IM を使用している場合、Windows 10 バージョン 20H2 および Windows 10 バージョン 2004 で問題が発生する可能性があります (support.microsoft.com)
- Windows 11 で新しい IME を使用すると、Excel が停止または終了する場合がある (support.microsoft.com)
■ Microsoft IME 以外の IME を使う
ジャストシステムの『ATOK』や Google の『Google 日本語入力』を使う事でも問題を回避できます。多くの場合で [以前のバージョンの Microsoft IME を使う] と同じ効果が得られます。
See also:
■ IME On / Off のキーとして〔Ctrl〕+〔Space〕を有効にする
〔Ctrl〕+〔Space〕を変換キーとして追加するには、[設定]
から 日本語 IME 設定
を検索して出すか、タスクバーの Microsoft IME アイコンを右クリックして [設定]
を開きます。
設定のキャプションは [時刻と言語 > 言語と地域 > Microsoft IME]
となっていると思いますので [キーとタッチのカスタマイズ]
をクリックします。
各キー / キーの組み合わせに好みの機能を割り当てます
にチェックを入れ、Ctrl + Space
に IME-オン/オフ を割り当てます。
〔Ctrl〕+〔Space〕で IME を On / Off する分には問題は発生しないようです。ただ、〔半角/全角〕キーを押せばやっぱりアプリケーションはクラッシュします。
〔半角/全角〕を無効にする手段は Windows 11 22H2 の設定に用意されていないようですし、以前の Microsoft IME ともキー設定を共有していないようなので、以前の Microsoft IME でキー設定を行って新しい Microsoft IME に戻すという手も使えないようです。
■ オプションの診断データを送信しない
[設定]
から [プライバシーとセキュリティ > 診断とフィードバック]
と辿り、[オプションの診断データを送信する] をオフにします。
IME の変換はトリガーに過ぎず、不具合の本当の原因はオプションの診断データ送信なのではないかという気も…。
■ アプリケーション側で浮動小数点例外を使わないようにする
Delphi / C++Builder はデフォルトでいくつかの浮動小数点例外を利用するようになっているため、多くの Delphi / C++Builder 製アプリケーションが影響を受けます。
ARM 系 CPU で浮動小数点例外がサポートされていない関係だと思われますが、FireMonkey アプリケーションではデフォルトで浮動小数点例外を使わないようになっており、問題が発生するのは主に VCL アプリケーションという事になります。
加えて、最近の Delphi / C++Builder でコンパイルされたものだけが対象という訳ではなく、古い Delphi / C++Builder でコンパイルされたアプリケーションでも発生しますし、浮動小数点例外を使うようになっていれば、言語を問わず同様の問題が発生すると思われます。
Visual C++ でもこの問題は発生します。ただし、デフォルトではすべての例外がマスクされているため、明示的に浮動小数点例外を使うコードを記述しない限り問題は起きません。ダイアログにエディットボックスを貼り付けて、次のようなコードを記述すると問題を再現できます。
#include "float.h"
...
unsigned int control_word;
_controlfp_s(&control_word, _EM_DENORMAL | _EM_UNDERFLOW | _EM_INEXACT, _MCW_EM);
つまり Windows 11 22H2 は 新しい IME を使う限り浮動小数点例外を扱えない という問題を抱えている事になります。
Delphi における対策例
Delphi だと次のコードで浮動小数点例外を使わないようにできます。
uses
..., System.Math;
...
SetExceptionMask(exAllArithmeticExceptions);
古い Delphi だと次のようなコードになります。
uses
..., Math;
...
SetExceptionMask([exInvalidOp..exPrecision]);
浮動小数点例外の種類は列挙型 TArithmeticException にて定義されています。SetExceptionMask()
では利用しない (マスクする) 浮動小数点例外を指定します。
exAllArithmeticExceptions
は次のすべてのフラグの集合です。
値 | 意味 |
---|---|
exInvalidOp | 無効な演算を行おうとした。 |
exDenormalized | 数値が非ゼロとして格納できるサイズより小さいサイズに切り詰められた。数値が非正規化された。 |
exZeroDivide | ゼロで除算しようとした。 |
exOverflow | 数値がサポートされている正の上限値を超えた。 |
exUnderflow | 数値が、サポートされている負の下限値を超えた。 |
exPrecision | 数値が精度の桁数を超えた。 |
SetExceptionMask() は Delphi 6 以降で実装されています。それよりも前のバージョンの Delphi 製アプリケーションで問題が発生する場合には Set8087CW() を使って同等の処理を行う必要があります。
Delphi 8 等 (Delphi for .NET) では {$FINITEFLOAT OFF} コンパイラ指令で浮動小数点例外を使わないようにできますが…もう誰も使っていないでしょうね。
See also:
- 浮動小数点例外 (DocWiki)
- 浮動小数点の例外 - Win32 apps (Microsoft Learn)
- 浮動小数点の例外チェック(Delphi)(docs.embarcadero.com)
- SetExceptionMask (DocWiki)
- TArithmeticException (DocWiki)
- Set8087CW (DocWiki)
- Set8087CW Procedure - Delphi in a Nutshell (oreilly.com)
- SetMXCSR (DocWiki)
- _control87, _controlfp (DocWiki)
もうちょっと詳しく
浮動小数点例外を全体的にオフにしたい場合は次のようにグローバル変数へデフォルトのマスクを格納しておき、
uses
..., System.Math;
...
var
BackupMask: TArithmeticExceptions;
...
BackupMask := SetExceptionMask(exAllArithmeticExceptions);
浮動小数点例外を使いたい所 (0 除算を try except で処理している所とか) でバックアップしたマスクを設定するといいかと思います。浮動小数点例外は EMathError で捕捉できます。
SetExceptionMask(BackupMask);
try
try
// 浮動小数点例外が出るかもしれないコード
except
on E: EMathError do
begin
// 浮動小数点例外が出た時の処理
end;
end;
finally
SetExceptionMask(exAllArithmeticExceptions);
end;
VCL がマスクを変更する事があるため、次のコードの方が適当かもしれません。
var CurrentMask := SetExceptionMask(BackupMask);
try
try
// 浮動小数点例外が出るかもしれないコード
except
on E: EMathError do
begin
// 浮動小数点例外が出た時の処理
end;
end;
finally
SetExceptionMask(CurrentMask);
end;
スレッドセーフではないので、スレッドを使っている時には注意が必要です。
Delphi でのデフォルトのマスクは [exDenormalized, exUnderflow, exPrecision] です。つまり exAllArithmeticExceptions - DefaultExceptionFlags です。
See also:
〔半角/全角〕キーを押しても落ちないけど?
エディットボックス等の入力項目が存在しないのであれば落ちません。
This issue is observed for applications which load certain components of the Text Services Framework (TSF).
「TSF の特定のコンポーネントをロードする」...つまりは入力項目が存在するアプリケーションで問題が発生するという事なのだろうと思われます。
エディットボックス等の入力項目が存在しない、一見この問題の影響を受けないようなアプリケーションであっても、ファイルオープン/セーブダイアログが使われているとファイル名に日本語名を入力しようとしてクラッシュしますのでご注意ください。
Delphi アプリケーションの場合、次のユニットを uses に加えていると浮動小数点例外が使われなくなるため、〔半角/全角〕キーを押しても落ちません。
ユニット | バージョン |
---|---|
SHDocVw | XE4 以降 (ユニット自体は Delphi 5 から存在) |
System.Win.InternetExplorer (SHDocVw の代替) |
XE8 以降 |
Vcl.Edge | 10.4 Sydney 以降 |
Winapi.EdgeUtils | 11 Alexandria 以降 |
initialization
FSetExceptMask(femALLEXCEPT);
FSetExceptMask()
は XE4 以降で実装されています。femALLEXCEPT
は次のすべてのフラグの集合です。
値 | 意味 |
---|---|
femINEXACT | 不正確な演算結果。 |
femUNDERFLOW | 数値が、サポートされている負の下限値を超えた。 |
femOVERFLOW | 数値がサポートされている正の上限値を超えた。 |
femDIVBYZERO | ゼロで除算しようとした。 |
femINVALID | 無効な演算を行おうとした。 |
当該ユニットが uses に加えられると、意図せず浮動小数点例外の処理が働かなくなる事がある
という、また別の心配事も。
See also:
例外処理を伴わない 0 除算の処理
次のような浮動小数点例外を利用したコードがあったとします。0 除算になったら c の値を 1 にするコードです。
program FP_Test;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Math;
var
a, b, c: Double;
begin
try
a := 100;
b := 0;
c := a / b;
except
on E: EMathError do
begin
c := 1;
end;
end;
Writeln(c);
end.
浮動小数点例外で exZeroDivide をマスクした場合、EZeroDivide は発生しなくなるため、別の方法で 0 除算を検出しなくてはなりません。
program FP_Test;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Math;
var
a, b, c: Double;
begin
SetExceptionMask(exAllArithmeticExceptions);
a := 100;
b := 0;
c := a / b;
if IsInfinite(c) or IsNan(c) then
//if c.IsInfinity or c.IsNan then
c := 1;
Writeln(c);
end.
0 除算の結果は Infinity になりますが、除数も被除数も 0 の場合には NaN となるため、IsInfinite()
と IsNan()
の両方で判定する必要があります。ただ…
program FP_Test;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Math;
var
a, b, c: Double;
begin
SetExceptionMask(exAllArithmeticExceptions);
a := 100;
b := 0;
if b = 0 then // または if IsZero(b) then
c := 1
else
c := a / b;
Writeln(c);
end.
「除数が 0 かどうか?」を先に判定して計算するのが筋だとは思います。どちらがコード書き換えの手間が掛からないか、要検討ですね。
See also:
おわりに
アプリケーションがクラッシュする原因は、「Windows 11 (22H2) で新しい Microsoft IME を使っていてオプションの診断データを送信するようになっていると、〔半角/全角〕キーが押されて IME が ON になったタイミングで浮動小数点例外が発生する」 という Microsoft IME のバグだと思われます。
■ Embaracadero の情報
Delphi / C++Builder の開発元である Embaracadero の Support Wiki にも本件に関する情報があります (2022/10/06 付)。
「Delphi 7 から~」 とありますが、Embarcadero が Delphi 7 以降でしかテストできていないだけで、実際には Delphi 2 ~ Delphi 6 (C++Builder 1 ~ 6) 製のアプリケーションもクラッシュします。
■ Microsoft の情報
一般チャネルで 2022/11/30 (日本時間) にリリースされた Windows 11 22H2 Moment 2 (KB5020044 / Build 22621.900) でこの問題は解決しました。
-
2022 年 11 月 29 日 — KB5020044 (OS ビルド 22621.900) プレビュー (support.microsoft.com)
お困りの方は Windows Update を最後まで適用してみてください。
See also:
- 「半角/全角」キーを押すと古いアプリが落ちる(Win11Home22H2) (answers.microsoft.com)
- Changing IME input modes might cause applications to become unresponsive - Windows 11, version 22H2 known issues and notifications (learn.microsoft.com)
- Some apps (e.g. Mery) crashing when pressing the half-width/full-width key while using the Japanese IME (フィードバック Hub)
- Mery などいくつかのアプリで日本語IMEを使用中に半角/全角キーを押下するとアプリがクラッシュする (フィードバック Hub)
- Microsoft IME (フィードバック Hub)
- 日本語 IME の使用中に半角/全角キーを押すとアプリケーションがクラッシュする (例: Ichitaro、CADVANTAGE 99) (フィードバック Hub)
- 全角/半角キーを押すと、アプリが強制終了する (フィードバック Hub)
- Windows 11 の最新のビルドで特定のアプリケーションで日本語IMEをオンまたは日本語入力するとアプリケーションがクラッシュする (フィードバック Hub)
■ 浮動小数点例外の修正が必要かどうかの判定
浮動小数点例外で問題が発生する環境は次の通りです。
- Windows 11 (Version 10.0、Build 22000 以降)
- 22H2 (Build 22621)
- UBR (Update Build Revision) が 521 以上 900 未満
これを判定するコードを書いてみました。そこそこ古い Delphi でもコンパイルが通るようになっています。
uses
..., Winapi.Windows;
...
function IsNeedDisableFPE: Boolean;
var
sFileName: string;
wUBR: WORD;
dwInfoSize, dwHandle: DWORD;
pInfo: Pointer;
pFileInfo: PVSFixedFileInfo;
arrBuf: array [0..MAX_PATH-1] of Char;
begin
result := False;
if (Win32Platform = VER_PLATFORM_WIN32_NT) and
(Win32MajorVersion = 10) and
(Win32MinorVersion = 0) and
(Win32BuildNumber = 22621) then
begin
GetSystemDirectory(@arrBuf, SizeOf(arrBuf));
sFileName := IncludeTrailingPathDelimiter(StrPas(arrBuf)) + 'NTOSKRNL.EXE';
dwInfoSize := GetFileVersionInfoSize(PChar(sFileName), dwHandle);
if dwInfoSize > 0 then
begin
GetMem(pInfo, dwInfoSize);
try
GetFileVersionInfo(PChar(sFileName), 0, dwInfoSize, pInfo);
VerQueryValue(pInfo, PathDelim, Pointer(pFileInfo), dwInfoSize);
wUBR := LoWord(pFileInfo.dwFileVersionLS);
result := (wUBR >= 521) and (wUBR < 900);
finally
FreeMem(pInfo, dwInfoSize);
end;
end;
end;
end;
UBR の値を Win32 API で取得する方法はなさそうです。レジストリ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
にある UBR
から持ってきてもいいのですが、コード例のように NTOSKRNL.EXE のファイルバージョンを確認した方が確実な気がします。
問題の出る環境を判定して浮動小数点例外のマスクを行えば、最低限の影響で済みます。
if IsNeedDisableFPE then
SetExceptionMask(exAllArithmeticExceptions);
…ただ、この問題が Microsoft によって解決されれば無用なコードではあるので、「Windows 11 で 22H2 を適用してしまったのなら、Windows の設定変更でやり過ごして!」って、ドキュメントやヘルプに書くのが現実的な解決法のような気はしますね。
…ただ、今となってはまず必要のないコードではあるので、「Windows 11 で 22H2 を適用してしまったのなら、最後まで Windows Update を適用して!」 って、ドキュメントやヘルプで注意喚起するのが現実的な解決法のような気はしますね。
See also: