|
・今日は久しぶりにほめかぶ作り。結構めんどいなー(汗)。あんまいい例じゃないけど、行き当たりばったりでどんどん組んでいって、今は最初のユーザーインターフェイスまわり。ベースは ATL/WTL で、使い方とか調べながら組んでいってます。
・アプリの雰囲気は、旧 VB 風っつーか、マック風っつーか、一番上にタイトルバーとメニューとツールバー、あとモードレスダイアログ4つって感じになります。今日は、そのダイアログを、ツールバーのボタンで表示/非表示にする部分を組んでました。ツールバーってコモンコントロールみたいなもんなんですね、そーゆーのも知らなかった(汗)。そういうアプリって作ったことないんですよ。フリーウェアはウィンドウすらなかったりするし。 ・簡単に書いとくと、ツールバーのボタンを「押しっぱなし」にするためには TB_CHECKBUTTON をツールバーのウィンドウに送ればOK。ちなみに WTL に CToolBarCtrl っつークラスがあるんでこれを利用すると楽。ツールバーのウィンドウハンドルの取得は、 WTL で SDI プロジェクト作ったら CMainFrame::OnCreate() で CreateSimpleToolBarCtrl() を呼び出している所があるからその戻り値を使えばOK。 ・ ATL/WTL は、ウィンドウハンドルとかがなんか直アクセスしたりすることが多いんで、その辺が複雑。その辺を解析しながら使っていく感じになりそうだなー。どっちかってゆーと、実際にプログラム組むよりも、ライブラリの解析の方が楽しいかもしんない(爆)。 ・でわまたっ! |
・今日もほめぱげ作り。ダイアログを「サイズ可変」にしたんだけど、ボタンとか自動的に移動したりしないんですね(汗)。てっきりそういうの全自動だと思ってた。 WM_SIZE でひとつひとつ移動したりサイズ変更したりするのかー、ちょっとめんどそうだなー。
・あと、レジストリまわりのテスト。 ATL に CRegKey っていうクラスがあるんでどんな感じに使えるのか調べてました。基本は API と同じ。引数を省略できる分、使いやすいかな。でも CWinApp::WriteProfileInt() みたいな便利なのはないっぽいです。あと、普通に書き込むよりも、メンバ変数と結び付ける形とかにしたいし。 ・もうそろそろ、 ATL/WTL 用ライブラリを作ってこうかなぁと思ってます。サポート用関数とか用意していかないと少し辛いんで。でも少し、なんだよね。たとえば TRACE() の代わりに AtlTrace() が用意されてたりと、結構至れり尽くせりだったり。だから、作るとしてもほんのちょっとかも。これ、公開するかどうかは未定。 ・でわまたっ! |
・今日はお休み〜。
・ぐはっ、お便り来てたのね! お便り紹介は明日します〜。 |
・今日はお便り紹介。まずは昨日送られてきたのから。
|
|
・可能でーす。基本は MSDN の SAVEBMP.C ってページにある方法でできます。このページのコードがちょっと古いのだったんで、ちょっと書き換えてみました。でもエラー処理してないので注意(汗)。ちょっと中途半端に書き換えちゃったかなー、元のコードの方が分かりやすいかもしんない。
BOOL SaveBitmapFile( HDC p_hDC, LPCTSTR p_pchFileName ) { HBITMAP hBmp = (HBITMAP)GetCurrentObject( p_hDC, OBJ_BITMAP ); BITMAPINFO stBmpInfo; stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader ); stBmpInfo.bmiHeader.biBitCount = 0; GetDIBits( p_hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS ); ULONG iBmpInfoSize; switch( stBmpInfo.bmiHeader.biBitCount ) { case 24: iBmpInfoSize = sizeof(BITMAPINFOHEADER); break; case 16: case 32: iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3; break; default: iBmpInfoSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ( 1 << stBmpInfo.bmiHeader.biBitCount ); break; } PBITMAPINFO pstBmpInfo; if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) ) { pstBmpInfo = (PBITMAPINFO)GlobalAlloc ( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize ); PBYTE pbtBmpInfoDest = (PBYTE)pstBmpInfo; PBYTE pbtBmpInfoSrc = (PBYTE)&stBmpInfo; ULONG iSizeTmp = sizeof( BITMAPINFOHEADER ); while( iSizeTmp-- ) { *( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ ); } } HANDLE hFile = CreateFile ( p_pchFileName, GENERIC_WRITE, 0 , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE , NULL ); BITMAPFILEHEADER stBmpFileHder; stBmpFileHder.bfType = 0x4D42; // 'BM' stBmpFileHder.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + iBmpInfoSize + pstBmpInfo->bmiHeader.biSizeImage; stBmpFileHder.bfReserved1 = 0; stBmpFileHder.bfReserved2 = 0; stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize; DWORD dRet; WriteFile ( hFile, (LPCVOID)&stBmpFileHder , sizeof(BITMAPFILEHEADER), &dRet, NULL ); PBYTE pBits = (PBYTE)GlobalAlloc ( GMEM_FIXED | GMEM_ZEROINIT , stBmpInfo.bmiHeader.biSizeImage ); HBITMAP hBmpOld; HBITMAP hTmpBmp = CreateCompatibleBitmap ( p_hDC , pstBmpInfo->bmiHeader.biWidth , pstBmpInfo->bmiHeader.biHeight ); hBmpOld = (HBITMAP)SelectObject( p_hDC, hTmpBmp ); GetDIBits ( p_hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight , (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS ); WriteFile ( hFile, (LPCVOID)pstBmpInfo , iBmpInfoSize, &dRet, NULL ); WriteFile( hFile, (LPCVOID)pBits , pstBmpInfo->bmiHeader.biSizeImage , &dRet, NULL ); SelectObject( p_hDC, hBmpOld ); DeleteObject( hTmpBmp ); CloseHandle( hFile ); GlobalFree( pstBmpInfo ); GlobalFree( pBits ); return TRUE; } // 呼び出し例。 DC が張り付いているビュークラスのメンバ関数です。 void CMy20001219View::On1() { // TODO: この位置にコマンド ハンドラ用のコードを追加してください CDC *pDC = GetDC(); CDC cMemDC; cMemDC.CreateCompatibleDC( pDC ); CBitmap cMemBmp; cMemBmp.CreateCompatibleBitmap( pDC, 100, 100 ); cMemDC.SelectObject( cMemBmp ); cMemDC.BitBlt( 0, 0, 100, 100, pDC, 0, 0, SRCCOPY ); SaveBitmapFile( cMemDC.GetSafeHdc(), "AAAAA.bmp" ); } ・な、ながっ!(汗) 基本は BITMAPINFO とかの構造体を用意して、ファイルに書き込んでってくだけ。だから、構造体の設定とかがしっかりできれば問題ないはず。あと、 SAVEBMP.C の例だとそのままだとうまくいかなかったんで、メモリ DC を作ってから渡してます。この部分は上の関数に埋め込んでもいいかも。ってゆーかもしかしたらこれいらないのかもなー、もちっと解析しないとちょっと分かんないです。……いやね、この処理って今回初めて試したんで(汗)。 ・もひとつお便り! |
|
・おひさです〜。えーっと、わて自身はこの辺はそれほど知識ないので、リンク紹介します。まず こちら。 waveOutWrite() の使用例です。このコードを使うときには、 #pragma comment( lib,"winmm.lib" ) と #include <mmsystem.h> を最初に加えて、 WaveProc() を宣言しておいて、 Animate() と Animate2() を呼ばないようにして、 const int LOOPMAX = 1; って感じに定義しといて、開くファイルを c:\\windows\\media\\tada.wav にして、あと HWND hwnd って定義してこの hwnd にトップウィンドウのハンドルを入れれば、コンパイル通ると思います。
・この例だと、途切れずにうまく続いているんで、これを参考にすればうまくいくかも。あと、なんか「ダブルバッファ」って言って、ふたつのバッファを入れ替えていく方法があるみたいです。アニメーションではよく使う方法だけど、音声でもこういうのあるんだなー。その例はこちら。でもこっちは試してないんで動くかどうか不明(汗)。 ・でわまたっ! |
・今日もお便り紹介!
|
|
・おお、うまくいったようですね。コードは、間違ったところをペーストしてもまずいんで今回はパスします。結局、 wave なんたらな API のルールが難しいってことなんかなー。英文で書いてあるのがなおさらなのかもしんない。わてがこの手のものや、ビットマップ関係とかに手を出しづらいのもそういうとこがあるからってのもあるかもしんない。
・……最近プログラミングしてないね(汗)。 ・でわまたっ! |
・今日もお便り紹介〜。
|
|
・はうぅ、ごめんなさい! これはこちらのミスでした。そのコードの前に、こういう部分があると思います。
ULONG iBmpInfoSize; switch( stBmpInfo.bmiHeader.biBitCount ) { case 24: iBmpInfoSize = sizeof(BITMAPINFOHEADER); break; case 16: case 32: iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3; break; default: iBmpInfoSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ( 1 << stBmpInfo.bmiHeader.biBitCount ); break; } ・その次の if は iBmpInfoSize の値で決まるんで、結局は stBmpInfo.bmiHeader.biBitCount が 24 かそれ以外か、が分岐の鍵になってます。で、 stBmpInfo.bmiHeader.biBitCount がなにかっていうと、1ピクセルに何ビット使ってるか、つまり色数を示しているんです。わての場合、画面の設定が True Coler (32ビット)なんでこの値が 32 なんだけど、たぶんそっちの環境が True Coler (24ビット)だからこの値が 24 になって、 if の中に入らないってことになっちゃってるんだと思うです。 ・結果、 GlobalAlloc() が呼ばれないから領域が作成されなくて、 pstBmpInfo->bmiHeader.biSizeImage で無理矢理アクセスしてるからアクセスバイオレーションしちゃうんです。で、解決方法。 PBITMAPINFO pstBmpInfo; の行を次のように書き換えてください。 PBITMAPINFO pstBmpInfo = &stBmpInfo; // こう書き換えてください。 if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) ) // 以下はそのまま。 // ... ・つまり pstBmpInfo に初期値として stBmpInfo のアドレスを入れておけばOK。これ、元のコード( SAVEBMP.C )にはありました(大汗)。教訓1。コードを書き換える時には最新の注意を払おう。教訓2。テストはいろんな環境で行おう。混乱させて申し訳なかったです。やっぱ SAVEBMP.C を元に調べていった方がいいかもしんない……。 ・でわまたっ! |
・むむむ、プログラミング辞書に「晴餅 弉勿忤 箕荷」なるものが送られてきた。いよいよかぶスタに中国読者が!? と思ったら文字化けらしい(汗)。「晴餅」は「タイプ」、「弉勿忤」は「ライブラリ」、「箕荷」は「ファイル」を半角カタカナで打ち込んだものらしいです。
・今日のお便り。 |
|
・プログラム送ってOKです。ってゆーか、送らないよりは送ってくれた方が解決できる可能性大です(爆)。説明聞くよりもコード見た方が分かるってことも多いし。 WinNT で動いて Win98 動かないってことも結構あります。たとえば Unicode まわりの実装は WinNT にしかされてないとか。で、今回のはちょっとした実装の違いによるものみたいです。こうすれば動くようになりました(うちの環境は Win98 なので)。
DWORD dwThreadId; CreateThread (0, 0, (LPTHREAD_START_ROUTINE)g_BufferThread , 0, 0, &dwThreadId); ・つまり第5引数でスレッド ID を受け取らなきゃダメってことみたいです。 WinNT の CreateThread() はこれが NULL でも動くけど、 Win98 の方はエラーになるって実装みたいです。こーゆー細かいとこの違いが結構大変だったり。うちのフリーウェアもそういうとこありそうだなぁ(汗)。 ・あと、 CreateThread() で呼んでる部分がちょっとアヤシイかな。2度連続で呼んでるけど、こうしちゃうと g_BufferThread() で処理してる途中に g_OutputThread() が走っちゃうんでまずそう。ちゃんと動いてはいるんだけど(爆)。 CreateThread() でひとつだけ関数を呼んで、その関数で最初に g_BufferThread() を、次に g_OutputThread() って形がいいと思うです。 ・あと CreateThread() の第3引数が g_BufferThread() とかの第1引数として渡されるんで、これに g_ThreadData のポインタを渡すようにすれば g_BufferThread() で使うことができます。これでグローバル変数になってる g_ThreadData をローカル変数にできるんですっきりしそう。その辺やってみてねー。 ・もひとつお便り。 |
|
・いえいえ、このページは質問してもらわないとお休みになっちゃうんで(爆)。で、添付ファイルの画像を見ると、 ASSERT してるみたいですね。
Debug Assertion Failed! Program: reverse.exe File: fopen.c Line: 54 Expression *file != _T( '\0' ) (以下略) ・このダイアログは、 MFC の ASSERT ってマクロが出力するもので、これでだいたい原因が分かるようになってます。ここに書いてあるとおり、 fopen.c の54行目で *file != _T( '\0' ) を評価して、これが FALSE だったんで ASSERT しています。これは実際に fopen.c の54行目を見た方が早いかも。 ・この ASSERT が言わんとしてることは「ファイル名を格納してる文字配列が空だよ」ってこと。つまり1文字目に終端文字が入っちゃってるんです。 DLL の中とかで fopen() を呼んでるところを探して、その部分の前に「文字配列が空か」のチェックをするようにすればこの ASSERT は発生しないと思います。ただ、これが「アプリを終了してからエラーが発生」の原因かどうかは不明。もちっと調べた方がいいかも。 ・でわまたっ! |
・今日もお便り紹介ッ!
|
|
・あらま、そうでしたか。そのエラーダイアログはというと……
Debug Error! Program: ADFY.exe Module: File i386\chkesp.c Line: 42 The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling converiton with a function pointer declared with different calling convertion. ・っていうエラーが出てますね。この原因は、おそらく関数ポインタを typedef してる部分に CALLBACK を付けているのが原因だと思います。例を挙げておきます。 void Called( int p_i ) { } void Test() { typedef void (CALLBACK *type_Called)( int ); type_Called pfnCalled = (type_Called)Called; pfnCalled( 100 ); } ・ただの関数ポインタを呼び出すときに、 CALLBACK を指定しちゃうと、レジスタの操作が変になって、それを検知して上のエラーが出る仕組みになってます。おそらく、 DLL の方は CALLBACK 指定が付いていないんだと思います。呼び出し規約が違うとレジスタへの渡し方とか変わってきちゃうからこういうエラーになっちゃうんで、 Exe と DLL で、 CALLBACK を付けるか付けないか、どっちかに統一すればうまくいくと思うです。 ・もひとつツッコミ! |
|
・なるほど、グローバル変数の値をフラグにして同期処理をしてるんですね。一方のスレッドでデータを読み込み、もう一方で音声出力、これなら読み込みつつ出力できるわけですね。スレッドに渡す変数は、スレッドが走ってる間寿命がありさえすれば、基本的に何でも構わない……はず(汗)。最適化が不具合の原因になる、っていうのはマルチスレッドに限らずよく聞く話だけどね。
・あるひとつのプロセスの中では、スレッドとメモリ領域は基本的に無関係だから、変数に関してはそれほど神経質になる必要はないと思うです。要は、スレッドが走ってる間に、その変数が移動しなきゃいいわけで、メンバ変数だからころころ位置が変わるなんてこたないんで大丈夫でしょう。たーだ、「うまく動いているコードには手を加えない方がいい」って格言もあるからなー。 ・でわまたっ! |
・今日もお便り紹介!
|
|
・つまり、エクスプローラーで言うと各ファイルの「サイズ」とか「ファイルの種類」の項目も、クリックしてエディットボックス状態にして書き込めるようにしたいってことですよね。ってゆーか、そういう機能付いてるリストコントロールってあるのかな(汗)。たぶん、デフォルトのままじゃ無理なんじゃないかなと思います。
・実現するとすれば、クリックしたときにエディットボックスそこに作成して入力状態にする、っていうのがあるかな。入力し終えたらエディットボックスを消して反映させる。つまり、リストコントロールがしてることを自前ですればいいかなってこと。めんどそうだけど(汗)。あとは、項目をクリックしたらダイアログを表示するようにして、全列まとめて設定できるようにするとか。これはユーザーの方が面倒かもしんない(汗)。 ・でわまたっ! |
・最近全然プログラミングしてないなー(汗)。そんな時はお便りが頼り( delete [] Zabuton )。とゆーわけで今日のお便り。
|
|
・ボタンを消すだけなら、ウィンドウスタイルから WS_SYSMENU を取り除くだけでOK。たとえばこんな感じ( SWP_DRAWFRAME のこと教えてもらってて助かった……)。
AfxGetMainWnd()->ModifyStyle ( WS_SYSMENU, 0, SWP_DRAWFRAME ); ・だけどー、これだとシステムメニューのアイコン(タイトルバー左端のアイコン。正式名称は「コントロールメニューボックス」)がなくなっちゃうんだよねー。これなくなるとまずい場合には、タイトルバーの文字列を空にして、 GetWindowDC() でウィンドウ全体のデバイスコンテキストを取得して、自前でアイコンとキャプションを書き込んで、アイコンをクリックしたらメニューを表示する、ってしないとあかんです。ちょっとめんどいかも。 ・でわまたっ! |
・今日もお便り紹介〜。
|
|
・おおっ、うまくいったみたいですね。あのエラーコード、前に一度経験してたんで見つけだせました。メンバ関数ポインタを普通の関数として呼び出したりとか色々してたからなー(汗)。またなんかあったら訊きにきてねん。
・もひとつお便り。 |
|
・起動方法によるかなー。 CreateProcess() で Exe を実行するんなら、 STARTUPINFO の設定を変更すればOK。たとえばこんな感じ。
STARTUPINFO stStartupInfo; memset( &stStartupInfo, 0, sizeof(stStartupInfo) ); stStartupInfo.cb = sizeof( stStartupInfo ); stStartupInfo.dwFlags = STARTF_USESHOWWINDOW; stStartupInfo.wShowWindow = SW_HIDE; PROCESS_INFORMATION stProcessInfo; CreateProcess ( "C:\\WINDOWS\\COMMAND.COM", NULL, NULL, NULL , FALSE, CREATE_NEW_CONSOLE, NULL, NULL , &stStartupInfo, &stProcessInfo ); ・自前で起動できない場合には、ちょっとめんどいかも。 DOS 窓のウィンドウ名は実行ファイル名から .exe を取り除いたもの、ウィンドウクラスは tty なんで、それを EnumWindows() で探し出してそのウィンドウを ShowWindow() で SW_HIDE にしちゃえばOK。 ・一瞬でも表示されちゃいけないって場合には、システムフックとして CBT フックをセットして、表示されつつあるウィンドウをチェックするって方法を取らなきゃダメかも。そうなるとちょっと難しいね。もしその Exe が自前で作ったものなら、ウィンドウアプリとして作って非表示化させた方が後々融通利かせやすいと思うです。 ・でわまたっ! |
・今日もお便り紹介〜。
|
|
・ DOS コマンドは、 Windows\command.com ってアプリが受け持ってて、たとえば command.com /c del D:\A.txt とかすればOK。これを CreateProcess() とかで実行すればうまくいきます。たとえばこんな感じ。
STARTUPINFO stStartupInfo; memset( &stStartupInfo, 0, sizeof(stStartupInfo) ); stStartupInfo.cb = sizeof( stStartupInfo ); stStartupInfo.dwFlags = STARTF_USESHOWWINDOW; stStartupInfo.wShowWindow = SW_HIDE; PROCESS_INFORMATION stProcessInfo; CreateProcess ( NULL , "C:\\WINDOWS\\COMMAND.COM /c del D:\\A.txt" , NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL , NULL, &stStartupInfo, &stProcessInfo ); ・なんか昨日のとすごく似てるな(爆)。コマンドライン全体を第2引数に入れなきゃいけない、ってことに気付くまでにえらい時間が掛かってしまった……。ま、こんな感じに CreateProcess() の第2引数に command.com と実行したいコマンドを書き込めばうまくいくと思うです。 ・でわまたっ! |
・最近イラストとゲームしかしてないよーな……ではお便り紹介〜。
|
|
・つまりエクスプローラーの左ペインみたいなのを実装したいってことですよね。それ、わても知りたい(泣)。これを簡単に実装する方法は、どうやらなさそうなんですよねー、残念ながら。あったら誰か教えて(汗)。
・似たようのなのとしては、ダイアログのサイズを変えたり、子ウィンドウとして上に貼り付けたりとかすれば一応それらしくは見えるんだけど、限界あるかなー。ホントにそのまんまのを作りたい場合には、シェルエクステンションの IShellFolder とか駆使しないといけないかも。 ・でわまたっ! |
・今日はお休み〜。
|
・今日もお休みです。
|
・おととい昨日とお休みだったのになぜか今日はあります(爆)。しかもお便りじゃないし。卒業論文でインターネット上を片っ端から漁る予定なんで、そのためのアプリをいっそ作ってしまおうと。卒論までに間に合うかどうかは不明(汗)。まー気合い入れて作ればあと2日もあればできちゃうでしょう。
・ダウンロードする部分はもうできてたんで、検索エンジン google に検索パラメーターを渡して検索結果を取得するって部分を昨日の夜と今日作ってました。こういうのって、別ポート開いて特別な方法で送信するんだと思ってたら、ただ URL にパラメーターくっつけるだけで良かったのね。その辺をブラウザがやってたんだなー。 ・のっけから漢字を URL に埋め込むためのエンコードにはまる(爆)。単に「あ」なら Shift-JIS で「 0x82A0 」、1バイトずつに分けて & をプレフィックスにして「 %82%A0 」でOK。文字コードとかバイトオーダーとか考えなくていいらしい(汗)。 isalnum() とか使って分類して置き換えて完成。 ・あとは google のパラメーターの解析をして、それをリクエストに加えて、あとは検索文字列をエンコードしたのをくっつければOK。おー検索結果が出てきた! それにしても、 http://www.google.com/ で日本語ページが出てくるのはどういう仕組みだろう(汗)。まーパラメーターで hl=ja ってすればいいんで問題ないんだけどね。とりあえず、このアプリは速攻で作ってしまおう。アイコンとかももう描いたし〜。 ・でわまたっ! |
(C)KAB-studio 2000 ALL RIGHTS RESERVED. |