자바스크립트를 활성화 해주세요

MFC 프로그램의 콘솔 실행 모드 추가

 ·  ☕ 11 min read

GUI 기반으로 작성한 프로그램은 직관적이지만, 해당 기능을 사용하기 위해 마우스를 사용해야 하며, 이로 인해 자동화하기 어렵다는 문제점이 있다. 물론 매크로 등을 활용할 수 있지만, 사람이 직접 수행하지 않을 뿐, 결국 프로그램으로 마우스 클릭 등을 반복하는 것이다.

GUI 기반으로 개발된 파일을 변환해주는 프로그램들을 CI/CD 과정에 사용하려 한다고 가정해보자.

  1. GUI를 실행하기 위한 Desktop Environment가 없는 Container/Build server일 수 있다.
  2. GUI가 있는 환경이라도, 해당 환경에 맞춰 매크로를 기록해야 한다.
  3. 매크로를 사용해도 Build server에 RDP 등으로 접속 시, 해상도 변경, 좌표 변경 등 이슈가 발생한다.
  4. 여러 GUI 기반 프로그램들을 실행할 때, 마우스 위치가 점유 자원으로서 병렬성을 방해한다.
  5. 해당 프로그램의 실행 결과도 GUI로 나타나므로, 이를 쉽게 확인할 수 없다.

위의 상황은 극단적으로 가정한 것이지만, 최소 1개 이상 실제 문제가 될 것이라 예상한다.

목표

  1. Window Manager(Windows Explorer, Gnome 등)나 Shell(Bash, Command Prompt 등)에서 실행 시, GUI 환경으로 실행
  2. Shell에서 실행할 때 특정 인자를 입력하면(혹은 Windows Manager의 Shortcut에 인자 추가) GUI를 띄우지 않고,
    해당 인자에 따라 기능을 수행한 뒤 종료.
  3. 필요에 따라 진행 과정에 대한 Log를 출력한다.
  4. 해당 프로그램의 성공/실패 여부에 따라 확인할 수 있게 Error code를 반환하며 종료한다.

위의 목표를 개발자 기준의 요구사항으로 해석하면 아래와 같다.

  1. 기존의 GUI 실행에 관련된 코드를 뒤엎지 않고 기능을 추가해야 한다.
  2. 해당 프로그램 실행의 인자를 읽고, 특정 조건 시 GUI를 실행시키는 함수를 호출하지 않아야 한다.
  3. stdio (특히 stdout, stderr)가 해당 프로그램을 실행시킨 shell과 연결되어 있어야 한다.
  4. GUI를 실행시키지 않은 상태에서 원하는 exit code로 프로그램을 종료시켜야 한다.
  5. MFC같이 안 좋은 GUI framework에서 개발해야 한다.

진단

이제 MFC 프로그램을 작성하고, Shell에서 실행시켜보자. MFC 응용 프로그램 프로젝트를 ConsoleGuiTool로 가정하겠다. (대화 상자 기반으로 작성하였으나, 다른 종류도 크게 문제되지 않는다.) 빌드하고 Command Prompt에서 실행시켜보면 아래 스크린샷과 같은 상황을 확인할 수 있을 것이다.

GUI 구동 시, Command Prompt와의 연결이 끊어진다

분명 해당 프로그램이 종료되지 않았음에도 Shell과의 연결이 끊어졌다. 요구사항 3, 4의 상황에서 문제가 생길 수 있다. (자세한 설명은 Command Prompt 명령어 설명에서 다루도록 한다.)

Console 실행 모드 추가하기

일단 수정해야 할 파일은 현재 MFC 프로그램의 진입점 부분이다. CWinApp을 상속하고 있는 클래스의 소스 코드를 찾자. (특별히 이름을 변경하지 않았다면, 프로젝트 이름과 같은 *.h, *.cpp일 것이다.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

// ConsoleGuiTool.h : main header file for the PROJECT_NAME application
//

#pragma once

#ifndef __AFXWIN_H__
	#error "include 'stdafx.h' before including this file for PCH"
#endif

#include "resource.h"		// main symbols


// CConsoleGuiToolApp:
// See ConsoleGuiTool.cpp for the implementation of this class
//

class CConsoleGuiToolApp : public CWinApp
{
public:
	CConsoleGuiToolApp();

// Overrides
public:
	virtual BOOL InitInstance();

// Implementation

	DECLARE_MESSAGE_MAP()
};

extern CConsoleGuiToolApp theApp;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

// ConsoleGuiTool.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "ConsoleGuiTool.h"
#include "ConsoleGuiToolDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CConsoleGuiToolApp

BEGIN_MESSAGE_MAP(CConsoleGuiToolApp, CWinApp)
	ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()


// CConsoleGuiToolApp construction

CConsoleGuiToolApp::CConsoleGuiToolApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}


// The one and only CConsoleGuiToolApp object

CConsoleGuiToolApp theApp;


// CConsoleGuiToolApp initialization

BOOL CConsoleGuiToolApp::InitInstance()
{
	// InitCommonControlsEx() is required on Windows XP if an application
	// manifest specifies use of ComCtl32.dll version 6 or later to enable
	// visual styles.  Otherwise, any window creation will fail.
	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// Set this to include all the common control classes you want to use
	// in your application.
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);

	CWinApp::InitInstance();


	// Create the shell manager, in case the dialog contains
	// any shell tree view or shell list view controls.
	CShellManager *pShellManager = new CShellManager;

	// Activate "Windows Native" visual manager for enabling themes in MFC controls
	CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));

	// Standard initialization
	// If you are not using these features and wish to reduce the size
	// of your final executable, you should remove from the following
	// the specific initialization routines you do not need
	// Change the registry key under which our settings are stored
	// TODO: You should modify this string to be something appropriate
	// such as the name of your company or organization
	SetRegistryKey(_T("Local AppWizard-Generated Applications"));

	CConsoleGuiToolDlg dlg;
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with OK
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with Cancel
	}
	else if (nResponse == -1)
	{
		TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
		TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n");
	}

	// Delete the shell manager created above.
	if (pShellManager != NULL)
	{
		delete pShellManager;
	}

	// Since the dialog has been closed, return FALSE so that we exit the
	//  application, rather than start the application's message pump.
	return FALSE;
}

해당 class에 기존 GUI환경의 init을 담당할 InitGuiWindow()와 콘솔모드로 실행할 RunConsole() 함수를 추가한다. 이후 기존의 InitInstance()함수를 InitGuiWindow()로 교체하고, 새로운 InitInstance()를 아래와 같이 작성한다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

// ConsoleGuiTool.h : main header file for the PROJECT_NAME application
//

#pragma once

#ifndef __AFXWIN_H__
	#error "include 'stdafx.h' before including this file for PCH"
#endif

#include "resource.h"		// main symbols


// CConsoleGuiToolApp:
// See ConsoleGuiTool.cpp for the implementation of this class
//

class CConsoleGuiToolApp : public CWinApp
{
public:
	CConsoleGuiToolApp();

// Overrides
public:
	virtual BOOL InitInstance();

// Implementation

	DECLARE_MESSAGE_MAP()

	BOOL InitGuiWindow();
	BOOL RunConsole();
};

extern CConsoleGuiToolApp theApp;
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120

// ConsoleGuiTool.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "ConsoleGuiTool.h"
#include "ConsoleGuiToolDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CConsoleGuiToolApp

BEGIN_MESSAGE_MAP(CConsoleGuiToolApp, CWinApp)
	ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()


// CConsoleGuiToolApp construction

CConsoleGuiToolApp::CConsoleGuiToolApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}


// The one and only CConsoleGuiToolApp object

CConsoleGuiToolApp theApp;


// CConsoleGuiToolApp initialization
BOOL CConsoleGuiToolApp::InitInstance()
{
	int nArgCnt = __argc;

	if (nArgCnt == 1)
	{
		return InitGuiWindow();
	}
	else
	{
		return RunConsole();
	}
}


BOOL CConsoleGuiToolApp::InitGuiWindow()
{
	// InitCommonControlsEx() is required on Windows XP if an application
	// manifest specifies use of ComCtl32.dll version 6 or later to enable
	// visual styles.  Otherwise, any window creation will fail.
	INITCOMMONCONTROLSEX InitCtrls;
	InitCtrls.dwSize = sizeof(InitCtrls);
	// Set this to include all the common control classes you want to use
	// in your application.
	InitCtrls.dwICC = ICC_WIN95_CLASSES;
	InitCommonControlsEx(&InitCtrls);

	CWinApp::InitInstance();


	// Create the shell manager, in case the dialog contains
	// any shell tree view or shell list view controls.
	CShellManager *pShellManager = new CShellManager;

	// Activate "Windows Native" visual manager for enabling themes in MFC controls
	CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));

	// Standard initialization
	// If you are not using these features and wish to reduce the size
	// of your final executable, you should remove from the following
	// the specific initialization routines you do not need
	// Change the registry key under which our settings are stored
	// TODO: You should modify this string to be something appropriate
	// such as the name of your company or organization
	SetRegistryKey(_T("Local AppWizard-Generated Applications"));

	CConsoleGuiToolDlg dlg;
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with OK
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with Cancel
	}
	else if (nResponse == -1)
	{
		TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
		TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n");
	}

	// Delete the shell manager created above.
	if (pShellManager != NULL)
	{
		delete pShellManager;
	}

	// Since the dialog has been closed, return FALSE so that we exit the
	//  application, rather than start the application's message pump.
	return FALSE;
}


BOOL CConsoleGuiToolApp::RunConsole()
{
	printf("Program is running in console mode\n");
	Sleep(5000);
	printf("It seems program did some job, 5 second passed\n");

	return FALSE;
}

일단 콘솔 모드 진입 조건은 실행파일 뒤에 인자가 하나라도 붙는 것으로 설정했다. 콘솔 모드 진입시, 콘솔 모드로 실행된다는 메시지를 출력하고, 5초 이후 무언가 완료되었다고 하고 종료되게 설정하였다. 빌드하고 Command Prompt에서 실행시켜보면 아래 스크린샷과 같은 상황을 확인할 수 있을 것이다.

콘솔 모드로 실행 시 5초 후 종료됨. (메시지 출력 안됨), 기존 GUI 구동은 잘 됨

일단 실행 인자 옵션에 따라 기존 GUI 구동을 유지한 상태에서, 콘솔 모드 실행에 성공했다. 하지만 printf()를 통한 메시지 출력이 되지 않는 것을 확인할 수 있다.

프로그램을 실행시킨 shell과 stdio 연결

해당 증상의 원인은 MFC 프로그램 실행 시, stdio가 기존 shell에서 떨어지기 때문이다. MFC 프로그램을 실행시킨 부모 프로세스(Command Prompt 혹은 PowerShell)의 stdio와 연결해야한다. 기존의 RunConsole() 함수를 아래와 같이 수정한다. (코드 상단에 #include <iostream>을 해야한다.)

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
BOOL CConsoleGuiToolApp::RunConsole()
{
	if (AttachConsole(ATTACH_PARENT_PROCESS))
	{
		FILE *pfStdin;
		FILE *pfStdout;
		FILE *pfStderr;
		freopen_s(&pfStdin, "CONIN$", "r", stdin);
		freopen_s(&pfStdout, "CONOUT$", "w", stdout);
		freopen_s(&pfStderr, "CONOUT$", "w", stderr);

		std::cin.clear();
		std::cout.clear();
		std::cerr.clear();
	}

	printf("Program is running in console mode\n");
	Sleep(5000);
	printf("It seems program did some job, 5 second passed\n");

	return FALSE;
}

L116~128의 내용은 부모 프로세스의 콘솔에 연결하고 stdin, stdout, stderr를 다시 여는 작업을 수행한다.

임의의 Exit Code로 종료시키기

원칙적으로는 main() 함수의 return 값으로 exit code를 보내는 것이 정석이나, 우리가 현재 수정 가능한 코드 범위에서는 main() 함수를 수정할 수 없기 때문에 차선책인 exit() 함수를 사용하면 된다.

stdin의 연결 상태를 확인할 겸, 아래와 같이 RunConsole() 함수를 수정한다.

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
BOOL CConsoleGuiToolApp::RunConsole()
{
	int nExitCode;

	if (AttachConsole(ATTACH_PARENT_PROCESS))
	{
		FILE *pfStdin;
		FILE *pfStdout;
		FILE *pfStderr;
		freopen_s(&pfStdin, "CONIN$", "r", stdin);
		freopen_s(&pfStdout, "CONOUT$", "w", stdout);
		freopen_s(&pfStderr, "CONOUT$", "w", stderr);

		std::cin.clear();
		std::cout.clear();
		std::cerr.clear();
	}

	printf("Program is running in console mode\n");
	Sleep(5000);
	printf("It seems program did some job, 5 second passed\n");
	printf("Enter exit code to get: ");
	std::cin >> nExitCode;

	exit(nExitCode);
	return FALSE;
}

콘솔 모드에서 stdio 동작 확인, 원하는 대로 exit code 반환

위 목표를 모두 달성했다. 이제 실행인자를 잘 입력 받았는지 확인하고, 이를 비교하기만 하면 된다.

실행 인자 사용을 위한 추가 설명

이전의 설명은 CWinApp::InitInstance() 메서드를 수정하여 GUI를 띄우지 않고 콘솔에서 입출력을 수행하는 부분을 설명했다. 실행 인자를 입력받는 방법은 __argc, __argv, __wargv 전역변수를 활용한다. 해당 변수는 stdlib.h에서 제공하며, Microsoft에서만 제공하는 확장 기능이다. (C,C++ 표준이 아님) (Multi-Byte Character Set으로 빌드한 경우 __argv를, Unicode Character Set으로 빌드한 경우 __wargv를 사용)

이외에도 GetCommandLine(), CWinApp::ParseCommandLine() 등의 방법으로도 실행 인자를 얻을 수 있다. 이 중 ParseCommandLine()에서 사용하는 CCommandLineInfo는 Windows에서 제공하는 기능의 일부를 해석해준다.

Command Prompt 명령어 설명

사실 Windows에서 제공하는 Command Prompt, PowerShell에서 GUI를 사용하는 프로그램을 실행시키면 자동으로 해당 shell에서 detach하게 되어있다. 현재 수정한 방식은 GUI를 화면에 출력하기 전에 기능을 수행하고 종료시킨 것이므로, 똑같이 shell에서 detach하게 되어있다.

Command Prompt에서 콘솔 모드 사용을 위해서는 아래와 같은 옵션을 고려해야 한다.

> start /wait \<PROGRAM\>

<PROGRAM>을 실행시키고, 해당 <PROGRAM>이 종료될 때 까지 기다린다.

> cmd /v:on /c "echo !time! ...

Batch file을 실행할 때, 환경변수 등은 %VAR%를 통해 그 값으로 치환할 수 있다. 하지만 위 예시와 같이 프로그램의 실행 시간을 측정하기 위해 한 명령행 안에 %VAR%로 넣을 경우, 즉시 치환된다.
(!time! 대신 %time%로 입력 시, 실제 소요시간과 관계없이 같은 시간이 나타난다.) 해당 변수에 접근하는 순간 치환되길 원한다면, 위와 같이 !VAR!로 표기해야 한다. 또한 위와 같은 delayed environment variable expansion을 사용하려면 현재 콘솔에 옵션을 설정하거나 cmd로 실행시킬 때, /v:on 옵션을 주고 실행시켜야 한다.

해설

C/C++로 작성한 프로그램의 기본 진입점은 main()함수이다. main()함수의 매개변수로 int argc, char** argv를 사용하여 실행 인자를 프로그램 내에서 확인할 수 있다.

하지만 MFC로 프로젝트를 생성할 경우, main()함수를 개발자가 직접 편집할 수 없다. 이전의 코드에서 봤던 것 처럼 개발자가 수정 가능한 프로그램의 최초 진입 지점은 CWinApp::InitInstance() 메서드이다.

main()함수 역할을 수행하는 WinAPI에서 제공하는 진입점은 _tWinMain() 함수이다. MFC에서 작성된 _tWinMain()함수의 정의는 VC\atlmfc\src\mfc\appmodul.cpp에서 확인할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "sal.h"

/////////////////////////////////////////////////////////////////////////////
// export WinMain to force linkage to this module
extern int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	_In_ LPTSTR lpCmdLine, int nCmdShow);

extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	_In_ LPTSTR lpCmdLine, int nCmdShow)
#pragma warning(suppress: 4985)
{
	// call shared/exported WinMain
	return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

/////////////////////////////////////////////////////////////////////////////
// initialize app state such that it points to this module's core state

BOOL AFXAPI AfxInitialize(BOOL bDLL, DWORD dwVersion)
{
	AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
	pModuleState->m_bDLL = (BYTE)bDLL;
	ASSERT(dwVersion <= _MFC_VER);
	UNUSED(dwVersion);  // not used in release build
#ifdef _AFXDLL
	pModuleState->m_dwVersion = dwVersion;
#endif
:

_tWinMain()에서 호출하는 AfxWinMain()의 정의는 VC\atlmfc\src\mfc\winmain.cpp에서 확인할 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "sal.h"


/////////////////////////////////////////////////////////////////////////////
// Standard WinMain implementation
//  Can be replaced as long as 'AfxWinInit' is called first

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	_In_ LPTSTR lpCmdLine, int nCmdShow)
{
	ASSERT(hPrevInstance == NULL);

	int nReturnCode = -1;
	CWinThread* pThread = AfxGetThread();
	CWinApp* pApp = AfxGetApp();

	// AFX internal initialization
	if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
		goto InitFailure;

	// App global initializations (rare)
	if (pApp != NULL && !pApp->InitApplication())
		goto InitFailure;

	// Perform specific initializations
	if (!pThread->InitInstance())
	{
		if (pThread->m_pMainWnd != NULL)
		{
			TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");
			pThread->m_pMainWnd->DestroyWindow();
		}
		nReturnCode = pThread->ExitInstance();
		goto InitFailure;
	}
	nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
	// Check for missing AfxLockTempMap calls
	if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
	{
		TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
			AfxGetModuleThreadState()->m_nTempMapLock);
	}
	AfxLockTempMaps();
	AfxUnlockTempMaps(-1);
#endif

	AfxWinTerm();
	return nReturnCode;
}

/////////////////////////////////////////////////////////////////////////////

AfxGetApp()를 통해 개발자가 정의한 Application 변수의 주소를 얻고, 해당 변수에서 InitInstance() 메서드를 호출한다. 해당 함수는 개발자가 재정의할 수 있으며, 우리는 이 InitInstance()함수를 수정하여 MFC dialog를 띄우지 않고 콘솔에서 처리할 수 있도록 변형한 것이다.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
:
// CConsoleGuiToolApp construction

CConsoleGuiToolApp::CConsoleGuiToolApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}


// The one and only CConsoleGuiToolApp object

CConsoleGuiToolApp theApp;

// CConsoleGuiToolApp initialization
BOOL CConsoleGuiToolApp::InitInstance()
:

다른 GUI framework에서의 진입점 (main 함수)

아래는 각 C/C++로 작성된 GUI framework의 진입점이다. 대부분의 GUI framework는 main() 함수를 편집할 수 있다.

WxWidget은 MFC와 같이 main() 함수를 편집할 수 없으므로, wxApp이 제공하는 메서드를 사용해야 한다.

#include <QApplication>
#include <QCommandLineParser>
#include <QCommandLineOption>

#include "mainwindow.h"

int main(int argc, char *argv[])
{
    Q_INIT_RESOURCE(application);

    QApplication app(argc, argv);
    QCoreApplication::setOrganizationName("QtProject");
    QCoreApplication::setApplicationName("Application Example");
    QCoreApplication::setApplicationVersion(QT_VERSION_STR);
    QCommandLineParser parser;
    parser.setApplicationDescription(QCoreApplication::applicationName());
    parser.addHelpOption();
    parser.addVersionOption();
    parser.addPositionalArgument("file", "The file to open.");
    parser.process(app);

    MainWindow mainWin;
    if (!parser.positionalArguments().isEmpty())
        mainWin.loadFile(parser.positionalArguments().first());
    mainWin.show();
    return app.exec();
}
#include <gtk/gtk.h>

#include "gtk_app.h"

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>

int main(int argc, char *argv[]) {
   Fl_Window* w = new Fl_Window(330, 190);
   new Fl_Button(110, 130, 100, 35, "Okay");
   w->end();
   w->show(argc, argv);
   return Fl::run();
}

참고 링크

[1] MFC 기본코드 분석(https://petra.tistory.com/1296)


JaeSang Yoo
글쓴이
JaeSang Yoo
The Programmer

목차