C# を久々に使ったので復習コード。
static IEnumerable<FoundFileData> FindFile(string dir, string file = "*", bool recursive = false)
{
string search = Path.Combine(dir, "*");
if (search.StartsWith(@"\\") == false && 250 < search.Length)
{
search.Insert(0, @"\\?\");
}
int error;
WIN32_FIND_DATA fd = new WIN32_FIND_DATA();
using (var h = new FindFileHandle(search, fd))
{
if (h.IsInvalid)
{
error = Marshal.GetLastWin32Error();
#if false
switch ((Win32Error)error)
{
case Win32Error.ERROR_PATH_NOT_FOUND:
case Win32Error.ERROR_FILE_NOT_FOUND:
case Win32Error.ERROR_NO_TOKEN:
// UNC を指定したときに発生
// 存在しない UNC を指定したときに発生
break;
default:
throw new System.ComponentModel.Win32Exception();
}
#endif
}
else
{
do
{
if (PathMatchSpecEx(fd.fileName, file, PMSF_MULTIPLE) == S_OK)
{
yield return new FoundFileData(dir, fd);
}
if (recursive && fd.fileName != "." && fd.fileName != ".." && (fd.fileAttributes & FILE_ATTRIBUTES_DIRECTORY) != 0)
{
IEnumerable<FoundFileData> sub = FindFile(Path.Combine(dir, fd.fileName), file, true);
var e = sub.GetEnumerator();
while (e.MoveNext())
{
yield return e.Current;
}
}
} while (h.FindNext(fd));
error = Marshal.GetLastWin32Error();
if (error != (int)Win32Error.ERROR_NO_MORE_FILES)
{
throw new System.ComponentModel.Win32Exception();
}
}
}
yield break;
}
static void Main(string[] args)
{
try
{
foreach (FoundFileData fd in FindFile(@"c:\Users\AsladaGSX\Downloads", "*.zip; *.exe", true))
{
Debug.WriteLine(Path.Combine(fd.dir, fd.fileName));
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
以下、p/Invoke
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class WIN32_FIND_DATA
{
public UInt32 fileAttributes = 0;
// creationTime was an embedded FILETIME structure.
public UInt32 creationTime_lowDateTime = 0;
public UInt32 creationTime_highDateTime = 0;
// lastAccessTime was an embedded FILETIME structure.
public UInt32 lastAccessTime_lowDateTime = 0;
public UInt32 lastAccessTime_highDateTime = 0;
// lastWriteTime was an embedded FILETIME structure.
public UInt32 lastWriteTime_lowDateTime = 0;
public UInt32 lastWriteTime_highDateTime = 0;
public UInt32 nFileSizeHigh = 0;
public UInt32 nFileSizeLow = 0;
public UInt32 dwReserved0 = 0;
public UInt32 dwReserved1 = 0;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String fileName = null;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public String alternateFileName = null;
}
public class FoundFileData
{
public string dir { get; private set; }
public string fileName { get; private set; }
/* めんどくさい。略。 */
internal FoundFileData(string dir, WIN32_FIND_DATA fd)
{
this.dir = dir;
this.fileName = fd.fileName;
}
}
const UInt32 FILE_ATTRIBUTES_DIRECTORY = 0x00000010;
class FindFileHandle : SafeHandle
{
private static IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
private static extern IntPtr FindFirstFile(string fileName, [In, Out] WIN32_FIND_DATA findFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
private static extern int FindClose(IntPtr hFindFile);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
private static extern int FindNextFile(IntPtr hFindFile, [In, Out] WIN32_FIND_DATA findFileData);
public FindFileHandle(string search, [In, Out] WIN32_FIND_DATA findFileData) : base(INVALID_HANDLE_VALUE, true)
{
this.handle = FindFirstFile(search, findFileData);
}
public override bool IsInvalid
{
get { return handle == INVALID_HANDLE_VALUE; }
}
protected override bool ReleaseHandle()
{
if (handle != IntPtr.Zero && handle != INVALID_HANDLE_VALUE)
{
return 0 != FindClose(handle);
}
return true;
}
public bool FindNext([In, Out] WIN32_FIND_DATA fd)
{
return 0 != FindNextFile(handle, fd);
}
}
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
private static extern UInt32 PathMatchSpecEx(string pszFile, string pszSpec, UInt32 dwFlags);
private const UInt32
// The pszSpec parameter points to a single file name pattern to be matched.
PMSF_NORMAL = 0x00000000,
// The pszSpec parameter points to a semicolon-delimited list of file name patterns to be matched.
PMSF_MULTIPLE = 0x00000001,
// If PMSF_NORMAL is used, ignore leading spaces in the string pointed to by pszSpec.
// If PMSF_MULTIPLE is used, ignore leading spaces in each file type contained in the string pointed to by pszSpec.
// This flag can be combined with PMSF_NORMAL and PMSF_MULTIPLE.
PMSF_DONT_STRIP_SPACES = 0x00010000;
// HRESULT value.
const UInt32
S_OK = 0,
S_FALSE = 1;
'''