またもカタログネタです。HBRUSH 型にもキャストできるという噂の定数 COLOR_* を使う事で標準コントロールと同じ見栄えに近づける事ができますが,具体的にどの定数がどんな色を持つのかはあんまし知りません。というワケで,こんなコード:
スクリーンショットを見て気付いたんだけど,`COLOR_HOTLIGHT + 1' という値はもしや,メニューやリストコントロール,ツリービューの選択項目の色では? VC++5.0 ではどのヘッダファイルにも定義されていませんでした。
いつもの事だけど。
->DrawEdge() を使ったコードとスクリーンショット;
自前のコントロールをつくらなければならないが,見栄えを Windows 標準のコントロールに合わせたい場合は DrawEdge() API が非常に便利。しかし API に渡す引数の組み合わせは豊富なのに,結果を一覧として見ることの出来るサンプルが見当たりません。というわけで,作ってみました。
製作時間は 30 分。しかしこれくらいの事でこんなに時間を掛けるのはアホもいいところ。main.cpp のメッセージループ,ウィンドウプロシージャの基本的な部分,WNDCLASSEX 構造体の設定とクラスの登録,それに ::CreateWindowEx() など,今日のコードの殆どは既存のコードからのコピペで充分なのに,それをせずに完全スクラッチで書いたのが敗因かと。精進せねば。
以前にもコンボボックスのサイズにまつわるネタを書きました。::CreateWindowEx() で "COMBOBOX" を生成する際のサイズの設定ですね。
今日は ::GetWindowRect() でコントロールのサイズを得た時のネタ。あまり重要でもないんだけど,とりあえずハッキリさせておきます。
->ソースコード;
まず,起動した状態:::CreateWindowEx() で高さに 400 を指定したにもかかわらず,高さは 20 ドットと取得されました。また赤線はクライアント x 座標 100 ドット目ののラインですが,コンボボックスの横幅はこれに 1 ドットだけ届いていません。即ち (0,0)-(100,20) がコンボボックスの座標ですが,実際に描画される範囲は (0,0)-(99,19)。指定した範囲の右および下辺が描画されないのは GDI 関数などでよく見られる特徴です。
項目を選択中:リストボックスが表示されますが,取得されるサイズの値に変化はありません。長い文字列を持つ項目がありますが,リストボックスのサイズにクリップされています。
長い文字列の項目を選択:コンボボックスのサイズは自動的には変化しません。取得されるコンボボックスのサイズも変化しません。
とりあえず今日はここまで。::CreateWindowEx() で指定するスタイルを変えればまた違う結果が出るかも知れません。
Winsock Programmer's FAQ 等のソケットの扱いについて解説しているいくつかの文書を読んでみると,かなり高い確率で「recv() で MSG_PEEK を使うべきではない」と記述されている気がします。頻度は数えてないけど。
だとすると,私の昨日のコードは良くないコード?
// disconnect かどうかをチェック
char testbuf;
if ( recv( sock, &testbuf, 1, MSG_PEEK ) == 0 )
break;
接続が切断されたかどうかを知るには recv() を使うべきですが,もし昨日のコードの「ソケットを待つスレッド」の中でこのテストを行えないならば,このテスト処理はワーカスレッドで行います。
if ( check_event( socket_event ) == RECEIVABLE ) {
int r = recv( sock, buffer, buffer_size, 0 );
if ( r == 0 )
disconnected(); // 何か処理
}
噂によれば,Windows98 の winsock では MSG_PEEK が正常に動かないという話も。また,BeOS の TCP スタックに MSG_PEEK は実装されていないそうです。
せめて Java のストリームクラスの available() のように,データが到着しているサイズだけでも返してくれる関数があればいいのに。
Fast TCP To Increase Speed Of File Transfers?
「それって UDP?」というツッコミが入ってはいますが,この FastTCP はちゃんとエラーチェックもするんスよね?(<- よくわかってない )
しっかし 100Gbps てマジですか。要するに 1 秒間で 12.5Gbytes の情報を送る事ができるわけで,その速度の恩寵を得るために私のソケットアプリケーションはどう書くべき?
同期ソケットでは recv() はある程度のバッファが溜まるまで処理を戻さないので UI 的にちょっとアレです。よってワーカスレッドとは別のスレッドを作り,select() でデータの受信を待ち,受信を確認し次第ワーカスレッドにイベントを送信,ワーカスレッドが recv() を行うのが常識的?
volatile _event socket_event;
volatile SOCKET sock = INVALID_SOCKET;
// 主スレッド
void worker_thread() {
sock = open_my_socket();
create_socket_waiting_thread(); // socket_waiting_thread() 起動
while ( socket_is_living() ) {
if ( check_event( socket_event ) == WAITING ) {
progress();
}
if ( check_event( socket_event ) == RECEIVABLE ) {
recv( sock, buffer, buffer_size, 0 );
}
process_other_messages();
}
}
// ソケットを待つスレッド
void socket_waiting_thread() {
while (! thread_terminated()) {
reset_event( socket_event );
fd_set fd;
FD_ZERO( sock, &fd );
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1;
if ( select( 1, 0, &fd, 0, &tv ) == 0 ) {
// disconnect かどうかをチェック
char testbuf;
if ( recv( sock, &testbuf, 1, MSG_PEEK ) == 0 )
break; // EOF
send_event( socket_event, RECEIVABLE );
}
else {
send_event( socket_event, WAITING );
}
// ::Sleep( 1 ); // 例えば Win ではこれが重要
}
}
いつもの通り,これは概念的なコード。socket_waiting_thread() の中で while ループを回していますが,何の工夫もしなければこのまま CPU 時間を食いつぶしてしまいます。CPU 時間というのは意外にあなどれなくて,ぶっきらぼうな処理をしていると別のアプリが迷惑を被ります ── 例えば VC++6.0 IDE の起動が平気で数分かかったりね。
例えば Windows では ::Sleep() で CPU 時間を明け渡します。引数はミリ秒単位。::Sleep(1) とすると 1/1000 秒の待機。すなわちこのループは 1 秒で最大 1000 回程度しか回る事はありません。1000 回で 12.5 GBytes を得るには,1 回の recv() で 13 MBytes ほどを読み出す必要があります。
えーと,あと 5 年くらいしないと,一つのアプリケーションが局所的な処理で 13 MBytes も使うのは許されないと思うわけですよお兄さんは。
とりあえず国民皆メモリ 1 GBytes 計画の発動キボン。
もう本日の h4 見出しそのまんま。ToolbarWindow32 ウィンドウを TBSTYLE_FLAT で生成したのに,こんなんなっちゃいます:
ちなみに,書いたコードはこんなの:
#ifndef STRICT
#define STRICT
#endif
#include <windows.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
HWND __stdcall
createToolbar( HWND parent, int id ) {
static bool initialized = false;
if (!initialized) {
INITCOMMONCONTROLSEX icc = {
sizeof( INITCOMMONCONTROLSEX ),
ICC_WIN95_CLASSES };
InitCommonControlsEx( &icc );
initialized = true;
}
HWND this_ = ::CreateWindowEx (
0,
"ToolbarWindow32",
"",
WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
parent,
(HMENU)id,
::GetModuleHandle(0),
0 );
if (!this_) return 0;
::SendMessage( this_, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
TBADDBITMAP tbbmp;
tbbmp.hInst = HINST_COMMCTRL;
tbbmp.nID = IDB_STD_SMALL_COLOR;
int i = ::SendMessage( this_, TB_ADDBITMAP, 3, (LPARAM)&tbbmp );
tbb[0].iBitmap += i;
tbb[1].iBitmap += i;
tbb[2].iBitmap += i;
tbb[3].iBitmap += i;
::SendMessage( this_, TB_ADDBUTTONS, 4, (LPARAM)tbbuttons );
return this_;
}
要するにスタイルには TBSTYLE_FLAT しか指定していないのに,あたかも TBSTYLE_TRANSPARENT を指定しているような雰囲気になっちゃったよ,という事。VC++5.0 ならば,少なくとも SDK では自前で描画しなければならないだろう,というのが ( 少しだけだけど ) 調査の結果。しかし私の場合は VC++6.0 でもコレなのでタチが悪いのです。
残念ながら今私はちょっと急いでおり FLAT な見栄えにこだわる暇がありませんが,これは宿題にしておきたいです。・・・で,今までに宿題にしておいた問題が溜まっているわけですがどうするよ>自分
この雑記ではまれに見かけるヘンなモノを紹介したりもするわけで,おそらくそれはソフトウェアのバグと呼んでよいものに起因するのです。
・・何これショウキ。
これはご覧の通り何の変哲もない Windows Media Player。しかし動画の 1 コマではありません。動画を再生し終えた後,この Windows Media Player をすっぽり覆い隠す大きさの Mozilla ウィンドウをアクティブにし,直後に再び Windows Media Player を全面に持ってくるとこの有様に。
Mozilla 以外の,他のアプリケーションをアクティブにしてもこの状態にはならないトコロがミソ。
さあ,バグを抱えているのは WMP さん? Mozilla さん?
会社の VC++6,0 IDE には最適化に関する設定項目があるんですよ。しかし私個人の所有する VC++5.0 Learning Edition の IDE にはそんなものは見当たりません。イヤな予感がしました。私の VC++5.0 はきちんと最適化を施してくれるのでしょうか?
とりあえずこんなコードを書いてみます。関数のインライン展開は C++ として正式に視野に入っているものであり,最適化の基本中の基本でしょキホン。
inline int func() { return 10; }
int main( int ac, const char *av[] ) {
int a = func();
return 0;
}
そしてコンパイル時に一工夫。今日の参考ページはこちら:
管理人は波多浩昭さん。他にもいくつかの技術的なメモを公開されていますが,中でも ( 少なくとも現在の私にとっては ) このリソースは最強。なかなか出会えるもんじゃありません。
解説によると,VC++ のコンパイラはコンパイルオプション /FAcs でアセンブリコードを出力してくれるとか。実際にコンパイルし,出力された .cod ファイルを眺めてみます。func() はどのように最適化されているのかな?
00004 e8 00 00 00 00 call ?func@@YAHXZ ; func
00009 89 45 fc mov DWORD PTR _a$[ebp], eax
call だって ( ここで一同大爆笑 )。・・これっぽっちも最適化されていません。
これはアレだつまり私に「最適化の鬼になれ」と言っているのですね MS 様?
日本語サポートサイトである Proxomtron-J より。ML が "荒れていた" 模様。開発者は,嫌気がさせばすぐに開発を中止する権利があるわけだけど。
優れたソフトウェアの歴史を紐といてみると,稀に「自分の意見は絶対的に正しい。その意見を聞き入れない開発者は異常である」と勘違いするユーザが出現し,掲示板や ML は荒れてくる事があるようです。すると開発者は嫌気がさし,開発の中止,場所を変えて規模の縮小,オープンソース化などのシナリオに分岐する事に。
Proxomitron はどのシナリオを辿るやら?
DirectX 8.1 SDK を入れてみたわけです。こっちをちゃんと踏まえていないと,そろそろ本当にヤバいのです。今さら DirectX 8? ─ 9 じゃなくて? ええ 8 ですとも。9 は一般に安定性が確認されるまで心の中でおあずけです。だって IE6 インストールでさえ不安材料がいっぱいだったんですから。すいません。私,Microsoft をあまり信用してはいません。
しかしまぁそんな IE6 とは違い,SDK は無事に目的のドライブにインストール完了。再起動しました。
再起動,20 分かかりました。
いつもの例の画面,WindowsMe のロゴが全面に出て頑張っている画面で,HDD がせわしく動いています。ハングアップではないようなので放置してみると,20 分後にようやくデスクトップの表示が開始ですよ。これ,ちょっと気ぃ焦ってる時の私ならば 10 分経過した後に電源を切っているところでしたマジで。
インストール自体は上手くいったし,次回起動時も普通なのでその後は特に何もしていませんが ( デベロッパ向けデバッグ用のランタイムをインストールしたため WinTV の起動時に ASSERT Failed エラーダイアログが出たりしますがこれはお約束 ) おそらく今年上半期で一番ハラハラした出来事かと。
Nightly をインストールしたところ,UA にはもう 1.5 の文字が。時間が経つのは速いです。"早い" じゃなくて。
私の癖の一つに,「キーボードで文章を書くと,改行の前 or 句読点の後に必ず Ctrl+S を押す」というモノがあります。自分ではまったく気付いていないのに自動的に文章が保存されているため,OS がブルースクリーンを出して落ちてしまうともう天を仰いじゃいますが,再起動してみるときちんと文章が保存されているので精神的。プログラマとしては良きかな善きかな。
ところで,Ctrl + S,もっと一般的に「コントロール押下時の処理」はどのように実現するのが常識? Google などで問い合わせてみました・・・が,意外にハッキリしていません。Ctrl + なにか なんてキーは結構よく使うと思うので,専用の WM_ メッセージなり VK_ 仮想キーなりが割り当てられていてもおかしくないと思ったわけですが,どうしたことやら。
通常は WM_KEYDOWN メッセージと ::GetKeyState() を用いて,次のように書くらしいです。
if ( msg == WM_KEYDOWN ) {
if ( ::GetKeyState( VK_CONTROL ) < 0 ) {
switch ( (int)wParam ) {
case 'A':
/* 全選択 */
break;
case 'S':
/* 保存 */
break;
case 'F':
/* 検索 */
break;
case 'G':
/* 次を検索 */
break;
case 'Z':
/* アンドゥ */
break;
case 'X':
/* カット */
break;
case 'C':
/* コピー */
break;
case 'V':
/* ペースト */
break;
default
/* その他 */
break;
}
}
return 0;
}
で,本当にこれでよいのか? と問い詰められると自信がないわけですが。
要するにこういう事。
上キーがアクセル,左右キーがハンドル,下キーがブレーキ,SPACE キーでジャンプします。ツルツル滑るので大変です。・・しっかし一枚絵を無理やり拡大しているだけなので汚いのなんの。今度はこれを解決してみようと思います。
ソースコードにはコメントが書かれてありませんが,まだ私の中で理論が完成していないのです。下手はコメントは思考の邪魔というスタンスで。
今日は以上にてっ。
こういうミスをする人はあまり居ないんじゃないかなと思うんだけど,とりあえずメモ。元々は ::SetTimer() でタイマをセットし,WM_TIMER を捕捉して処理を行っていたアプリケーションがありました。しかし Win9x(Me) ではこのタイマの精度がアレなので,::_beginthreadex() を使ってスレッドを作ったのです。
リンク時に何やらぁゃιぃウォーニング。
LINK : warning LNK4098: defaultlib "LIBCMT" は他のライブラリの使用と競合しています; /NODEFAULTLIB:library を使用してください
とりあえず気にせずに実行してみると:
Runtime Error!
Program: e:\path\app.exe
R6016
- not enough space for thread data
このアプリケーションはもともとシングルスレッドとして作られていたので,それぞれのコードの中間コードを構築したライブラリにはシングルスレッド用の C ランタイムライブラリが用いられていました。そして今日はそのうち一つのファイルだけにマルチスレッドの仕組みを組み込み,そのファイルだけをコンパイル。そりゃライブラリが他の中間コードと競合するのも無理はありません。
というわけで,一度全ての中間ファイルを削除し,事なきを得ましたとさ。
去年の春先だったか,「プロジェクトの設定」で「マルチスレッド」にしていなくても ::_beginthreadex() を使ったコードがコンパイルできてしまい,WindowsNT で使えなかった〜てな記憶があるんですが,今日はきちんと「マルチスレッド」になっていないとコンパイルが通らないようになっています。
process.h を覗いてみると,_MT という識別子が define されていないと ::_beginthreadex などが見えなくなるように工夫されています。あれれ,自分でこんな事をやったんだっけ? しかしファイルのタイムスタンプは・・・何もいじってないことを意味している模様。
まぁいいか。
ちょちょちょっと待った! もうすぐ,今年上半期の最終月である 6 月が始まりませんか? 私,今年はナニをしてたんだろ。。。こうして気分はますますネガティブに。。。
IE6 インストールの失敗例は意外に多く報告されてる模様。私もちょっと失敗気味でした。
症状は「最小構成インストール、またはブラウザのカスタマイズ」を選び自分でオプションを選択すると,インストールに失敗するというもの。IE5.5 を消したくなかったので,なんとか IE6 を別ドライブのフォルダにインストールしてみたかったのです。幸い,この段階では何のファイルも破壊されないので安心。
仕方がないので「標準インストール(I) - コンポーネントの標準構成」を選択してインストール。IE5.5 が消えてしまったのは残念だと思ったわけですが,しかし意外な結末に。
次のマークアップで,
<div style="float: right; width: 49%; border: 1px solid black;">
右側
</div>
<div class="leftbox" style="width: 49%; border: 1px solid black;">
左側
</div>
IE5.5 と Mozilla1.4b での表示はこんな感じ:
IE5.5 の方,左側に配置したボックスが小さくレンダリングされています。この不具合を回避するためによく用いられるのが,こんな IE 拡張機能。
<!--[if IE 5]><style type="text/css">
.leftbox { width: 100% }
</style><![endif]-->
HTML の中で条件分岐が使えます。で,この IE5.5 の不具合は IE6 でも解決されていなかったんですねー。その証拠に,上のスクリーンショットは実は IE6 のものだったりします ( いや当て付けでなくて,IE5.5 は消しちゃったんで )。
つまり,こう書く必要もあったわけで:
<!--[if IE 5]><style type="text/css">
.leftbox { width: 100% }
</style><![endif]-->
<!--[if IE 6]><style type="text/css">
.leftbox { width: 100% }
</style><![endif]-->
私のサイトの重要な箇所はほとんど全滅でした。とほほ。
しかし,これじゃアタクシ的には IE5.5 も IE6 も変わりません。本当に IE6 のインストールに成功してるのかなあ? かなーり不安げ。