BinaryViewer製作

Chapter9. ファイルデータの描画(1)


アプリケーション起動時に空ドキュメントを開かないようにする

ファイルデータ表示処理の前に、「MFCの森」のTipsで紹介している方法 を使って、 アプリケーション起動時に空のドキュメントを開かないようにしておきましょう。

BOOL CBinaryViewerApp::InitInstance() { // ・・・ CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // 空ドキュメントを開かないようにする。 if(cmdInfo.m_nShellCommand == CCommandLineInfo::FileNew){ cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing; } // コマンド ラインで指定されたディスパッチ コマンドです。アプリケーションが // /RegServer、/Register、/Unregserver または /Unregister で起動された場合、 False を返します。 if (!ProcessShellCommand(cmdInfo)) return FALSE; // ・・・ }

コマンドライン引数の解析処理(ParseCommandLine関数)の結果、CCommandLineInfo::FileNew である場合に何もしないように書き換えてやります。

アドレス部の描画

材料が整ったので、データの表示処理に入ります。まずは、一番左のアドレス番号を表示するところです。

基本的には上の方から画面に表示できるだけ16ずつ値を追加して表示してやればよいのですが、実際のデータが少ない場合は データが存在する行まで表示することにします。また、常にデータの先頭から表示するのではなく、画面スクロールを考慮すると 開始アドレスの計算も行う必要も出てきます。ただし、これはスクロール処理の中で対応していきます。

それからもうひとつ。先頭の行はガイダンス表示を行っていますので、表示開始位置は1行目(0行目から数えて)からとなります。

void CBinaryViewerView::OnDraw(CDC* pDC) { //------------------------------------------------------------------------- // 描画準備処理 //------------------------------------------------------------------------- // ・・・ //------------------------------------------------------------------------- // 背景色を塗る //------------------------------------------------------------------------- // ・・・ //------------------------------------------------------------------------- // ガイダンス表示 //------------------------------------------------------------------------- // ・・・ //------------------------------------------------------------------------- // 各行の描画 //------------------------------------------------------------------------- DWORD dwViewLine = 1; // View上の行数(0行目はガイダンス部) DWORD dwBufferLine = 0; // Document上の行数 DWORD dwShowMaxLine; // View内に表示できる行数 dwShowMaxLine = (clientRect.Height() / tm.tmHeight); // 出力バッファ CHAR strOutput[256]; ZeroMemory(strOutput, sizeof(CHAR)*256); // Viewの最終行まで描画する or 最後のバイナリデータまで描画する まで繰り返し while(dwViewLine <= dwShowMaxLine && (pDoc->GetLength()/16) >= dwBufferLine){ //--------------------------------------------------------------------- // アドレス表示 //--------------------------------------------------------------------- // 出力データ生成 sprintf(strOutput, " %08X ", dwBufferLine*16); // 描画 pDC->SetTextColor(RGB(0xFF, 0xFF, 0)); pDC->TextOut(0, tm.tmHeight*dwViewLine, strOutput, (int)strlen(strOutput)); ZeroMemory(strOutput, sizeof(CHAR)*256); // 次の行へ dwViewLine++; dwBufferLine++; } }

それほど難しいことは行っていません。行単位での繰り返しループの中で、ガイダンス部の描画で行ったように アドレス番号を表示しています。
また、CBinaryViewerDocクラス に読み込んだデータのサイズを取得するための関数 GetLength() を追加しています。m_dwBufSize をそのまま返す関数です。(私はインライン関数としてヘッダファイル BinaryViewerDoc.h に記述しました)

また、ここでは2つのカウンタ(dwViewLine, dwBufferLine)を用意しています。 前者はView画面内の現在の表示位置(行)を表し、後者は実際のバイナリデータの行番号を示しています。
これは、前述のとおりスクロール処理などにより、現在の画面が常にDocumentデータの先頭から表示するとは限らないためです。

バイナリ部の描画

バイナリ部の描画はViewとDocumentの連携により実現します。表示したい部分をViewがDocumentへ要求し、Documentはそれに応えて情報を提供します。

CBinaryViewerDocクラス に以下の関数を追加します。
これは、取得したい場所と長さを受け取り、同じく引数として受け取った格納用のバッファへバイナリデータをコピーしてあげる関数です。

// 指定部分のデータを取得する // dwStart : (IN)取得開始位置(0〜) // dwLength : (IN)取得するデータの長さ&出力バッファの長さ // pOutBuf : (OUT)データを格納する出力バッファ // 戻り値 : 実際にバッファに格納したサイズ DWORD CBinaryViewerDoc::GetBuffer(DWORD dwStart, DWORD dwLength, LPBYTE pOutBuf) { // 引数チェック if(dwStart > m_dwBufSize || pOutBuf == NULL){ return 0; } DWORD dwRetSize; // 実際に書き込んだサイズ DWORD dwEnd = dwStart + (dwLength-1); // 取得最終位置 if(dwEnd > (m_dwBufSize-1)){ // バイナリデータの長さに調整 dwRetSize = dwStart + dwLength - m_dwBufSize; }else{ dwRetSize = dwLength; } // 出力バッファへコピー memcpy(pOutBuf, m_pBuffer+dwStart, dwRetSize); return dwRetSize; }

View側では、上記の関数を使用してデータを行単位で取得し、適当な形に加工した 上で描画します。
CBinaryViewerDoc::GetBuffer関数 から取得したデータはバイナリ値(数値)です。 TextOut関数 で描画するためには、この数値を文字列へ変換する処理が必要となります。

void CBinaryViewerView::OnDraw(CDC* pDC) { // ・・・ //------------------------------------------------------------------------- // 各行の描画 //------------------------------------------------------------------------- DWORD dwViewLine = 1; // View上の行数(0行目はガイダンス部) DWORD dwBufferLine = 0; // Document上の行数 DWORD dwShowMaxLine; // View内に表示できる行数 dwShowMaxLine = (clientRect.Height() / tm.tmHeight); // 出力バッファ CHAR strOutput[256]; ZeroMemory(strOutput, sizeof(CHAR)*256); // バイナリデータ処理用 BYTE byBinBuf[16]; // バイナリ取得用バッファ DWORD byBinCount; // 行内のバイナリデータ数 LPBYTE pBinBufPos; CHAR* pStrOutputPos; // Viewの最終行まで描画する or 最後のDocumentデータまで描画する まで繰り返し while(dwViewLine <= dwShowMaxLine && (pDoc->GetLength()/16) >= dwBufferLine){ //--------------------------------------------------------------------- // アドレス表示 //--------------------------------------------------------------------- // ・・・ //--------------------------------------------------------------------- // バイナリ表示 //--------------------------------------------------------------------- // 1行分のバイナリデータを取得 byBinCount = pDoc->GetBuffer(dwBufferLine*16, 16, byBinBuf); if(byBinCount == 0){ break; } pStrOutputPos = strOutput; pBinBufPos = byBinBuf; *pStrOutputPos = 0x20; // 先頭スペース pStrOutputPos++; // 取得したバイナリを変換文字列に変換 for(DWORD i = 0; i < byBinCount; i++){ sprintf(pStrOutputPos, "%02X ", *pBinBufPos); pStrOutputPos += 3; // 次の書き込み位置へ(スペースを含む) pBinBufPos++; } // 描画 pDC->SetTextColor(RGB(0x00, 0x00, 0x00)); pDC->TextOut(tm.tmAveCharWidth*BV_LINENUMBER_NUM, tm.tmHeight*dwViewLine, strOutput, (int)strlen(strOutput)); // 次の行へ dwViewLine++; dwBufferLine++; } }

ここまでの実行結果は以下のようになります。

ファイルデータの描画

上記は、アプリケーション起動後に BinaryViewerDoc.h を読み込んだ場合の処理結果です。サイズの小さいファイルを開いたり、 Viewのサイズを変更しても問題なく表示できていると思います。

これで残すは キャラクタ部 のみとなりました。
キャラクタ部はバイナリデータを文字として表したものを表示するのですが、日本語などのマルチバイト文字の処理という やっかいな問題が存在します。テキストエディタでは各行の先頭から文章・文字が始まるためそれほど苦労はないのですが、バイナリビューワでは 強引に16バイトを1行として分断しているため、行の先頭が2バイト文字の後半を指している 場合があります。その辺りも含めて次回対応します。


トップへ
Programトップへ
←前へ →次へ