x0FANSA-hk 님의 블로그
Windows에서 시스템 창을 띄우는 C언어 함수 본문
파이썬 tkinter의 경우 매우 쉽게 GUI를 만들 수 있습니다. 그러나 C언어의 경우 이러한 전용 GUI 모듈이 없습니다. 그래도 C언어 자체가 오래되고 널리퍼진 언어이기에, Windows 운영체제에서 사용가능한 기계어로 컴파일되는 경우에서만큼은 windows.h를 통해서 간단한 GUI를 만들 수 있게 해줍니다. 따라서 오늘은 이러한 함수들을 간략하게 다뤄보려고 합니다.
MessageBoxW()
#include <windows.h>
int main(void)
{
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_OK);
return 0;
}

가장 기초적인 함수 입니다. 실행하면 이러한 팝업창이 출력됩니다. 보시다시피 두번째 매개변수는 본문에, 세번째 매개변수는 창제목에 있음을 확인할 수 있습니다. 사실 이러한 함수의 매개변수들을 외울 필요는 없습니다. windows에서 제공하는 함수들 대부분은 Microsoft사에서 미리 함수구조를 공유합니다. 가령 MessageBoxW() 함수의 경우 아래의 주소에서 확인할 수 있습니다.
https://learn.microsoft.com/ko-kr/windows/win32/api/winuser/nf-winuser-messageboxw
MessageBoxW 함수(winuser.h) - Win32 apps
MessageBoxW(유니코드) 함수는 시스템 아이콘, 단추 집합 및 간략한 애플리케이션별 메시지가 포함된 모달 대화 상자를 표시합니다.
learn.microsoft.com
int MessageBoxW(
[in, optional] HWND hWnd,
[in, optional] LPCWSTR lpText,
[in, optional] LPCWSTR lpCaption,
[in] UINT uType
);
MS사 사이트에 있는 내용을 가지고 간략하게 매개변수를 해석하자면...
1번 매개변수: 만들 메시지 상자의 소유자 창에 대한 핸들입니다. 테스트 삼아 그냥 바로 띄우면 NULL을 써도 문제없지만, 특정 프로그램이 띄우면 그 프로그램에 대한 값이 들어가야 합니다.
2번 매개변수: 표시할 메세지 입니다. 위 예시 알림창의 가운데에 있는 "Hello, World!"에 해당하는 것입니다.
3번 매개변수: 표시할 창 제목 입니다. 위 예시 알림창의 좌측상단에 있는 "알림"에 해당하는 것입니다.
4번 매개변수: 같은 메세지박스여도 예/아니요 로 선택을 하거나, 구성이 다르거나, 아이콘을 넣게 할수도 있습니다. 이때의 메세지박스 형태를 결정합니다.






//왼쪽상단부터 차례대로
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_YESNO);
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_ABORTRETRYIGNORE);
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_HELP);
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_ICONEXCLAMATION);
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_ICONINFORMATION);
MessageBoxW(NULL, L"Hello, World!", L"알림", MB_ICONERROR);
이런식으로 말이죠!
GetOpenFileNameW()
#include <windows.h>
#include <stdio.h>
wchar_t ADDRESS[MAX_PATH]=L"";
OPENFILENAMEW ofn;
int main() {
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize=sizeof(ofn);
ofn.hwndOwner=NULL;
ofn.lpstrFile=ADDRESS;
ofn.nMaxFile=MAX_PATH;
ofn.lpstrFilter=L"모든 파일 (*.*)\0*.*\0텍스트 파일 (*.txt)\0*.txt\0\0";
ofn.Flags=OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST;
if (GetOpenFileNameW(&ofn)) {
MessageBoxW(NULL,ADDRESS,L"Selected File Address",MB_OK);
}
getchar();
return 0;
}
GetOpenFileNameW()는 파일탐색기를 열어서 파일명과 파일경로를 가져오는 함수입니다. 이때 파일탐색기 자체가 마우스로 조작하는 GUI이기에, 직접 터미널창에 경로를 타자로 칠 필요 없이 가져올 수 있습니다. 또한, 호출된 그 시점의 파일탐색기를 열기때문에, 코드가 작성되고 파일 경로가 변했어도 이에 대응을 할 수 있습니다.


https://learn.microsoft.com/ko-kr/windows/win32/api/commdlg/nf-commdlg-getopenfilenamew
GetOpenFileNameW 함수(commdlg.h) - Win32 apps
사용자가 열 파일 또는 파일 집합의 드라이브, 디렉터리 및 이름을 지정할 수 있는 열기 대화 상자를 만듭니다. (유니코드)
learn.microsoft.com
BOOL GetOpenFileNameW(
[in, out] LPOPENFILENAMEW unnamedParam1
);
역시 매개변수를 해석해 보겠습니다.
1번 매개변수: LPOPENFILENAMEW라는 자료형의 변수 하나가 끝입니다?? 일거 같지만 LPOPENFILENAMEW은 포인터입니다. 즉, 주소만 가지고 있고 실체는 매개변수가 가르키는 구조체변수 입니다. LPOPENFILENAMEW자료형은...
typedef OPENFILENAMEW* LPOPENFILENAMEW;
이기에, 실질적으로 사용하려면 OPENFILENAMEW을 알아야 합니다.
- 1번 매개변수가 가르키는 구조체:
https://learn.microsoft.com/ko-kr/windows/win32/api/commdlg/ns-commdlg-openfilenamew
OPENFILENAMEW(commdlg.h) - Win32 apps
GetOpenFileName 및 GetSaveFileName 함수가 열기 또는 다른 이름으로 저장 대화 상자를 초기화하는 데 사용하는 정보를 포함합니다. 사용자가 대화 상자를 닫은 후 시스템은 이 구조에서 사용자의 선택
learn.microsoft.com
typedef struct tagOFNW {
DWORD lStructSize;
HWND hwndOwner;
HINSTANCE hInstance;
LPCWSTR lpstrFilter;
LPWSTR lpstrCustomFilter;
DWORD nMaxCustFilter;
DWORD nFilterIndex;
LPWSTR lpstrFile;
DWORD nMaxFile;
LPWSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCWSTR lpstrInitialDir;
LPCWSTR lpstrTitle;
DWORD Flags;
WORD nFileOffset;
WORD nFileExtension;
LPCWSTR lpstrDefExt;
LPARAM lCustData;
LPOFNHOOKPROC lpfnHook;
LPCWSTR lpTemplateName;
LPEDITMENU lpEditInfo;
LPCSTR lpstrPrompt;
void *pvReserved;
DWORD dwReserved;
DWORD FlagsEx;
} OPENFILENAMEW, *LPOPENFILENAMEW;
당연히 이걸 다 아는건 불가능하고 불필요합니다. 필요한거만 살펴보죠.
lpstrFilter: 확장자 필터입니다. 가끔 우리가 탐색기로 파일을 고를 때, 특정 유형의 파일만 강제하는 경우가 있습니다. 이때 들어가는 매개변수입니다. 가령...


//형식: "보여줄이름\0확장자\0"
wchar_t szFilter[] = L"mix Files (*.mix)\0*.mix\0All Files (*.*)\0*.*\0";
ofn.lpstrFilter = szFilter;
MeshMixer로 저장된 파일을 가져오려면, ".mix"확장자의 파일만 가져올 수 있는데, 이때 다른것을 선택하지 않게 .mix만 선택가능하도록 할 수 있습니다.
lpstrFile: 선택된 파일의 경로를 담는 버퍼입니다. 저희가 주소를 알아내려면 해당 멤버의 값을 추출하면 됩니다.
ofn.lpstrFile=ADDRESS;
...
if (GetOpenFileNameW(&ofn)) {
MessageBoxW(NULL,ADDRESS,L"Selected File Address",MB_OK);
}
위의 예제중 일부를 가져온건데, 이렇게 구조체에 변수만 대입해두면, GetOpenFileNameW()함수가 실행될 때, 알아서 ADDRESS에도 경로를 저장해 줍니다.
Flags: 대화상자의 동작방식을 결정합니다.
아무튼간에, 이 거대하고 거대한 OPENFILENAMEW구조체 그 자체를 매개변수로 직접 넘기는건 비효율적이기에, 포인터로 만들어서 주소로 넘겨서 동작합니다.
여기서 두가지 주의할 점이라면, 파일을 직접 여는 함수는 아닙니다. 즉, 실행은 하지 않고 파일 주소만 가져다줍니다. 두번째로 파일'만' 됩니다. 즉, 폴더 경로는 가져올 수 없습니다.
ChooseColorW()
#include <windows.h>
#include <stdio.h>
#include <wchar.h>
static COLORREF acrCustClr[16];
CHOOSECOLORW cc;
int main() {
memset(&cc, 0, sizeof(cc));
cc.lStructSize=sizeof(cc);
cc.hwndOwner=NULL;
cc.lpCustColors=(LPDWORD)acrCustClr;
cc.rgbResult=RGB(255, 0, 0);
cc.Flags=CC_FULLOPEN|CC_RGBINIT;
if (ChooseColorW(&cc)) {
BYTE r=GetRValue(cc.rgbResult);
BYTE g=GetGValue(cc.rgbResult);
BYTE b=GetBValue(cc.rgbResult);
WCHAR szMessage[32];
swprintf_s(szMessage, 100, L"RGB: (%d, %d, %d)", r, g, b);
MessageBoxW(NULL, szMessage, L"Color Selected", MB_OK);
}
getchar();
return 0;
}


색상을 받고, 그 색상코드를 반환하는 간략한 함수 입니다. 창을 띄워서 Windows가 미리 시스템상 만들어놓은 GUI에서 색을 골라 변수에 담는 역할이 전부인 기능입니다. 이번 함수도 역시 시스템 구조체를 직접적으로 사용하는 함수입니다.
https://learn.microsoft.com/en-us/windows/win32/api/commdlg/nc-commdlg-choosecolorw
ChooseColorW - Win32 apps
Wide string version of ChooseColor
learn.microsoft.com
BOOL Choosecolorw(
LPCHOOSECOLORW unnamedParam1
)
간략하게 해석해보자면, 제일 먼저 코드 최상단에 CHOOSECOLORW타입의 변수 cc를 선언한게 눈에 띕니다. CHOOSECOLORW는 시스템에서 이미 만들어놓은 구조체 자료형으로, 이번 코드에서 자주 쓰이게 될 것입니다. 그리고 밑으로 내려가다 보면 우리의 함수가 if조건문 안에 있는걸 볼 수 있습니다. "if (ChooseColorW(&cc))"라고 되어있는데, 매개변수로 주소(&가 붙어있으니)를 받네요? 맞습니다. ChooseColorW()함수도 CHOOSECOLORW라는 변수의 포인터를 매개변수로 받아서 동작합니다. 즉 동작방식 자체는 이전에 소개했던 GetOpenFileNameW()랑 비슷합니다.
그러므로, 저희는 이 함수가 건드는 데이터의 실체는 CHOOSECOLORW자료형의 cc라는 걸 알 수 있습니다. 따라서 CHOOSECOLORW구조체 중 중요한것만 다뤄볼게요.
https://learn.microsoft.com/ko-kr/windows/win32/api/commdlg/ns-commdlg-choosecolorw
CHOOSECOLORW - Win32 apps
CHOOSECOLORW(유니코드) 구조체(commdlg.h)에는 ChooseColor 함수가 색 대화 상자를 초기화하는 데 사용하는 정보가 포함되어 있습니다.
learn.microsoft.com
typedef struct tagCHOOSECOLORW {
DWORD lStructSize;
HWND hwndOwner;
HWND hInstance;
COLORREF rgbResult;
COLORREF *lpCustColors;
DWORD Flags;
LPARAM lCustData;
LPCCHOOKPROC lpfnHook;
LPCWSTR lpTemplateName;
} CHOOSECOLORW, *LPCHOOSECOLORW;
rgbResult: 색상정보를 저장하는 변수입니다. 보통 32비트 컬러로 저장하합니다.
Flags: 이전과 똑같이 대화상자의 동작방식을 결정합니다.
lpCustColors: rgbResult와 같이 색상정보를 저장한 곳의 포인터입니다. 다수의 색이나 팔레트를 다룰때 유용하게 쓰입니다.

ShellAboutW()
지금은 많이 바뀌었지만 Windows10이나 Windows11 초기형에서는 프로그램의 도움말이나 정보를 출력하기 위해 ShellAbout창을 띄우곤 했습니다. 현재는 그림판이나 메모장 같은 많은 내장 프로그램도 정보를 띄우는 디자인이 바뀌는 추세지만, 기준으로도 'regedit'등은 볼 수 있습니다.



그런데 '저렇게 생긴 창을 보여준다.'가 끝입니다. 이러한 형식의 GUI를 띄우는거 빼고는 아무런 기능도, 의미도 없습니다. 다만, 개발자들이 우리 프로그램이 뭐인지 최소한 잘 보여주기 위해서 많이 쓰였던 함수입니다.
#include <windows.h>
int main() {
ShellAboutW(NULL, L"My Program 1.0", L"Copyright 2026. RHK All rights reserved.", NULL);
return 0;
}
기능만큼이나 코드도 휑~
https://learn.microsoft.com/ko-kr/windows/win32/api/shellapi/nf-shellapi-shellaboutw
ShellAboutW 함수(shellapi.h) - Win32 apps
ShellAbout 대화 상자를 표시합니다. (유니코드)
learn.microsoft.com
INT ShellAboutW(
[in, optional] HWND hWnd,
[in] LPCWSTR szApp,
[in, optional] LPCWSTR szOtherStuff,
[in, optional] HICON hIcon
);
매개변수를 해석하자면...
1번 매개변수: 역시 이 창을 띄울 창의 핸들입니다. 테스트에서는 간단하게 NULL을 하셔도 문제없어요.
2번 매개변수: 창 제목에 붙을 프로그램 이름 입니다.
3번 매개변수: 본문에 들어갈 상세문구의 문자열을 받습니다.
4번 매개변수: 창에 표시할 아이콘의 핸들을 받습니다.

그렇구나~
ShellExecuteW()
무언가를 실행하는 함수입니다. 위의 ShellAbout()처럼 코드 자체는 짧지만 기능은 결코 짧지 않습니다.
https://learn.microsoft.com/ko-kr/windows/win32/api/shellapi/nf-shellapi-shellexecutew
ShellExecuteW 함수(shellapi.h) - Win32 apps
지정된 파일에 대한 작업을 수행합니다. (ShellExecuteW)
learn.microsoft.com
HINSTANCE ShellExecuteW(
[in, optional] HWND hwnd,
[in, optional] LPCWSTR lpOperation,
[in] LPCWSTR lpFile,
[in, optional] LPCWSTR lpParameters,
[in, optional] LPCWSTR lpDirectory,
[in] INT nShowCmd
);
1번 매개변수: 어떤창을 부모로 할지 부모핸들에 대한 매개변수를 받습니다.
2번 매개변수: 동작에 대한 매개변수가 들어갑니다. open, print, runas, explore, edit, find, properties같은 파일에 대한 다양한 동작을 정할 수 있습니다.
3번 매개변수: 각종 주소가 들어갑니다. 파일 주소, 폴더 주소 뿐 아니라 인터넷 주소도 넣을 수 있어요. 이때 주소내용을 실행하기에 적절한 프로그램이 같이 실행되기도 합니다.(인터넷 주소면 기본브라우저, 동영상이면 플레이어가 같이)
4번 매개변수: 추가명령을 넣을 수 있습니다. 이건 프로그램마다 다릅니다.
5번 매개변수: 프로그램이 동작을 시작할 때, 기본 디렉토리를 명시해 줍니다.
6번 매개변수: 프로그램이 처음 실행될때의 상태(스타일)를 표기합니다.
ShellExecuteW()함수 예제
이거 하나로 할 수 있는게 너무나도 많기 때문에 예제를 몇가지 다뤄보겠습니다.
사진 열기
#include <windows.h>
int main() {
ShellExecuteW(NULL, L"open", L"C:\\Users\\RHK\\Desktop\\dynamic_type.jpg", NULL, NULL, SW_SHOWNORMAL);
return 0;
}
그런데 사진을 열기 위해서는 대전제가 하나 있습니다. 사진을 뭘로 열지를 정하지 않았잖아요. 물론 대부분의 경우 사진을 보려고 갤러리앱으로 열겠지만, 사진의 구조를 열거나 바이너리를 보기 위해서 2진수 형태로 볼수도 있잖아요. 이건 다른 파일도 마찬가지이기에, ShellExecuteW()함수의 경우, 해당파일의 기본 실행프로그램을 우선으로 실행시킵니다.

저의 경우, jpg파일을 사진앱으로 열기 때문에, 이번에도 사진앱으로 파일이 실행된 걸 볼 수 있습니다.


기본 브라우저로 구글 접속하기
#include <windows.h>
int main() {
ShellExecuteW(NULL, L"open", L"https://www.google.com", NULL, NULL, SW_SHOWNORMAL);
return 0;
}
"https://www.google.com"를 열라는건데, 보통 인터넷 링크의 경우 브라우저로 접속하기 때문에, chrome이나 egde가 자동으로 실행되어 해당 링크에 접속되는걸 볼 수 있을 것입니다.

크롬을 시크릿모드로 켜기
#include <windows.h>
int main() {
ShellExecuteW(NULL, L"open", L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", L"--incognito", NULL, SW_SHOWNORMAL);
return 0;
}
일단 크롬이 실질적으로 실행되는 exe파일의 경로를 알아야 합니다.(보통의 경우 위의 경로에 설치되어 있습니다.) 크롬의 시크릿모드 명령어인 " --incognito "를 4번째 인자로 대입하여 크롬을 열긴하되, 시크릿모드로 열라고 추가명령을 해주었습니다.

CMD를 관리자권한으로 실행하기
#include <windows.h>
int main() {
ShellExecuteW(NULL, L"runas", L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, SW_SHOWNORMAL);
return 0;
}
2번매개변수가 동작에 따라 다르다고 했습니다. 계속 단순히 실행하는 "open"만 쓰던것과는 다르게 이번에는 관리자권한으로 실행하라는 "runas"를 사용하였습니다. 즉, 이번 예제는 관리자권한으로 CMD를 열어보는것 입니다.

-끝!-
'Cluster' 카테고리의 다른 글
| 리버싱엔지니어링 실전 - 조부모님 신혼선물 수리하기 (0) | 2026.07.07 |
|---|---|
| 8번출구 패러디 TUI 게임 (0) | 2026.06.18 |
