例えば画像ファイルを一覧できるHTMLファイルを書き出したい場合など、HTMLファイルの位置から見た画像ファイルへの相対パスを知りたいことがある。このようなときはPathRelativePathToを利用する。
...が、この関数は引数として相対パスを格納するバッファサイズが指定できないなど今後変更されそうな雰囲気を匂わせているAPIでもある。そのためここでは手作業で変換する関数を作成した。と、もっともらしく書きたいが実際はコードを書いたときにPathRelativePathToの存在を知らなかっただけだったりもする。開発で利用するならMicrosoftが用意しているので誤動作の心配がないPathRelativePathToを利用した方がいいだろう。
ちなみにこの関数もPathRelativePathToも、相対パスで表現できないパスが与えられた場合は処理に失敗する。
依存環境:ATL#include "atlstr.h"
//
// 相対パスの取得
//
//Aから見たBへの相対パスを返す。
//ドライブが異なるなど相対パス化できないときは関数は失敗する。
//パスはフルパスで渡すこと!
//一般的にはAPIとして用意されているPathRelativePathToを利用する
//
bool GetRelatedPath(CAtlString* pstrRelatedPath,LPCTSTR pszPathA,bool bPathAisFolder,LPCTSTR pszPathB,bool bPathBisFolder)
{
int nFind;
CAtlString strPathA;
CAtlString strPathB;
if(pstrRelatedPath == NULL)
return false;
*pstrRelatedPath = _T("");
if(pszPathB == NULL || pszPathB == NULL)
return false;
strPathA = pszPathA;
strPathB = pszPathB;
if(strPathA.GetLength() == 0 || strPathA.GetLength() == 0)
return false;
if(bPathAisFolder == false)
{
//パスAからファイル名部分を取り除き、フォルダ名にする(末尾は「¥」ではない)
nFind = strPathA.ReverseFind(_T('\\'));
if(nFind < 0)
return false; //どんなファイルパスでも最低1つは「¥」があるはず
strPathA = strPathA.Left(nFind);
}
if(bPathBisFolder == false)
{
//パスBからファイル名部分を取り除き、フォルダ名にする(末尾は「¥」ではない)
nFind = strPathB.ReverseFind(_T('\\'));
if(nFind < 0)
return false; //どんなファイルパスでも最低1つは「¥」があるはず
strPathB = strPathB.Left(nFind);
}
if(strPathA.GetLength() == 0 || strPathA.GetLength() == 0)
return false;
nFind = strPathB.Find(strPathA);
if(nFind == 0)
{
//以下のような関係だった場合
//PATHA=c:\\aaa\\bbb\\aa.txt
//PATHB=c:\\aaa\\bbb\\ccc\\aa.txt
//結果=ccc/aa.txt
*pstrRelatedPath = pszPathB;
*pstrRelatedPath = pstrRelatedPath->Right(pstrRelatedPath->GetLength() - strPathA.GetLength() - 1);
return true;
}
while(1)
{
//以下のような関係だった場合
//PATHA=c:\\aaa\\bbb\\ccc\\aa.txt
//PATHB=c:\\aaa\\bbb\\aa.txt
//結果=../aa.txt
// ↑この場合はwhileの1度目のループで結果が取得できる
//
//PATHA=c:\\aaa\\bbb\\ccc\\aa.txt
//PATHB=c:\\aaa\\bbb\\ddd\\aa.txt
//結果=../ddd/aa.txt
// ↑この場合はwhileの2度目のループで結果が取得できる
nFind = strPathA.Find(strPathB);
if(nFind == 0)
{
*pstrRelatedPath = pszPathB;
*pstrRelatedPath = pstrRelatedPath->Right(pstrRelatedPath->GetLength() - strPathB.GetLength() - 1);
strPathA = pszPathA;
strPathA = strPathA.Right(strPathA.GetLength() - strPathB.GetLength() - 1);
nFind = strPathA.Replace(_T("\\"),_T("\\"));
int i;
for(i = 0; i < nFind; i++)
*pstrRelatedPath = _T("..\\") + *pstrRelatedPath;
return true;
}
nFind = strPathB.ReverseFind(_T('\\'));
if(nFind < 0)
break;
strPathB = strPathB.Left(nFind);
}
return false;
}
void Test(void)
{
bool ret;
CAtlString strPath;
ret = GetRelatedPath(&strPath,_T("c:\\aaa\\bbb\\ccc\\aa.txt"),false,_T("c:\\aaa\\bbb\\ddd\\aa.txt"),false);
if(ret)
::MessageBox(NULL,strPath,_T(""),MB_OK);
else
::MessageBox(NULL,_T("取得に失敗しました"),_T(""),MB_OK);
}
