[MFC] 실행 파일 경로 구하기

Visual C++ 로 개발 당시 실행하고 있는 파일의 현재 경로를 구하려고 GetCurrentDirectory 라는 API 로 사용했다가 낭패를 본적이 있습니다.
이 GetCurrentDirectory 프로그램의 작업디렉토리이지 절대 프로그램이 존재하는 디렉토리가 아닙니다. 이를 해결하기 위하여 다음 함수를 사용합니다.

GetModuleFileName

우선 중요 API 는 위의 GetModuleFileName 입니다.

DWORD GetModuleFileName(
  [in, optional] HMODULE hModule,
  [out]          LPSTR   lpFilename,
  [in]           DWORD   nSize
);

첫번째 인수를 살펴보면 현재 실행되고 있는 모듈의 핸들 또는 NULL이 옵니다. 

만약 첫번째 인수로 NULL이 온다면, 자신의 실행 경로를 반환합니다. 

두번째 인수는 자신의 실행 경로를 받을 포인터가, 

세번째 인수로는 자신의 실행 경로가 들어갈 버퍼의 길이를 말합니다. 

GetModuleFileName API 함수를 통해, 자신의 실행 경로를 받아와보도록 합시다.

#include <stdio.h>
#include <windows.h>

int main()
{
	char Path[MAX_PATH];
	GetModuleFileName(NULL, Path, MAX_PATH);

	printf("PATH : %s\n", Path);
	return 0;
}
결과:
PATH : D:\ConsoleApplication1\x64\Debug\ConsoleApplication1.exe

첫번째 인수를 NULL로 준 덕분에, Path에 자신의 실행 경로가 들어갑니다. 출력해보면, 실행 경로가 출력됨을 확인하실 수 있습니다.

그러나 문제점이 있습니다. 

실행 파일의 파일명까지 반환됐네요.. 

우리가 원하는것은 폴더만 구하는 것입니다. 

위 실행경로에서 파일명을 자르는 방법은 다양합니다. 그중 몇가지 소개해 드리겠습니다.

1. CString 의 Left 메소드를 이용하여 자르는 방식

다음 처럼 CString 으로 Path의 뒤쪽부터 앞으로 검색을 하여 첫 “\” 가 나오는 부분을 자르면 됩니다. 

뒤쪽부터 검색하는 메소드인 ReversdFind 를 이용해 뒤에서 검색을 하고 자릅니다. 

D:\WORK\Test\Debug\Test.exe 일경우 뒤쪽의 \ 를 찾아서 앞의 Path 만 취하는 방식입니다.

CString GetExecutableFolderPath()
{
    CString folderPath;
    GetModuleFileName(NULL, folderPath.GetBuffer(_MAX_PATH), _MAX_PATH);
    folderPath.ReleaseBuffer();

    int pos = folderPath.ReverseFind('\\');
    if (pos != -1)
        folderPath = folderPath.Left(pos + 1);

    return folderPath;
}

int main()
{
    CString path = GetExecutableFolderPath();
    wprintf(L"Path  : %s\n", path);
}
결과:

결과 path : D:\ConsoleApplication1\x64\Debug

2. Win32API 인 PathRemoveFileSpec 를 이용하는 방법

PathRemoveFileSpec 는 파일명이 포함된 경로에서 파일명을 제외한 Path 만을 남겨줍니다.

MSDN 의 예제를 잠시 살펴 보겠습니다.
참고로 shlwapi.lib 를 참조해주어야 합니다.

#include <windows.h>
#include <iostream>
#include "Shlwapi.h"

void main(void)
{
    // Path to include file spec.
    char buffer_1[] = "C:\\TEST\\sample.txt";
    char* lpStr1 = buffer_1;

    // Print the path with the file spec.
    printf("원본 Path : %s\n", lpStr1);

    // "PathRemoveFileSpec". 실행
    PathRemoveFileSpec(lpStr1);

    // Print the path without the file spec.
    printf( "결과 path : %s\n", lpStr1);
}
결과:
원본 Path : C:\TEST\sample.txt
결과 path : C:\TEST

우리가 원하는 Api  이네요.

자 그럼 실행 파일 경로를 구해보죠

#include <windows.h>
#include <iostream>
#include "Shlwapi.h"

void main(void)
{
    char szTemp[300];
    GetModuleFileName(NULL, szTemp, 300);

    PathRemoveFileSpec(szTemp);

    // Print the path without the file spec.
    printf( "결과 path : %s\n", szTemp);
}
결과:
결과 path : D:\ConsoleApplication1\x64\Debug

와우 참 쉽죠..

참고로 파일명에서 확장자만 따라 분리하는 API 도 존재합니다. 

PathFindExtension 입니다.

PathFindExtension(a.cpp) 일경우 .cpp 만을 분리해 냅니다.

3. _splitpath_s  라는 함수로 경로를 분리

드라이브, 폴더, 파일이름, 확장자 이렇게 4로 분리 할수 있는데 

분리된 토큰중 드라이브와 폴더를 남기고 다시 조합하는 방법입니다.

_splitpath_s  는 경로에서 분리하는 것이고

_makepath_s 는 분리된 토큰을 붙이는 함수입니다.

#include <windows.h>
#include <iostream>

void main(void)
{
    char lpszFilePath[_MAX_PATH];
    if (::GetModuleFileName(NULL, lpszFilePath, _MAX_PATH) > 0)
    {
        char path_buffer[_MAX_PATH];
        char drive[_MAX_DRIVE];
        char dir[_MAX_DIR];
        char fname[_MAX_FNAME];
        char ext[_MAX_EXT];

        _splitpath_s(lpszFilePath, drive, _MAX_DRIVE, dir, _MAX_DIR, NULL, 0, NULL, 0);
        _splitpath_s(lpszFilePath, NULL, 0, NULL, 0, fname, _MAX_FNAME, ext, _MAX_EXT);
        _makepath_s(path_buffer, drive, dir, fname, ext);

        printf("결과 drive : %s\n", drive);
        printf("결과 dir : %s\n", dir);
        printf("결과 fname : %s\n", fname);
        printf("결과 ext : %s\n", ext);
        printf("결과 Full Path : %s\n", path_buffer);


        _makepath_s(path_buffer, drive, dir,NULL,NULL);
        printf("결과 Path : %s\n", path_buffer);
    }
}
결과:
결과 drive : D:
결과 dir : \ConsoleApplication1\x64\Debug\
결과 fname : ConsoleApplication1
결과 ext : .exe
결과 Full Path : D:\ConsoleApplication1\x64\Debug\ConsoleApplication1.exe
결과 Path : D:\ConsoleApplication1\x64\Debug\

잘 조합하면 유용한 값을 사용 할 수 있을것 같습니다.

결론

MFC 에서 현재 실행 폴더를 구하고 원하는 결과 값을 찾기 위해 몇가지 방법을 알아보았습니다.

Leave a Comment