質問者
Windows Vista、7 のコンボボックスの挙動について
-
Windows Vista、7 にて、編集不可コンボボックスに対してある操作を行うと、
選択項目に空の項目がないにも関わらず、値が空になってしまう現象が発生します。
なお、Windows 8.1 や 10 では発生しませんでした。
【再現手順】
1. 値のある編集不可コンボボックスを表示する
2. コンボボックスの▼をクリックする(この時マウスを離さないこと)
3. マウスを押下を維持したまま、ESCキーを押下する
4. コンボボックスが閉じられ、値がクリアされる
※ 一度でもコンボボックスをクリック→離す動作をしてしまうと発生しない模様。
その場合は、再度プログラム側からコンボボックスに値をセットすれば再現する模様。ちなみに、Windows 標準の電卓アプリやダイヤラーでは、
コンボボックスの値が空になることが想定されていないのか、
空にするとアプリがクラッシュしてしまいます。
https://twitter.com/hk1v/status/735106508606689284
https://twitter.com/hk1v/status/735120056481251328
コンボボックスだったらなんでも発生するというわけでなく、
コモンコントロール6.0(XPビジュアルスタイル)が有効であり、
OS標準?のコンボボックスを使ったときのみ発生するようです。
【現象発生】
・C#というか.NETのWinFormで使えるComboBoxの場合
・C++にてMFCで使えるComboBoxの場合
・Win32APIのCreateWindowEx()にてComboBoxを作成した場合
自分が作ったプログラムに対しては、
編集不可コンボボックスの場合でも空になっていないか
チェックするようにソースを修正して対応しました。
こんな操作する人たぶんいないと思うので、ほとんどの場合問題ないんでしょうけど、
ESCキーで値が消せてしまうのって、コンボボックスの仕様なんでしょうか。
(Win7以前の動作が正しいのか、Win8以降の動作が正しいのか。。。)
【試した環境】
・Windows 10 Pro ビルド10586(i7-6700K, IntelHD)
・Windows 10 Pro ビルド10586(i7-4790K, GeForce980Ti)
・Windows 8.1 (i3-6100T, IntelHD)
・Windows 7 Professional SP1(i7-2600K, IntelHD)
・Windows 7 Home Premium SP1(i7-870, GeForce750Ti)
・Windows Vista Home Premium SP2(C2D T7500, IntelGMA)
質問
すべての返信
-
Windows のコントロールの不具合でしょう。
もっとも、Windows 7 やそれ以前の OS はサポートフェーズの状態からして、修正されることはありませんので、この現象を気にするのであれば、アプリ側で対策するしかありません。- 編集済み AzuleanMVP, Moderator 2016年5月29日 21:38
-
【再現手順】の操作を行うと、コンボボックスの所有しているリストボックスが覚えている"内部的なインデックス"の項目に選択が戻るという挙動のようです。そして、Windows 7 などのOSでは、LB_SETCURSELによって選択項目を変更した場合、この"内部的なインデックス"が更新されないようです(不具合?)。ユーザーがリストボックスを開き項目をクリックして選択を変更した場合は更新されるようです。また、Windows 8 や 10 では LB_SETCURSEL でも更新されていました。
"内部的なインデックス"を更新するには下記のようにやれば出来るようです。void SetComboCurSel(HWND hCombo, DWORD_PTR dwIndex) { COMBOBOXINFO info = { sizeof(COMBOBOXINFO) }; if (GetComboBoxInfo(hCombo, &info) && info.hwndList) { LPBYTE lpByte = (LPBYTE)GetWindowLongPtr(info.hwndList, 0); #ifdef _WIN64 //現在の選択項目 lpByte[36] = LOBYTE(LOWORD(dwIndex)); lpByte[37] = HIBYTE(LOWORD(dwIndex)); lpByte[38] = LOBYTE(HIWORD(dwIndex)); lpByte[39] = HIBYTE(HIWORD(dwIndex)); //以前の選択項目 lpByte[120] = LOBYTE(LOWORD(dwIndex)); lpByte[121] = HIBYTE(LOWORD(dwIndex)); lpByte[122] = LOBYTE(HIWORD(dwIndex)); lpByte[123] = HIBYTE(HIWORD(dwIndex)); #else //現在の選択項目 lpByte[20] = LOBYTE(LOWORD(dwIndex)); lpByte[21] = HIBYTE(LOWORD(dwIndex)); lpByte[22] = LOBYTE(HIWORD(dwIndex)); lpByte[23] = HIBYTE(HIWORD(dwIndex)); //以前の選択項目 lpByte[96] = LOBYTE(LOWORD(dwIndex)); lpByte[97] = HIBYTE(LOWORD(dwIndex)); lpByte[98] = LOBYTE(HIWORD(dwIndex)); lpByte[99] = HIBYTE(HIWORD(dwIndex)); #endif InvalidateRect(hCombo,0,1); } }
※あくまで内部的な値なのでOSの仕様変更などで設定・更新できなくなる可能性があります。
- 編集済み kenjinote 2016年5月30日 2:57
-
私はComboBoxを継承したコントロールを作り、そこでこんな対応をしています。
protected override void OnKeyDown(KeyEventArgs e) { if (this.DroppedDown) { if (e.KeyCode == Keys.Escape) { int index = this.SelectedIndex; this.DroppedDown = false; if (this.SelectedIndex != index && this.SelectedIndex == -1) { this.SelectedIndex = index; } e.Handled = true; } } if (!e.Handled) { base.OnKeyDown(e); } }
-
前の私の投稿は、Win7とWin8以降との挙動の違いにつて調べ分かったことを1つの情報として提示したもので、賛同を得るという内容ではありませんでした。問題がありましたら削除します。
私自身は、問題かどうかを判断する立場にはありませんので、あくまで個人的な意見を寄せているだけと捉えてください。
(モデレーターロールはいただいていますが、あくまで回答としてマークなどのごく限られた補助が目的です)さて、「調べていてわかったこと提示した」ということですが、全員が「読むだけ」で済ます保障はないので、「危ない情報は載せない」方が良いと個人的には思っています。今回の質問者だけではなく、第三の同じ困りごとを抱えた方も見る可能性もあり、広まっていくものであり、いつか利用されてしまう可能性があるためです。(もっとも、利用した人の自己責任であることは変わりません)
また、Windows 製品にはリバースエンジニアリングを禁じる EULA があるので、それに抵触するとみなされると、調べた人がライセンス違反となるので今回の情報の提示や動き方が気になったという具合です。
(コントロールが具体的にどのアドレスをどのような情報として扱っているかは、逆アセンブルか、メモリを見て推測・動かして裏付けをとることで中身を探ったかになりそうなので。もっとも、この行為をリバースエンジニアリングとみなされるのかどうか、私は知らないのですが…リスクを考えてやらないようにしています)