エクスプローラの右クリックで出て来る「送る」メニューの拡張ツール ShoSendTo


クラス図


//////////////////////////////////////////////////////////////////////////////

#define _WIN32_WINNT 0x0500

#include <windows.h>
#include <tchar.h>
#include <cstdio>

// Windows NT 4.0,Windows 95,Internet Explorer 4.0 以降
#include <shlobj.h>   // for SHGetSpecialFolderPath, IShellLink
#include <imagehlp.h> // for MakeSureDirectoryPathExists
#include <intshcut.h> // for IUniformResourceLocator

// Windows 2000,Windows Me 以降
#include <msi.h>        // for MsiGetShortcutTarget, MsiGetComponentPath, MAX_FEATURE_CHARS

#ifdef _DEBUG
#include <crtdbg.h>   // for _RPT0
#include <cassert>
#endif  //  _DEBUG

#pragma comment(lib, "shell32.lib" ) // for SHGetSpecialFolderPath
#pragma comment(lib, "imagehlp.lib") // for MakeSureDirectoryPathExists
#pragma comment(lib, "msi.lib"     ) // for MsiGetShortcutTarget, MsiGetComponentPath

/////////////////////////////////// マクロ ///////////////////////////////////

// 最大文字列長
#define LARGE_NUMBER                    4096
#define MAX_TEXT_LENGTH                 (_MAX_PATH + LARGE_NUMBER)
// アプリケーション名
#define APPLICATION_TEXT                "ShoSendTo"
// 作成者名
#define COMPANY_TEXT                    "Sho's Software"
// 著作権表示用年
#define COPYRIGHT_YEAR_TEXT             "2003-2009"
// ヴァージョン
#define VERSION_TEXT                    "1.16"
// ショートカットの拡張子
#define SHORTCUT_EXTENSION_TEXT         "lnk"
// インターネット ショートカットの拡張子
#define INTERNETSHORTCUT_EXTENSION_TEXT "url"
// 「送る」追加時に付加するラベル
#define SHORTCUT_LABEL_TEXT             "["APPLICATION_TEXT"]"

//////////////////////////////////////////////////////////////////////////////

// ユーティリティ
namespace Utility {

// デバッグ クラス
class Debug
{
public:
   // デバッグ時のトレース
   static void Trace(LPCTSTR messageText, ...)
   {
#ifdef _DEBUG
       const size_t    maxTextLength(MAX_TEXT_LENGTH);
       va_list         argumentList;
       va_start(argumentList, messageText);
       TCHAR           buffer[maxTextLength];
       ::vsprintf(buffer, messageText, argumentList);
       Output(buffer);
#else   //  _DEBUG
       messageText;
#endif  //  _DEBUG
   }

   // デバッグ出力
   // Just-In-Time (JIT) デバッグが有効な場合はデバッガに文字列を出力
   // (Microsoft Visual C++ 依存)
   static void Output(LPCTSTR messageText /* デバッグ出力する文字 */)
   {
#ifdef _DEBUG
       _RPT0(_CRT_WARN, messageText);
       ::fputs(messageText, stderr);
#else   //  _DEBUG
       messageText;
#endif  //  _DEBUG
   }

   // アサート
   static void Assert(bool value)
   {
#ifdef _DEBUG
       assert(value);
#else   //  _DEBUG
       value;
#endif  //  _DEBUG
   }
};

// コンソール クラス
class Console
{
public:
   // コンソール出力
   static void Output(LPCTSTR messageText)
   {
#ifdef  _CONSOLE
       ::fputs(messageText, stdout);
#else   //  _CONSOLE
       ::MessageBox(::GetDesktopWindow(), messageText, _T(APPLICATION_TEXT" ヘルプ"), MB_OK | MB_ICONINFORMATION);
#endif  //  _CONSOLE
   }
};

// ファイル クラス
class File
{
public:
   // ディレクトリの作成
   //   フルパスで指定したディレクトリを作成
   //   指定された各ディレクトリがまだ存在しない場合,それらのディレクトリを順に作成
   //   (Windows 95,NT3.1 以降)
   static bool CreateDirectory(LPTSTR directoryName)
   {
       return !!::MakeSureDirectoryPathExists(directoryName);
   }

   // ディレクトリの準備
   static bool PrepareDirectory(LPTSTR directoryName)
   {
       RegulateDirectoryName(directoryName);
       const DWORD attribute(::GetFileAttributes(directoryName));
       return (attribute == DWORD(-1)) ? CreateDirectory(directoryName)
                                       : IsDirectory(attribute);
   }

   // ディレクトリへのファイルのコピー
   static bool CopyToDirectory(LPCTSTR sourcePathName, LPTSTR destinationDirectoryName, LPTSTR destinationPathName)
   {
       RegulateDirectoryName(destinationDirectoryName);
       TCHAR   fileName      [_MAX_FNAME];
       TCHAR   extensionName [_MAX_EXT  ];
       ::_tsplitpath(sourcePathName, NULL, NULL, fileName, extensionName);

       ::_tcscpy(destinationPathName, destinationDirectoryName);
       ::_tcscat(destinationPathName, fileName                );
       ::_tcscat(destinationPathName, extensionName           );

       return !!::CopyFile(sourcePathName, destinationPathName, false);
   }

   // ディレクトリへのファイルのコピー
   static bool CopyToDirectory(LPCTSTR sourcePathName, LPTSTR destinationDirectoryName, LPCTSTR destinationFileName, LPTSTR destinationPathName)
   {
       RegulateDirectoryName(destinationDirectoryName);

       TCHAR   fileName      [_MAX_FNAME];
       TCHAR   extensionName [_MAX_EXT  ];
       ::_tsplitpath(destinationFileName, NULL, NULL, fileName, extensionName);

       ::_tcscpy(destinationPathName, destinationDirectoryName);
       ::_tcscat(destinationPathName, fileName                );
       ::_tcscat(destinationPathName, extensionName           );

       return !!::CopyFile(sourcePathName, destinationPathName, false);
   }

   // ディレクトリ名の正規化
   static void RegulateDirectoryName(LPTSTR directoryName)
   {
       const size_t directoryNameTextLength(::_tcslen(directoryName));
       if (directoryName[directoryNameTextLength - 1] != _T('\\')) {
           directoryName[directoryNameTextLength    ] = _T('\\');
           directoryName[directoryNameTextLength + 1] = _T('\0');
       }
   }

   // ディレクトリ名とファイル名を連結してパス名へ
   static void MakePathName(LPTSTR pathName, LPCTSTR directoryName, LPCTSTR fileName)
   {
       ::_tcscpy(pathName, directoryName);
       RegulateDirectoryName(pathName);
       ::_tcscat(pathName, fileName);
   }

   // 現プロセスの実行ファイルのパス名の取得
   static bool GetCurrentProcessFileName(LPTSTR pathName /* パス名受け取り用バッファ */)
   {
       const HMODULE moduleHandle = ::GetModuleHandle(NULL);
       if (moduleHandle != HMODULE(0))
           return (::GetModuleFileName(moduleHandle, pathName, _MAX_PATH) > 0);
       return false;
   }

   // 「送る」フォルダのパス名の取得
   static bool GetSendToPath(LPTSTR pathName /* パス名用バッファ */)
   {
       return GetSpecialFolderPath(pathName, CSIDL_SENDTO);
   }

#ifdef _DEBUG
   // 「お気に入り」フォルダのパス名の取得
   static bool GetFavoritesPath(LPTSTR pathName /* パス名用バッファ */)
   {
       return GetSpecialFolderPath(pathName, CSIDL_FAVORITES);
   }
#endif //   _DEBUG

   // システムの特殊なフォルダのパス名の取得
   // (Windows NT 4.0, Windows 95 以降)
   //   Windows 2000, Windows NT 4.0 + IE 4.0, Windows 98, Windows 95 + IE 4.0 以降であれば SHGetSpecialFolderPath が使える
   //   また,システム ディレクトリは GetSystemDirectory,
   //   Windows ディレクトリは GetWindowsDirectory,
   //   一時ファイル用のディレクトリは GetTempPath がそれぞれ使用出来る.
   static bool GetSpecialFolderPath(LPTSTR pathName /* パス名用バッファ */, int nFolder /* フォルダを指定する CSIDL の値 */)
   {
       bool        temp(false);
       IMalloc*    pMalloc;
       if (::SHGetMalloc(&pMalloc) == NOERROR) {
           ITEMIDLIST* pItemIdList;
           if (::SHGetSpecialFolderLocation(::GetDesktopWindow(), nFolder, &pItemIdList) == NOERROR) {
               if (::SHGetPathFromIDList(pItemIdList, pathName))
                   temp = true;
               pMalloc->Free(pItemIdList);
           }
           pMalloc->Release();
       }
       return temp;
   }

   // ショートカットの作成
   static bool CreateShortcut(LPCTSTR targePathName /* ショートカットのリンク先 */, LPCTSTR targetArgument /* コマンドライン引数 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
       bool            temp(false);
       HRESULT         result = ::CoInitialize(NULL);
       if (SUCCEEDED(result)) {
           IShellLink* pShellLink;
           result = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pShellLink);
           if (SUCCEEDED(result)) {
               IPersistFile*   pPersistFile;
               result = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
               if (SUCCEEDED(result)) {
                   result = pShellLink->SetPath(targePathName);
                   if (SUCCEEDED(result)) {
                       result = (targetArgument == NULL) ? S_OK : pShellLink->SetArguments(targetArgument);
                       if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                           result = pPersistFile->Save(linkPathName, true);
#else   //  _UNICODE
                           //  Unicodeに変換
                           wchar_t wlinkPathName[_MAX_PATH];
                           ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
                           result = pPersistFile->Save(wlinkPathName, true);
#endif  //  _UNICODE
                           if (SUCCEEDED(result))
                               temp = true;
                       }
                   }
                   pPersistFile->Release();
               }
               pShellLink->Release();
           }
           CoUninitialize();
       }
       return temp;
   }

   // ショートカットの作成
   static bool CreateShortcut(LPCTSTR targetPathName /* ショートカットのリンク先 */, LPCTSTR targetArgument /* コマンドライン引数 */, LPCTSTR linkDirectoryName /* ショートカットを置くディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR linkFileName[_MAX_PATH];
       ::_tcscpy(linkFileName, linkName                      );
       ::_tcscat(linkFileName, _T("."SHORTCUT_EXTENSION_TEXT));
       TCHAR linkPathName[_MAX_PATH];
       MakePathName(linkPathName, linkDirectoryName, linkFileName);
       return CreateShortcut(targetPathName, targetArgument, linkPathName);
   }

#ifdef _DEBUG
   static bool CreateInternetShortcut(LPCTSTR urlText /* URL 文字列 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
       bool            temp(false);
       HRESULT         result = ::CoInitialize(NULL);
       if (SUCCEEDED(result)) {
           IUniformResourceLocator *pUniformResourceLocator    = NULL;
           result                                              = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IUniformResourceLocator, (LPVOID*)&pUniformResourceLocator);
           if (SUCCEEDED(result)) {
               IPersistFile *pPersistFile  = NULL;
               result                      = pUniformResourceLocator->QueryInterface(IID_IPersistFile, (void **)&pPersistFile);
               if (SUCCEEDED(result)) {
                   result = pUniformResourceLocator->SetURL(urlText, 0);
                   if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                       result = pPersistFile->Save(linkPathName, TRUE);
#else   //  _UNICODE
                       //  Unicodeに変換
                       wchar_t wlinkPathName[_MAX_PATH];
                       ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
                       result = pPersistFile->Save(wlinkPathName, TRUE);
#endif  //  _UNICODE
                       temp = SUCCEEDED(result);
                   }
                   pPersistFile->Release();
               }
               pUniformResourceLocator->Release();
           }
       }
       return temp;
   }

   // インターネット ショートカットの作成
   static bool CreateInternetShortcut(LPCTSTR urlText /* URL 文字列 */, LPCTSTR linkDirectoryName /* ショートカットを置くディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR linkFileName[_MAX_PATH];
       ::_tcscpy(linkFileName, linkName                              );
       ::_tcscat(linkFileName, _T("."INTERNETSHORTCUT_EXTENSION_TEXT));
       TCHAR linkPathName[_MAX_PATH];
       MakePathName(linkPathName, linkDirectoryName, linkFileName);
       return CreateInternetShortcut(urlText, linkPathName);
   }

   // 「送る」への追加
   static bool CreateSendTo(LPCTSTR targetPathName, LPCTSTR targetArgument, LPCTSTR linkName)
   {
       TCHAR directoryName[_MAX_DIR];
       return GetSendToPath(directoryName)
              ? CreateShortcut(targetPathName, targetArgument, directoryName, linkName)
              : false;
   }

   // 「お気に入り」への追加
   static bool CreateFavorites(LPCTSTR urlText /* URL 文字列 */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR directoryName[_MAX_DIR];
       return GetFavoritesPath(directoryName)
              ? CreateInternetShortcut(urlText, directoryName, linkName)
              : false;
   }
#endif  //  _DEBUG

#if     _MSC_VER >= 1300
   // WindowsInstaller で作成されたショートカットからのパス名の取得
   static bool ReadMsiShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, size_t targePathNameBufferLength /* ショートカットのリンク先の最大文字数 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
       if (targePathName != NULL) {
           const size_t    bufferLength = 39;
           TCHAR           productCode  [bufferLength         ];
           TCHAR           featureId    [MAX_FEATURE_CHARS + 1];
           TCHAR           componentCode[bufferLength         ];
           UINT            result = ::MsiGetShortcutTarget(linkPathName, productCode, featureId, componentCode);
           if (result == ERROR_SUCCESS) {
               DWORD targePathNameLength = DWORD(targePathNameBufferLength);
               result = ::MsiGetComponentPath(productCode, componentCode, targePathName, &targePathNameLength);
               if (result == INSTALLSTATE_LOCAL)
                   return true;
           }
       }
       return false;
   }

   // WindowsInstaller で作成されたショートカットからのパス名の取得
   static bool ReadMsiShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
       return ReadMsiShortcut(targePathName, MAX_TEXT_LENGTH, linkPathName);
   }
#endif  //  _MSC_VER

   // ショートカットからのパス名の取得
   static bool ReadShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
#if     _MSC_VER >= 1300
       if (ReadMsiShortcut(targePathName, linkPathName))
           return true;
#endif  //  _MSC_VER

       bool            temp(false);
       HRESULT         result = ::CoInitialize(NULL);
       if (SUCCEEDED(result)) {
           IShellLink* pShellLink;
           result = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pShellLink);
           if (SUCCEEDED(result)) {
               IPersistFile* pPersistFile;
               result = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
               if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                   result = pPersistFile->Load(linkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#else   //  _UNICODE
                   //  Unicodeに変換
                   wchar_t wlinkPathName[_MAX_PATH];
                   ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
                   result = pPersistFile->Load(wlinkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#endif  //  _UNICODE
                   if (SUCCEEDED(result)) {
                       WIN32_FIND_DATA findData;
                       result = pShellLink->GetPath(targePathName, _MAX_PATH, &findData, 0);
                       if (SUCCEEDED(result)) {
                           result = (targetArgument == NULL) ? S_OK : pShellLink->GetArguments(targetArgument, int(targetArgumentMaxTextLength));
                           if (SUCCEEDED(result))
                               temp = true;
                       }
                   }
                   pPersistFile->Release();
               }
               pShellLink->Release();
           }
           CoUninitialize();
       }
       return temp;
   }

#ifdef  _DEBUG
   // ショートカットからのパス名の取得
   static bool ReadShortcut(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkDirectoryName /* ショートカットが在るディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR linkFileName[_MAX_PATH];
       ::_tcscpy(linkFileName, linkName                      );
       ::_tcscat(linkFileName, _T("."SHORTCUT_EXTENSION_TEXT));
       TCHAR linkPathName[_MAX_PATH];
       MakePathName(linkPathName, linkDirectoryName, linkFileName);
       return ReadShortcut(targePathName, targetArgument, targetArgumentMaxTextLength, linkPathName);
   }

   // 「送る」からのパス名の取得
   static bool ReadSendTo(LPTSTR targePathName /* ショートカットのリンク先 */, LPTSTR targetArgument /* コマンドライン引数 */, size_t targetArgumentMaxTextLength /* コマンドライン引数の最大文字数 */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR directoryName[_MAX_DIR];
       return GetSendToPath(directoryName)
              ? ReadShortcut(targePathName, targetArgument, targetArgumentMaxTextLength, directoryName, linkName)
              : false;
   }
#endif  //  _DEBUG

   // インターネット ショートカットからの URL の取得
   static bool ReadInternetShortcut(LPTSTR urlText /* URL 文字列 */, LPCTSTR linkPathName /* ショートカットのパス名 */)
   {
       bool            temp(false);
       HRESULT         result = ::CoInitialize(NULL);
       if (SUCCEEDED(result)) {
           IUniformResourceLocator *pUniformResourceLocator = NULL;
           result                                           = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER, IID_IUniformResourceLocator, (LPVOID*)&pUniformResourceLocator);
           if (SUCCEEDED(result)) {
               IPersistFile *pPersistFile = NULL;
               result                     = pUniformResourceLocator->QueryInterface(IID_IPersistFile, (void **)&pPersistFile);
               if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                   result = pPersistFile->Load(linkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#else   //  _UNICODE
                   //  Unicodeに変換
                   wchar_t wlinkPathName[_MAX_PATH];
                   ::MultiByteToWideChar(CP_ACP, 0, linkPathName, -1, wlinkPathName, _MAX_PATH);
                   result = pPersistFile->Load(wlinkPathName, STGM_READ | STGM_SHARE_DENY_WRITE);
#endif  //  _UNICODE
                   if (SUCCEEDED(result)) {
                       LPSTR buffer;
                       result = pUniformResourceLocator->GetURL(&buffer);
                       if (SUCCEEDED(result)) {
#ifdef  _UNICODE
                           ::MultiByteToWideChar(CP_ACP, 0, buffer, -1, urlText, _MAX_PATH);
#else   //  _UNICODE
                           ::_tcscpy(urlText, buffer);
#endif  //  _UNICODE
                           temp = true;
                       }
                   }
                   pPersistFile->Release();
               }
               pUniformResourceLocator->Release();
           }
       }
       return temp;
   }

#ifdef  _DEBUG
   // インターネット ショートカットからの URL の取得
   static bool ReadInternetShortcut(LPTSTR urlText /* URL 文字列 */, LPCTSTR linkDirectoryName /* ショートカットが在るディレクトリ */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR linkFileName[_MAX_PATH];
       ::_tcscpy(linkFileName, linkName                              );
       ::_tcscat(linkFileName, _T("."INTERNETSHORTCUT_EXTENSION_TEXT));
       TCHAR linkPathName[_MAX_PATH];
       MakePathName(linkPathName, linkDirectoryName, linkFileName);
       return ReadInternetShortcut(urlText, linkPathName);
   }

   // 「お気に入り」からの URL の取得
   static bool ReadFavorites(LPTSTR urlText /* URL 文字列 */, LPCTSTR linkName /* ショートカットの名称 */)
   {
       TCHAR directoryName[_MAX_DIR];
       return GetFavoritesPath(directoryName)
              ? ReadInternetShortcut(urlText, directoryName, linkName)
              : false;
   }

   // パス名で指定されたファイルまたはディレクトリが存在するかどうか
   static bool IsExist(LPTSTR pathName)
   {
       const DWORD attribute(::GetFileAttributes(pathName));
       return (attribute != DWORD(-1));
   }

   // 指定されたパス名がディレクトリかどうか
   static bool IsDirectory(LPCTSTR pathName)
   {
       const DWORD attribute(::GetFileAttributes(pathName));
       return IsDirectory(attribute);
   }
#endif  //  _DEBUG

   // 属性がディレクトリかどうか
   static bool IsDirectory(DWORD attribute)
   {
       return ((attribute & FILE_ATTRIBUTE_DIRECTORY) != 0);
   }

   // 指定されたパス名がショートカットかどうか
   static bool IsShortcut(LPCTSTR pathName, LPTSTR fileNameWithoutExtension = NULL)
   {
       TCHAR           fileName      [_MAX_FNAME];
       TCHAR           extensionName [_MAX_EXT  ];
       ::_tsplitpath(pathName, NULL, NULL, fileName, extensionName);
       if (fileNameWithoutExtension != NULL)
           ::_tcscpy(fileNameWithoutExtension, fileName);
       return (::_tcsicmp(extensionName, _T("."SHORTCUT_EXTENSION_TEXT)) == 0);
   }

   // 指定されたパス名がインターネット ショートカットかどうか
   static bool IsInternetShortcut(LPCTSTR pathName, LPTSTR fileNameWithoutExtension = NULL)
   {
       TCHAR           fileName      [_MAX_FNAME];
       TCHAR           extensionName [_MAX_EXT  ];
       ::_tsplitpath(pathName, NULL, NULL, fileName, extensionName);
       if (fileNameWithoutExtension != NULL)
           ::_tcscpy(fileNameWithoutExtension, fileName);
       return (::_tcsicmp(extensionName, _T("."INTERNETSHORTCUT_EXTENSION_TEXT)) == 0);
   }

   // ファイルの削除
   static bool DeleteFile(LPCTSTR pathName)
   {
       return !!::DeleteFile(pathName);
   }
};

// ファイル検索 クラス
class FindFile : protected File
{
public:
   FindFile(LPCTSTR directoryName)
   {
       ::_tcscpy(directoryName_, directoryName);

       TCHAR pathName[_MAX_PATH];
       MakePathName(pathName, directoryName, _T("*."SHORTCUT_EXTENSION_TEXT));
       handle_ = ::FindFirstFile(pathName, &ffBlk_);
   }

   virtual ~FindFile()
   {
       ::FindClose(handle_);
   }

   bool IsValid() const
   {
       return (handle_ != INVALID_HANDLE_VALUE);
   }

   bool IsDirectory() const
   {
       return File::IsDirectory(ffBlk_.dwFileAttributes);
   }

   LPCSTR FileName() const
   {
       return ffBlk_.cFileName;
   }

   void PathName(LPTSTR pathName) const
   {
       MakePathName(pathName, directoryName_, FileName());
   }

   bool NextFile()
   {
       return !!::FindNextFile(handle_, &ffBlk_);
   }

private:
   HANDLE          handle_;
   WIN32_FIND_DATA ffBlk_;
   TCHAR           directoryName_[_MAX_PATH];
};

// クリップボード クラス
class Clipboard
{
public:
#ifdef _DEBUG
   // クリップボードからテキストの取得
   static bool GetText(LPTSTR buffer /* テキスト用バッファ */, size_t bufferSize /* テキスト用バッファのサイズ */)
   {
       bool temp(false);
       if (::OpenClipboard(HWND(0))) {
           if (::IsClipboardFormatAvailable(CF_TEXT)) {
               const HGLOBAL data = ::GetClipboardData(CF_TEXT);
               if (data != HGLOBAL(0)) {
                   const LPCTSTR text = LPCTSTR(::GlobalLock(data));
                   if (text != NULL) {
                       const size_t    length(::_tcslen(text));
                       if (length < bufferSize) {
                           ::memcpy(buffer, text, sizeof(TCHAR) * (length + 1));
                           temp = true;
                       }
                   }
                   ::GlobalUnlock(data);
               }
           }
           ::CloseClipboard();
       }
       return temp;
   }
#endif  //  _DEBUG

   // クリップボードへのテキストのコピー
   static bool SetText(LPCTSTR text /* テキスト */)
   {
       Utility::Debug::Assert(text != NULL);

       bool            temp(false);
       const size_t    length(sizeof(TCHAR) * (::_tcslen(text) + 1));
       const HGLOBAL   data = ::GlobalAlloc(GMEM_MOVEABLE, length);
       if (data != NULL) {
           const LPVOID buffer = ::GlobalLock(data);
           if (buffer != NULL) {
               ::memcpy(buffer, text, length);
               ::GlobalUnlock(data);
               if (::OpenClipboard(::GetDesktopWindow())) {
                   if (::EmptyClipboard()) {
                       if (::SetClipboardData(CF_TEXT, data) != HANDLE(0))
                           temp = true;
                   }
                   ::CloseClipboard();
               }
           }
           ::GlobalFree(data);
       }
       return temp;
   }
};

}
// namespace Utility

//////////////////////////////////////////////////////////////////////////////

// アプリケーション
namespace ShoSendTo {

// アプリケーション クラス
class Application
{
public:
   Application() : agent_(&help_)
   {}

#ifdef  _CONSOLE

   // メイン ルーチン
   void Run(int argc, LPTSTR argv[])
   {
       TCHAR text[MAX_TEXT_LENGTH];
       Argument(argc, argv, text);
       agent_->Run(text);
   }

#else   //  _CONSOLE

   // メイン ルーチン
   void Run(LPTSTR commandLine)
   {
       TCHAR text[MAX_TEXT_LENGTH];
       Argument(commandLine, text);
       agent_->Run(text);
   }

#endif  //  _CONSOLE

private:
   // エージェント クラス
   class Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR text) const = 0;

   protected:
       // クリップボードへのテキストのコピー
       static void ToClipboard(LPCTSTR text)
       {
           if (Utility::Clipboard::SetText(text)) {
#ifdef _DEBUG
               Test(text);
#endif  //  _DEBUG
           }
       }

       // ショートカットの作成
       static bool CreateShortcut(LPCTSTR targetPathName, LPCTSTR targetArgument, LPCTSTR linkDirectoryName, LPCTSTR linkName)
       {
           TCHAR tempLinkName[_MAX_PATH];
           ::_tcscpy(tempLinkName, SHORTCUT_LABEL_TEXT);
           ::_tcscat(tempLinkName, linkName           );
           return Utility::File::CreateShortcut(targetPathName, targetArgument, linkDirectoryName, tempLinkName);
       }

#ifdef _DEBUG
       // 「送る」の追加
       static bool CreateSendTo(LPCTSTR targetPathName, LPCTSTR targetArgument, LPCTSTR linkName)
       {
           TCHAR tempLinkName[_MAX_PATH];
           ::_tcscpy(tempLinkName, SHORTCUT_LABEL_TEXT);
           ::_tcscat(tempLinkName, linkName           );
           return Utility::File::CreateSendTo(targetPathName, targetArgument, tempLinkName);
       }
#endif  // _DEBUG

       // 「送る」の処理
       virtual void ProcessSendTo(LPCTSTR /* pathName */) const
       {}

       // 「送る」の列挙
       void EnumSendTo() const
       {
           TCHAR directoryName[_MAX_DIR];
           if (Utility::File::GetSendToPath(directoryName)) {
               Utility::FindFile findFile(directoryName);
               if (findFile.IsValid()) {
                   bool temp(false);
                   do {
                       if (IsSendTo(findFile.FileName())) {
                           TCHAR pathName[_MAX_PATH];
                           findFile.PathName(pathName);
                           temp = findFile.NextFile();
                           ProcessSendTo(pathName);
                       } else {
                           temp = findFile.NextFile();
                       }
                   } while (temp);
               }
           }
       }

   private:
       static bool IsSendTo(LPCTSTR fileName)
       {
           const TCHAR label[] = _T(SHORTCUT_LABEL_TEXT);
           return (::_tcsnicmp(fileName, label, ::_tcslen(label)) == 0);
       }

#ifdef _DEBUG
       // テスト
       static void Test(LPCTSTR text)
       {
           TCHAR buffer[MAX_TEXT_LENGTH];
           if (Utility::Clipboard::GetText(buffer, MAX_TEXT_LENGTH)) {
               Utility::Debug::Output(_T("Clipboard Test\n"));
               Utility::Debug::Trace(_T("\tClipboard - Set:(%s), Get(%s)\n"), text, buffer);
               Utility::Debug::Assert(::_tcscmp(text, buffer) == 0);
           } else {
               Utility::Debug::Assert(false);
           }
       }
#endif  //  _DEBUG
   };

   // ヘルプ クラス
   class Help : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR /* text */) const
       {
           DisplayHelp();
       }

   private:
       // ヘルプの表示
       static void DisplayHelp()
       {
           static const LPCTSTR helpText =
               _T(APPLICATION_TEXT" Version "VERSION_TEXT" Copyright (C) "COPYRIGHT_YEAR_TEXT" "COMPANY_TEXT".\n"
                  "  Usage: "APPLICATION_TEXT" PathName [/U] [/P] [/I] [/Q] [/A] [/R] [/H] [/?]\n"
                  "\t\n"
                  "\t/U Copy URL Text to Clipboard.\n"
                  "\t/P Copy PathName Text to Clipboard.\n"
                  "\t/I Install "APPLICATION_TEXT" to your Windows.\n"
                  "\t/Q Uninstall "APPLICATION_TEXT" from your Windows.\n"
                  "\t/A Add the shortcut to SendTo on your Windows.\n"
                  "\t/R Remove the shortcut from SendTo on your Windows.\n"
                  "\t/H Show this help.\n"
                  "\t/? Show this help.\n");

           Utility::Console::Output(helpText);
       }
   };

   // URL 文字列作成クラス
   class MakeURL : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR text) const
       {
           TCHAR   buffer[MAX_TEXT_LENGTH];
           if (Utility::File::IsShortcut(text)) {
               TCHAR   targetPathName[_MAX_PATH      ];
               TCHAR   targetArgument[MAX_TEXT_LENGTH];
               if (!Utility::File::ReadShortcut(targetPathName, targetArgument, MAX_TEXT_LENGTH, text))
                   return;
               ToURL(buffer, targetPathName);
           } else if (Utility::File::IsInternetShortcut(text)) {
               if (!Utility::File::ReadInternetShortcut(buffer, text))
                   return;
           } else {
               ToURL(buffer, text);
           }
           ToClipboard(buffer);
       }

   private:
       static void ToURL(LPTSTR buffer, LPCTSTR text)
       {
           ::_tcscpy(buffer, _T("file://"));
           size_t  index(::_tcslen(buffer));
           for (LPCTSTR pointer = text; *pointer != _T('\0') && index < MAX_TEXT_LENGTH; pointer++) {
               if (*pointer == _T('\\'))
                   buffer[index] = _T('/');
               else
                   buffer[index] = *pointer;
#ifndef _UNICODE
               if (_ismbblead(*pointer)) {
                   pointer++;
                   index++;
                   buffer[index] = *pointer;
               }
#endif  //  _UNICODE
               index++;
           }
           buffer[index] = _T('\0');
       }
   };

   // パス名作成クラス
   class MakePathName : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR text) const
       {
           if (Utility::File::IsShortcut(text)) {
               TCHAR   buffer        [MAX_TEXT_LENGTH];
               TCHAR   targetArgument[MAX_TEXT_LENGTH];
               if (Utility::File::ReadShortcut(buffer, targetArgument, MAX_TEXT_LENGTH, text))
                   ToClipboard(buffer);
           } else {
               ToClipboard(text);
           }
       }
   };

   // インストーラ クラス
   class Installer : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR text) const
       {
           TCHAR destinationDirectoryName[_MAX_DIR ];
           ::_tcscpy(destinationDirectoryName, text);
           TCHAR targetPathName          [_MAX_PATH];
           Install(destinationDirectoryName, targetPathName);
           CreateShortcuts(targetPathName);
       }

   private:
       // インストール
       static bool Install(LPTSTR destinationDirectoryName, LPTSTR targetPathName)
       {
           if (Utility::File::PrepareDirectory(destinationDirectoryName)) {
               TCHAR sourcePathName[_MAX_PATH];
               if (Utility::File::GetCurrentProcessFileName(sourcePathName)) {
                   if (Utility::File::CopyToDirectory(sourcePathName, destinationDirectoryName, targetPathName))
                       return true;
               }
           }
           return false;
       }

       // ショートカットの作成
       static bool CreateShortcuts(LPCTSTR targetPathName)
       {
               const TCHAR linkNameURL             [] = _T("URL→クリップボード"               );
               const TCHAR linkNamePath            [] = _T("パス名→クリップボード"           );
               const TCHAR linkNameAddShortcut     [] = _T("「送る」へのショートカットの追加"  );
               const TCHAR linkNameRemoveShortcut  [] = _T("「送る」からのショートカットの削除");
               const TCHAR optionTextURL           [] = _T("/U"                                );
               const TCHAR optionTextPath          [] = _T("/P"                                );
               const TCHAR optionTextAddShortcut   [] = _T("/A"                                );
               const TCHAR optionTextRemoveShortcut[] = _T("/R"                                );

               TCHAR directoryName[_MAX_DIR];
               if (Utility::File::GetSendToPath(directoryName)) {
                   CreateShortcut(targetPathName, optionTextURL           , directoryName, linkNameURL           );
                   CreateShortcut(targetPathName, optionTextPath          , directoryName, linkNamePath          );
                   CreateShortcut(targetPathName, optionTextAddShortcut   , directoryName, linkNameAddShortcut   );
                   CreateShortcut(targetPathName, optionTextRemoveShortcut, directoryName, linkNameRemoveShortcut);
                   return true;
               }
               return false;
       }
   };

   // アンインストーラ クラス
   class Uninstaller : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR /* text */) const
       {
           Uninstall();
       }

   protected:
       // 「送る」の処理
       virtual void ProcessSendTo(LPCTSTR pathName) const
       {
           Utility::File::DeleteFile(pathName);
       }

   private:
       // アンインストール
       void Uninstall() const
       {
           EnumSendTo();
       }
   };

   // ショートカットを「送る」に追加するクラス
   class AddShortcut : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR text) const
       {
           AddShortcutToSendTo(text);
       }

   private:
       // ショートカットを「送る」に追加
       static void AddShortcutToSendTo(LPCTSTR pathName)
       {
           TCHAR directoryName[_MAX_DIR];
           if (Utility::File::GetSendToPath(directoryName)) {
               TCHAR fileNameWithoutExtension[_MAX_FNAME];
               if (Utility::File::IsShortcut(pathName, fileNameWithoutExtension)) {
                   TCHAR   fileName           [_MAX_FNAME];
                   TCHAR   extensionName      [_MAX_EXT  ];
                   ::_tsplitpath(pathName, NULL, NULL, fileName, extensionName);
                   TCHAR   destinationFileName[_MAX_DIR  ];
                   ::_tcscpy(destinationFileName, _T(SHORTCUT_LABEL_TEXT));
                   ::_tcscat(destinationFileName, fileName               );
                   ::_tcscat(destinationFileName, extensionName          );

                   TCHAR   destinationPathName[_MAX_PATH];
                   Utility::File::CopyToDirectory(pathName, directoryName, destinationFileName, destinationPathName);
               } else {
                   CreateShortcut(pathName, NULL, directoryName, fileNameWithoutExtension);
               }
           }
       }
   };

   // ショートカットを「送る」から削除するクラス
   class RemoveShortcut : public Agent
   {
   public:
       // 処理ルーチン
       virtual void Run(LPCTSTR text) const
       {
           RemoveShortcutFromSendTo(text);
       }

   private:
       // ショートカットを「送る」から削除
       static void RemoveShortcutFromSendTo(LPCTSTR pathName)
       {
           TCHAR directoryName[_MAX_DIR];
           if (Utility::File::GetSendToPath(directoryName)) {
               TCHAR   destinationFileName     [_MAX_FNAME];
               ::_tcscpy(destinationFileName, _T(SHORTCUT_LABEL_TEXT));
               TCHAR   fileNameWithoutExtension[_MAX_FNAME];
               if (Utility::File::IsShortcut(pathName, fileNameWithoutExtension)) {
                   TCHAR   fileName     [_MAX_FNAME];
                   TCHAR   extensionName[_MAX_EXT  ];
                   ::_tsplitpath(pathName, NULL, NULL, fileName, extensionName);
                   ::_tcscat(destinationFileName, fileName     );
                   ::_tcscat(destinationFileName, extensionName);
               } else {
                   ::_tcscat(destinationFileName, fileNameWithoutExtension      );
                   ::_tcscat(destinationFileName, _T("."SHORTCUT_EXTENSION_TEXT));
               }
               TCHAR   destinationPathName     [_MAX_PATH ];
               Utility::File::MakePathName(destinationPathName, directoryName, destinationFileName);
               Utility::File::DeleteFile(destinationPathName);
           }
       }
   };

#ifdef  _CONSOLE

   // コマンドライン引数の解析
   void Argument(size_t argc, LPTSTR argv[], LPTSTR destinationText)
   {
       bool isExistText(false);

       for (size_t i = 1; i < argc; i++) {
           if (int(argv[i][0]) == _T('/') || int(argv[i][0]) == _T('-')) {
               for (size_t j = 1; argv[i][j] != _T('\0'); j++)
                   Switch(argv[i][j]);
           } else if (!isExistText) {
               ::_tcscpy(destinationText, argv[i]);
               isExistText = true;
           }
       }
   }

#else   //  _CONSOLE

   // コマンドライン引数からのトークンの切り出し
   static LPTSTR ToToken(LPTSTR commandLine = NULL)
   {
       static LPTSTR pointer = NULL;

       if (commandLine == NULL) {
           if (pointer == NULL)
               return NULL;
       } else {
           pointer = commandLine;
       }

       LPTSTR  start = NULL;
       bool    firstState          (true );
       bool    doubleQuotationState(false);

       for (; *pointer != _T('\0'); pointer++) {
           switch (*pointer) {
               case _T('\"'):
                   doubleQuotationState    = !doubleQuotationState;
                   *pointer                = '\0';
                   break;
               case _T(' ' ):
               case _T('\t'):
               case _T('\a'):
               case _T('\n'):
                   if (!firstState && !doubleQuotationState) {
                       *pointer = '\0';
                       pointer++;
                       goto L1;
                   }
                   break;
               default      :
                   if (firstState) {
                       firstState  = false;
                       start       = pointer;
                   }
                   break;
           }
#ifndef _UNICODE
           if (_ismbblead(*pointer))
               pointer++;
#endif  //  _UNICODE
       }
L1:
       return start;
   }

   // コマンドライン引数の解析
   void Argument(LPTSTR commandLine, LPTSTR destinationText)
   {
       bool isExistText(false);

       for (LPTSTR token = ToToken(commandLine); token != NULL; token = ToToken()) {
           if (int(token[0]) == _T('/') || int(token[0]) == _T('-')) {
               for (size_t j = 1; token[j] != _T('\0'); j++)
                   Switch(token[j]);
           } else if (!isExistText) {
               ::_tcscpy(destinationText, token);
               isExistText = true;
           }
       }
   }

#endif  //  _CONSOLE

   // コマンドラインで与えられたオプション スイッチの解析
   void Switch(TCHAR character)
   {
       switch (::_totlower(int(character))) {
           case _T('h'):
           case _T('?'): agent_ = &help_           ; break;
           case _T('u'): agent_ = &makeURL_        ; break;
           case _T('p'): agent_ = &makePathName_   ; break;
           case _T('i'): agent_ = &installer_      ; break;
           case _T('q'): agent_ = &uninstaller_    ; break;
           case _T('a'): agent_ = &addShortcut_    ; break;
           case _T('r'): agent_ = &removeShortcut_ ; break;
       }
   }

   // エージェント オブジェクト
   Agent*          agent_         ;
   Help            help_          ;
   MakeURL         makeURL_       ;
   MakePathName    makePathName_  ;
   Installer       installer_     ;
   Uninstaller     uninstaller_   ;
   AddShortcut     addShortcut_   ;
   RemoveShortcut  removeShortcut_;
};

}
// namespace ShoSendTo

//////////////////////////////////////////////////////////////////////////////

// アプリケーションのエントリー ポイント
#ifdef  _CONSOLE

int _tmain(int argc, LPTSTR argv[])
{
   ShoSendTo::Application application;
   application.Run(argc, argv);
   return 0;
}

#else   //  _CONSOLE

int APIENTRY _tWinMain(HINSTANCE /* instanceHandle */, HINSTANCE /* prevInstanceHandle */, LPTSTR commandLine, int /* showCommand */)
{
   ShoSendTo::Application application;
   application.Run(commandLine);
   return 0;
}

#endif  //  _CONSOLE

//////////////////////////////////////////////////////////////////////////////

/*
 クラス図

 +-------------+
 |             |
 +------------------------------------------------------+
 |                                                      |
 |  Utility                                             |
 |                                                      |
 |     +-------+      +-------------+   +-----------+   |
 |     |       |      |             |   |           |   |
 |     | File  |      | Console     |   | Debug     |   |
 |     |       |      |             |   |           |   |
 |     +-------+      +-------------+   +-----------+   |
 |         △                                            |
 |         |                                            |
 |   +-----------+    +-----------+                     |
 |   |           |    |           |                     |
 |   | FindFile  |    | Clipboard |                     |
 |   |           |    |           |                     |
 |   +-----------+    +-----------+                     |
 |                                            {Global}  |
 |                                                      |
 +------------------------------------------------------+


 +-------------+
 |             |
 +--------------------------------------------------------------------------------------------------------------------+
 |                                                                                                                    |
 |  ShoSendTo                                                                                                         |
 |                                                                                                                    |
 |  +-------------+                                                                                                   |    +------+
 |  |             |---------------------------------------------------------------------------------------------------+----|      |
 |  | Application |                                                                                                   |    | main |
 |  |             |                                                                                                   |    |      |
 |  +-------------+                                                                                                   |    +------+
 |        |                                                                                                           |
 |        |                                                                                                           |
 |        |                                                                                                           |
 |        ↓                                                                                                           |
 |     +-------+                                                                                                      |
 |     |       |                                                                                                      |
 |     | Agent |                                                                                                      |
 |     |       |                                                                                                      |
 |     +-------+                                                                                                      |
 |         △                                                                                                          |
 |         |                                                                                                          |
 |         +---------+---------------+----------------+---------------+-----------------+-----------------+           |
 |         |         |               |                |               |                 |                 |           |
 |         |         |               |                |               |                 |                 |           |
 |     +------+  +---------+  +--------------+  +-----------+   +-------------+  +-------------+  +----------------+  |
 |     |      |  |         |  |              |  |           |   |             |  |             |  |                |  |
 |     | Help |  | MakeURL |  | MakePathName |  | Installer |   | Uninstaller |  | AddShortcut |  | RemoveShortcut |  |
 |     |      |  |         |  |              |  |           |   |             |  |             |  |                |  |
 |     +------+  +---------+  +--------------+  +-----------+   +-------------+  +-------------+  +----------------+  |
 |                                                                                                                    |
 +--------------------------------------------------------------------------------------------------------------------+
*/


Software - ShoSendTo