ksj09406 5e667af35a A-1: 폴더·파일 고르기에서 프로그램이 죽던 문제
원인은 경로에 섞인 슬래시였다. 폴더 칸에 C:/Users/... 처럼 슬래시가 섞인
값이 들어 있으면 Directory.Exists 는 통과시키지만, 대화상자의
InitialDirectory 는 윈도우 셸(COM)이 그대로 해석하다 ArgumentException 을
던진다. 그 예외는 대입할 때가 아니라 ShowDialog() 안쪽에서 올라오기 때문에
대입 줄만 try 로 감싸 둔 파일 비교 쪽도 똑같이 죽게 돼 있었다.

- Services/PathUtil.cs 신설. 셸에 넘길 경로는 전부 여기를 거친다.
- 폴더·파일 고르기 둘 다 ShowDialog() 를 감쌌다. 끊긴 네트워크 드라이브처럼
  정규화로 걸러지지 않는 경우에는 시작 폴더 없이 한 번 더 연다.
- 3차 작업 지시서와 진행 상황 문서 신설·갱신.

검사: PathTest 31건 신설(356 -> 387건). 실제 실행으로 왼쪽·오른쪽 폴더 10회
연속, 파일 비교의 기준·내 파일 각 1회 확인.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 09:28:13 +09:00

86 lines
3.0 KiB
C#

using System;
using System.IO;
namespace MergeHub.Services
{
/// <summary>
/// 경로를 "윈도우 셸이 받아들이는 모양"으로 맞춰 주는 곳.
///
/// 왜 필요한가 - .NET 의 <see cref="Directory.Exists"/> 는 <c>C:/a/b</c> 처럼 슬래시를 섞어 써도
/// 통과시킨다. 그런데 파일 대화상자의 <c>InitialDirectory</c> 는 셸(COM)이 그대로 해석하고,
/// 슬래시가 섞인 경로를 받으면 <see cref="ArgumentException"/> 을 던진다.
/// 그 예외는 <c>ShowDialog()</c> 안쪽에서 올라오기 때문에 대입할 때 감싸 봐야 소용이 없고,
/// 잡지 않으면 프로그램이 그대로 죽는다(실제로 "왼쪽 폴더..." 를 누르면 죽던 원인이 이것이다).
///
/// 그래서 셸에 넘기는 경로는 반드시 여기를 거친다. 확인할 수 없는 경로는 빈 문자열이 되고,
/// 부르는 쪽은 그때 <c>InitialDirectory</c> 를 아예 지정하지 않으면 된다.
/// </summary>
public static class PathUtil
{
/// <summary>폴더 경로를 정규화한다. 실제로 있는 폴더가 아니면 빈 문자열.</summary>
public static string FullFolderPath(string? path)
{
var full = Normalize(path);
if (full.Length == 0) return string.Empty;
try
{
return Directory.Exists(full) ? full : string.Empty;
}
catch (IOException)
{
return string.Empty;
}
catch (UnauthorizedAccessException)
{
return string.Empty;
}
}
/// <summary>파일 경로가 든 폴더를 정규화해서 돌려준다. 확인할 수 없으면 빈 문자열.</summary>
public static string FolderOfFile(string? filePath)
{
var full = Normalize(filePath);
if (full.Length == 0) return string.Empty;
try
{
return FullFolderPath(Path.GetDirectoryName(full));
}
catch (ArgumentException)
{
return string.Empty;
}
}
/// <summary>
/// 구분자를 역슬래시로 맞추고 상대 경로를 절대 경로로 편다.
/// 경로로 쓸 수 없는 글자가 들어 있으면 빈 문자열.
/// </summary>
private static string Normalize(string? path)
{
if (string.IsNullOrWhiteSpace(path)) return string.Empty;
try
{
return Path.GetFullPath(path!.Trim());
}
catch (ArgumentException)
{
return string.Empty;
}
catch (NotSupportedException)
{
return string.Empty;
}
catch (PathTooLongException)
{
return string.Empty;
}
catch (IOException)
{
return string.Empty;
}
}
}
}