ドナドナされるプログラマのメモ

Windows用アプリのプログラミングメモ

ガンダムオンラインで回復ビームが他人から見えないことがあるバグ

ガンオンで死体を起こそうと回復ビームを当ててたら、それを遮って修理しろアピールをする人がいた。何故だろうと思ってリプレイを見てみたら、回復ビームが写っていない!そりゃ、死体を起こそうとしているのは分からないし、修理アピールもするわ。

以下、リプレイによる検証動画。回復ビームが出ていた証拠に死体は無事復活したし、復帰ポイントも入っている。なので、自分がビームを出していたのは間違いない。しかし、リプレイでも回復ビームが見えない。なので、これはガンダムオンラインのバグであろう。

結論。バグが悪い。


ガンオンで回復ビームが他人から見えない

ガンダムオンラインのCPU使用率およびGPU使用率

ガンダムオンラインにおけるCPU使用率とGPU使用率をメモ。

CPUはRyzen3700X, GPUGeForce GTX 1070、解像度は1920×1080で画質は最高。全ていずれもCPU使用率20%程度、GPU使用率20%程度。CPU使用率の波形から、相変わらず2スレッドぐらいしか使っておらずマルチコアよりも高クロックの方が快適度の高い設計に見える。

ロビー:

f:id:donadonasan:20200409151437p:plain

ガンダムオンライン ロビーでのCPU・GPU使用率

ロビー。

f:id:donadonasan:20200409153033p:plain

ガンダムオンライン 戦闘中の機体選択画面におけるCPUおよびGPU利用率@ニューヤーク

戦闘中。

f:id:donadonasan:20200409154912p:plain

ガンダムオンライン 戦闘中におけるCPUおよびGPU使用率@ア・バオア・クー

 

重いイベントハンドラの処理状況をダイアログで表示する

背景

重い処理の進捗状況をダイアログで表示する場合、自分がよくやるのは

  • 進捗状況を示すモードレスダイアログを作成
  • 重い処理をAfxBeginThread()により別のワーカースレッドとして起動
  • モードレスダイアログは適宜タイマによって状況を更新
  • モードレスダイアログ上のキャンセルボタンが押された場合はフラグを立てる
  • 重い処理内では適宜フラグを監視し必要に応じて処理を中断する

というもの。たいていはこれで問題なく動く。

問題

しかし、この実装だと例えばOnUpdate()にてCTreeViewやCListViewに数十万個のアイテムを追加する場合、フレームワークがOnUpdate()を呼び出している間モードレスダイアログのGUIが無反応・無更新となる問題がある。

原因

この原因は、ワーカースレッドとして作られたスレッドで新たにダイアログを作っても、そのスレッドプロシージャはウインドウプロシージャの一部となってしまうことにある。すなわち、メインウィンドウのウィンドウプロシージャが非常に重い処理を呼び出してしまった場合はモードレスダイアログ上のGUI向けのものも含めウィンドウメッセージがキューにたまり続け、処理されないのである。

2020/05/05追記

より詳しい記事を追加。

解決方法

  • 対症療法:重いイベントハンドラ内の処理もマルチスレッド化してしまう
  • 根本対策:ダイアログのウィンドウプロシージャをメインウィンドウから独立させる(ワーカースレッドではなく、ユーザーインターフェーススレッドにする)

donadona.hatenablog.jp

An example of user interface thread implementation

参考

dodonpa.la.coocan.jp

 

CListViewにポップアップメニューを追加したけどON_UPDATE_COMMAND_UIが送られてこない件について

事象

CListViewにポップアップメニューを追加し、条件に応じてメニューのEnable/Disableを切り替えようとMFCの流儀に則りON_UPDATE_COMMAND_UIのハンドラを追加した。しかし、ポップアップメニュー表示時になぜかON_UPDATE_COMMAND_UIが来ない。

原因

CListViewは、ON_UPDATE_COMMAND_UIの送信に必要なOnInitMenuPopup()を実装していない。

対策

ここを参考に、OnInitMenuPopup()を実装する。

https://support.microsoft.com/sr-latn-me/help/242577/you-cannot-change-the-state-of-a-menu-item-from-its-command-user-inter

ダイアログバー上のエディットボックスから文字列を取得する方法

ダイアログバー上のエディットボックスから文字列を取得するのにちょっと苦労したのでメモ。

問題の背景

ダイアログを作る場合、通常はCDialog等を継承した、そのダイアログ固有のクラスを作成する。しかしMicrosoft曰く、CDialogBarは通常固有のクラスを作成しないらしい。これは、ダイアログバーはメインウィンドウの一部で、各コントロールからのメッセージはメインウィンドウのウィンドウプロシージャで処理するという設計だからだろう。このため、メッセージハンドラはCMainFrameに書く必要がある。*1

問題

しかし、固有のクラスを作らなかった場合、メッセージハンドラにてダイアログバー上のエディットボックスにはどうやってアクセスすればよいのだろうか?単純にCMainFrameからGetDlgItemText(ID_DlgBar_Edit)を呼び出しても失敗するし、固有のクラスを作っても上手く行かない。

解決方法

実は答えは単純で、CMainFrame上のメッセージハンドラから、ダイアログバーのオブジェクト(デフォルトだとm_wndDlgBarだと思う)に対しGetDlgItemText(ID_DlgBar_Edit)すればよい。

m_wndDlgBar.GetDlgItemText(ID_DlgBar_Edit, str);

その他の操作をしたい場合は、同様にGetDlgItem()すればよい。

*1:なお、リソースエディタはこの仕様に非対応なため、メッセージハンドラは手動で追加する必要があるようだ。

mozjpegをつかった画像変換ソフトの開発 その8

mozjpegを多並列で実行するGUIを備えた画像変換ソフト、無事Vectorにて公開。

www.vector.co.jp

 そのうち、英語版もどこかで公開予定だけど、どこにしよう?Vectorってどう見ても国内オンリーなのよね。英語ページを作れる気がしない。

2020/04/24追記

githubにて英語版を公開しました。
Released MozJpegGUI which is multi-threaded GUI interface for mozjpeg.

github.com

CDocumentからCViewを取得する一例

今時ドキュメントビューアーキテクチャなんて人は居ないだろうけど、メモ。

CDocumentからCViewにメッセージを投げたいときや関数を直接呼び出したいときに困るのが、CViewのアドレス取得方法。特に、自分はCSplitterWndを多用したためCViewが複数あり決め打ち取得ができなずに困ってしまった。そこで以下のような簡単なコードを書いてみた。

Example of finding CView from CDocument

ここにたどり着くまでに4時間かかってしまったorz