/// Copyright(c) 2016 pakkin. All Rights Reserved.
/// [改訂履歴]
/// 2016.01.26 作成
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
private static string Rpad(string target, int padLen)
{
int len = Encoding.GetEncoding("Shift-Jis").GetByteCount(target);
if (padLen - len > 0) target += string.Join("", Enumerable.Repeat(" ", padLen - len));
return target;
}
private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern uint FormatMessage(
[In] uint dwFlags
, [In] IntPtr lpSource
, [In] uint dwMessageId
, [In] uint dwLanguageId
, [Out] StringBuilder lpBuffer
, [In] int nSize
, [In] IntPtr Arguments
);
private static string GetLastWind32ErrorMessage()
{
uint cd = (uint)Marshal.GetLastWin32Error();
StringBuilder buff = new StringBuilder(255);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, cd, 0, buff, buff.Capacity, IntPtr.Zero);
return buff.ToString().TrimEnd('\r','\n');
}
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
private static extern bool PathRelativePathTo(
[Out] StringBuilder pszPath
, [In] string pszFrom
, [In] FileAttributes dwAttrFrom
, [In] string pszTo
, [In] FileAttributes dwAttrTo
);
private static string GetRelativePath(string baseDir, string destDir)
{
StringBuilder sb = new StringBuilder(260);
bool res = PathRelativePathTo(sb, baseDir, FileAttributes.Directory, destDir, FileAttributes.Directory);
//別ドライブの場合絶対パスを返したいのであればコメントアウトを解除
//return res ? sb.ToString() : destDir;
return res ? sb.ToString() : GetLastWind32ErrorMessage();
}
static void Main(string[] args)
{
string[,] tests = new string[,] {
{ @"C:\TEMP\1", @"c:" }
, { @"C:\TEMP\1", @"c:\" }
, { @"C:\TEMP\1", @"c:\temp" }
, { @"C:\TEMP\1", @"c:\temp\1" }
, { @"C:\TEMP\1", @"c:\temp\1\1" }
, { @"C:\TEMP\1", @"c:\temp\2" }
, { @"C:\TEMP\1", @"c:\temp\2\2" }
, { @"C:\TEMP\1", @"c:\temx" }
, { @"C:\TEMP\1", @"c:\temx\1" }
, { @"C:\TEMP\1", @"c:\temx\1\1" }
, { @"C:\TEMP\1", @"c:\temx\2" }
, { @"C:\TEMP\1", @"c:\temx\2\2" }
, { @"C:\TEMP\1", @"d:\" }
, { @"C:\TEMP\1", @"d:\temp" }
, { @"\\NET\TEMP\1", @"\\net2" }
, { @"\\NET\TEMP\1", @"\\net2\temp\1" }
, { @"\\NET\TEMP\1", @"\\net" }
, { @"\\NET\TEMP\1", @"\\net\temp" }
, { @"\\NET\TEMP\1", @"\\net\temp\1" }
, { @"\\NET\TEMP\1", @"\\net\temp\1\1" }
, { @"\\NET\TEMP\1", @"\\net\temp\2" }
, { @"\\NET\TEMP\1", @"\\net\temp\2\2" }
, { @"\\NET\TEMP\1", @"\\net\temx" }
, { @"\\NET\TEMP\1", @"\\net\temx\1" }
, { @"\\NET\TEMP\1", @"\\net\temx\1\1" }
, { @"\\NET\TEMP\1", @"\\net\temx\2" }
, { @"\\NET\TEMP\1", @"\\net\temx\2\2" }
};
Console.WriteLine("起点 ターゲット GetRelativeDirectory PathRelativePathTo(API) 比較");
Console.WriteLine("------------ -------------- -------------------- -------------------------------------- ----");
for( int i = 0; i < tests.GetLength(0); i++)
{
string result1 = GetRelativeDirectory(tests[i, 0], tests[i, 1]);
string result2 = GetRelativePath(tests[i, 0], tests[i, 1]);
int comp = result1.CompareTo(result2);
Console.WriteLine("{0,-12} {1,-14} {2,-20} {3} {4}", tests[i, 0], tests[i, 1], result1, Rpad(result2, 38), comp);
}
Console.ReadKey();
}
private static string GetRelativeDirectory(string baseDir, string destDir)
{
// 同一パス→"."
if (destDir.Equals(baseDir, StringComparison.CurrentCultureIgnoreCase)) return ".";
// パスを解体
string[] dd = destDir.Split('\\');
string[] bd = baseDir.ToLower().Split('\\');
// 未設定→絶対パス
if (dd.Length == 0 || bd.Length == 0) return destDir;
// ネットワークパス個別処理
if (dd[0].Length == 0) dd[2] = @"\\" + dd[2];
if (bd[0].Length == 0) bd[2] = @"\\" + bd[2];
dd = dd.Where(x => x.Length > 0).ToArray();
bd = bd.Where(x => x.Length > 0).ToArray();
// ドライブ違い→絶対パス
if (!dd[0].Equals(bd[0], StringComparison.CurrentCultureIgnoreCase)) return destDir;
// その他→baseからdestまでの経路算出
var ddi = dd.Select((x, i) => new { vx = x.ToLower(), ix = i }); // 配列番号固定化(ix)
var ddf = ddi.Where(x => x.vx != (x.ix < bd.Length ? bd[x.ix] : "")); // パス相違配列番号のみ抽出
int p = ddf.Count() == 0 ? dd.Length : ddf.Select(x => x.ix).Min(); // パス相違開始位置(最小)取得
string[] up = Enumerable.Repeat("..", bd.Length - p).ToArray(); // baseからの上位階層移動
if (up.Length == 0) up = new string[] { "." }; // 上位階層移動無し
string[] down = dd.Where((x, i) => i >= p).ToArray(); // 下位階層移動
return string.Join("\\", up.Concat(down)); // 結合
}
}
}
Copyright(c) 2014-2022 pakkin. All Rights Reserved.