MFC 윈도우 프로그래밍 – 널널한 개발자 TV
https://www.youtube.com/watch?v=ZUGbkZVXMA4&list=PLXvgR_grOs1CyJDnWeUTqbmKG1VFQM72e&index=1
- MFC SDI 구조 주소록 예제 작성방법
- 키보드 이벤트 처리 핵심원리
- 키보드 해킹 원리 (※취급주의)
MFC Win32 API
메시지 드리븐 방식
설치 시 :
최신 v143 빌드 도구용 C++ MFC(x86 및 x64)
v143 빌드 도구에 대한 C++/CLI 지원(최신)
v143 빌드 도구용 C++ 모듈(x64/x86)
Windows 11 SDK (10.0.22000.0)
Windows 10 SDK (10.0.20348.0)
개발 설정 : Visual C++
Dark Theme (2019) – Mads Kristensen
도구 – 옵션 – 환경 – 글꼴 및 색 – Consolas 11
디버그 – 창 – 메모리 윈도우 (오른쪽에 도킹 4 바이트 씩 표)
wWinMain – 시작점
윈도우 – 속성 : (클래스) 종류
새 프로젝트
타입 : Single Document
스타일 : MFC standard
# APP
InitInstance() 메인 함수 시작
ExitInstance() 인스턴스 종료할 때
CWinAPP : 제어
CFrameWnd : GUI
CView : GUI
CDocument : 자료구조
1. MFC SDI 구조 주소록 예제 작성방법
# 클래스 뷰 – View 클래스에서 – 추가 – 변수 추가
m_wndList, CListBox, private
# 리스트박스 만들기
클래스 뷰 – View 클래스 – 속성 – 메시지 탭 – WM_CREATE
m_wndList.Create(WS_CHILD | WS_VISIBLE | LBS_STANDARD | WS_HSCROLL, CRect(0, 0, 300, 400), this, 1234); // ID는 임시로 1234로
# 리소스 뷰 – 메뉴
주소관리
1. 주소추가(ID_New_Addr)
2. 검색(ID_SEARCH)
// 이벤트 처리기 추가 (View에 만들어라.)
# 리소스 뷰 – 다이얼로그 – 다이얼로그 추가
IDD_DIALOG_NEW_ADDR
클래스 추가 – CDlgNewAddr
# 방금 만든 다이얼르그 박스에 에디트 컨트롤 2개 추가
IDC_EDIT_NAME, IDC_EDIT_PHONE
변수 추가 (값) : m_strName, m_strPhone
# 만든 대화상자를 사용하기 위해서 View 파일 상단에 추가
#include “CDlgNewAddr.h” 추가
Ctrl + T : 다이얼로그 테스트
Ctrl + D : 탭 오더
# 클래스 뷰 – 클래스 추가
클래스 이름 : CUserData
베이스 클래스 : CObject (메모리 릭 방지를 더 편하게 하기 위해)
# pch.h 파일은 모든 파일이 포함하기 때문에 여기에 #include 하면 전역적으로 사용하기 편함
#include “CUserData.h”
# 클래스 뷰 – Doc 파일 – 함수 추가 (데이터 저장 함수)
함수 이름 : NewAddr
액세스 : public
매개 변수 : CString name, CString phone
# Doc 파일에 추가
CPtrList m_ptrList (포인터를 링크드리스트 1차원리스트로 관리)
# 클래스 뷰 – Doc 파일 – 함수 추가 (메모리 해제 함수)
함수 이름 : ReleaseList()
반환 형식 : void
# View 파일에서 소멸자에서 메모리 해제작업을 해준다. ReleaseList()
# 클래스 뷰 – Doc 파일 – 함수 추가
함수 이름 : FindUser
반환 형식 : UserData
매개 변수 : CString name
# attempting to reference a deleted function 에러 메시지 뜨면 복사 생성자를 안만들어서 그런것임.
# Doc 헤더 파일
protected:
CPtrList m_ptrList;
# Doc 소스 파일
int CAddrBookDoc::NewAddr(CString name, CString phone)
{
CUserData* pNew = new CUserData(name, phone);
m_ptrList.AddTail(pNew);
return 0;
}
void CAddrBookDoc::ReleaseList()
{
POSITION pos = m_ptrList.GetHeadPosition();
while (pos != NULL) {
CUserData* pNode = (CUserData*)m_ptrList.GetNext(pos);
OutputDebugString(pNode->GetName());
delete pNode;
}
}
CUserData CAddrBookDoc::FindUser(CString name)
{
POSITION pos = m_ptrList.GetHeadPosition();
while (pos != NULL) {
CUserData* pNode = (CUserData*)m_ptrList.GetNext(pos);
if (pNode->GetName() == name)
return *pNode;
}
return CUserData(_T(""), _T(""));
}
# View 헤더 파일
private:
CListBox m_wndList;
# View 소스 파일
#include "CDlgNewAddr.h"
CAddrBookView::~CAddrBookView()
{
GetDocument()->ReleaseList();
}
int CAddrBookView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_wndList.Create(WS_CHILD | WS_VISIBLE | LBS_STANDARD | WS_HSCROLL,
CRect(0, 0, 300, 400), this, 1234); // 임시로 1234로
//for (int i = 0; i < 10; i++)
// m_wndList.AddString(_T("TEST STRING"));
return 0;
}
void CAddrBookView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
m_wndList.MoveWindow(0, 0, 300, cy);
}
void CAddrBookView::OnNewAddr()
{
CDlgNewAddr dlg;
if (dlg.DoModal() == IDOK)
{
m_wndList.AddString(dlg.m_strName + _T("[") + dlg.m_strPhone + _T("]"));
GetDocument()->NewAddr(dlg.m_strName, dlg.m_strPhone);
}
}
void CAddrBookView::OnSearch()
{
CDlgNewAddr dlg;
if (dlg.DoModal() == IDOK)
{
CUserData user = GetDocument()->FindUser(dlg.m_strName);
if (user.GetName().IsEmpty())
AfxMessageBox(_T("Not Found"));
else
AfxMessageBox(user.GetName() + _T(" ") + user.GetPhone());
}
}
# CUserData 헤더 파일
#pragma once
#include <afx.h>
class CUserData :
public CObject
{
public:
CUserData(CString name, CString phone) {
m_strName = name;
m_strPhone = phone;
}
CUserData(const CUserData& rhs) {
// 복사 생성자
this->m_strName = rhs.m_strName;
this->m_strPhone = rhs.m_strPhone;
}
CString GetName() { return m_strName; }
CString GetPhone() { return m_strPhone; }
private:
CString m_strName;
CString m_strPhone;
};
#pch.h 파일 (클래스의 전역적 사용을 위해) #include "framework.h" #include "CUserData.h"
2. 키보드 이벤트 처리 핵심원리
# 클래스 뷰 – View 클래스 – 속성 – 메시지 탭 – WM_KEYDOWN
void CHelloMFCView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CClientDC dc(this);
static CString strData;
CString strTmp;
strTmp.Format(_T("%c"), nChar);
strData += strTmp;
dc.TextOut(10, 10, strData);
CView::OnKeyDown(nChar, nRepCnt, nFlags);
}
2. 키보드 해킹 원리
HHOOK g_hook;
LRESULT CALLBACK KeyHookCallback(int code, WPARAM wParam, LPARAM lParam)
{
LPKBDLLHOOKSTRUCT khs = (LPKBDLLHOOKSTRUCT)lParam;
if (wParam == WM_KEYDOWN)
{
OutputDebugStringA((char*)&khs->vkCode);
}
return CallNextHookEx(g_hook, code, wParam, lParam);
}
void CMFCHookDlg::SetHook()
{
if (g_hook != NULL)
UnHook();
g_hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyHookCallback, NULL, NULL);
}
void CMFCHookDlg::UnHook()
{
if (g_hook != NULL)
{
UnhookWindowsHookEx(g_hook);
}
}