デスクトップやお気に入り、マイドキュメントなどの特殊フォルダにアクセスするとき、例えば「c:\windows\」や「c:\Program Files\」のようにソースコードに書いてはいけない。必ず専用のAPIを利用してパスを取得する必要がある。取得にはSHGetFolderPath、SHGetSpecialFolderPath、SHGetFolderLocationなどを利用する。
現在のプラットフォームでは何も考えずにSHGetFolderPathを利用すればいい。しかし古いプラットフォームもサポートしたプログラムにしようと考えるとやっかいなことになる可能性がある。SHGetFolderPathをshfolder.libに実装したりshell32.dllに変えたり...と過去に何回かの変更があるからだ。
ここで紹介する関数は現在のプラットフォームではSHGetFolderPathを、古いものではSHGetSpecialFolderPathを利用して特殊フォルダのパスを取得している。
取得できるフォルダには...
CSIDL_ADMINTOOLS
CSIDL_COMMON_ADMINTOOLS
CSIDL_APPDATA
CSIDL_COMMON_APPDATA
CSIDL_COMMON_DOCUMENTS
CSIDL_COOKIES
CSIDL_HISTORY
CSIDL_INTERNET_CACHE
CSIDL_LOCAL_APPDATA
CSIDL_MYPICTURES
CSIDL_PERSONAL
CSIDL_PROGRAM_FILES
CSIDL_PROGRAM_FILES_COMMON
CSIDL_SYSTEM
CSIDL_WINDOWS
などなどがある。これらはshlobj.hに定義されているのでそちらを参考にするといいだろう。
#include "shlobj.h"
//
// SHGetSpecialFolderPathの全プラットフォーム対応バージョン
//
BOOL SafeSHGetSpecialFolderPath(HWND hwndOwner,LPTSTR lpszPath,int nFolder,BOOL fCreate)
{
BOOL (CALLBACK* g_pfnSHGetSpecialFolderPath)(HWND,LPTSTR,int,BOOL);
HRESULT (CALLBACK* g_pfnSHGetFolderPath)(HWND,int,HANDLE,DWORD,LPTSTR);
int i;
BOOL ret;
HRESULT hr;
HMODULE hDLL;
TCHAR pszDllFile[][15] = {_T("shfolder.dll"),_T("shell32.dll")};
if(lpszPath == NULL)
return FALSE;
*lpszPath = NULL;
ret = FALSE;
for(i = 0; i < 2; i++)
{
hDLL = ::LoadLibrary(pszDllFile[i]);
if(hDLL == NULL)
continue;
#ifdef UNICODE
(*(FARPROC*)&g_pfnSHGetSpecialFolderPath = ::GetProcAddress(hDLL,"SHGetSpecialFolderPathW"));
(*(FARPROC*)&g_pfnSHGetFolderPath = ::GetProcAddress(hDLL,"SHGetFolderPathW"));
#else
(*(FARPROC*)&g_pfnSHGetSpecialFolderPath = ::GetProcAddress(hDLL,"SHGetSpecialFolderPathA"));
(*(FARPROC*)&g_pfnSHGetFolderPath = ::GetProcAddress(hDLL,"SHGetFolderPathA"));
#endif
if(g_pfnSHGetSpecialFolderPath)
ret = g_pfnSHGetSpecialFolderPath(hwndOwner,lpszPath,nFolder,fCreate);
if(ret == FALSE && g_pfnSHGetFolderPath)
{
hr = g_pfnSHGetFolderPath(hwndOwner,nFolder | (fCreate ? CSIDL_FLAG_CREATE : 0),NULL,SHGFP_TYPE_DEFAULT,lpszPath);
ret = (SUCCEEDED(hr)) ? TRUE : FALSE;
}
::FreeLibrary(hDLL);
if(ret)
return TRUE;
}
return FALSE;
}
void Test(void)
{
BOOL ret;
TCHAR pszPath[MAX_PATH*2];
ret = SafeSHGetSpecialFolderPath(NULL,pszPath,CSIDL_DESKTOPDIRECTORY,FALSE);
if(ret)
::MessageBox(NULL,pszPath,_T(""),MB_OK);
else
::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}
