ソースコード置き場へのリンク
最近なぜかしら@PAGESが繋がらなくなったので、ソース置き場をSiteMixに代えました。
ついでにVisual Web Developer 2010 Expressでサイトの構成をいろいろいじってる最中なもんで、
リンク先が頻繁に変更になってます。
記事ごとにリンクを直すのも大変なんで、ここに一括してリンクを貼っておきます。
しつこいぐらいにログを出すテスト
一人でプログラムを作っているときにはそれほど必要に感じませんけど、
プロジェクトチームでプログラムを作っていると、
何が原因でプログラムが動かないのか気になりますよね。
そんなとき役に立つのが、やっぱログですよね。
そこで、しつこいぐらいにログを出すシステムを作りました。
まずはログ出力用のストリームクラスです。
TLogStream.h
//---------------------------------------------------------------------------- /// @file TLogStream.h /// @brief ログ出力ストリームクラスヘッダ /// @details ログ出力ストリームクラスです。 /// @version 0.1.1 /// @date 2015/05/03 /// @author Copyright (C) 2015 hidakas1961 All rights reserved. //---------------------------------------------------------------------------- #pragma once #include <TDebugStream.h> #include <fstream> #include <stdio.h> #include <stdarg.h> // 共通ライブラリ名前空間 namespace Common { //------------------------------------------------------------------------ // 名前空間使用宣言 //------------------------------------------------------------------------ using namespace std; //------------------------------------------------------------------------ // クラス宣言 //------------------------------------------------------------------------ class TLogStream; //------------------------------------------------------------------------ // グローバル関数 //------------------------------------------------------------------------ /// @brief ログストリーム作成関数 /// @param [in] filename ファイル名 /// @param [in] plog ログ出力ストリームポインタ /// @param [in] tabsize タブサイズ /// @param [in] header ヘッダー文字列 /// @param [in] line 行番号 /// @param [in] date 日付出力フラグ /// @param [in] time 時刻出力フラグ /// @return ログストリームクラスオブジェクト /// @details ログストリームを作成します。 TLogStream & CreateLog( const char * filename = 0, ostream * plog = 0, int tabsize = 4, const char * header = 0, long line = 0, bool date = false, bool time = false ); /// @brief ログオブジェクト取得関数 /// @return ログストリームクラスオブジェクト /// @details ログストリームクラスオブジェクトを取得します。 TLogStream & LogOut(); //------------------------------------------------------------------------ /// @brief ログ出力ストリームクラス /// @details ログ出力ストリームクラスです。 //------------------------------------------------------------------------ class TLogStream { //-------------------------------------------------------------------- // 動的変数 //-------------------------------------------------------------------- private : ofstream * m_pfile; ///< ファイル出力ストリームポインタ ostream * m_plog; ///< ログ出力ストリームポインタ int m_tabsize; ///< タブサイズ const char * m_header; ///< ヘッダー文字列 int m_block; ///< ブロック数 TDebugStream * m_pdebug; ///< デバッグ出力ストリームポインタ long m_line; ///< 行番号 bool m_date; ///< 日付出力フラグ bool m_time; ///< 時刻出力フラグ //-------------------------------------------------------------------- // 構築子と解体子 //-------------------------------------------------------------------- public : /// @brief デフォルト構築子 /// @param [in] filename ファイル名 /// @param [in] plog ログ出力ストリームポインタ /// @param [in] tabsize タブサイズ /// @param [in] header ヘッダー文字列 /// @param [in] line 行番号 /// @param [in] date 日付出力フラグ /// @param [in] time 時刻出力フラグ explicit TLogStream( const char * filename = 0, ostream * plog = 0, int tabsize = 4, const char * header = 0, long line = 0, bool date = false, bool time = false ); /// @brief 解体子 virtual ~TLogStream(); //-------------------------------------------------------------------- // 演算子オーバーロード関数 //-------------------------------------------------------------------- public : /// @brief 左シフト演算子オーバーロード関数 /// @param [in] param 引数 /// @return ログ出力ストリーム参照アドレス /// @details 引数を出力します。 template< class T > TLogStream & operator <<( T param ) { * m_plog << param; return * this; } //-------------------------------------------------------------------- // 動的関数 //-------------------------------------------------------------------- public : /// @brief ヘッダー出力関数 /// @param [in] block ブロック数 /// @return ログ出力ストリーム参照アドレス /// @details ヘッダー文字列とブロックインデントを出力します。 inline virtual TLogStream & Header( int block = -1 ) { // ヘッダー文字列を出力します。 if ( m_header ) * m_plog << m_header << ":"; // 行番号を出力します。 if ( m_line > 0 ) { WriteFormat( "%06ld:", m_line++ ); } // 日付を出力します。 if ( m_date ) { char date[ 256 ]; GetDateFormat( LOCALE_SYSTEM_DEFAULT, LOCALE_NOUSEROVERRIDE, 0, 0, date, ArrayCount( date ) ); WriteFormat( "%s:", date ); } // 時刻を出力します。 if ( m_time ) { char time[ 256 ]; GetTimeFormat( LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, 0, "HH':'mm':'ss", time, ArrayCount( time ) ); WriteFormat( "%s:", time ); } // ブロックインデントを出力します。 return Indent( block ); } /// @brief ブロックインデント出力関数 /// @param [in] block ブロック数 /// @return ログ出力ストリーム参照アドレス /// @details ブロックインデントを出力します。 inline virtual TLogStream & Indent( int block = -1 ) { // ブロック数を調べます。 if ( block < 0 ) { // 現在のブロック数を適用します。 block = m_block; } // ブロック数を巡回します。 for ( int n = 0; n < block; n++ ) { // タブサイズを巡回します。 for ( int m = 0; m < m_tabsize; m++ ) { // 空白を出力します。 * m_plog << " "; } } return * this; } /// @brief 改行出力関数 /// @return ログ出力ストリーム参照アドレス /// @details 改行を出力します。 inline virtual TLogStream & NewLine() { // 改行を出力します。 * m_plog << endl; return * this; } /// @brief ブロック開始行出力関数 /// @param [in] name ブロック名 /// @return ログ出力ストリーム参照アドレス /// @details ブロック開始行を出力します。 inline virtual TLogStream & Begin( const char * name ) { // ヘッダーを出力してブロックカウンタをインクリメントしてブロック開始文字とブロック名を出力します。 Header( m_block++ ) << "{ // " << name; // 改行します。 return NewLine(); } /// @brief ブロック終了行出力関数 /// @param [in] name ブロック名 /// @return ログ出力ストリーム参照アドレス /// @details ブロック終了行を出力します。 inline virtual TLogStream & End( const char * name ) { // ブロックカウンタをデクリメントしてヘッダーとブロック終了文字とブロック名を出力します。 Header( --m_block ) << "} // " << name; // 改行します。 return NewLine(); } /// @brief 文字列出力関数 /// @param [in] text 文字列 /// @return ログ出力ストリーム参照アドレス /// @details 文字列を出力します。 inline TLogStream & Write( const char * text ) { // 文字列を出力します。 if ( text ) * m_plog << "\"" << text << "\""; else * m_plog << "(null)"; return * this; } /// @brief 文字列出力関数 /// @param [in] text 文字列 /// @return ログ出力ストリーム参照アドレス /// @details 文字列を出力します。 inline TLogStream & Write( char * text ) { return Write( const_cast< const char * >( text ) ); } /// @brief 文字列出力関数 /// @param [in] wtext 文字列 /// @return ログ出力ストリーム参照アドレス /// @details 文字列を出力します。 inline TLogStream & Write( const wchar_t * wtext ) { // ワイド文字列をマルチバイト文字列に変換します。 char text[ 1024 ] = { 0 }; if ( ! WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, wtext, -1, text, 1024, 0, 0 ) ) { // throw( this ); } // 文字列を出力します。 return Write( text ); } /// @brief 文字列出力関数 /// @param [in] wtext 文字列 /// @return ログ出力ストリーム参照アドレス /// @details 文字列を出力します。 inline TLogStream & Write( wchar_t * wtext ) { return Write( const_cast< const wchar_t * >( wtext ) ); } /// @brief 文字列出力関数 /// @param [in] text 文字列 /// @return ログ出力ストリーム参照アドレス /// @details 文字列を出力します。 inline TLogStream & Write( string text ) { return Write( text.c_str() ); } /// @brief 出力関数 /// @param [in] param 引数 /// @return ログ出力ストリーム参照アドレス /// @details 引数を出力します。 template< class T > inline TLogStream & Write( T param ) { // 引数を出力します。 * m_plog << param; return * this; } /// @brief ヘッダー付き行出力関数 /// @param [in] param 引数 /// @return ログ出力ストリーム参照アドレス /// @details ヘッダー付きで引数を行出力します。 template< class T > inline TLogStream & Line( T param ) { // ヘッダーを出力します。 Header(); // 引数を出力します。 Write( param ); // 改行します。 return NewLine(); } /// @brief データ出力関数 /// @param [in] name データ名 /// @param [in] value 値 /// @param [in] lf 改行フラグ /// @return ログ出力ストリーム参照アドレス /// @details データ名と値を出力します。 template< class T > TLogStream & Data( const char * name, T value, bool lf = true ) { // ヘッダーとデータ名を出力します。 Header() << name << " = "; // 値を出力します。 Write( value ); // 改行します。 if ( lf ) NewLine(); return * this; } /// @brief 16進データ出力関数 /// @param [in] name データ名 /// @param [in] value 値 /// @param [in] lf 改行フラグ /// @return ログ出力ストリーム参照アドレス /// @details データ名と値を16進数形式で出力します。 template< class T > TLogStream & Hex( const char * name, T value, bool lf = true ) { // 16進数文字列を作成します。 char buffer[ 256 ]; sprintf_s( buffer, ArrayCount( buffer ), "0x%08X", value ); // ヘッダーとデータ名と値を出力します。 Header() << name << " = " << buffer; // 改行します。 if ( lf ) NewLine(); return * this; } /// @brief 書式付き文字列出力関数 /// @param [in] format 書式文字列 /// @param [in] args 可変引数 /// @return ログ出力ストリーム参照アドレス /// @details 書式付き文字列を出力します。 inline virtual TLogStream & WriteFormatArgs( const char * format, va_list args ) { // 展開後の文字列サイズを取得します。 size_t size = _vscprintf( format, args ) + 1; // 書式付き文字列をバッファーに展開します。 char * buffer = new char[ size ]; vsprintf_s( buffer, size, format, args ); // 展開された文字列を出力します。 * m_plog << buffer; // 文字列バッファーを削除します。 delete[] buffer; return * this; } /// @brief 書式付き文字列出力関数 /// @param [in] format 書式文字列 /// @param [in] ... 可変引数 /// @return ログ出力ストリーム参照アドレス /// @details 書式付き文字列を出力します。 inline virtual TLogStream & WriteFormat( const char * format, ... ) { // 書式付き文字列を出力します。 va_list args; va_start( args, format ); return WriteFormatArgs( format, args ); } /// @brief 書式付き文字列行出力関数 /// @param [in] format 書式文字列 /// @param [in] ... 可変引数 /// @return ログ出力ストリーム参照アドレス /// @details 書式付き文字列を行出力します。 inline virtual TLogStream & WriteFormatLine( const char * format, ... ) { // ヘッダーを出力します。 Header(); // 書式付き文字列を出力します。 va_list args; va_start( args, format ); WriteFormatArgs( format, args ); // 改行します。 return NewLine(); } }; } // Common
TLogStream.cpp
//---------------------------------------------------------------------------- /// @file TLogStream.cpp /// @brief ログ出力ストリームクラス /// @details ログ出力ストリームクラスです。 //---------------------------------------------------------------------------- #include <TLogStream.h> //---------------------------------------------------------------------------- // 名前空間使用宣言 //---------------------------------------------------------------------------- using namespace std; using namespace Common; //---------------------------------------------------------------------------- // ローカル変数 //---------------------------------------------------------------------------- /// @brief 静的初期化構造体 /// @details 静的な初期化終了処理を行います。 static struct TInit { TLogStream * m_plog; ///< ログストリームポインタ /// 構築子 TInit() : m_plog( 0 ) {} /// 解体子 ~TInit() { // ログストリームを削除します。 if ( m_plog ) { s_init.m_plog->Line( "ログストリームオブジェクトを削除しました。" ); delete m_plog; } } } s_init; ///< 静的初期化構造体オブジェクト //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- // ログストリーム作成関数 TLogStream & Common::CreateLog( const char * filename, ostream * plog, int tabsize, const char * header, long line, bool date, bool time ) { // ログストリームポインタを調べます。 if ( s_init.m_plog ) { // ログストリームを削除します。 s_init.m_plog->Line( "ログストリームオブジェクトを削除しました。" ); delete s_init.m_plog; } // ログストリームオブジェクトを作成します。 s_init.m_plog = new TLogStream( filename, plog, tabsize, header, line, date, time ); s_init.m_plog->Line( "ログストリームオブジェクトを作成しました。" ); // ログストリームオブジェクトを返します。 return * s_init.m_plog; } // ログオブジェクト取得関数 TLogStream & Common::LogOut() { // ログストリームポインタを調べます。 if ( ! s_init.m_plog ) { // ログストリームオブジェクトを作成します。 s_init.m_plog = new TLogStream(); s_init.m_plog->Line( "ログストリームオブジェクトを作成しました。" ); } // ログストリームオブジェクトを返します。 return * s_init.m_plog; } //---------------------------------------------------------------------------- // ログ出力ストリームクラス //---------------------------------------------------------------------------- // 構築子と解体子 //---------------------------------------------------------------------------- // デフォルト構築子 TLogStream::TLogStream( const char * filename, ostream * plog, int tabsize, const char * header, long line, bool date, bool time ) : m_pfile ( 0 ) , m_plog ( 0 ) , m_tabsize( tabsize ) , m_header ( header ) , m_block ( 0 ) , m_pdebug ( 0 ) , m_line ( line ) , m_date ( date ) , m_time ( time ) { // ファイル名を調べます。 if ( filename ) { // ファイル出力ストリームを作成します。 m_plog = m_pfile = new ofstream( filename ); } // ログ出力ストリームポインタを調べます。 else if ( m_plog ) { // ログ出力ストリームポインタを設定します。 m_plog = plog; } else { // デバッグ出力ストリームを作成してログ出力ストリームポインタに設定します。 m_plog = m_pdebug = new TDebugStream(); } } /// @brief 解体子 TLogStream::~TLogStream() { // デバッグ出力ストリームを削除します。 if ( m_pdebug ) delete m_pdebug; // ファイル出力ストリームを削除します。 if ( m_pfile ) { m_pfile->close(); delete m_pfile; } }
解説
クラス構築時のオプションで、ヘッダー文字列や行番号や日付や時刻を表示できるようになってます。
デフォルトではウザいので出さないようにしています。
あとファイル名を指定すればファイルに出力しますが、
デフォルトではデバッグコンソールに出力します。
次にこれを使ったサンプルプログラムをご紹介します。
ウィンドウサンプルプログラムヘッダ(main.h)
//---------------------------------------------------------------------------- /// @file main.h /// @brief ウィンドウサンプルプログラムヘッダ /// @details ウィンドウサンプルプログラムです。 //---------------------------------------------------------------------------- /// @mainpage ウィンドウサンプルプログラム /// /// @section 概要 /// ウィンドウアプリケーションのサンプルプログラムです。 /// /// @section 履歴 /// - 2015/05/01 開発を開始しました。 /// - 2015/05/03 ログ出力を改良しました。 /// /// @version 0.1.1 /// @date 2015/05/03 /// @author Copyright (C) 2015 hidakas1961 All rights reserved. //---------------------------------------------------------------------------- #pragma once #include <windows.h> //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- /// @brief ウィンドウズメイン関数 /// @param [in] hInstance 現在のインスタンスのハンドル\n /// アプリケーションの現在のインスタンスのハンドルが入ります。 /// @param [in] hPrevInstance 以前のインスタンスのハンドル\n /// アプリケーションの前のインスタンスのハンドルが入ります。\n /// Win32 アプリケーションでは、常に NULL です。\n /// すでに他のインスタンスが存在するかどうかを調べるには、関数を使って一意の名前の付いたミューテックスを作成してください。\n /// CreateMutex 関数は、すでにそのミューテックスが存在していても成功しますが、関数を呼び出すと ERROR_ALREADY_EXISTS が返ります。\n /// GetLastError 関数からこの値が返された場合、既に同じアプリケーションの他のインスタンスが存在しており、それが最初のミューテックスを作成しています。 /// @param [in] lpCmdLine コマンドライン\n /// アプリケーションのコマンドラインが格納された NULL で終わる文字列へのポインタが入ります。\n /// プログラム名は含まれません。コマンドライン全体を取り出すには、GetCommandLine 関数を使ってください。 /// @param [in] nCmdShow 表示状態\n /// ウィンドウの表示状態の指定が入ります。次のいずれかの値が渡されます。\n /// <table> /// <tr><th>値</th> <th>意味</th></tr> /// <tr><td>SW_HIDE</td> <td>ウィンドウを非表示にし、他のウィンドウをアクティブにします。</td></tr> /// <tr><td>SW_MAXIMIZE</td> <td>ウィンドウを最大化します。</td></tr> /// <tr><td>SW_MINIMIZE</td> <td>ウィンドウを最小化し、Z オーダーで次のトップレベルウィンドウをアクティブにします。</td></tr> /// <tr><td>SW_RESTORE</td> <td>ウィンドウをアクティブにして表示します。<br> /// 最小化または最大化されていたウィンドウは、元の位置とサイズに戻ります。<br> /// 最小化されているウィンドウを元に戻す場合は、このフラグをセットします。</td></tr> /// <tr><td>SW_SHOW</td> <td>ウィンドウをアクティブにして、現在の位置とサイズで表示します。</td></tr> /// <tr><td>SW_SHOWMAXIMIZED</td> <td>ウィンドウをアクティブにし、最大化して表示します。</td></tr> /// <tr><td>SW_SHOWMINIMIZED</td> <td>ウィンドウをアクティブにし、最小化して表示します。</td></tr> /// <tr><td>SW_SHOWMINNOACTIVE</td><td>ウィンドウを最小化して表示します。<br> /// SW_SHOWMINIMIZED と似ていますが、この値を指定した場合は、ウィンドウはアクティブ化されません。</td></tr> /// <tr><td>SW_SHOWNA</td> <td>ウィンドウを現在の位置とサイズで表示します。<br> /// SW_SHOW と似ていますが、この値を指定した場合は、ウィンドウはアクティブ化されません。</td></tr> /// <tr><td>SW_SHOWNOACTIVATE</td> <td>ウィンドウを直前の位置とサイズで表示します。<br> /// SW_SHOWNORMAL と似ていますが、この値を指定した場合は、ウィンドウはアクティブ化されません。</td></tr> /// <tr><td>SW_SHOWNORMAL</td> <td>ウィンドウをアクティブにして表示します。<br> /// ウィンドウが最小化または最大化されていた場合は、その位置とサイズを元に戻します。<br> /// 初めてウィンドウを表示するときには、このフラグを指定します。</td></tr> /// </table> /// @return 関数が WM_QUIT メッセージを受け取って正常に終了する場合は、メッセージの wParam パラメータに格納されている終了コードを返してください。\n /// 関数がメッセージループに入る前に終了する場合は、0 を返してください。 /// @details WinMain 関数は、Win32 アプリケーションの初期エントリポイントです。Windows システムが呼び出します。\n /// WinMain 関数では、アプリケーションの初期化とメインウィンドウの表示を行ったうえで、 /// アプリケーション実行のその他の部分のためのトップレベルの制御構造であるメッセージ取得/ディスパッチループに入ってください。 /// WM_QUIT メッセージを受け取ったときに、メッセージループを終了させます。\n /// この時点で、WinMain 関数はアプリケーションを終了させ、WM_QUIT メッセージの wParam パラメータで受け取った値を返さなければなりません。 /// PostQuitMessage 関数を呼び出した結果として WM_QUIT メッセージを受け取った場合は、wParam パラメータの値は、 /// PostQuitMessage 関数の nExitCode パラメータの値となります。\n /// ANSI アプリケーションは、WinMain 関数の lpCmdLine パラメータによりコマンドライン文字列にアクセスすることができます。 /// ただし、この文字列にはプログラム名は含まれません。\n /// WinMain 関数が Unicode 文字列を返せないのは、lpCmdLine パラメータがデータ型として LPTSTR ではなく LPSTR を使っているためです。 /// GetCommandLineW 関数を使えば Unicode のコマンドライン文字列を取得できます。 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
ウィンドウサンプルプログラム(main.cpp)
//---------------------------------------------------------------------------- /// @file main.cpp /// @brief ウィンドウサンプルプログラム /// @details ウィンドウサンプルプログラムです。 //---------------------------------------------------------------------------- #include "main.h" #include "TSampleWindow.h" #include <TApplication.h> #include <TLogStream.h> //---------------------------------------------------------------------------- // 名前空間使用宣言 //---------------------------------------------------------------------------- using namespace Common; using namespace Window; using namespace Sample; //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- // ウィンドウズメイン関数 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { #ifdef _LOGFILE // ログファイルを作成します。 CreateLog( _LOGFILE ); #endif // _LOGFILE // ログに関数ヘッダーを出力します。 LogOut().Begin( __func__ ); // ログにタイトルを出力します。 LogOut().Line( "≪ウィンドウサンプルプログラム≫" ); // サンプルウィンドウを作成します。 TSampleWindow sample; // アプリケーションを実行します。 int result = TApplication( & sample ).Run( hInstance, hPrevInstance, lpCmdLine, nCmdShow ); // ログに関数の結果を出力します。 LogOut().Data( "result", result ); // ログに関数フッターを出力します。 LogOut().End( __func__ ); return result; }
解説
なるべく使う側で手間をかけたくないので、
使うときにオブジェクトを生成するようにしました。
それがグローバル関数のLogOut()関数です。
いきなりLogOut()関数が呼ばれた場合は、デフォルトのログ出力ストリームクラスオブジェクトを作成します。
で、プログラムが終了するときに静的なデータと共にオブジェクトも削除するようになってるのでごみは残りません。
ただしデフォルトでなくログファイルに出力したいときは、
明示的にCreateLog()関数を呼んでください。
これを使ったときのログファイルのサンプルをご紹介します。
WindowSample.log
"ログストリームオブジェクトを作成しました。" { // WinMain "≪ウィンドウサンプルプログラム≫" { // Window::TWindow::TWindow name = "class Sample::TSampleWindow" "ウィンドウクラスオブジェクトを構築しました。" } // Window::TWindow::TWindow { // Window::TApplication::TApplication pWnd = 001DF714 pWnd->Name() = "class Sample::TSampleWindow" "アプリケーションクラスオブジェクトを構築しました。" } // Window::TApplication::TApplication { // Window::TApplication::Run hInstance = 0x01120000 hPrevInstance = 0x00000000 lpCmdLine = "コマンドライン引数" nCmdShow = 1 { // Window::TWindow::InitWindow hParent = 00000000 hMenu = 00000000 { // Window::TWindow::WindowProc hWnd = 0x000401E8 uMsg = "WM_GETMINMAXINFO" wParam = 0x00000000 lParam = 0x001DEF14 } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCCREATE" wParam = 0x00000000 lParam = 0x001DEEE8 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCCALCSIZE" wParam = 0x00000000 lParam = 0x001DEF34 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_CREATE" wParam = 0x00000000 lParam = 0x001DEEAC { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc m_hWnd = 000401E8 } // Window::TWindow::InitWindow { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SHOWWINDOW" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_WINDOWPOSCHANGING" wParam = 0x00000000 lParam = 0x001DF44C { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_WINDOWPOSCHANGING" wParam = 0x00000000 lParam = 0x001DF44C { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_ACTIVATEAPP" wParam = 0x00000001 lParam = 0x00000C8C { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCACTIVATE" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_GETICON" wParam = 0x00000002 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_GETICON" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_GETICON" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_ACTIVATE" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_IME_SETCONTEXT" wParam = 0x00000001 lParam = 0xC000000F { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_IME_NOTIFY" wParam = 0x00000002 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SETFOCUS" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCPAINT" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_ERASEBKGND" wParam = 0x3F010EFD lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_WINDOWPOSCHANGED" wParam = 0x00000000 lParam = 0x001DF44C { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCCALCSIZE" wParam = 0x00000001 lParam = 0x001DF05C { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCPAINT" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_ERASEBKGND" wParam = 0x9B010A39 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SIZE" wParam = 0x00000000 lParam = 0x020803B0 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_MOVE" wParam = 0x00000000 lParam = 0x004D0036 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_PAINT" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Sample::TSampleWindow::OnWmPaint } // Sample::TSampleWindow::OnWmPaint result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_GETICON" wParam = 0x00000002 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_GETICON" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_GETICON" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_IME_NOTIFY" wParam = 0x00000001 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_IME_NOTIFY" wParam = 0x00000002 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_INPUTLANGCHANGE" wParam = 0x00000080 lParam = 0x04110411 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCHITTEST" wParam = 0x00000000 lParam = 0x011A01A8 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SETCURSOR" wParam = 0x000401E8 lParam = 0x02000001 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_MOUSEMOVE" wParam = 0x00000000 lParam = 0x00CD0172 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCHITTEST" wParam = 0x00000000 lParam = 0x011A01A8 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SETCURSOR" wParam = 0x000401E8 lParam = 0x02000001 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_MOUSEMOVE" wParam = 0x00000000 lParam = 0x00CD0172 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SYSKEYDOWN" wParam = 0x00000012 lParam = 0x20380001 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SYSKEYDOWN" wParam = 0x00000012 lParam = 0x60380001 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SYSKEYDOWN" wParam = 0x00000073 lParam = 0x203E0001 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_SYSCOMMAND" wParam = 0x0000F060 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_CLOSE" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "0x00000090( Unknown )" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_WINDOWPOSCHANGING" wParam = 0x00000000 lParam = 0x001DE674 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_WINDOWPOSCHANGED" wParam = 0x00000000 lParam = 0x001DE674 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCACTIVATE" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnDefProc result = 1 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_ACTIVATE" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_ACTIVATEAPP" wParam = 0x00000000 lParam = 0x00000C8C { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_KILLFOCUS" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_DESTROY" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc { // Window::TWindow::WindowProc hWnd = 0x000401E8", ""class Sample::TSampleWindow" uMsg = "WM_NCDESTROY" wParam = 0x00000000 lParam = 0x00000000 { // Window::TWindow::OnMessageProc { // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc result = 0 } // Window::TWindow::OnDefProc result = 0 } // Window::TWindow::OnMessageProc } // Window::TWindow::WindowProc result = 0 } // Window::TApplication::Run { // Window::TApplication::~TApplication "アプリケーションクラスオブジェクトを解体しました。" } // Window::TApplication::~TApplication result = 0 } // WinMain { // Window::TWindow::~TWindow name = "class Sample::TSampleWindow" "ウィンドウクラスオブジェクトを解体しました。" } // Window::TWindow::~TWindow "ログストリームオブジェクトを削除しました。"
エクセルのハイパーリンクを作るやつ
04/26 フォルダの中身をエクセルのハイパーリンクにするやつを作りました。
05/02 UNICODE対応にしたので記事を更新しました。
05/04 ログ機能強化とフォルダ構成の変更を行いました。
main.h
//---------------------------------------------------------------------------- /// @file main.h /// @brief エクセルハイパーリンクメーカーヘッダ /// @details エクセルハイパーリンクメーカーです。 //---------------------------------------------------------------------------- /// @mainpage エクセルハイパーリンクメーカー /// /// @section 概要 /// エクセルハイパーリンクを作成します。 /// /// @section history 履歴 /// - 2015/05/01 開発を開始しました。 /// - 2015/05/02 ワイド文字のファイル名が開けない問題が分かったのでワイド文字対応にしました。 /// - 2015/05/03 ログ出力を改良しました。 /// /// @version 0.2.1 /// @date 2015/05/03 /// @author Copyright (C) 2015 hidakas1961 All rights reserved. //---------------------------------------------------------------------------- #pragma once //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- /// @brief ワイド文字メイン関数 /// @param [in] argc コマンドライン引数の個数 /// @param [in] argv コマンドライン引数の文字列配列 /// @return 終了コード /// @details ワイド文字(Unicode?)バージョンのメイン関数です。 int wmain( int argc, wchar_t * argv[] );
main.cpp
//---------------------------------------------------------------------------- /// @file main.cpp /// @brief エクセルハイパーリンクメーカー /// @details エクセルハイパーリンクメーカーです。 //---------------------------------------------------------------------------- #include "main.h" #include <TLogStream.h> #include <locale.h> #include <codecvt> #include <conio.h> //---------------------------------------------------------------------------- // 名前空間使用宣言 //---------------------------------------------------------------------------- using namespace std; using namespace Common; //---------------------------------------------------------------------------- // ローカル変数 //---------------------------------------------------------------------------- static const size_t maxpath = 1024; ///< フルパス最大文字数 static const size_t maxdepth = 100; ///< 最大ディレクトリ深さ static bool hidden = false; ///< 非表示ファイル許可フラグ static bool sysfile = false; ///< システムファイル許可フラグ static wofstream * pfout = 0; ///< ファイル出力ストリームポインタ static wostream * pout = 0; ///< 出力ストリームポインタ static size_t count = 0; ///< リンク数カウンタ static wchar_t root[ maxpath ]; ///< 入力ディレクトリ名 static bool nexts[ maxdepth ]; ///< 次ファイル有無フラグテーブル //---------------------------------------------------------------------------- // ローカル関数 //---------------------------------------------------------------------------- /// @brief ファイルスキップ判定関数 /// @param [in] data ファイル検索データ /// @return なし /// @details ファイルをスキップするか判定します。 static bool IsSkip( WIN32_FIND_DATAW & data ) { LogOut().Begin( __func__ ); LogOut().Hex ( "data ", data ); bool result = false; if ( ! wcscmp( data.cFileName, L"." ) || ! wcscmp( data.cFileName, L".." ) ) { result = true; } else if ( data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) { result = ! hidden; } else if ( data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) { result = ! sysfile; } LogOut().Data( "result", result ); LogOut().End ( __func__ ); return result; } /// @brief 属性情報出力関数 /// @param [in] path パス /// @param [in] pdata ファイル検索データポインタ /// @return なし /// @details 属性情報を出力します。 static void OutputAttributes( const wchar_t * path, WIN32_FIND_DATAW * pdata = 0 ) { LogOut().Begin( __func__ ); LogOut().Data ( "path", path ); // ファイル属性を取得します。 DWORD attr; if ( pdata ) { // ファイル検索データからファイル属性を取得します。 attr = pdata->dwFileAttributes; } else { // ファイルパスからファイル属性を取得します。 attr = GetFileAttributesW( path ); } if ( attr != static_cast< DWORD >( -1 ) ) { // ディレクトリでないか調べます。 if ( ! ( attr & FILE_ATTRIBUTE_DIRECTORY ) ) { // 拡張子を取得します。 wchar_t ext[ _MAX_EXT ]; _wsplitpath_s( path, 0, 0, 0, 0, 0, 0, ext, _MAX_EXT ); if ( ext[ 0 ] == L'.' ) { * pout << ( ext + 1 ); } else { * pout << L"なし"; } } } * pout << L"\t"; LogOut().End( __func__ ); } /// @brief インデント出力関数 /// @param [in] depth ディレクトリ深さ /// @return なし /// @details インデントを出力します。 static void OutputIndent( size_t depth ) { LogOut().Begin( __func__ ); LogOut().Data ( "depth", depth ); // ディレクトリ深さを巡回します。 for ( size_t i = 0; i < depth; i++ ) { // ディレクトリ深さを調べます。 if ( i == depth - 1 ) { // 次のファイル有無フラグを調べます。 if ( nexts[ i ] ) { * pout << L"├"; } else { * pout << L"└"; } } // 次のファイル有無フラグを調べます。 else if ( nexts[ i ] ) { * pout << L"│"; } * pout << L"\t"; } LogOut().End( __func__ ); } /// @brief ハイパーリンク式出力関数 /// @param [in] path パス /// @param [in] name 表示名 /// @return なし /// @details ハイパーリンク式を出力します。 static void OutputHyperLink( const wchar_t * path, const wchar_t * name ) { LogOut().Begin( __func__ ); LogOut().Data ( "path ", path ); LogOut().Data ( "name ", name ); LogOut().Data ( "count", count++ ); * pout << L"=HYPERLINK(\"" << path << L"\",\"" << name << L"\")" << endl; LogOut().End( __func__ ); } /// @brief ディレクトリ出力関数 /// @param [in] subdir サブディレクトリ /// @param [in] depth ディレクトリ深さ /// @return なし /// @details ディレクトリを出力します。 static void OutputDirectory( const wchar_t * subdir = 0, size_t depth = 0 ) { LogOut().Begin( __func__ ); LogOut().Data ( "subdir", subdir ); LogOut().Data ( "depth ", depth ); // 開始ディレクトリをコピーします。 wchar_t path[ maxpath ]; wcscpy_s( path, root ); size_t len = wcslen( path ); if ( path[ len - 1 ] != '\\' ) { wcscat_s( path, L"\\" ); len++; } // サブディレクトリを追加します。 size_t len2 = len; if ( subdir ) { wcscat_s( path, subdir ); len2 = wcslen( path ); if ( path[ len2 - 1 ] != '\\' ) { wcscat_s( path, L"\\" ); len2++; } } // ワイルドカードを追加します。 wcscat_s( path, L"*.*" ); // ファイルを検索します。 WIN32_FIND_DATAW data, next; HANDLE handle = FindFirstFileW( path, & data ); if ( handle != INVALID_HANDLE_VALUE ) { // 先頭ファイルを検索します。 bool result; while ( ( result = FindNextFileW( handle, & data ) != FALSE ) && IsSkip( data ) ); // 検索ファイルを巡回します。 for ( ; result; data = next ) { // 次のファイルを検索します。 while ( ( result = FindNextFileW( handle, & next ) != FALSE ) && IsSkip( next ) ); // 次のファイル有無テーブルを設定します。 nexts[ depth ] = result; // パスを作成します。 path[ len2 ] = '\0'; wcscat_s( path, data.cFileName ); // 属性情報を出力します。 OutputAttributes( path, & data ); // インデントを出力します。 OutputIndent( depth + 1 ); // ハイパーリンク式を出力します。 OutputHyperLink( path, data.cFileName ); // サブディレクトリでかつリパースポイントでないか調べます。 if ( ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && ! ( data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) ) { // サブディレクトリを出力します。 OutputDirectory( path + len, depth + 1 ); } } FindClose( handle ); } LogOut().End( __func__ ); } //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- // ワイド文字メイン関数 int wmain( int argc, wchar_t * argv[] ) { #ifdef _LOGFILE // ログファイルを作成します。 CreateLog( _LOGFILE ); #endif // _LOGFILE LogOut().Begin( __func__ ); // ロケールを設定します。 setlocale( LC_ALL, "Japanese" ); // タイトルを表示します。 LogOut().Line ( "≪エクセルハイパーリンクメーカー≫" ); cout << "≪エクセルハイパーリンクメーカー≫" << endl << endl; bool error = false; ///< エラーフラグ const wchar_t * dirname = 0; ///< ディレクトリ名 const wchar_t * outfile = 0; ///< 出力ファイル名 // コマンドライン引数を巡回します。 for ( int i = 1; i < argc; i++ ) { // オプションスイッチか調べます。 if ( argv[ i ][ 0 ] == '-' ) { // オプションスイッチの種類を調べます。 if ( ! wcscmp( argv[ i ] + 1, L"o" ) ) { // 出力ファイル名を設定します。 if ( ! outfile ) { if ( i + 1 < argc && argv[ i + 1 ][ 0 ] != '-' ) { outfile = argv[ ++i ]; } else { error = true; cout << endl << "出力ファイル名がありません。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else { error = true; cout << endl << "出力パスオプションが重複しています。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else if ( ! wcscmp( argv[ i ] + 1, L"hidden" ) ) { // 非表示ファイル許可フラグをセットします。 if ( ! hidden ) { hidden = true; wcout << L"\"" << argv[ i ] << L"\" : 非表示ファイル許可" << endl; } else { error = true; cout << endl << "非表示ファイル許可オプションが重複しています。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else if ( ! wcscmp( argv[ i ] + 1, L"system" ) ) { // システムファイル許可フラグをセットします。 if ( ! sysfile ) { sysfile = true; wcout << L"\"" << argv[ i ] << L"\" : システムファイル許可" << endl; } else { error = true; cout << endl << "システムファイル許可オプションが重複しています。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else { error = true; cout << endl << "不明なオプションです。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } // ディレクトリ名を調べます。 else if ( ! dirname ) { // ディレクトリ名を設定します。 dirname = argv[ i ]; } else { error = true; cout << endl << "余分な引数です。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } // エラーフラグを調べます。 while ( ! error ) { // ディレクトリ名を調べます。 if ( ! dirname ) { // カレントディレクトリを設定します。 dirname = L"."; } // 入力ディレクトリ名のフルパスを取得します。 if ( GetFullPathNameW( dirname, maxpath, root, 0 ) ) { wcout << L"入力ディレクトリ : \"" << root << L"\"" << endl; // 入力ディレクトリが存在するか調べます。 DWORD attr = GetFileAttributesW( root ); if ( attr != static_cast< DWORD >( -1 ) ) { // ディレクトリか調べます。 if ( attr & FILE_ATTRIBUTE_DIRECTORY ) { // 出力ファイル名を調べます。 if ( outfile ) { // フルパスを取得します。 wchar_t path[ maxpath ]; if ( GetFullPathNameW( outfile, maxpath, path, 0 ) ) { wcout << L"出力ファイル名 : \"" << path << L"\"" << endl; // 出力パスがディレクトリ名でないか調べます。 DWORD attr = GetFileAttributesW( path ); if ( attr != static_cast< DWORD >( -1 ) ) { if ( attr & FILE_ATTRIBUTE_DIRECTORY ) { error = true; cout << endl << "出力ファイル名はディレクトリです。" << endl; break; } } // ファイル出力ストリームを作成します。 pfout = new wofstream( path, ios::binary ); // 出力モードを設定します。 pfout->imbue( locale( locale(""), new codecvt_utf16< wchar_t, 0x10ffff, static_cast< codecvt_mode >( generate_header | little_endian ) >() ) ); // 出力ストリームポインタを設定します。 pout = pfout; } else { error = true; cout << endl << "不正なファイル名です。" << endl; break; } } else { // 出力ストリームポインタを設定します。 pout = & wcout; } } else { error = true; cout << endl << "ディレクトリではありません。" << endl; break; } } else { error = true; cout << endl << "入力ディレクトリが存在しません。" << endl; break; } } else { error = true; cout << endl << "不正なディレクトリ名です。" << endl; break; } // タイトル行を出力します。 * pout << L"拡張子\tパス" << endl; // 開始ディレクトリの属性情報を出力します。 OutputAttributes( root ); // 開始ディレクトリのインデントを出力します。 OutputIndent( 0 ); // 開始ディレクトリのハイパーリンクを出力します。 OutputHyperLink( root, root ); // サブディレクトリを出力します。 OutputDirectory(); // 出力ストリームを削除します。 if ( pfout ) { pfout->close(); delete pfout; } cout << endl << "正常に終了しました。" << endl; break; } #ifdef _DEBUG // キー入力を要求します。 cout << endl << "何か押してください。" << endl; _getch(); #endif // _DEBUG LogOut().End( __func__ ); return 0; }
ソースコード
以下のプロジェクトをソリューションに追加してビルドしてください。
開発環境は Windows Vista VC++ 2010 Express です。
共通ライブラリ
Excelハイパーリンクメーカー
以上です。
VC++のフィルター設定とかめんどくさいので
04/25 補助用のツールを作りました。
05/02 UNICODE 対応にしました。
05/04 ログ機能強化とフォルダ構成の変更を行いました。
main.h
//---------------------------------------------------------------------------- /// @file main.h /// @brief VCプロジェクトファイルメーカーヘッダ /// @details VCプロジェクトファイルメーカーです。 //---------------------------------------------------------------------------- /// @mainpage VCプロジェクトファイルメーカー /// /// @section 概要 /// VCプロジェクトファイルを作成します。 /// /// @section history 履歴 /// - 2015/05/01 開発を開始しました。 /// - 2015/05/02 ワイド文字のファイル名が開けない問題が分かったのでワイド文字対応にしました。 /// - 2015/05/03 ログ出力を改良しました。 /// /// @version 0.2.1 /// @date 2015/05/03 /// @author Copyright (C) 2015 hidakas1961 All rights reserved. //---------------------------------------------------------------------------- #pragma once //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- /// @brief ワイド文字メイン関数 /// @param [in] argc コマンドライン引数の個数 /// @param [in] argv コマンドライン引数の文字列配列 /// @return 終了コード /// @details ワイド文字(Unicode?)バージョンのメイン関数です。 int wmain( int argc, wchar_t * argv[] );
main.cpp
//---------------------------------------------------------------------------- /// @file main.cpp /// @brief VCプロジェクトファイルメーカー /// @details VCプロジェクトファイルメーカーです。 //---------------------------------------------------------------------------- #include "main.h" #include <TLogStream.h> #include <locale.h> #include <codecvt> #include <conio.h> //---------------------------------------------------------------------------- // 名前空間使用宣言 //---------------------------------------------------------------------------- using namespace std; using namespace Common; //---------------------------------------------------------------------------- // ローカル変数 //---------------------------------------------------------------------------- /// アイテムグループ出力タイプ enum enumItemType { ITEM_FILTER, ///< フィルターファイル用のフィルターアイテムグループ ITEM_FILTER_FILE, ///< フィルターファイル用のファイルアイテムグループ ITEM_PROJECT_FILE, ///< プロジェクトファイル用のファイルアイテムグループ }; static const size_t maxpath = 1024; ///< フルパス最大文字数 static const size_t maxdepth = 100; ///< 最大ディレクトリ深さ static bool hidden = false; ///< 非表示ファイル許可フラグ static bool sysfile = false; ///< システムファイル許可フラグ static wofstream * pfout = 0; ///< ファイル出力ストリームポインタ static wostream * pout = 0; ///< 出力ストリームポインタ static size_t count = 0; ///< リンク数カウンタ static wchar_t root[ maxpath ]; ///< 入力ディレクトリ名 static bool nexts[ maxdepth ]; ///< 次ファイル有無フラグテーブル //---------------------------------------------------------------------------- // ローカル関数 //---------------------------------------------------------------------------- /// @brief ファイルスキップ判定関数 /// @param [in] data ファイル検索データ /// @return なし /// @details ファイルをスキップするか判定します。 static bool IsSkip( WIN32_FIND_DATAW & data ) { LogOut().Begin( __func__ ); LogOut().Hex ( "data ", data ); bool result = false; if ( ! wcscmp( data.cFileName, L"." ) || ! wcscmp( data.cFileName, L".." ) ) { result = true; } else if ( data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) { result = ! hidden; } else if ( data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) { result = ! sysfile; } LogOut().Data( "result", result ); LogOut().End ( __func__ ); return result; } /// @brief ディレクトリ出力関数 /// @param [in] item アイテムタイプ /// @param [in] subdir サブディレクトリ /// @param [in] depth ディレクトリ深さ /// @return なし /// @details ディレクトリを出力します。 static void OutputDirectory( int item, const wchar_t * subdir = 0, size_t depth = 0 ) { LogOut().Begin( __func__ ); LogOut().Data ( "item ", item ); LogOut().Data ( "subdir", subdir ); LogOut().Data ( "depth ", depth ); // 開始ディレクトリをコピーします。 wchar_t path[ maxpath ]; wcscpy_s( path, root ); size_t len = wcslen( path ); if ( path[ len - 1 ] != '\\' ) { wcscat_s( path, L"\\" ); len++; } // サブディレクトリを追加します。 size_t len2 = len; if ( subdir ) { wcscat_s( path, subdir ); len2 = wcslen( path ); if ( path[ len2 - 1 ] != '\\' ) { wcscat_s( path, L"\\" ); len2++; } } // ワイルドカードを追加します。 wcscat_s( path, L"*.*" ); // ファイルを検索します。 WIN32_FIND_DATAW data, next; HANDLE handle = FindFirstFileW( path, & data ); if ( handle != INVALID_HANDLE_VALUE ) { // 先頭ファイルを検索します。 bool result; while ( ( result = FindNextFileW( handle, & data ) != FALSE ) && IsSkip( data ) ); // 検索ファイルを巡回します。 for ( ; result; data = next ) { // 次のファイルを検索します。 while ( ( result = FindNextFileW( handle, & next ) != FALSE ) && IsSkip( next ) ); // 次のファイル有無テーブルを設定します。 nexts[ depth ] = result; // パスを作成します。 path[ len2 ] = '\0'; wcscat_s( path, data.cFileName ); // アイテムの種類を調べます。 if ( item == ITEM_FILTER ) { if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { * pout << L" <Filter Include=\"" << ( path + len ) << L"\" />" << endl; } } else if ( item == ITEM_FILTER_FILE ) { if ( ! ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) { if ( subdir ) { * pout << L" <None Include=\"" << ( path + len ) << L"\">" << endl; * pout << L" <Filter>" << subdir << L"</Filter>" << endl; * pout << L" </None>" << endl; } else { * pout << L" <None Include=\"" << ( path + len ) << L"\" />" << endl; } } } else if ( item == ITEM_PROJECT_FILE ) { if ( ! ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) { * pout << L" <None Include=\"" << ( path + len ) << L"\" />" << endl; } } // サブディレクトリでかつリパースポイントでないか調べます。 if ( ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && ! ( data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) ) { // サブディレクトリを出力します。 OutputDirectory( item, path + len, depth + 1 ); } } FindClose( handle ); } LogOut().End( __func__ ); } //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- // ワイド文字メイン関数 int wmain( int argc, wchar_t * argv[] ) { #ifdef _LOGFILE // ログファイルを作成します。 CreateLog( _LOGFILE ); #endif // _LOGFILE LogOut().Begin( __func__ ); // ロケールを設定します。 setlocale( LC_ALL, "Japanese" ); // タイトルを表示します。 LogOut().Line ( "≪VCプロジェクトファイルメーカー≫" ); cout << "≪VCプロジェクトファイルメーカー≫" << endl << endl; bool error = false; ///< エラーフラグ const wchar_t * dirname = 0; ///< ディレクトリ名 const wchar_t * outfile = 0; ///< 出力ファイル名 // コマンドライン引数を巡回します。 for ( int i = 1; i < argc; i++ ) { // オプションスイッチか調べます。 if ( argv[ i ][ 0 ] == '-' ) { // オプションスイッチの種類を調べます。 if ( ! wcscmp( argv[ i ] + 1, L"o" ) ) { // 出力ファイル名を設定します。 if ( ! outfile ) { if ( i + 1 < argc && argv[ i + 1 ][ 0 ] != '-' ) { outfile = argv[ ++i ]; } else { error = true; cout << endl << "出力ファイル名がありません。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else { error = true; cout << endl << "出力パスオプションが重複しています。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else if ( ! wcscmp( argv[ i ] + 1, L"hidden" ) ) { // 非表示ファイル許可フラグをセットします。 if ( ! hidden ) { hidden = true; wcout << L"\"" << argv[ i ] << L"\" : 非表示ファイル許可" << endl; } else { error = true; cout << endl << "非表示ファイル許可オプションが重複しています。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else if ( ! wcscmp( argv[ i ] + 1, L"system" ) ) { // システムファイル許可フラグをセットします。 if ( ! sysfile ) { sysfile = true; wcout << L"\"" << argv[ i ] << L"\" : システムファイル許可" << endl; } else { error = true; cout << endl << "システムファイル許可オプションが重複しています。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } else { error = true; cout << endl << "不明なオプションです。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } // ディレクトリ名を調べます。 else if ( ! dirname ) { // ディレクトリ名を設定します。 dirname = argv[ i ]; } else { error = true; cout << endl << "余分な引数です。" << endl; wcout << L"\"" << argv[ i ] << L"\"" << endl; break; } } // エラーフラグを調べます。 while ( ! error ) { // ディレクトリ名を調べます。 if ( ! dirname ) { // カレントディレクトリを設定します。 dirname = L"."; } // 入力ディレクトリ名のフルパスを取得します。 if ( GetFullPathNameW( dirname, maxpath, root, 0 ) ) { wcout << L"入力ディレクトリ : \"" << root << L"\"" << endl; // 入力ディレクトリが存在するか調べます。 DWORD attr = GetFileAttributesW( root ); if ( attr != static_cast< DWORD >( -1 ) ) { // ディレクトリか調べます。 if ( attr & FILE_ATTRIBUTE_DIRECTORY ) { // 出力ファイル名を調べます。 if ( outfile ) { // フルパスを取得します。 wchar_t path[ maxpath ]; if ( GetFullPathNameW( outfile, maxpath, path, 0 ) ) { wcout << L"出力ファイル名 : \"" << path << L"\"" << endl; // 出力パスがディレクトリ名でないか調べます。 DWORD attr = GetFileAttributesW( path ); if ( attr != static_cast< DWORD >( -1 ) ) { if ( attr & FILE_ATTRIBUTE_DIRECTORY ) { error = true; cout << endl << "出力ファイル名はディレクトリです。" << endl; break; } } // ファイル出力ストリームを作成します。 pfout = new wofstream( path, ios::binary ); // 出力モードを設定します。 pfout->imbue( locale( locale(""), new codecvt_utf16< wchar_t, 0x10ffff, static_cast< codecvt_mode >( generate_header | little_endian ) >() ) ); // 出力ストリームポインタを設定します。 pout = pfout; } else { error = true; cout << endl << "不正なファイル名です。" << endl; break; } } else { // 出力ストリームポインタを設定します。 pout = & wcout; } } else { error = true; cout << endl << "ディレクトリではありません。" << endl; break; } } else { error = true; cout << endl << "入力ディレクトリが存在しません。" << endl; break; } } else { error = true; cout << endl << "不正なディレクトリ名です。" << endl; break; } // フィルターファイル用のテキストを出力します。 * pout << L"以下のテキストを \"~.vcxproj.filters\" にコピーしてください。" << endl; * pout << L"~ここから~" << endl; * pout << L"<?xml version=\"1.0\" encoding=\"utf-8\"?>" << endl; * pout << L"<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">" << endl; * pout << L" <ItemGroup>" << endl; // フィルターグループを出力します。 OutputDirectory( ITEM_FILTER ); * pout << L" </ItemGroup>" << endl; // ファイルグループを出力します。 * pout << L" <ItemGroup>" << endl; OutputDirectory( ITEM_FILTER_FILE ); * pout << L" </ItemGroup>" << endl; * pout << L"</Project>" << endl; * pout << L"~ここまで~" << endl; // プロジェクトファイル用のテキストを出力します。 * pout << endl << L"以下のテキストを \"~.vcxproj\" の適当な場所に挿入してください。" << endl; * pout << L"~ここから~" << endl; * pout << L" <ItemGroup>" << endl; // ファイルグループを出力します。 OutputDirectory( ITEM_PROJECT_FILE ); * pout << L" </ItemGroup>" << endl; * pout << L"~ここまで~" << endl; // 出力ストリームを削除します。 if ( pfout ) { pfout->close(); delete pfout; } cout << endl << "正常に終了しました。" << endl; break; } #ifdef _DEBUG // キー入力を要求します。 cout << endl << "何か押してください。" << endl; _getch(); #endif // _DEBUG LogOut().End( __func__ ); return 0; }
ソースコード
以下のプロジェクトをソリューションに追加してビルドしてください。
開発環境は Windows Vista VC++ 2010 Express です。
共通ライブラリ
VCプロジェクトファイルメーカー
以上です。
エクセルのハイパーリンクのJPGファイルをIE以外で開くには?
エクセル用のハイパーリンク作成ツールを作っていて気がついたのですが、
何故だかハイパーリンクのJPGファイルを開こうとすると、
JPGファイルの既定のプログラムの設定を無視して、
超大っ嫌いなIEが立ち上がってしまうんですよね。
せっかくデフォルトのブラウザをクロームに設定しているっつぅのに。(--〆)
そこでグーグル先生でいろいろ調べたんだけど、
ハイパーリンクのファイルの関連付けは単純には変えられないらしくて、
結局レジストリをいじることになりました。
解決策はレジストリエディタで
\HKEY_CLASSES_ROOT\.jpg
を丸ごと削除することでした。
削除するのは怖いので名前を変えてみたのですが、
ちゃんとハイパーリンクでJPGの既定のプログラムが立ち上がるようになりました。
おしまい。
DLLとストリームのサンプルプログラム
DLLにしてみるテスト
いつもよく使う関数やマクロを共通ライブラリにしようと思って、
どうせならDLLの練習を兼ねてDLLにしてみました。
家で使っているのはVC++ 2010 Expressなんですが、
こいつは2012 Expressなどと違って空のソリューションが作れない仕様になっているのね。
だから最初に何らかのプロジェクトも一緒に作ってやる必要があるんですよね。
ところが新規でプロジェクトを作るときってのは、
最初にプロジェクト名を聞いてきて、デフォルトではソリューション名も同じにされちゃうんですよね。
それでわざわざソリューション名のところで別の名前を入力する必要があるわけなんです。
で、CommonというDLLプロジェクトを最初に作って、
次にSampleというアプリケーションプロジェクトを追加したわけなんですが、
何もない空っぽのDLLプロジェクトをビルドしようとすると、Common.libが無いとか言って怒られるんですよね。
本当は無くても動くんだけど、しょうがないのでDllMain()を追加しました。
Common.h
//---------------------------------------------------------------------------- /// @file Common.h /// @brief 共通ライブラリモジュールヘッダ /// @details すべてに共通のライブラリです。 //---------------------------------------------------------------------------- #pragma once #include <windows.h> //---------------------------------------------------------------------------- // マクロ定義 //---------------------------------------------------------------------------- #ifdef COMMON_EXPORTS #define COMMON_API __declspec(dllexport) ///< DLL宣言マクロ #else // COMMON_EXPORTS #define COMMON_API __declspec(dllimport) ///< DLL宣言マクロ #endif // COMMON_EXPORTS #ifndef __func__ #define __func__ __FUNCTION__ ///< 関数名事前定義マクロ #endif // __func__ /// @brief トークン文字列取得マクロ /// @details トークンを文字列として取得します。 #define STRING( token ) #token /// @brief マクロトークン文字列取得マクロ /// @details マクロを展開して得られたトークンを文字列として取得します。 #define MACRO_STRING( macro ) STRING( macro ) //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- /// @brief DLLメイン関数 /// @param [in] hModule モジュールハンドル /// @param [in] ul_reason_for_call 関数を呼び出す理由 /// @param [in] lpReserved 予約済み /// @retval TRUE 成功 /// @retval FALSE 失敗 /// @details DLLモジュールのエントリーポイントです。 COMMON_API BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ); //---------------------------------------------------------------------------- /// @brief 共通ライブラリ名前空間 /// @details 共通ライブラリ名前空間です。 //---------------------------------------------------------------------------- namespace Common {}
Common.cpp
//---------------------------------------------------------------------------- /// @file Common.cpp /// @brief 共通ライブラリモジュール //---------------------------------------------------------------------------- #include "Common.h" // DLLメイン関数 BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch ( ul_reason_for_call ) { case DLL_PROCESS_ATTACH : // ここでDLLの初期化処理を行います。 break; case DLL_THREAD_ATTACH : case DLL_THREAD_DETACH : break; case DLL_PROCESS_DETACH : // ここでDLLの終了処理を行います。 break; } return TRUE; }
サンプルプロジェクトのほうは、デバッグコンソールにストリームでログを出力するだけのものです。
main.h
//---------------------------------------------------------------------------- /// @file main.h /// @brief メインモジュールヘッダ /// @details メインモジュールです。 //---------------------------------------------------------------------------- #pragma once //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- /// @brief メイン関数 /// @return なし /// @details メイン関数です。 extern void main(); /// @brief テスト関数 /// @param [in] param 引数 /// @return 戻り値 /// @details テスト用の関数です。 extern int func( int param );
main.cpp
//---------------------------------------------------------------------------- /// @file main.cpp /// @brief メインモジュール /// @details メインモジュールです。 //---------------------------------------------------------------------------- /// @mainpage コンソールサンプルプログラム /// /// @section summary 概要 /// コンソールアプリケーションのサンプルプログラムです。 /// /// @section history 履歴 /// - 2015/04/04 開発を開始しました。 /// - 2015/04/05 まだ開発中です。 /// /// @version 1.0.0 /// @date 2015/04/05 /// @author Copyright (C) 2015 hidakas1961 All rights reserved. //---------------------------------------------------------------------------- #include "main.h" #include <TDebugOut.h> #include <TLogOut.h> #include <conio.h> //---------------------------------------------------------------------------- // 名前空間使用宣言 //---------------------------------------------------------------------------- using namespace std; using namespace Common; //---------------------------------------------------------------------------- // ローカル変数 //---------------------------------------------------------------------------- static TDebugOut debugout; ///< デバッグ出力ストリーム static TLogOut logout( debugout ); ///< ログ出力ストリーム //---------------------------------------------------------------------------- // グローバル関数 //---------------------------------------------------------------------------- // メイン関数 void main() { // 共通DLLはプロジェクト設定のほうでロード済みなので(やっても良いみたいですが)不要です。 // LoadLibraryA( "Common" ); logout << "サンプルプログラムです。" << endl; logout << __FILE__ << ":" << __LINE__ << endl << endl; logout.Begin( __func__ ); int result = func( 123 ); logout.DataValue( "func()", result ); logout.End( __func__ ); cout << endl << "何か押してください。" << endl; _getch(); } // テスト関数 int func( int param ) { logout.Begin( __func__ ); int result = -param; logout.DataValue( "result", result ); logout.End( __func__ ); return result; }
うちのパソコンはCPUがセレロンでOSがビスタという超しょぼいノートパソコンなので、
C++の標準ライブラリを使おうとすると、信じられないくらい遅くなって精神的に良くないので、
今まで避けて通ってきたのですが、仕事でC++を使うことになって止むなく標準ライブラリに着手しました。
今まで画面出力関係はすべてprintf()でまかなってきたのですが、やはりcoutとかいうストリーム使うと、
いちいちcharとTCHARで関数を使い分ける必要もなくて、超便利なことに気が付いてしまいました。
そこでまずはログ出力用の出力ストリームクラスを作ってみました。
TLogOut.h
//---------------------------------------------------------------------------- /// @file TLogOut.h /// @brief ログ出力クラスヘッダ /// @details ログ出力クラスです。 //---------------------------------------------------------------------------- #pragma once #include "Common.h" #include <iostream> #include <fstream> #include <stdio.h> #include <stdarg.h> //---------------------------------------------------------------------------- // 共通ライブラリ名前空間 //---------------------------------------------------------------------------- namespace Common { //------------------------------------------------------------------------ /// @brief ログ出力クラス /// @details ログ出力クラスです。 //------------------------------------------------------------------------ class TLogOut { //-------------------------------------------------------------------- // 定数 //-------------------------------------------------------------------- private : static const int m_defIndent = 2; ///< デフォルトインデント数 //-------------------------------------------------------------------- // 動的変数 //-------------------------------------------------------------------- private : std::ofstream * m_ofs; ///< ファイル出力ストリームポインタ std::ostream * m_out; ///< 出力ストリームポインタ int m_indent; ///< インデント数 int m_block; ///< ブロック数 const char * m_header; ///< ヘッダー文字列 //-------------------------------------------------------------------- // 構築子と解体子 //-------------------------------------------------------------------- public : /// @brief デフォルト構築子 /// @param [in] stream ストリーム参照アドレス /// @param [in] indent インデント数 /// @param [in] header ヘッダー文字列 inline explicit TLogOut( std::ostream & stream = std::clog, int indent = m_defIndent, const char * header = 0 ) : m_ofs( 0 ), m_out( & stream ), m_indent( indent ), m_block( 0 ), m_header( header ) {} /// @brief ログファイル名指定構築子 /// @param [in] filename ファイル名 /// @param [in] indent インデントタブ空白数 /// @param [in] header ヘッダー文字列 inline explicit TLogOut( const char * filename, int indent = m_defIndent, const char * header = 0 ) : m_ofs( new std::ofstream( filename ) ), m_out( m_ofs ), m_indent( indent ), m_block( 0 ), m_header( header ) {} /// @brief 解体子 inline virtual ~TLogOut() { if ( m_ofs ) m_ofs->close(); } //-------------------------------------------------------------------- // 演算子オーバーロード関数 //-------------------------------------------------------------------- public : /// @brief 左シフト演算子オーバーロード関数 /// @param [in] param 引数 /// @return ログ出力ストリーム参照アドレス /// @details 引数をログストリームに出力します。 template< class T > std::ostream & operator <<( T & param ) const { return * m_out << param; } //-------------------------------------------------------------------- // 動的テンプレート関数 //-------------------------------------------------------------------- public : /// @brief データ出力関数 /// @param [in] name データ名 /// @param [in] value 値 /// @return ログ出力ストリーム参照アドレス /// @details データ名と値を出力します。 template< class T > std::ostream & DataValue( const char * name, T value ) const { // ヘッダーを出力します。 Header( m_block ); // データ名と値を出力します。 return * m_out << name << " = " << value << std::endl; } //-------------------------------------------------------------------- // 動的関数 //-------------------------------------------------------------------- public : /// @brief インデント数設定関数 /// @param [in] indent インデント数 /// @return ログ出力ストリーム参照アドレス /// @details インデント数を設定します。 inline virtual std::ostream & Indent( int indent ) { m_indent = indent; return * m_out; } /// @brief ヘッダー文字列設定関数 /// @param [in] header ヘッダー文字列 /// @return ログ出力ストリーム参照アドレス /// @details ヘッダー文字列を設定します。 inline virtual std::ostream & Header( const char * header ) { m_header = header; return * m_out; } /// @brief ヘッダー出力関数 /// @param [in] block ブロック数 /// @return ログ出力ストリーム参照アドレス /// @details ログヘッダー文字列を出力します。 inline virtual std::ostream & Header( int block ) const { // ヘッダー文字列を出力します。 if ( m_header ) * m_out << m_header; // ブロック数を巡回します。 for ( int n = 0; n < block; n++ ) { // インデント数を巡回します。 for ( int m = 0; m < m_indent; m++ ) { // 空白を出力します。 * m_out << " "; } } return * m_out; } /// @brief ブロック開始行出力関数 /// @param [in] name ブロック名 /// @return ログ出力ストリーム参照アドレス /// @details ブロック開始行を出力します。 inline virtual std::ostream & Begin( const char * name ) { // ヘッダーを出力してブロックカウンタをインクリメントします。 Header( m_block++ ); // ブロック開始文字とブロック名を出力します。 return * m_out << "{ " << name << std::endl; } /// @brief ブロック終了行出力関数 /// @param [in] name ブロック名 /// @return ログ出力ストリーム参照アドレス /// @details ブロック終了行を出力します。 inline virtual std::ostream & End( const char * name ) { // ブロックカウンタをデクリメントしてヘッダーを出力します。 Header( --m_block ); // ブロック終了文字とブロック名を出力します。 return * m_out << "} " << name << std::endl; } /// @brief 書式文字列出力関数 /// @param [in] format 書式文字列 /// @param [in] ... 可変引数 /// @return ログ出力ストリーム参照アドレス /// @details 書式付き文字列を出力します。 inline virtual std::ostream & Printf( const char * format, ... ) const { // ヘッダーを出力します。 Header( m_block ); // 展開後の文字列サイズを取得します。 va_list args; va_start( args, format ); size_t size = _vscprintf( format, args ) + 1; // 書式付き文字列をバッファーに展開します。 char * buffer = new char[ size ]; vsprintf_s( buffer, size, format, args ); // 展開された文字列を出力します。 * m_out << buffer << std::endl; // 文字列バッファーを削除します。 delete[] buffer; va_end( args ); return * m_out; } }; }
これだけだと標準ストリームとファイルストリームぐらいにしか出せないので、
どうせならいつもデバッグに使っているDebugOutputString()に出せるようにしたいと思いました。
そこでstd::ostreamクラスの派生クラスを作りました。
TDebogOut.h
//---------------------------------------------------------------------------- /// @file TDebugOut.h /// @brief デバッグ出力クラスヘッダ /// @details デバッグ出力クラスです。 //---------------------------------------------------------------------------- #pragma once #include "Common.h" #include <iostream> #include <windows.h> //---------------------------------------------------------------------------- // 共通ライブラリ名前空間 //---------------------------------------------------------------------------- namespace Common { //------------------------------------------------------------------------ /// @brief デバッグ出力クラス /// @details デバッグ出力クラスです。 //------------------------------------------------------------------------ class TDebugOut : public std::ostream { //-------------------------------------------------------------------- /// @brief ストリームバッファクラス /// @details ストリームバッファクラスです。 //-------------------------------------------------------------------- class TStreamBuf : public std::streambuf { //---------------------------------------------------------------- // 定数 //---------------------------------------------------------------- private : static const size_t m_blocksize = 256; ///< バッファーブロックサイズ //---------------------------------------------------------------- // 動的変数 //---------------------------------------------------------------- private : char * m_buffer; ///< 文字列バッファー size_t m_size; ///< バッファーサイズ size_t m_len; ///< 文字列長 //---------------------------------------------------------------- // 構築子と解体子 //---------------------------------------------------------------- public : /// @brief デフォルト構築子 inline explicit TStreamBuf() : std::streambuf(), m_buffer( 0 ), m_size( 0 ), m_len( 0 ) {} /// @brief 解体子 inline virtual ~TStreamBuf() { delete[] m_buffer; } //---------------------------------------------------------------- // 動的関数 //---------------------------------------------------------------- public : /// @brief オーバーフロー関数 /// @param [in] ch 文字コード /// @return 文字コード /// @details 1文字を書き込みます。 inline virtual int_type overflow( int_type ch = EOF ) { // EOF でないか調べます。 if ( ch != EOF ) { // バッファーサイズを調べます。 while ( m_size < m_len + 2 ) { // バッファーサイズを更新します。 if ( m_size += m_blocksize >= m_len + 2 ) { // 新規文字列バッファーを確保します。 char * buffer = new char[ m_size += m_blocksize ]; // 確保に失敗したか調べます。 if ( ! buffer ) throw( 0 ); // 現在のバッファー内容をコピーして削除します。 if ( m_buffer ) { memcpy( buffer, m_buffer, m_len + 1 ); delete[] m_buffer; } // バッファポインタを更新します。 m_buffer = buffer; break; } } // バッファーに書き込みます。 m_buffer[ m_len++ ] = ch; } return ch; } /// @brief 同期関数 /// @return 終了コード /// @details バッファー文字列を出力します。 inline virtual int sync() { // バッファー文字列をデバッグコンソールに出力します。 if ( m_buffer ) { m_buffer[ m_len ] = 0; OutputDebugStringA( m_buffer ); } // バッファーを削除します。 delete[] m_buffer; m_buffer = 0; m_size = 0; m_len = 0; return 0; } }; //-------------------------------------------------------------------- // 動的変数 //-------------------------------------------------------------------- private : std::streambuf * m_streambuf; ///< ストリームバッファー //-------------------------------------------------------------------- // 構築子と解体子 //-------------------------------------------------------------------- public : /// @brief デフォルト構築子 inline explicit TDebugOut() : std::ostream( m_streambuf = new TStreamBuf ) {} /// @brief 解体子 inline virtual ~TDebugOut() { delete m_streambuf; } }; }
はずかしい話ですが、DebugOutputStringA()の方でも漢字が出せるということを今まで知りませんでした。
もう恥ずかしくって"tchar.h"なんてインクルードできないです。
DLLで文字列定数を使うときの注意点
たぶんだけど、DLLで作ったクラス内で文字列定数をインラインで使用することはできない。
ていうかたぶん、ポインタを使うことはできないのではないのか。
それはたぶん、DLLだから実際にリンクされるまでアドレスが決定しないからなんだろね。
たとえば
class TClass { private : static const char * m_str1; const char * m_str2; public : inline TClass() : m_str2( m_str1 ) {} }; const char * TClass::m_str1 = "String";
これだとリンクエラーになるのだが、これを
class TClass { private : static const char * m_str1; const char * m_str2; public : TClass(); }; const char * TClass::m_str1 = "String"; TClass::TClass() : m_str2( m_str1 ) {}
これだとエラーにならないのだ。