ファイルをバイナリ形式で表示する (C/C++編)
ファイル一覧で表示中のファイル名またはディレクトリ名をダブルクリックしたときに、クリックしたのがファイル名であれば、現在表示中のファイルデータの内容をクリックされたファイル名のデータに更新します。
クリックされたのがディレクトリ名であればファイル一覧の表示をクリックされたディレクトリの一覧に更新します。
処理の流れは以下のようになります。
リストビューをダブルクリックしたときのイベントハンドラ
ディレクトリ名をダブルクリックした場合
ドキュメントへディレクトリ名の変更を通知する
ディレクトリツリーの選択ノードを変更する
ファイル一覧の表示を変更する
ファイル名をダブルクリックした場合
ドキュメントへファイル名の変更を通知する
ファイルを読み込む
ファイルデータの表示を変更する
リストビューの項目をダブルクリックしたときのイベントハンドラ
リストビューの項目をダブルクリックしたときのイベント処理を行います。
ファイル一覧からファイルまたはディレクトリをダブルクリックしたときのイベント処理を追加します。
「クラスウィザード」を開いて、「CFileListView」クラスに「メッセージ」タブから「NM_DBLCLK」を選択してイベントハンドラ「OnNMDblclk()」を追加します。
ソースコード [FileListView.cpp]
/****************************************************************************/
/*!
* @brief ファイル一覧の項目がダブルクリックされたときの操作イベント (NM_DBLCLK).
*
* @param [in] pNMHDR 通知メッセージ(NMHDR)構造体のポインタ.
* @param [out] pResult 戻り値を格納する領域のポインタ.
*
* @retval なし.
*/
void CFileListView::OnNMDblclk( NMHDR *pNMHDR, LRESULT *pResult )
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR);
*pResult = 0;
CListCtrl& listCtrl = GetListCtrl();
//! ドキュメント・クラスのオブジェクトを取得する.
CMFCApp001Doc* pDoc = dynamic_cast( GetDocument() );
if( pDoc == NULL ){
return;
}
//! クリックされた項目のインデックスを取得する.
int idx = listCtrl.GetNextItem( -1, LVNI_SELECTED );
if( idx < 0 ){
return;
}
//! 該当するファイルリストの番号を取り出す.
LVITEM lvi;
memset( &lvi, 0, sizeof( lvi ) );
lvi.mask = LVIF_PARAM;
lvi.iItem = idx;
listCtrl.GetItem( &lvi );
int sel_index = lvi.lParam;
//! クリックされたファイルの絶対パスを取得する.
CString filename = pDoc->GetFilePath( sel_index );
if( pDoc->IsFile( sel_index ) ){
//! ファイルの場合は内容をバイナリ形式で表示する.
pDoc->ShowFile( filename );
//! ステータスバーにファイル名を表示する.
CMainFrame* frame = dynamic_cast( GetParentFrame() );
if( frame ){
frame->StatusMessage( filename );
}
}else{
//! ディレクトリの場合はサブディレクトリを展開する.
pDoc->ExpandDirectory( filename );
}
}
最初にダブルクリックされたリストのインデックスを取得します。
取得したインデックスを基にリストビューから該当インデックスに対応したユーザーパラメータを取り出します。
取り出したユーザーパラメータはドキュメントが保持しているファイルリストのインデックスなので、これでドキュメントにあるファイル情報が利用できるようになります。
次にドキュメントのファイル情報から、ダブルクリックされたファイル名の絶対パスを取得します。
ダブルクリックされたファイル名が「ファイル」の場合、ファイルの内容をバイナリ形式で表示する為に、ドキュメントにファイル名を通知します。
併せてステータスバーにも表示するファイル名を通知します。
ダブルクリックされたファイル名が「ディレクトリ」の場合、ドキュメントにディレクトリの移動を通知します。
指定されたディレクトリまでのノードを展開して該当ノードを選択状態にします。
ファイル一覧ビューからディレクトリツリー・ビューへの選択中ディレクトリの変更通知ですので、ディレクトリツリー・ビューへ新たに選択されるディレクトリの絶対パス名を通知します。
ドキュメントで保持しているファイルリストも、新たに選択されるディレクトリのファイルリストに更新します。
ソースコード [MFCApp001Doc.cpp]
/****************************************************************************/
/*!
* @brief 指定したディレクトリを展開表示する.
*
* @param [in] path 展開するディレクトリの絶対パス.
*
* @retval なし.
*/
void CMFCApp001Doc::ExpandDirectory( CString& path )
{
CFileTreeView* treeView = GetFileTreeView();
if( treeView != NULL ){
//! ディレクトリツリーの該当ディレクトリを展開する.
treeView->ExpandAndSelectDirectory( path );
//! ファイル一覧を更新する.
UpdateFileList( path );
}
}
通知を受けたディレクトリツリー・ビューでは、指定されたディレクトリまでのノードを展開して該当ノードを選択状態にします。
指定された絶対パスのディレクトリ名から、各ディレクトリ名を取り出す為にString::Find()メソッドでセパレータ「\」で区切って取り出すのですが、指定された絶対パスのディレクトリ名は最後にセパレータ文字が付いていないので、「\」を絶対パスの最後に付加します。
ルートノードから子ノードのディレクトリ名と指定された絶対パスのディレクトリ名から取り出したディレクトリ名を比較して、一致した子ノードから孫ノードのディレクトリ名と指定された絶対パスのディレクトリ名から取り出した次のディレクトリ名を比較して、一致したら....
という具合に指定された絶対パスのディレクトリ名が無くなるまで繰り返します。
このとき一致したノードが展開していなかった場合は展開します。
(これ表示上の問題もありますが、ノードを展開しないとサブディレクトリのリストが子ノードに反映されないので探索がここで終わってしまいます。)
最後まで探索して見つかったノードを選択状態にします。
(ついでにツリービューにフォーカスを移します。)
ソースコード [FileTreeView.cpp]
/****************************************************************************/
/*!
* @brief 指定されたディレクトリのノードまでを展開表示する.
*
* @param [in] path ディレクトリの絶対パス名.
*
* @retval なし.
*/
void CFileTreeView::ExpandAndSelectDirectory( CString& path )
{
CTreeCtrl& treeCtrl = GetTreeCtrl();
HTREEITEM hItem = treeCtrl.GetRootItem();
//! Find()検索で最後まで検索するようにパスの最後に検索文字を付加する.
CString wkPath = path + _T("\\");
int next;
int pos = 0;
while( hItem && (next = wkPath.Find( '\\', pos )) != -1 ){
//! 子ノードが無い場合は処理を終了する.
if( !treeCtrl.ItemHasChildren( hItem ) ){
hItem = NULL;
break;
}
//! "\"が連続している場合は読み飛ばす.
if( next == pos ){
pos = next + 1;
continue;
}
//! ディレクトリ名を絶対パスから取り出す.
CString dirName = wkPath.Mid( pos, (next - pos) );
pos = next + 1;
hItem = treeCtrl.GetChildItem( hItem );
while( hItem != NULL ){
//! ノードのディレクトリ名を取り出す.
CString nodeName = treeCtrl.GetItemText( hItem );
//! 同じディレクトリ名のノードを見つけたらループを抜ける.
if( dirName.CompareNoCase( nodeName ) == 0 ){
UINT state = treeCtrl.GetItemState( hItem, TVIS_EXPANDED );
if( state != TVIS_EXPANDED ){
treeCtrl.Expand( hItem, TVE_EXPAND );
}
break;
}
//! 次のノードを取得する.
hItem = treeCtrl.GetNextItem( hItem, TVGN_NEXT );
}
}
//! ノードを選択状態にする.
if( hItem ){
treeCtrl.SelectItem( hItem );
SetFocus();
}
}
ファイルデータの表示は以下の手順で行います。
表示するファイルの内容を読み込みます。
次にファイルデータ表示ビューのスクロールバーの位置を初期化します。
最後にファイルデータ表示ビューを再描画します。
ソースコード [MFCApp001Doc.cpp]
/****************************************************************************/
/*!
* @brief ファイルの内容を表示する.
*
* @param [in] filename 読み込むファイルの絶対パス.
*
* @retval TRUE = OK. / FALSE = NG.
*/
BOOL CMFCApp001Doc::ShowFile( CString& filename )
{
//! ファイルを読み込む.
if( !LoadFile( filename ) ){
return FALSE;
}
//! ファイル内容を表示するウインドウを再描画する.
CFileDataView* view = GetFileDataView();
if( view != NULL ){
view->Clear();
view->InvalidateRect( NULL, FALSE );
}
return TRUE;
}
表示するファイルの読み込みは以下の手順で行います。
ファイルを読み込みモードでオープンします。
ファイルサイズを取得します。
ファイルサイズが読み込み上限(10MB)を超えていた場合は読み込むサイズを読み込み上限値にします。
ファイルの読み込み用バッファを確保します。
(ファイルの読み込み用バッファが確保してある場合は一旦解放してからバッファを再確保します。)確保した読み込み用バッファにファイルの内容を読み込みます。
(読み込むデータサイズ分のメモリは確保してあるので一気に読み込みます。)ファイルをクローズして終了します。
ソースコード [MFCApp001Doc.cpp]
/****************************************************************************/
/*!
* @brief 指定されたファイルの内容を読み込む.
*
* @param [in] filename 読み込むファイルの絶対パス.
*
* @retval TRUE = OK. / FALSE = NG.
*/
BOOL CMFCApp001Doc::LoadFile( CString& filename )
{
CFile file;
//! ファイルを読み込みモードでオープンする.
if( !file.Open( filename, CFile::modeRead | CFile::shareDenyNone ) ){
return FALSE;
}
//! ファイルのバイト数を取得する.
DWORD size = (DWORD)file.GetLength();
/*!
* ファイルサイズが読み込みサイズの上限を超える場合は
* サイズの上限までしか読み込まない.
*/
if( size > MAX_FILE_SIZE ){
size = MAX_FILE_SIZE;
}
//! ファイルの読み込み用バッファが残っている場合は、一旦解放する.
if( m_FileBuff != NULL ){
delete [] m_FileBuff;
m_FileBuff = NULL;
}
try {
//! ファイルサイズ分のファイルの読み込み用バッファを確保する.
m_FileBuff = new BYTE[size];
//! ファイルを読み込み用バッファに1回で読み込む.
DWORD readsize = file.Read( m_FileBuff, size );
if( size != readsize ){
size = readsize;
}
//! ファイルをクローズする.
file.Close();
m_FileSize = size;
return TRUE;
}catch( ... ){
TRACE0( "ファイルの読み込みに失敗しました。\n" );
//! ファイルの読み込みが失敗した場合もファイルをクローズする.
file.Close();
return FALSE;
}
}