Game Develop

[C++][WINAPI]FileDialog(파일열기대화상자) 오픈해서 파일경로명 얻어오기 본문

C++/winAPI

[C++][WINAPI]FileDialog(파일열기대화상자) 오픈해서 파일경로명 얻어오기

MaxLevel 2021. 11. 14. 00:10

두가지 방법이 있다.

첫번째방법은 상당히 오래전부터 사용한 방법이고, 두번째 방법은 윈도우비스타 이후부터 권장되어진 방법이다.

 

https://docs.microsoft.com/en-us/windows/win32/api/commdlg/nf-commdlg-getopenfilenamea

 

GetOpenFileNameA function (commdlg.h) - Win32 apps

Creates an Open dialog box that lets the user specify the drive, directory, and the name of a file or set of files to be opened.

docs.microsoft.com

MSDN에서 직접 언급했다.

샘플코드같은것들도 들어가서 조금만 뒤지면 나오긴 한다. 다만 이 글을 보시는분은 그냥 아래 따라하면 된다.

헤더는 commdlg.h를 인클루드한다. 어떤 헤더를 인클루드해야되는지, 라이브러리를 써야하는지는 MSDN에 전부 나와있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    OPENFILENAME OFN;
    TCHAR filePathName[100= L"";
    TCHAR lpstrFile[100= L"";
    static TCHAR filter[] = L"모든 파일\0*.*\0텍스트 파일\0*.txt\0fbx 파일\0*.fbx";
 
    memset(&OFN, 0sizeof(OPENFILENAME));
    OFN.lStructSize = sizeof(OPENFILENAME);
    OFN.hwndOwner = hWnd;
    OFN.lpstrFilter = filter;
    OFN.lpstrFile = lpstrFile;
    OFN.nMaxFile = 100;
    OFN.lpstrInitialDir = L".";

    if (GetOpenFileName(&OFN) != 0) {
        wsprintf(filePathName, L"%s 파일을 열겠습니까?", OFN.lpstrFile);
        MessageBox(hWnd, filePathName, L"열기 선택", MB_OK);
 
        string t = ToString(OFN.lpstrFile);
        
        int a = 0;
    }
cs

L이 붙은거는 한글과같은 멀티바이트때문에 사용하는건데 보통 widecharacter라고 한다.

즉, OFN.lpstrFile은 wstring이기 때문에 따로 string으로 변환해주는 함수를 만들어줘야한다.

 

1
2
3
4
5
6
string Utility::ToString(wstring value)
{
    string temp;
    temp.assign(value.begin(), value.end());
    return temp;
}
cs

이러면 끝. 파일 열고싶은 부분에서 저 코드 실행하면된다. 참고로 winapi를 조금이라도 해보셨다는 가정하에 쓴거라서 hWnd에 대해 따로 설명 안하려했는데 나같은경우는 winapi프로젝트 만들고 InitInstance함수에서 CreateWindowW로 값 받고, extern해서 사용하고 있다. 이미 winapi프로젝트로 생성해서 개발중이신분들은 hWnd값은 알아서 잘 받아오실테니까 그냥 쓰시면 된다.

 

 

두번째 방법에서 사용하는 winapi. MSDN링크는 아래와 같다.

https://docs.microsoft.com/en-us/windows/win32/shell/common-file-dialog

 

Common Item Dialog - Win32 apps

Starting with Windows Vista, the Common Item Dialog supersedes the older Common File Dialog when used to open or save a file.

docs.microsoft.com

마이크로소프트 깃허브에 있는 샘플코드 링크는 아래와 같다.

https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/winui/shell/appplatform/commonfiledialog/CommonFileDialogApp.cpp

 

GitHub - microsoft/Windows-classic-samples: This repo contains samples that demonstrate the API used in Windows classic desktop

This repo contains samples that demonstrate the API used in Windows classic desktop applications. - GitHub - microsoft/Windows-classic-samples: This repo contains samples that demonstrate the API u...

github.com

 

참고로 MSDN링크에도 똑같은 샘플코드있다. 다만 깃허브에 올라와있는 코드가 더 보기편하니까..

샘플코드에 있는 CDialogEventHandler라는 클래스 따로 만들어주고, FileDialog를 열려는 씬에서 CDialogEventHandler_CreateInstance()함수 만들어줘서 hr값 리턴받아서 사용하면 된다. 

 

그리고 주의할점이, 딱히 빠뜨린거 없이 샘플코드가져다 썼는데 LNK2019에러가 뜰 수도 있다.

LNK2019는 발생하는 원인이 워낙 다양해서 악명높은 에러다. 이 경우에는 헤더파일은 찾아서 컴파일시 오류는 안나지만, lib파일을 찾지못해서 링크에러가 나는것같다.

나같은경우는 CDialogEventHandler의 메소드중에 QueryInterface라고 있는데, 이게 리턴을 Shlwapi.h에 있는 QISearch라는 함수의 반환값으로 리턴한다.근데 QISearch에서 LNK2019가 떴다. LNK2019에러 뜨면 대충 어디서 링크에러가 뜨는지는 보여준다. 아마 나만 그럴거같진 않고 다른분들도 뜰 가능성이 상당히 높다. 그럴땐 그냥 lib파일을 아래와 같이 명시적으로 선언해주면 된다.

#pragma comment(lib, "shlwapi")

 

위와 같이 해주던지 아니면 프로젝트 셋팅에서 연결시켜주던지 하면 된다. 

선택된 파일의 경로는 pszFilePath에 담기니까 가져가서 쓰면된다. 마찬가지로 wstring형이다.