WindowsプログラマのWebアプリへの挑戦日記。あとパンとか。

Archive for the ‘プログラミング’ Category

C++でバイト列をBase64や16進数表示へ変換

金曜日, 6月 27th, 2008 Posted in C++ | 2 Comments »

バイト列をBase64や16進数表示へ変換

C++でバイト列をBase64変換するには、暗号化で使用するCrypt APIが使えます。Crypt APIにCryptBinaryToStringという関数があり、これを使うとBase64変換や16進数のASCII表示が簡単にできます。じつは、自前でBase64変換とか実装した後に気がついたんですけどね。

CryptBinaryToStringを使うには、Crypt32.libをリンクしてwincrypt.hをインクルードします。引数は次のとおりです。

第1引数:const BYTE*:変換元データ
第2引数:DWORD:変換元データのサイズ
第3引数:DWORD:変換形式のフラグ
第4引数:LPTSTR:変換後データ
第5引数:DWORD*:変換後データサイズ

CryptBinaryToStringの第3引数にフラグで変換形式を指定します。フラグは次のとおり。

  1. CRYPT_STRING_BASE64HEADER
  2. CRYPT_STRING_BASE64
  3. CRYPT_STRING_BINARY
  4. CRYPT_STRING_BASE64REQUESTHEADER
  5. CRYPT_STRING_HEX
  6. CRYPT_STRING_HEXASCII
  7. CRYPT_STRING_BASE64X509CRLHEADER
  8. CRYPT_STRING_HEXADDR
  9. CRYPT_STRING_HEXASCIIADDR

Base64文字列のみが欲しい場合は「CRYPT_STRING_BASE64」を指定します。「CRYPT_STRING_BASE64HEADER」や「CRYPT_STRING_BASE64REQUESTHEADER」を指定すると、前後に文字列が付いてきます。16進数で表示する場合は、「CRYPT_STRING_HEX」を指定します。16進数とASCII文字を表示する場合は、「CRYPT_STRING_HEXASCII」。16進数とASCII文字とアドレスを表示する場合は、「CRYPT_STRING_HEXASCIIADDR」。実際に試してみるとわかるのでフラグを変えていろいろやってみてください。

第4引数にNULLを指定すると第5引数に変換後のデータサイズが返ってくるので、メモリを確保してもう一度この関数を呼びます。

表示例

CRYPT_STRING_HEXASCIIADDRを指定するとバイト列は次のような文字列へ変換されます。

  1. 0000    81 29 fd 28 07 38 c6 a1  c8 fe a6 fd ce 5a 79 eb   .).(.8.......Zy.
  2. 0010    bb 8c 3c 75 be 6c 8b 72  d1 14 d3 5e 4d 70 73 2b   ..<u.l.r...^Mps+
  3. 0020    25 5b f0 87 88 bd ec 1e  3b 06 e0 88 6d 23 60 bf   %[......;...m#`.
  4. 0030    a4 9f af 57 aa b9 ca 06                            ...W....

CRYPT_STRING_BASE64を指定するとバイト列は次のような文字列へ変換されます。

  1. gSn9KAc4xqHI/qb9zlp567uMPHW+bIty0RTTXk1wcyslW/CHiL3sHjsG4IhtI2C/pJ+vV6q5ygY=

Base64文字列や16進数表示をバイト列に変換

Base64文字列や16進数表示からバイト列に変換する場合は、CryptStringToBinaryを使います。第5引数まではCryptBinaryToStringと同じです。第3引数にはCryptBinaryToStringのフラグに加えて、次のフラグが指定できます。

  1. CRYPT_STRING_BASE64_ANY
  2. CRYPT_STRING_ANY
  3. CRYPT_STRING_HEX_ANY

第6引数:DWORD*:文字列で実際にBase64文字列や16進数文字列が始まるまでの文字数を返します。NULLも指定できます。
第7引数:DWORD*:実際に変換に使われた形式のフラグが返ります。NULLも指定できます。

第7引数の使い道としては、変換しようとしている文字列が、次のうちどれで変換されたものかわからない場合に、

  1. CRYPT_STRING_HEXADDR
  2. CRYPT_STRING_HEXASCIIADDR
  3. CRYPT_STRING_HEX
  4. CRYPT_STRING_HEXRAW
  5. CRYPT_STRING_HEXASCII

第3引数にCRYPT_STRING_HEX_ANYをしていすると、正しい変換形式が自動で選択されて、第7引数に選択された変換形式が返ってくるという仕組みです。

詳しくは、CryptStringToBinary Function (Windows)を熟読してください。

実装例

では、例として次の4つの変換についてC++のコードを載せておきます。

  • バイト列を16進数のASCII表示に変換する
    1. DWORD dwDst = 0;
    2. LPTSTR pstrDst = NULL;
    3. if( CryptBinaryToString( bSrc, nLen, CRYPT_STRING_HEXASCIIADDR, NULL, &dwDst ) ){
    4.        pstrDst = new TCHAR[ dwDst + 1 ];
    5.        if( CryptBinaryToString( bSrc, nLen, CRYPT_STRING_HEXASCIIADDR, pstrDst, &dwDst ) ){
    6.                //pstrDst:バイト列の16進数表示文字列
    7.        }
    8. }
    9. delete pstrDst;
  • 16進数のASCII表示をバイト列に変換する

    1. DWORD dwDst = 0;
    2. BYTE* pbDist = NULL;
    3. if( CryptStringToBinary( bSrc, nLen, CRYPT_STRING_HEXASCIIADDR, NULL, &dwDst, NULL, NULL ) ){
    4.        pbDist = new BYTE[ dwDst ];
    5.        if( CryptStringToBinary( bSrc, nLen, CRYPT_STRING_HEXASCIIADDR, pbDist, &dwDst, NULL, NULL ) ){
    6.                //pbDist:バイト列
    7.        }
    8. }
    9. delete pbDist;
  • バイト列をBase64文字列に変換する
    1. DWORD dwDst = 0;
    2. LPTSTR pstrDst = NULL;
    3. if( CryptBinaryToString( bSrc, nLen, CRYPT_STRING_BASE64, NULL, &dwDst ) ){
    4.        pstrDst = new TCHAR[ dwDst + 1 ];
    5.        if( CryptBinaryToString( bSrc, nLen, CRYPT_STRING_BASE64, pstrDst, &dwDst ) ){
    6.                //pstrDst:Base64文字列
    7.        }
    8. }
    9. delete pstrDst;
  • Base64文字列をバイト列に変換する
    1. DWORD dwDst = 0;
    2. BYTE* pbDist = NULL;
    3. if( CryptStringToBinary( bSrc, nLen, CRYPT_STRING_BASE64, NULL, &dwDst, NULL, NULL ) ){
    4.        pbDist = new BYTE[ dwDst ];
    5.        if( CryptStringToBinary( bSrc, nLen, CRYPT_STRING_BASE64, pbDist, &dwDst, NULL, NULL ) ){
    6.                //pbDist:バイト列
    7.        }
    8. }
    9. delete pbDist;

C++でUTF-8をShift-JISに変換する

水曜日, 6月 25th, 2008 Posted in C++ | 1 Comment »

前回、Shift-JISをUTF-8に変換する方法を書きました。今回は逆のUTF-8をShift-JISに変換する方法です。

この変換も、

UTF-8→UTF-16→Shift-JIS

の順番で変換する必要があります。

以下が変換のコードです。エラー処理は省略しています。

  1. BOOL ConvUtf8toSJis( BYTE* pSource, BYTE* pDist, int* pSize )
  2. {
  3.    *pSize = 0;
  4.  
  5.    //UTF-8からUTF-16へ変換
  6.    const int nSize = ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pSource, -1, NULL, 0 );
  7.  
  8.    BYTE* buffUtf16 = new BYTE[ nSize * 2 + 2 ];
  9.    ::MultiByteToWideChar( CP_UTF8, 0, (LPCSTR)pSource, -1, (LPWSTR)buffUtf16, nSize );
  10.  
  11.    //UTF-16からShift-JISへ変換
  12.    const int nSizeSJis = ::WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)buffUtf16, -1, NULL, 0, NULL, NULL );
  13.    if( !pDist ){
  14.        *pSize = nSizeSJis;
  15.        delete buffUtf16;
  16.        return TRUE;
  17.    }
  18.  
  19.    BYTE* buffSJis = new BYTE[ nSizeSJis * 2 ];
  20.    ZeroMemory( buffSJis, nSizeSJis * 2 );
  21.    ::WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)buffUtf16, -1, (LPSTR)buffSJis, nSizeSJis, NULL, NULL );
  22.  
  23.    *pSize = lstrlen( (char*)buffSJis );
  24.    memcpy( pDist, buffSJis, *pSize );
  25.  
  26.    delete buffUtf16;
  27.    delete buffSJis;
  28.  
  29.    return TRUE;
  30. }

この関数の使用方法は、まずはじめにpDistにNULLを渡して変換後のサイズを取得します。変換後のサイズでpDistのメモリを確保したあと、もう一度この関数を呼びます。

次のような感じ。

  1. int nSize = 0;
  2. ConvSJisToUtf8( pSource, NULL, &nSize );
  3. BYTE* pDist = new BYTE[ nSize + 1 ];
  4. ZeroMemory( pDist, nSize + 1 );
  5. ConvSJisToUtf8( pSource, pDist, &nSize );

次回はBase64変換について書きます。

C++でShift-JISをUTF-8に変換する

水曜日, 6月 11th, 2008 Posted in C++ | 1 Comment »

Windowsアプリケーションでもネットワークにつながって、サーバアプリケーションと連携することが多くなってきてますねぇ。ネットワークといえば文字コードはUTF-8なイメージがあるワタクシですが、C++ではShift-JISなアプリばっかり作ってきたので、UTF-8に変換するにはどうしたらいいの?ってことでまとめます。

C++でShift-JISをUTF-8に変換するには、

Shift-JIS→UTF-16→UTF-8

の順番で変換する必要があります。

そもそも、UTF-16とUTF-8はどう違うのか。UTF-16は文字に割り当てられる番号(コードポイント)が2バイトで格納されます。一方UTF-8は、0~127のコードポイントは1バイトで格納され、128から上のコードポイントは、2~6バイトで格納されます。0~127のコードポイントでは、ASCIIと同じ文字が定義されているので、英語のテキストを扱うときには、ASCIIとUTF-8でまったく同じになります。

このあたりのことは、「Joel on Software」にわかりやすく解説してあります。

「Joel on Software」には、Unicodeに関する項目を「すべてのソフトウェア
開発者が絶対確実に知っていなければならないUnicodeとキャラクタセットに
関する最低限のこと(言い訳なし!)」として解説してあり、この項目を読むだけでもこの本を買う価値はあると思います。もちろん他の項目もすごくためになります。なかりおすすめ。

実際の変換はコードを見ればわかると思います。エラー処理は省略しています。

  1. BOOL ConvSJistoUtf8( BYTE* pSource, BYTE* pDist, int* pSize )
  2. {
  3.    *pSize = 0;
  4.  
  5.    //ShiftJISからUTF-16へ変換
  6.    const int nSize = ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)
  7. pSource, -1, NULL, 0 );
  8.  
  9.    BYTE* buffUtf16 = new BYTE[ nSize * 2 + 2 ];
  10.    ::MultiByteToWideChar( CP_ACP, 0, (LPCSTR)pSource, -1, (LPWSTR)
  11. buffUtf16, nSize );
  12.  
  13.    //UTF-16からShift-JISへ変換
  14.    const int nSizeUtf8 = ::WideCharToMultiByte( CP_UTF8, 0, (LPCWSTR)
  15. buffUtf16, -1, NULL, 0, NULL, NULL );
  16.    if( !pDist ){
  17.        *pSize = nSizeUtf8;
  18.        delete buffUtf16;
  19.        return TRUE;
  20.    }
  21.  
  22.    BYTE* buffUtf8 = new BYTE[ nSizeUtf8 * 2 ];
  23.    ZeroMemory( buffUtf8, nSizeUtf8 * 2 );
  24.    ::WideCharToMultiByte( CP_UTF8, 0, (LPCWSTR)buffUtf16, -1, (LPSTR)
  25. buffUtf8, nSizeUtf8, NULL, NULL );
  26.  
  27.    *pSize = lstrlen( (char*)buffUtf8 );
  28.    memcpy( pDist, buffUtf8, *pSize );
  29.  
  30.    delete buffUtf16;
  31.    delete buffUtf8;
  32.  
  33.    return TRUE;
  34. }

この関数の使用方法は、まずはじめにpDistにNULLを渡して変換後のサイズを
取得します。変換後のサイズでpDistのメモリを確保したあと、もう一度この関数を呼びます。

次のような感じ。

  1. int nSize = 0;
  2. ConvSJisToUtf8( pSource, NULL, &nSize );
  3. BYTE* pDist = new BYTE[ nSize + 1 ];
  4. ZeroMemory( pDist, nSize + 1 );
  5. ConvSJisToUtf8( pSource, pDist, &nSize );

次回はUFT-8をShift-JISに変換する方法です。
今回の逆なだけです。

AGAG – Amazonアソシエイトサポートツール リリース

日曜日, 6月 8th, 2008 Posted in AGAG, cakePHP, PHP | No Comments »

Amazonアソシエイト・プログラム(アフィリエイト)の利用をサポートするAGAGというサービスを開始しました。

AGAG – Amazonアソシエイトサポートツール
http://www.agag.its55.com/

AGAG

Amazonアソシエイト・プログラムを使用して雑誌などを紹介しようとすると、雑誌が発売されるたびにリンクを変更しなくてはいけなくて、とても面倒でした。そこで、キーワードを登録しておくと自動で最新の商品を表示するサービスを作ってみました。

商品を検索するキーワードを登録しておくと、AGAGの自動サーチ機能が毎日自動でAmazonに検索をかけて、その検索結果を新しい商品から指定個数分表示します。AGAGが発行するコードをブログなどに貼っておくと、勝手にブログに表示される商品が新しいものに変わります。

このサービスを利用するには、ユーザ登録が必要です。

このサービスはCakePHPを使って作りました。初めてCakePHPで作ったサービスで、CakePHPってどんな感じかなというのを探るために作りました。

作ってみた感想ですが、CakePHPいい!

それほど難しいことはしてなくて、CakePHPができる範囲内で作ればかなり楽です。まあセッション周りとかちょこちょこ思い通りにならないこともあったけど、それを妥協しても作りやすさを選んだほうがプライベートで作るにはいいのかなと思ってます。

AGAGに関しての質問や要望などありましたら、このブログにてご連絡ください。

AGAGは今後機能追加などしていく予定です。

coreserverにgo-pearをインストールする

月曜日, 5月 26th, 2008 Posted in PHP, サーバー | 2 Comments »

coreserverにPEARの管理ツールgo-pearをインストールしてみました。手順は次のとおり。

/public_html以下のブラウザからアクセスできるところにgo-pearのファイルをコピーするディレクトリを作成します。テキトウに。PEARをインストールするディレクトリはブラウザからアクセスできないところにしたほうがいいでしょう。今回は、次のディレクトリにインストールしました。

  1. /virtual/xxx/lib/go_pear/

パーミッションは777としときます。

go-pearはhttp://pear.php.net/go-pearをすべてコピー&ペーストで「go-pear.php」というファイル名でローカルに保存した後サーバへアップロードしました。

ブラウザからgo-pear.phpにアクセスします。「Welcome to go-pear 1.1.1!」のページでは、Next>>をクリックします。

Configurationの「1. Installation prefix ($prefix)」にインストール先のディレクトリを指定します。

  1. /virtual/xxx/lib/go_pear/

「10. Filename of WebFrontend」にブラウザからアクセスできるディレクトリを指定します。Installボタンをクリックするとインストールが開始し、しばらくたつとインストール完了の画面に移行します。

coreserverではPHPはデフォルトでSAFEMODEで動いています。PEARの管理画面はSAFEMODEだと使えないみたいなので、CGIで動かすように設定する必要があります。CGIで動かすには、PHPをCGIとして動かす方法について – CORESERVER.JP:コアサーバーに書いてあるように以下の内容を.htaccessとしてファイルに保存しサーバの「10. Filename of WebFrontend」で指定したディレクトリへアップロードします。

  1. AddHandler application/x-httpd-phpcgi .php

PEARの管理画面には認証をかけたほうがいいので、Basic認証の設定をしときます。「10. Filename of WebFrontend」で指定したディレクトリの.htaccessに以下を付け加えます。

  1. AuthType Basic
  2. AuthName "Input your ID and Password."
  3. AuthUserFile /virtual/xxx/.htpassword
  4. require valid-user

coreserverの管理画面で「ツール」から.htpasswordに記述する文字列を生成し、その文字列を.htpasswordにコピー&ペーストし、AuthUserFileで指定したディレクトリに保存します。

PEARを使う場合は、PHPのインクルードパスにPEARのパスを追加します。たとえばPHPのコードでインクルードパスを追加する場合は次のように実装します。

  1. $path = "/virtual/xxx/lib/go_pear/PEAR/";
  2. $inc_path = get_include_path();
  3. set_include_path(get_include_path() . PATH_SEPARATOR . $path);

ユニットテストの実践: CppUnitの導入(実装編)

木曜日, 5月 1st, 2008 Posted in C++, CppUnitの導入 | 1 Comment »

前回、CppUnitを導入しました。今回は実装編です。

本来はテストファーストで、CppUnitでテストケースを実装したあとにメインの実装を行うものなんでしょうけど、今回はすでに実装が終わったプロジェクトのユニットテストを行いました。今回作成したテストアプリはGUI版です。

まずはVCでプロジェクトを新規作成します。MFC AppWizard(exe)でダイアログベースで作成します。

[プロジェクト]-[設定]から[プロジェクトの設定]ダイアログを起動し、[C/C++]タブのカテゴリ[コード生成]で使用するランタイム ライブラリで[マルチスレッド(DLL、デバッグ)]を選択します。

同じく[C/C++]タブのカテゴリ[プロセッサ]インクルードファイルのパスに、メインのプロジェクトのパスを入力します。

[リンク]タブのカテゴリ[一般]オブジェクト・ライブラリ モジュールに、設定の対象がDebugの場合、

  1. cppunitd.lib testrunnerd.lib

Releaseの場合、

  1. cppunit.lib testrunner.lib

と入力します。

ここで、メインアプリのプロジェクトをワークスペースに追加しときます。そして[プロジェクト]-[依存関係]で、テストプロジェクトがメインプロジェクトに依存するように設定します。

テストアプリのStdAfx.hに次のincludeを記載します。

  1. #include <cppunit/ui/mfc/TestRunner.h>
  2. #include <cppunit/ui/text/TestRunner.h>
  3. #include <cppunit/extensions/TestFactoryRegistry.h>
  4. #include <cppunit/extensions/HelperMacros.h>
  5. #include <cppunit/CompilerOutputter.h>

C++アプリケーションの効率的なテスト手法(CppUnit編)なんかには、StdAfx.hは削除するとの記載がありますが、ボクの環境では削除しなくても問題ないです。てか削除するとダイアログベースのアプリなんでビルドできませんので。コンソールアプリの場合は必要ないでしょうね。

テストアプリのアプリクラス(CWinAppを継承しているクラス)のInitInstance()を次のように書き換えます。

  1. BOOL CTestApp::InitInstance()
  2. {
  3.     AfxEnableControlContainer();
  4.  
  5. #ifdef _AFXDLL
  6.     Enable3dControls();         // 共有 DLL 内で MFC を使う場合はここをコールしてください。
  7. #else
  8.     Enable3dControlsStatic();   // MFC と静的にリンクする場合はここをコールしてください。
  9. #endif
  10.  
  11.     CPPUNIT_NS::MfcUi::TestRunner runner;
  12.     runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() );
  13.     runner.run();
  14.  
  15.     return FALSE;
  16. }

メインプロジェクトのテスト対象となるクラスと対になるテスト用クラスを作成するのが流儀のようです。次のようなクラスを作成します。

  1. class CMainClass;
  2. class CTestClass : public CPPUNIT_NS::TestFixture  
  3. {
  4.     CPPUNIT_TEST_SUITE( CTestClass );
  5.     CPPUNIT_TEST( testFunc1 );
  6.     CPPUNIT_TEST( testFunc2 );
  7.     CPPUNIT_TEST_SUITE_END();
  8.  
  9. public:
  10.     CTestClass();
  11.     virtual ~CTestClass();
  12.  
  13.     void setUp();
  14.     void tearDown();
  15.  
  16.     void testFunc1();
  17.     void testFunc2();
  18.  
  19. private:
  20.     CMainClass* m_pApp;
  21. };

CPPUNIT_TEST_SUITEマクロとCPPUNIT_TEST_SUITE_ENDマクロの間にCPPUNIT_TESTマクロで、テスト対象となるテスト用の関数を登録します。setUp()はテスト用関数が実行される直前に呼ばれ、tearDown()はテスト終わったあとに呼ばれます。たとえばsetUp()でCMainClass* m_pAppをnewして、tearDown()でdeleteするような使い方になります。

では実装の方は、次のような感じ。

  1. CPPUNIT_TEST_SUITE_REGISTRATION( CTestClass );
  1. void CTestClass::setUp()
  2. {
  3.     m_pApp = new CMainClass();
  4. }
  5.  
  6. void CTestClass::tearDown()
  7. {
  8.     delete m_pApp;
  9. }
  10.  
  11. void CTestClass::testFunc1()
  12. {
  13.     CPPUNIT_ASSERT_EQUAL( TRUE, m_pApp->Func1( 0 ) );
  14.     CPPUNIT_ASSERT_EQUAL( FALSE, m_pApp->Func1( -1 ) );
  15. }
  16.  
  17. void CTestClass::testFunc2()
  18. {
  19.     CPPUNIT_ASSERT( m_pApp->Func2( 0 ) );
  20.     CPPUNIT_ASSERT( m_pApp->Func2( -1 ) );
  21. }

CPPUNIT_ASSERT_EQUALは、第一引数と第二引数が等しい場合にテスト成功。違ったらテスト失敗となります。CPPUNIT_ASSERTは、引数がTRUEだったらテスト成功。FALSEだったらテスト失敗となります。ほかにも、

  1. CPPUNIT_ASSERT
  2. CPPUNIT_ASSERT_MESSAGE
  3. CPPUNIT_FAIL
  4. CPPUNIT_ASSERT_EQUAL_MESSAGE
  5. CPPUNIT_ASSERT_DOUBLES_EQUAL

などあります。ここでは詳しくは省略。

さて、ここでビルドするとリンクエラーが発生します。メインプロジェクトのCMainClass::Func1()やCMainClass::Func2()にリンクできないと。ここでしばらく悩みましたが、どうやらメインプロジェクトのCMainClassのCPPファイルを、テストプロジェクトに追加しないといけないようです。なんかこれ気持ち悪いんですけど。しょうがないんですかね。

これでちゃんとビルドが通ります。

実行するとダイアログが起動され、Browseからテストケースが確認できます。通常はALL Testsを選んでおいて、Autorun at startupにチェックを入れておけば、起動と同時にすべてのテストを実行してくれるので楽です。

【関連エントリ】
ユニットテストの実践: CppUnitの導入(インストール編)

ユニットテストの実践: CppUnitの導入(インストール編)

金曜日, 4月 25th, 2008 Posted in C++, CppUnitの導入 | 3 Comments »

恥ずかしながら今までユニットテストツールを使ったことがなかったんですが、soraさんおすすめの、アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣を読んだら、いても立ってもいられなくなり、会社の環境にCppUnitを導入しました。

CppUnitとは、C++のユニットテストを自動化してくれるステキなツールです。

導入編

導入に関しては、それほど難しくないです。ボクが今回入れた環境は、Visual C++ 6.0です。手順としては以下の通り。

CppUnitをダウンロード。実は去年の夏くらいにダウンロードしてましたが、当時は導入をあきらめてました。今回はこのときにダウンロードしたCppUnitを使いました。(cppunit-1.12.0.tar.gz)

ダウンロードしたファイルを解凍します。解凍先を\CppUnit\とします。

次のプロジェクトをVC6で開いてビルドします。

  1. \CppUnit\examples\msvc6\CppUnitTestApp\CppUnitTestApp.dsp

\CppUnit\lib以下にいろいろなDLLやらLIBやらが作成されます。ちなみにCppUnitTestAppはCppUnit自体のユニットテストです。実行するとGUIが表示されるのでいろいろいじってみましょう。

VCのメニュー[ツール]-[オプション]からオプションダイアログを開き、[ディレクトリ]タブの表示するディレクトリ[インクルードファイル]に

  1. \CppUnit\include

を、表示するディレクトリ[ライブラリファイル]に、

  1. \CppUnit\lib

を追加します。ここに設定したディレクトリはすべてのプロジェクトに適応されます。プロジェクトごとに設定したい場合は[プロジェクトの設定]ダイアログでやってください。また、次のファイルを\windows\system32の中へコピーしてください。

  1. testrunnerd.dll
  2. testrunner.dll

次回は実践編です。

【関連エントリ】
ユニットテストの実践: CppUnitの導入(実装編)

日付データをCOleDateTimeに変換する

水曜日, 4月 16th, 2008 Posted in C++ | 2 Comments »

iTunesのライブラリファイル(iTunes Music Library.xml)を解析してゴニョゴニョしたいなぁと思って、VC6で解析ツールを作っています。

その中で、[2008-04-14T08:49:55Z]というgmtの書式で表される日時同士を比較したいがために、COleDateTimeに変換する関数を作りました。

値のチェックとかは省略してますよ。
言語はC++です。

  1. BOOL ConvStrToDate( LPTSTR pstrDate, COleDateTime& dateTime )
  2. {
  3.     if( !pstrDate ){
  4.         return FALSE;
  5.     }
  6.  
  7.     char sep[] = "-:TZ";
  8.     char* token = NULL;
  9.    
  10.     std::vector<std::string> vDateString;
  11.     token = strtok( pstrDate, sep );
  12.     vDateString.push_back( token );
  13.     while( token ){
  14.         token = strtok( NULL, sep );
  15.         if( token ){
  16.             vDateString.push_back( token );
  17.         }
  18.     }
  19.  
  20.     if( !vDateString.size() ){
  21.         return FALSE;
  22.     }
  23.  
  24.     char *stopstring;
  25.  
  26.     const int year = (int)strtol( vDateString[ 0 ].c_str(), &stopstring, 10 );
  27.     const int month = (int)strtol( vDateString[ 1 ].c_str(), &stopstring, 10 );
  28.     const int day = (int)strtol( vDateString[ 2 ].c_str(), &stopstring, 10 );
  29.     const int hour = (int)strtol( vDateString[ 3 ].c_str(), &stopstring, 10 );
  30.     const int min = (int)strtol( vDateString[ 4 ].c_str(), &stopstring, 10 );
  31.     const int sec = (int)strtol( vDateString[ 5 ].c_str(), &stopstring, 10 );
  32.  
  33.     dateTime.SetDateTime( year, month, day, hour, min, sec );
  34.  
  35.     return TRUE;
  36. }

なんかまずいところがあればご指摘お願いシマス!

Basic認証ではまる

月曜日, 3月 31st, 2008 Posted in サーバー | No Comments »

恥ずかしながらApacheのBasic認証をやろうとしてはまりまくったのでエントリ。
サーバーはVineLinux4.2。
Apacheは元々入っていたヤツ。

httpd.confでBasic認証の設定をし、ページにアクセスしようとすると
認証ダイアログは表示されるけど認証に失敗する現象があった。

  1. <directory>
  2. Order Allow,Deny
  3. Allow from all
  4. AuthType Basic
  5. AuthName "test"
  6. AuthUserFile /var/www/password/.htpasswd
  7. Require valid-user
  8. </directory>

AuthUserFileには、/home/xxxなど以下のファイルを指定すると権限がないと怒られる。

怒られるのもエラーログを見ないと怒られたことに気づかない。

  1. /etc/apache2/logs/error_log

パスワードファイルをおくのは、ユーザapacheが参照できるところじゃないといけない。

cakePHPのセッションでハマる

金曜日, 3月 21st, 2008 Posted in cakePHP, PHP | No Comments »

cakePHPを使って簡単なサイトを作っているんですが、セッションまわりで謎が多いのでちょっとまとめます。(自分用)

まず、セッションに入れたはずなのに画面遷移するとデータがなくなっている現象から。

HogeControllerのDoSomethingというアクションで、次のようにセッションにデータを入れます。

  1. $this->Session->write( 'search_category', $category );
  2. $this->Session->write( 'search_word', $word );

そしてDoSomethingのviewでは、DoOtherへリンクしているとします。

アクションDoOtherでセッションを確認すると、次のようになっていました。

  1. [search_category]=;
  2. [search_word]=;

DoSomethingで入れた値がDoOtherでは消えています。しかしキーは残っています。

cakePHPのフォーラムで次のようなスレッドを発見。
“cake1.1.18にversionアップしたらSessionが使えない?” フォーラム – CakePHP Users in Japan

どうやらCAKE_SECURITYがhightだとセッション開始のたびに

  1. $this->renew();

が呼ばれ

  1. session_regenerate_id()

されてしまうようです。
なのでとりあえずCAKE_SECURITYをmediumに変更。
んが、これでも問題は解決せず。

んで、同じくcakePHPフォーラムで次のようなスレッドを発見。
“セッションまわりの挙動がおかしい?時” フォーラム – CakePHP Users in Japan

viewで使っている画像のパスがrelative(相対パス?)だったら、アクションが複数回呼ばれることがあるらしい。

開発中のサイトでは、コントローラの$layoutは\cake\libs\view\templates\layouts\default.thtmlを\app\views\layouts\default.thmlにコピーしてレイアウトに関する部分だけ変更して使っていたので、

  1. <link rel="shortcut icon" href="<?php echo $this->webroot . 'favicon.ico'; ?>" type="image/x-icon" />

の部分を削除するとDoOtherでもセッションから正しいデータを取得することができました。

もう一つセッションに関する問題があるんですが、長くなりそうなので別記事で。