2021년 1월 27일 수요일
코로나 시대와 새로운 취미
2021년 1월 26일 화요일
[MFC] 코드 실행 순서
응용프로그램이 시작되기 전에 프로그램이 사용할 메모리나 기타 실행에 필요한 기본적인 요건들을 운영체제가 할들. 그러한 작업이 완료되면 운영체제는 __WinMainCRTStartup() 코드를 실행한다. 이 함수에서는 전역 객체를 생성하고 실제 WinMain()을 호출한다.
여기서 말하는 전역 객체는 모든 프로젝트 생성시 하나 존재하는 theApp 객체이다. 이 객체는 CWinApp 클래스를 상속받은 CSDI_SequenceApp 객체이다.
WinMain()은 다시 AfxWinMain() 함수를 호출하게 되는데, 이 함수는 내부적으로 AfxInInit()함수를 호출하여 MFC 프레임 워크를 초기화하고, 우리 눈에 보이는 코드들이 실행에 들어간다.
CSDI_SequenceApp()
- 눈에 보이지 않는 코드들에 의해서 응용 프로그램 객체는 생성될 것이며 이 시점에서 응용 프로그램 객체의 생성자가 호출된다.
CSDI_ASequenceApp::InitApplication()
- MFC에서는 더이상 사용되지 않는 멤버 함수이다.
CSDI_SequelceApp::InitInstance()
- 응용 프로그램이 초기화 되는 부분이며, 이와 관련한 코드들이 집결되는 곳이다.
ex) 응용프로그램의 설정 정보 로딩(윈도우 크기, 옵션, 스타일 등)
스플래시 윈도우 초기화 (일반적으로 출력은 CMainFrame의 OnCreate() 함수)
응용 프로그램의 중복 실행 방지
트라이얼(Trial) 버전의 기간 검사 루틴
운영체제 버전 확인 및 프로그램 실행 여부 결정
프로그램 사용자 인증 (특정 사용자만 프로그램을 실행하도록 하고 싶은 경우)
프로그램 실행 시 인자로 전달되는 매개변수 처리
중요 맴버 함수 :
AddDocTemplate() - 도큐먼트 템플릿 객체를 응용 프로그램 객체에 등록한다
ParseCommandLine() - 매개 변수에 따라서 기본적인 명령을 수행하기에 앞서 내용을 분석
ProcessShellCommand() - 매개 변수로 전달된 프로그램 실행 옵션이나 열어야 하는 문서를 지정한 경우, 파일의 열기 혹은 빈 문서의 생성과 같은 작업을 실행 (ex 워드 파일을 더블클릭하면 자동으로 워드가 실행되어 파일을 로딩)
CSDI_SequenceDoc::CSDI_SequenceDoc()
- 도큐먼트 객체의 생성자를 호출하면 응용 프로그램 객체에 이어 두 번째로 생성된다. 중요한 것은 현재까지 사용자 인터페이스와 관련한 객체, 즉 윈도우 객체에는 아무 것도 생성되지 않았다.
CMAinFrame::CmainFrame()
- 응용 프로그램 인터페이스의 틀이 되는 프레임 윈도우 객체를 생성한다. 객체가 생성되었을 뿐, 아직 아무 것도 만들어지거나 나타나는 것은 없다.
CMainFrame::LoadFrame()
- CFrameWnd 객체가 생성된 후 이 응용 프로그램 윈도우와 관련한 메뉴, 가속기, 아이콘과 같은 리소스들을 로딩하고 윈도우를 생성한다. 아직 화면에는 어무 것도 보이지 않는다.
CMainFrame::PreCreateWindow()
- 윈도우가 생성되기 직전에 호출되는 함수이다.
이 함수의 역할 중 가장 중요한 것은 생성되는 윈도우의 스타일 정의 혹은 변경이다. 기본적으로 생성되는 윈도우는 아주 흔하게 볼 수 있는 모든 요소를 가지고 있따. 이러한 사항들을 변경하고자 한다면 인자로 전달된 CREATESTRUCT 구조체의 값을 변경하면 된다.
CMainFrame::PreSubclassWindow()
- 서브 클래싱 직전에 호출되는 함수. 서브클래싱이랑 윈도우 프로시저 함수를 따로 두는 것을 의미한다.
CMAinFrame::OnCreate() - Call
- WM_CREATE 메시지를 받았을 때 호출되는 메시지 핸들러이다. LoadFrame() 함수가 호출되면서 윈도우가 생성되고, 그 때 WM_CREATE 메시지는 자동으로 발생한다. 이 함수에서 하는 일은 CMainFrame 객체 내에 있는 몇몇 차일드 윈도우(툴바나 상태 표시줄 등)를 생성하는 일을 한다. 이 함수 내에서 상위 클래스의 OnCreate() 함수를 명시적으로 호출함으로써, 다른 함수들이 또 다시 호출된다.
1) CMainFrame::OnCreateClient() - Call
상위 클래스의 명시적 호출로 인해 호출되는 첫 번째 함수인데, 함수의 주된 역할은 클라이언트 뷰를 생성하는 일이다.
2) CSDI_SequenceView::CSDI_SequenceView()
클라이언트 뷰의 생성자가 호출된다.
3) CSDI_Sequence::Create() - Call
클라이언트의 윈도우를 생성한다. 그리고 이 함수의 호출결과로 아래 네 개의 함수들이 추가로 호출된다.
CSDI_Sequence::PreCreateWindow()
앞서 프레임 윈도우 생성시의 함수와 같은 역할이다.
CSDI_Sequence::PreSubclassWindow()
앞서 프레임 윈도우 생성시의 함수와 같은 역할이다.
CSDI_Sequence::OnCreate() - Called
클라이언트 뷰에 WM_CREATE 메시지가 전달되고, 그 메시지 헨들러가 호출된 것이다. 결국 각각의 윈도우는 자기만의 메시지 핸들러를 두게 되며, 자신에게 해당되는 메시지를 받아 처리한다.
CSDI_Sequence::OnCreate() - Return
CSDI_Sequence::OnShowWindow() - Call
윈도우가 화면에 나타나거나 사라질 때 호출된다. 좀 더 정확히는 WM_SHOWWINDOW메시지에 대한 핸들러이다.
CSDI_Sequence::OnShowWindow() - Return
CSDI_Sequence::Create() - Return
- 클라이언트 뷰 윈도우의 생성이 완료되었다.
CMainFrame::OnCreateClient() - Return
- 프레임 윈도우는 클라이언트 뷰를 생성하는 작업을 끝냈다.
CMAinFrame - m_wndToolBar.CreateEx
CMainFrame - m_wndStatusBar.Create
CMainFrame::OnCreate() - Return
CSDI_Sequence::OnNewDocument() - Call
- 현재 프로그램이 실행될 때 엑셀이나 워드처럼 응용 프로그램과 연결된 데이터 파일을 오픈한 경우가 아니므로 빈 문서, 즉 새 문서를 연다. 관련된 것이 없다고 하더라도 SDI구조를 가졌기 때문에 내부적으로 문서를 만든다.
CSDI_Sequence::OnNewDocument() - Return
CSDI_Sequence::OnInitialUpdate() - Call
- 클라이언트 뷰의 생성이 완료되면 응용 프로그램의 프레임 윈도우는 자신의 클라이언트 뷰에 WM_INITIALUPDATE 메시지를 보내게 되고, 그 메시지를 받은 클라이언트 뷰는 이 메시지 핸들러를 호출한다.이 함수는 매우 중요하다. 이 메시지 핸들러는 새로운 문서가 열릴 때마다 호출된다. 그러나 응용 프로그램과 연결된 문서가 존재하지 않고 파일을 열 일이 없다면, 딱 한번만 수행될 것이다.
CSDI_Sequence::OnInitialUpdate() - Return
* 초기화 완료 ==============================
CMainFrame::OnActivateApp() - Call
CMainFrame::OnActivateApp() - Return
CMainFrame::OnActivate() - Call
CMainFrame::OnActivate() - Return
초기화가 완료된 후, 응용 프로그램은 활성화되어 모니터 화면의 맨 위로 나타나게 될 것이다. 두 함수는 화면에 응용 프로그램이 나타나는 것 까지로 보면 된다.
CMainFrame::OnShowWindow() - Call
CMainFrame::OnShowWindow() - Return
응용 프로그램이 활성화되고 프레임 윈도우가 화면에 나타나게 된다. 작업 표시줄에 자신의 항목도 하나 생겼을 것이고 모니터 화면에서 맨 위에 보여질 것이다.
### CSDI_SequenceApp::Run() - Call ###
메시지 루프를 시작하는 함수이다. 결국 이 함수는 내부적으로 ::PeekMessage() 함수를 호출하여 자신에게 메시지가 있는지 검사를 한다. 만일 처리할 메시지가 없다면 사용자가 입력을 받거나 혹은 마우스 이벤트가 발생한다거나 하는 일이 없다는 것이다.
하나의 메시지를 처리하는 세 가지 방법이 있다. 첫 번째는 CMainFrame의 WindowProc() 함수에 코드를 추가하는 것이고, 두 번째는 CMainFrame의 OnCreate() 메시지 핸들러 아래에 코드를 추가하는 것이다. 마지막으로 CMainFrame의 PreTranslateMessage() 멤버 함수에 코드를 추가하는 방법이다. 이 함수는 TranslateMessage() 함수가 호출되기 전에 호출된다. 즉 메시지 큐에서 메시지가 꺼내지고 해석되기 전에 호출되는 함수이다.
* 응용 프로그램 종료 ==============================
CMainFrame::OnClose() - Call
- 닫기 혹은 Alt+F4를 입력하면 WM_CLOSE 메시지가 발생한다. OnClose() 함수는 WM_CLOSE 메시지에 대한 핸들러이다. 이 함수가 리턴하기 전에 내부적으로 아래의 함수가 이어서 호출된다.
CMainFrame::OnShowWindow() - Call
CMainFrame::OnShowWindow() - Return
CMainFrame::OnActivate() - Call
CMainFrame::OnActivate() - Return
CMainFrame::OnActivateApp() - Call
CMainFrame::OnActivateApp() - Return
자원의 반납이나 차일드 윈도우의 내용을 저장하는 것 정도의 코딩을 한다.
CMainFrame::DestroyWindow() - Call
OnClose() 함수는 실제로 윈도우를 파괴하기 위해서 이 함수를 호출하게 되는데 이 함수가 호출되고 나면 윈도우가 파괴되므로 그 전에 차일드 윈도우를 모두 파괴하여야 한다. 이를 알리기 위해서 내부적으로 WM_DESTROY와 WM_NCDESTROY 메시지가 연이어 전달된다. WM_DESTROY는 윈도우 전체가 파괴되는 신호라면 WM_NCDESTROY는 프레임의 테두리가 파괴되는 신호이다.
CMainFrame::OnDestroy() - Call
CMainFrame::OnDestroy() - Return
CSDI_Sequence::OnDestroy() - Call
CSDI_Sequence::OnDestroy() - Return
CSDI_Sequence::PostNcDestroy() - Call
CSDI_SequenceView::~CSDI_SequenceView()
CSDI_Sequence::PostNcDestroy() - Return
CMainFrame::PostNcDestroy() - Call
CMAinFrame::~CmainFrame()
CMainFrame::PostNcDestroy() - Return
CMainFrame::DestroyWindow() - Return
응용 프로그램의 최상위 부모 윈도우인 메인 프레임 윈도우가 완전히 파괴되었다.
CSDI_SequenceDoc::~CSDI_SequenceDoc()
화면상에 나타나는 사용자 인터페이스, 즉 윈도우 객체들이 모두 파괴된 후 관련된 도큐먼트 객체가 소멸된다.
CMainFrame::OnClose() - Return
- 클라이언트 뷰, 도큐먼트 객체 뿐만 아니라, 자신도 이미 파괴되었다. 이제 남은 것은 응용프로그램이 사용한 자원들을 반납하는 것 뿐이다.
CSDI_ASequenceApp::ExitInstance()
- 모든 객체들이 소멸한 가운데, 응용 프로그램의 최종 종료에 앞서서 마지막으로 호출되는 함수이다. 따라서 이 함수에는 응용프로그램의 설정을 저장한다거나, 중복 실행 방지를 위해 획득했던 시스템 리소스를 반납한다거나, 메모리를 해제하는 등 최종 마무리 작업을 해야 한다.
### CSDI_SequenceApp::Run() - Call ###
- Run()의 리턴은 메시지 루프의 종료를 의미하므로 프로그램이 최종 종료되는 것이다. 이 과정에서 우리 눈에 보이지 않는 코드가 실행되는데, AfcWinInit와 반대로 AfxWinTerm() 이라는 함수가 수행되어 초기화했던 MFC 라이브러리를 해제한다.
[출처] [MFC] 코드 실행 순서|작성자 메이
2021년 1월 15일 금요일
헝가리안 표기법(Hungarian Notation)
10, 15년전 Microsoft의 개발자 중 헝가리 사람의 프로그래머가 쓰던 변수 명명법.
MS 내부에서 따라 쓰기 시작하던 것이 점차 전세계의 프로그래머들에게 널리 퍼져 이젠 프로그램 코딩시 변수 명명의 표준적인 관례가 되었다. 그러나 실제로 현장에서 일하다 보면 헝가리안 표기법을 제대로 지키는 개발자는 그리 많지 않다. 어느 정도 개발 경험을 가지고 있는 프로그래머는 물론 심지어 시중의 프로그래밍 서적에서 조차 저자마다 변수명을 개인에 따라 가지각색으로 짓고 있어서 처음 프로그램을 배우는 입문자들이 변수 명명에 대한 기준을 제대로 잡지 못하고 있는 실정이다.
그러나 변수 명명에 관한 표준화된 관례를 지켜주면 코드의 가독성을 높여줄 뿐만 아니라 예를 들어 카운터 변수를 count라고 지을지 cnt라고 지을지 고민하지 않아도 되는 편리함을 누릴 수 있다.
Prefix |
Data Type |
Description |
Example |
b |
BOOL |
any boolean type |
BOOL bTrue; |
c |
char |
character type |
char cLetter; |
i |
int |
integer for index |
int iCars; |
n |
int |
number, quantity |
int nNum; |
l |
long |
long type |
long lDistance; |
u |
unsigned |
unsigned type |
unsigned uPercent |
f |
float |
floating point |
float fPercent; |
d |
double |
double floating point |
double dPercent; |
w |
WORD |
unsigned word |
WORD wCnt |
dw |
DWORD |
unsigned double word |
DWORD dwLength |
p |
* |
any pointer |
int *piAddr; |
pfn |
* |
function pointer |
int (*pifnFunc1)(int x, int y); |
rg, a |
array |
stands for range |
float rgfTemp[16]; |
sz |
* |
Zero-terminated string of characters |
char szText[16]; |
s |
static |
a static variable |
static short ssChoice; |
t |
struct |
a user defined type |
|
e |
enum |
variable which takes enumerated values |
|
E |
enum |
Enumerated type |
|
g_ |
Global |
Global Variable |
String *g_psBuffer; |
m_ |
Member |
class private member variable |
int m_iMember; |
k |
constant formal parameter |
... |
void vFunc(const long klGalaxies) |
r |
reference formal parameter |
... |
void vFunc(long &rlGalaxies) |
prg |
... |
dynamically allocated array |
char *prgGrades; |
v |
Void |
|
|
x/y |
... |
used as size |
int xWitdth, yHeight; |
h |
handle |
handle to something |
hMenu |
Format
x_xXxxxxxx 0123456789 |
0 : 변수의 위치를 지정한다. g(전역변수), m(멤버변수), 없음(지역변수)
1 : 0 위치에 g 나 m 을 지정한 경우 _ 을 기술한다.
2 : 자료형의 종류를 나타낸다.
3 ~ : 변수의 의미 있는 이름을 기술하며, 3 위치는 대문자를 사용한다. 변수 이름이 너무 긴 경우 자음만을 기술한다. 예) g_nCnt
Example of type specific variable naming
int g_nCnt :정수형 글로벌 카운터
unsigned char ucByte; :한 바이트 데이타
char cChar; :한 문자
unsigned char rgucByte[10]; :바이트 데이타 10개
char rgcChar[10]; :문자 데이터 10개
char szChar[16 +1]; :문자 16개를 저장할 수 있는 문자열 공간