본문으로 바로가기

파일의 IT 블로그

  1. Home
  2. 프로그래밍/C#
  3. [C#] 전체 드라이브에서 특정 파일 빠르게 찾기 :: 게임 경로 인식

[C#] 전체 드라이브에서 특정 파일 빠르게 찾기 :: 게임 경로 인식

· 댓글개 · KRFile
using System;

namespace MyApp // Note: actual namespace depends on the project name.
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            Program myProgram = new Program();
            string? result = await myProgram.search_file_all_dir_async("isaac-ng.exe");
            Console.WriteLine($"탐색된 경로 : {result}");
        }
        
		/*
        root 경로에서 재귀적으로 모든 파일을 탐색하고 "file_name" 으로 시작하는 파일의 절대 경로를 취득
        취소 토큰에서 취소가 감지되면 즉시 중단
        취소 조건 : 파일을 찾으면 다른 드라이브 탐색은 모두 취소
        해당 함수는 특정 드라이브에서만 파일을 찾는것이므로, 해당 함수를 비동기로 돌려서 병렬로 돌리면 전체 드라이브에서 파일을 찾을 수 있음.
        병렬로 돌리는 코드는 search_file_all_dir_async 함수임.
        */
        
        public string? search_file_all_dir(string file_name, string root = @"C:\", CancellationToken token = default)
        {
            try
            {
                foreach (string dir in Directory.GetDirectories(root))
                {
                    try
                    {
                        string[] files = Directory.GetFiles(dir, file_name);

                        if (files.Length > 0)
                        {
                            return files[0];
                        }
                    }
                    catch (Exception)
                    {
                        continue;
                    }

                    if (token.IsCancellationRequested)
                    {
                        return null;
                    }

                    // 재귀 호출하여 하위 디렉토리 탐색
                    string? found_file = search_file_all_dir(file_name, dir, token);

                    if (found_file != null)
                    {
                        return found_file;
                    }
                }
            }
            // 권한이 없는 디렉토리는 건너뛰기
            catch (UnauthorizedAccessException)
            {
                return null;
            }

            return null;
        }
		
        /*
        파일 탐색 함수를 비동기 & 병렬로 돌려서
        전체 드라이브에서 파일을 빠르게 탐색하여 "file_name" 의 절대 경로를 획득할 수 있도록 함.
        */
        
        public async Task<string?> search_file_all_dir_async(string filename)
        {
            string[] drives = Environment.GetLogicalDrives();
            var tasks = new List<Task<string?>>();
            CancellationTokenSource cts = new CancellationTokenSource();

            foreach (string drive in drives)
            {
                Console.WriteLine($"Searching {drive}...");

                tasks.Add(Task.Run(() =>
                {
                    try
                    {
                        string? result = search_file_all_dir(filename, drive, cts.Token);
                        Console.WriteLine($"Completed search on {drive}");
                        return result;
                    }
                    catch (OperationCanceledException)
                    {
                        return null;
                    }
                }));
            }

            while (tasks.Count > 0)
            {
                Task<string?> finishedTask = await Task.WhenAny(tasks);

                if (!string.IsNullOrEmpty(finishedTask.Result))
                {
                    cts.Cancel();
                    return finishedTask.Result;
                }

                tasks.Remove(finishedTask);
            }

            return null;
        }
    }
}

해당 코드를 사용하면 전체 드라이브에서 특정 파일을 빠르게(?) 찾아낼 수 있습니다.

보통 어떤 프로그램을 사용해보면 게임 경로를 자동으로 인식해서 찾아내는 기능이 있는 경우가 있는데 이를 구현하고 싶어서 작성한 코드입니다.

 

.NET 6.0 에서 실행시켰으며, string에 null을 허용하고자 string? 타입을 활용했습니다. (널 허용은 6.0부터 업데이트된 내용이라 그냥 string으로 써도 무관하긴함)

 

search_file_all_dir() 함수는 특정 드라이브에서 "동기적" , 재귀적으로 모든 폴더와 파일을 탐색하며 특정 파일을 찾아내는 함수입니다.

당연히 search_file_all_dir() 을 그냥 돌리면 느리니깐 search_file_all_dir_async() 에서 모든 드라이브에 대해 Task를 생성해서 비동기적으로 search_file_all_dir() 함수를 돌립니다.

 

저는 드라이브를 총 5개 쓰고 있는데 이러면 비동기 작업이 5개 돌아갑니다. (정확히는 쓰레드가 5개 생성되는걸로 보입니다. 디버깅창에서도 확인했고 Task 자체가 내부적으로 Thread로 구현되어 있기 때문.)

 

static async Task Main(string[] args)
{
    Program myProgram = new Program();
    string? result = await myProgram.search_file_all_dir_async("isaac-ng.exe");
    Console.WriteLine($"탐색된 경로 : {result}");
}

Searching C:\...
Searching D:\...
Searching E:\...
Searching F:\...
Searching G:\...
Completed search on E:\
탐색된 경로 : E:\SteamLibrary\steamapps\common\The Binding of Isaac Rebirth\isaac-ng.exe

사용법에 관련해선 이런식으로 사용하면 됩니다. 전체 드라이브에서 비동기적으로 파일을 찾다가 특정 파일을 찾아내는 순간 모든 비동기 작업(Task) 은 취소가 되고 찾은 절대 경로가 반환됩니다.

 

저는 아이작이란 게임의 경로를 전체 드라이브에서 자동 인식하기 위해 해당 코드를 사용중입니다.

 

사실은 해당 코드는 ChatGPT로 생성했습니다 ㅋㅋㅋ.. "작성" 했다고 표현했지만 사실상 "생성" 했다고 표현한게 맞은 셈이지요. C# 한지도 좀 됐고 Task 개념도 공부하기 귀찮아서.. 저는 지금까지 Thread만 사용했었거든요. 그래도 GPT가 생성한 코드가 제대로 작동을 안해서 조금 고치고 작동하도록 고친 예제긴 합니다.

 

참고로 "빠르다" 라는 표현은 자신 컴퓨터 드라이브 사양에 따라 천차만별입니다.

당연하지만 드라이브 속도가 느릴수록, 드라이브 갯수가 많을수록 탐색 속도가 천차만별로 다운됩니다.

 

다만 해당 코드는 각 드라이브에 따라 쓰레드를 생성하기 때문에 속도를 조금더 늘리고 싶다면 폴더 별로 쓰레드(또는 Task)를 생성하는 방법이 있습니다.

그렇게 되면 드라이브에 존재하는 모든 폴더에 대해 쓰레드가 한계까지 생성되기 때문에 CPU에 엄청난 부하와 렉이 발생할 것이라 그렇게 까진 하지 않았습니다.. 

 

 

SNS 공유하기
💬 댓글 개
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.