syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

ATL::CPathユーティリティ

(これは2010-10-31に書いた故OCNブログの記事を移植したものです)

Visual C++ 付属の ATL にはわりと便利なユーティリティ クラスがあるんですが、あまり知られてないのでひとつ紹介します。

ATL::CPath は Windows Shell API の Path*関数を薄くラップしたクラスなんですが、ATL::CString とともに Windows アプリでは重宝するかと。
atlpath.h をインクルードすれば、非 ATL/MFC プロジェクトであっても使えるようになります。

#include <atlpath.h>

void AtlPathTest()
{
    const LPCWSTR pAnswerStr[] = { L"No", L"Yes" };
    {
        const CPathW paths[] = {
            CPathW(L"C:\\test_file.txt"),
            CPathW(L"test_file.txt"),
            CPathW(L".\\test_file.txt"),
            CPathW(L"..\\test_file.txt"),
            CPathW(L"\\\\127.0.0.1\\shared\\test_file.txt"),
        };

        for (int i = 0; i < sizeof(paths) / sizeof(*paths); ++i)
        {
            wprintf(L"'%s' is relative path ? %s\n", static_cast<LPCWSTR>(paths[i]), pAnswerStr[paths[i].IsRelative()]);
        }
        // --> N, Y, Y, Y, N
        puts("");
    }

    {
        // '\\' の代わりに '/' を使うと、意図した結果にならない。
        const LPCWSTR srcPath = L"C:\\directory\\test_file.txt";
        CPathW path = srcPath;
        wprintf(L"RemoveFileSpec('%s') = \n", srcPath);
        path.RemoveFileSpec();
        wprintf(L"'%s'\n\n", static_cast<LPCWSTR>(path)); // 'C:\directory'
    }

    {
        const LPCTSTR srcPath = _T("C:\\directory\\test_file.txt");
        CPath path = srcPath;
        _tprintf(_T("StripPath('%s') = \n"), srcPath);
        path.StripPath();
        _tprintf(_T("'%s'\n\n"), static_cast<LPCTSTR>(path)); // 'test_file.txt'

        path = srcPath;
        _tprintf(_T("RemoveExtension('%s') = \n"), srcPath);
        path.RemoveExtension();
        _tprintf(_T("'%s'\n\n"), static_cast<LPCTSTR>(path)); // 'C:\directory\test_file'
    }

    {
        const LPCSTR path1 = "C:\\directory";
        const LPCSTR path2 = "test_file.txt";
        CPathA path = path1;
        path += path2;
        printf("'%s' + '%s' = \n", path1, path2);
        printf("'%s'\n\n", static_cast<LPCSTR>(path)); // 'C:\directory\test_file.txt'
    }
}

CString は C++ 標準の std::string や std::wstring に比べて、速度面やメモリ効率面においてかなり最適化されているし、ANSIUnicode 変換も簡単にできて、COM 用の _bstr_t との相互変換もサポートされているなど、VBDelphi の文字列に引けを取らない機能を持っているので、ATL/MFC 開発ではまず CString をいかに使いこなせるか、が重要になってきます。ちなみに MFC と ATL は有償ライブラリなので、無償版(Express エディション)の Visual C++ では使えません*1。また、Windows 以外の OS や Visual C++ 以外のコンパイラーへの移植性を考えると、標準 C/C++ ライブラリの使い方も習得しておいたほうがよいでしょう。なお、Unicode 版の CStringW に関しては Windows ストア アプリ開発でも使えますが、MBCS 版の CStringA はデスクトップ アプリ専用です。

あと実際のデバッグで役に立つ機能といえば、ATLTRACE() と ATLASSERT() マクロです。MFC には同等機能として TRACE() と ASSERT() というマクロが存在しますが、ATL 版は非 MFC プロジェクトでも使えるので便利です。ちなみに標準 C ライブラリの assert() マクロは、MSVC 実装ではアサーション失敗時のプログラム停止位置が DebugBreak() 呼び出しまでさかのぼってしまうため使いづらいので、可能であれば ATL 版や MFC 版のアサート、もしくは CRT 版の _ASSERTE() を使うと楽になります*2

*1:Visual Studio 2013 以降は小規模チームや個人開発に限り無償で利用可能な Community エディションが用意されていて、Community エディションでは Professional エディション以上と同様に ATL/MFC を使うことができます。

*2:Visual Studio 2013 では改善されていて、assert() マクロでもアサーションが失敗した位置で停止するようになっています。