190624(1일차) - Win32 API
교육을 받으면서 노트필기 했던 내용을 날것 그대로 업로드합니다.
윈도우 기본
윈도우 객체
메세지(키보드 마우스)
자식윈도우, 다이얼로그
앞으로 win32로 개발할 일은 없다고 보면된다.
윈도우즈.net 과정은 윈도우로 공부하게된다.
2000년도 : win32 API (GUI 라이브러리) (C언어 형태의 라이브러리 : 함수형 라이브러리)
이후에 객체지향 개발방법론이 이슈가 되었고 C++이 각광을 받기시작했다.
함수였던 것을 C++형태의 클래스화 (+a)한것을 MFC라고 한다.
90년대 말 되면서 컴포넌트라는 말이 많이 뜨기 시작했다. 모듈화 되어있는 것들을 소프트웨어로도 만들어보자 (소프트웨어 컴포넌트)
컴포넌트를 가져다 쉽게 쓸 수 있는 기능을 가진 COM ActiveX. (여러 라이브러리들의 집합) 2008년 까지 많이 사용이 되었다.
2000년도에 이렇게 가다가는 큰일 날 것 같아서 (Java가 엄청 크기시작) .Net이라는 개념이 등장한다. (1.0버전 등장) .Net이 가장 중요하다. 앞으로 모든 윈도우즈 프로그래밍은 닷넷을 사용하게 될 것이다.
아직까지 MFC를 사용하기는 함. openCV가 C++계열로 만들어져있다. 반도체에러검사, 반도체이미지추출 등을 만드는 회사들은 MFC를 기본적으로 사용한다. (장비회사들, 타이어 오류검출 등) 대부분 MFC로 개발한다.
Database, Network 는 항상 따라온다. os와 밀접한 윈도우즈 시스템 프로그래밍.
win32등으로 개발하지 않을 것이기 때문에 응용까지는 배우지 않는다. 그러나 기존 기술을 기반으로 다음 기술이 나오기 때문에 지금 배우는 내용을 완전히 이해하면 이후에 다른 공부도 쉬워진다.
GUI프로그래밍을 어떤 스타일로 해야하는지. 등을 한달간 공부를 할 것 이다. 기존에 프로그래밍을 한 사람들은 괜찮게 들을 수 있다.
.Net : C#(90%) + VB.Net(10%↓)
GUI : WinForm, WPF 이 두가지가 있다. WPF가 더 나은 환경이긴 하지만 WinFrom도 무시할 수 없다.
.Net은 뭐냐? 닷넷이전에 개발된 프로그램은 Native App이라고 한다. .NET을 사용한 개발은 .Net APP이라고 부른다.
두개가 어떻게 다르냐
Native App : OS의 바이너리로 바로 만들어진다. OS가 이해하는 명령과 데이터로 만들어진다.
.NET APP : OS의 바이너리로 만들어지지 않고 .Net바이너리로 만들어진다. (자바 쪽에서는 JVM) 같은 닷넷에서는 CLR(커먼랭기지런타임)이 이해하는 런타임으로 만들어진다. 장점은 CLR을 리눅스에 깔면 리눅스에서도 돌릴 수 있다. (mac에서는 안만들어줌...) 리눅스에서는 오픈소스로 개발이 되었다.(마소가 만든게 아니라.)
스마트폰, 노트북 등의 경계가 무너지고있는데 기준은? 전원(독립전원), 휴대성(갖고 다닐 수 있는지 등), UI(터치식인가 등) 등으로 나눈다. -> 하지만 경계가 무너져 가고 있다. 개발한 SW가 데스크탑,모바일 등에서도 돌아가야한다.
기존 Native app은 불가능하다. Window10의 목적은 Native app은 모두 버려지고 .NET으로 가는 방향이다. Native app과 .NET은 외형적으론 같지만 내부적으로는 완전히 다르다.. OS에서 모두 돌아가게 하기는 정말 힘들었을 것이다. 윈10 이후에는 네이티브앱을 버릴 것이다.
지금은 모든 것이 웹으로 만들어진다. 앞으로 웹의 세상이 될 것이다. 지금 까지 웹은 Back-end 개발 Front-end 개발. 두 개로 나누어져 있다. 웹은 기본적으로 서버와 클라이언트가 존재해야한다. 클라이언트는 이미 브라우저로 만들어져있다. 개발자라면 크롬을 쓰자. 클라이언트에서 돌아가는 프로그램을 만드는 개발자는 프론트앤드 개발자. 서버를 개발하는 쪽은 백앤드 개발자라고한다. 프론트앤드( HTML5(구조), CSS3(스타일), JavaScript(동적Dynamic 으로 구조/스타일변경하고싶을때) ) 3개로 표준화가 되어있다. 기존에 스크립트언어가 다른 종류도 많았지만 자바스크립트로 표준이 되었다. 프로그래머라면 최소한 이거를 공부할 것. 앞으로 모든 SW는 이쪽으로 갈 것이다. 모든 회사이 SW는 거의 다 이쪽으로 갈 것이다.
Back-end 개발자. 기존에는 서버가 정적 html을 주는 역할이었다. 응답만해주는 역할. 다음에 우리가 만든 네트워크 서버는 TCP/IP를 이용. 웹서버는 HTTP프로토콜을 이해하고 있는 서버 IIS(MS가 만들어 놓은 웹서버), Apache(오픈소스) 등. DBMS에서 꺼내오는 작업이 필요하다.. 서버는 응답만하니까 불가능하다. 그래서 DB와 서버 중간에 (웹서버-웹애플리케이션서버-데이터베이스서버) 웹애플리케이션서버가 있다. 웹애플리케이션 서버는 동적 HTML을 생성하는 역할을 한다. 웹서버에 로그인 정볼르 넘기면 웹 애플리케이션 서버가 DB에서 가져와서 인증을 거친후 해당하는 동적HTML을 생성하여 응답한다. MS 쪽에서는 ASP.NET, 자바에서는 JSP 그리고 PHP.
Node.js : 표준으로 선택한 JS를 사용. DB연결, 동적HTML작성 등 기능을 제공한다. 플루터, 리액트 등. 결론적으로 서버,클라이언트 등 필요한 불편한 점을 그냥 데스크탑 어플리케이션 처럼 돌린다면? (원래는 다 따로 개발을 했었다) 하나로 할 수 있게 만들어 보고자 한 것이다. 크롬의 V8이 그런 역할을 하고 있다. MS에서 오픈소스로 만들어 놓은 VS-CODE가 있다. Node.js 의 일렉트론 앤진을 사용한 것이다. 웹기술인데 데스크톱앱처럼 사용가능하다. 훨신 가볍고 더 좋다. (패키지형태의 설치)
결론은 닷넷을 공부하고 있지만 웹의 세계로 가야한다. 앞으로 Node.js 계열. (플루터 아마존, 리액트 페이스북)
Script : 인터프리터를 사용하여 실시간으로 번역.
자바스크립트를 써서 미리 만들어 놓은 라이브러리가 있다. JQuery(자바스크립트 라이브러리) 몇 년간 엄청나가 만들어졌다. 2년 마다 업데이트가 되고 안정화가 되야 버전업이 된다.
정적(Static):컴파일타임
동적(Dynamic):런타임, 실행시간에
애플리케이션 프로그래밍을 하는 인터페이스
Use
|
|
|
APP
|
|
|
OS
|
|
|
Os를 이용해서 app을 만들어 user에게 서비스.
OS 동작 방식
GUI 사용 원리(매커니즘)
UI 컨트롤 이해
WIn32 API는 라이브러리이다
애플리케이션을 만들 때 사용하는 라이브러리이다.
win32 : c기반
MFC(API를 클래스화함) : C++ 기반
(기본코드) 좋은 코드지만 -> 단순하게 변경해서 사용하겠다.
MS에서 만든용어 -> 문서들의 집합 -> 을 프로젝트라고 하기로 약속함.
-> 프로젝트들의 집합을 솔루션(MS에서)이라고 한자고 했다. (다른 곳에서는 workspace)
프로젝트는 2가지 환경으로 나누어진다.
CUI(커맨드라인) : 텍스트 방식, IO작업을 텍스트로 한다.(간단)
GUI(그래픽) : 그래픽 방식 , IO작업을 그래픽을 사용해서 한다. (복잡)
1장-실습1-01.cpp : 가장 간단한 형식.
F6 : 빌드
최신 키매핑을 사용.
\
하위 시스템을 윈도우로 변경한다.
가장 기본적인 앱을 실행해 보았다.
FirApp.exe를 실행
사용자가 input할 수 있는 영역(마우스)
위 부분을 주석처리하면 UI가 생성되지 않는다. (종료시 작업관리자에서 프로세스 종료해주어야한다.)
프로세스는 보이는영역(View|Window|UI)/보이지않는 영역으로 나누어진다.
c/c++은 순차적으로 보인다.
하지만, api는 메세지 기반이라 메세지에 따라서 코드가 실행된다. (win32를 해놓으면 다른 GUI는 어렵지 않다.)
멀티바이트로 바꾼다.
-
멀티바이트 문자코드 : MBCS(멀티 바이트 캐릭터 셋)
-
유니코드바이트 문자코드 : WBCS(와이드 바이트 캐릭터 셋)
이 두 방식 모두를 하는 것 T를 포함한 문자집합이다. 이후에 다시 배우도록 하겠다.
윈도우 프로그램은 WinMain, WinProc없이는 돌아가지 않는다.
쓰레드가있는데 프로세스는 멀티프로세싱이 가능하다. 여러가지 프로세르를 동시에 실행.
하나의 프로세스는 독립적인 작업을 실행한다. 실행하는 주체를 프로세스로 바라봤다.
깊이있게보면 프로세스 내에 쓰레드가 있다. 쓰레드가 실제 명령들을 수행하는 주체이다.
쓰레드가 2개이면 하나의 프로그램내에서 명령을 실행할 수 있는 주체는 2개다. (2개명령을 독립적으로 실행)
시스템 프로그래밍에서 다시 공부를 할 것이다.
메인스레드, 프라이머리스레드 : 프라이머리 스레드가 메인함수를 호출하는 역할을 한다.
메인스레드가 위를 반복한다. 명령을 실행하는 작업을 메인스레드가 한다.
WndProc는 메세지를 처리한다. WndProc가 사실 더 중요하다.
WndProc는 언제 호출하는가? (메인스레드가 실행한다.)
(윈도우) OS의 메세지 매커니즘을 이해 해야한다.
OS는 프로세스 하나만 사용하는 것이 아니다. 메세지를 순차적으로 애플리케이션에 분배함으로써 독립적으로 처리하도록 기능하는 동작을 OS가 한다. 윈도우에서 발생하는 어떤 사건을 Event라고한다. (ex. 마우스이동(마우스 이벤트)) 이러한 이벤트를 OS가 해석해서 메세지로 만들어낸다. 이벤트는 사건을 의미하는 것이고. (마우스이동) 마우스가 이동했다는 메세지가 발생한다.
만들어진 메세지는 시스템 메세지 Queue에 쌓인다. (OS가 한다.)
앱은 애플리케이션 메세지큐를 가진다. 발생한 이벤트는 시스템메세지큐에 저장됨. 자신의 프로그램에서 발생한 메세지만 중요하다.
이 때 메세지는 2가지로 나뉜다.
-
QUEUE 메세지대부분(꼭 그런건 아니지만) UI메세지 이다.GetMessage. 메세지가 프로시저에 전달되어야한다.
-
비QUEUE 메세지OS와 밀접하게 관계되는 메세지 (WmCreate, WmDestroy 등 메세지는 바로 프로시저에 전달된다.)OS가 처리
(중요:모든 메세지가 QUEUE를 경유하지 않는다.)
프로시저 : 윈도우 메세지를 처리하는 함수라는 뜻이다.
WndProc는 모든메세지(비큐, 큐메세지)를 모두 처리한다.
큐메세지
GetMessage. 메세지가 프로시저에 전달되어야한다. 프로시저랑 OS도 전달해주어야한다. 이것을 메인스레드가 해야한다.
GetMessage에서 프로시저를 실행할 수 없다. (어느 메세지가 우선순위가 높은지 낮은지 모르기 때문이다.) 쓰레드는 하나이기 때문이다. 그래서 필요한 것이바로 내가꺼냈든 OS가 꺼냈든 OS는 우선순위를 알고있다. 꺼내고나서 그 메세지를 다시 OS에게 넘겨주어야한다. 그래야 OS는 내메세지와 비큐메세지를 우선순위를 구분한 후에 각각 처리한다. 모든 메세지에는 우선순위가 부여되어있다.
결론 OS에게 처리해주라고 하는 메세지가 DispatchMessage이다. (프로시저를 호출하는 과정) 그 타이밍을 OS가 처리한다. Dispatch하는 순간 OS가 우선순위를 구분하여 순차적으로 보낸 메세지를 처리하게된다. 윈도우 프로시저는 OS가 호출하는 것이고 먼저처리해야되는 메세지를 처리한 이후 내가 보낸 메세지가 처리된다.
OS가 받아서 내부적으로 WndProc을 호출한다. WndProc이 호출될 때 내부적으로 발생하는 메세지도 엄청 호출된다. WM_MOUSEMOVE메세지 Z좌표는 앞으로 튀어나오는 좌표인데 가장 위쪽에 있는 것을 TOP WINDOW.
한번 점을 찍어보자. DC(그리기도구, Device Context)를 사용. GetDC() 리소스를 얻어오면 반납을 해야한다. Win32는 반납을 직접해주어야한다.
lParam 에서 마우스 좌표 정보를 가져올 수 있다.
return DefWindowProc(hwnd, iMsg, wParam, lParam); //- Default Window Proc 작업수행. 모든 메세지의 기본작업을 처리해준다.
WM_QUIT 메세지만 false
나머지 메세지들은 true
return 으로 반환받기(일반적으로 오류값), 파라미터로(일반적으로 정보) 반환받기.
cf. 프로그램은 무한반복에 대한 연습이다. 다른사람한테 설명할 수 있어야한다. 이해만 하면 알고있는 내용이 전달이 안된다. 스터디 시간때 말로 공부하라.
디버깅
매개변수
-
in : Read
-
out : Write
에러와 버그의 차이점
에러 : 실행파일이 안만들어진다. (오타)
버그 : 내가 의도한대로 동작하지 않는 프로그램.
#include <stdio.h>
void printarray(const int arr[], const int size) {
for (int i = 0; i < size; ++i) {
printf("%d\n", arr[i]);
}
printf("\n");
}
void incrementarray(int arr[], int size) {
for (int i = 0; i < size; ++i)
arr[i]++;
}
int main() {
int arr[10] = { 10,20,30,40,50 }; // 나머지는 0으로 초기화
for (int i = 0; i < 10; ++i) {
printf("%d\n", arr[i]);
}
printf("\n");
printarray(arr, 10);
incrementarray(arr, 10);
printarray(arr, 10);
}
프로그램 생성할 때 2가지 경우가 있다.
-
Debug 모드(개발판) : 개발할 때 주로 사용. 파일이 크다. 속도도 느리다.
-
Release 모드(배포판) : 디버그 같은 기능이 필요없어 파일이 작다. 디버그를 사용할 수 없다.
디버그 툴을 사용해봅시다.
F5 : 디버그
Shift F5 : 디버그 종료
F10 : Setp over. 이 프로그램이 가장 시작포인트(Entry Point)부터 시작한다. (MFC는 안보이거든요.) 한줄 씩 실행한다.
┖함수도 한줄로 인식하고 한줄만에 실행한다.
F11 : Step into. 함수 안으로 들어간다.
F9 : 브레이크 포인트 생성/제거
쇼포인트 : 노란 화살표
이진검색으로 찾을 때 2^20 일때 20번 만에 찾아낼 수 있다.
현재 캐럿이 위치하고 있는 원소를 보여준다.
자동창, 지역창, 조사식 창을 많이 사용하게된다.
지역창 : 로컬 범위에 정의 된 변수를 표시 합니다
포인터를 사용시 원하는 개수만큼 보고싶을 때 위와 같이 콤마(,)사용하여 조사식에서 확인한다.
방금 업데이트 된 것은 빨간색으로 표시된다.
정리
-
원하는 위치까지 실행 (디버그모드, 브레이크포인트)
-
정보를 확인하는 자동창, 지역창, 조사식창
hwnd : 핸들. 핸들테이블이라는 것을 만들어서 관리할 수 있는 번호를 반납받는다.
DispatchMessage : 메세지가 os에 전달됬고. os가 프로시저를 호출했고. 그 프로세스가 리턴을 했다는 뜻이다.
디스패치가 반환했다는 것은 처리를 했다는 뜻이다.
비큐 메세지 이기 때문에 getMessage와 무관하게 작동한다.
프로시저 : 메세지를 처리하는 함수 (WndProc)
dispatch 또는 비큐메세지 발생시 OS가 WndProc를 호출한다. (또는 큐메세지)
명령을 실행할 수 있는 스레드가 하나라서 메세지를 하나씩 실행한다.(딱 한줄 씩 실행가능)
A쓰레드, B쓰레드를 했다가 한다면 RS레지스터에 넣다가 바꿨다가 한다. 이게 컨텍스트 스위칭이다. (스케쥴러에 의해)
메인쓰레드는 메인함수가 시작될 때 시작하고 종료될 때 종료된다.(우아한 종료) -> 이후 프로세스가 죽는다.
(cf: .net은 단 하나의 포그라운드(?) 스레드가 살아있다면 죽지 않는다.)
프로그램을 실행하면 프로세스가 된다. 프로세스를 프로그램의 인스턴스라고 한다.
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg,
WPARAM wParam, LPARAM lParam); // 프로시저 선언
LPSTR lpszCmdLine, //커맨드라인(명령행 인수)
명령행 인수. 부가적으로 들어오는 문자열. 뒤에 파일명이 있으면 오픈.
#include <windows.h>
#include <TCHAR.H>
// 프로시저 선언
LRESULT CALLBACK WndProc( // LRESULT : (반환형)(확장을 위해서 이렇게 했다.
//CALLBACK : 서버(OS)가 호출하는 함수.
HWND hwnd, // 윈도우 핸들
UINT iMsg, //unsigned int, 메세지의 번호는 정수값을 갖는다.
WPARAM wParam, // 역사적인 문제. word(2byte-> 현재는 4byte). 추가적 정보를 넘겨준다.
LPARAM lParam); //long(4byte)
int WINAPI WinMain(
HINSTANCE hInstance, // 현재 나의 프로세스의 인스턴스
HINSTANCE hPrevInstance, // 나를 실행한 부모프로세스의 인스턴스
LPSTR lpszCmdLine, //커맨드라인(명령행 인수)
int nCmdShow) // 윈도우를 보여줄 때의 형태를 정한다. (최대화|최소화|default)
{
HWND hwnd; // 핸들 : 식별번호(ID) (4byte 짜리 정수)
MSG msg; // 메세지 (6개정보보관)
WNDCLASS WndClass; //WND구조체
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = WndProc; //메세지 처리 함수를 지정(윈도우 프로시저,프로시저 함수시정) , 콜백함수지정 (주소지정)
//long pointer function 윈도우 프로시저, 함수포인터에 WndProc.
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = _T("Window Class Name");//클래스이름, 등록하고자 하는 윈도우 클래스의 이름 //윈도우를 만들 때 이 이름을 이용하므로 꼭 기억해야한다.
RegisterClass(&WndClass);//윈도우 클래스는 RegisterClass를 이용해서 커널에 등록한다.
hwnd = CreateWindow(_T("Window Class Name"), // 비큐 메세지가 생성되어 WndProc가 호출된다.
_T("Window Title Name"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
/*
윈도우를 만든 다음에는 커널이 부여한 윈도우 출력 형태에 따라
ShowWindow()와 UpdateWindow()함수를 이용해 윈도우를 화면에 보여준다.
프로그램에서는 nCmdShow값을 커널에서 받은 기본값으로 그대로 사용한다.
*/
ShowWindow(hwnd, /*nCmdShow*/SW_SHOW); // WM_PAINT를 큐에보관
//hwnd : 나타낼 윈도우 핸들값. (CreateWindow의 리턴값)
//nCmdShow : 윈도우를 화면에 나타내는 방법으로 상수값을 제공한다..
// SW_HIDE : 윈도우를 화면에서 사라지게 해 다른 윈도우 활성화
// SW_MAXIMIZE : 윈도우를 최대화
// SW_MINIMIZE : 윈도우를 최소화
// SW_SHOW : 윈도우를 활성화해서 화면에 보여준다.
UpdateWindow(hwnd);// WM_PAINT를 큐에보관 한것을 바로 처리.
//BOOL UpdateWindow(HWND hWnd);
//윈도우에 WM_PAINT 메세지를 보내 기본 출력을 한다.+
//윈도우가 보여지고 난 후 최초에 발생하는 WM_PAINT메세지를 즉시 처리하기 위해 사용한다.
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc( // dispatch 할때 실행된다.
HWND hwnd,
UINT iMsg,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
int create = 0; // 스택변수
switch (iMsg)
{
case WM_CREATE: // 윈도우가 생성되는 시점에 초기화 작업을 이 곳에 하면된다.
create = 1;
break;
case WM_MOUSEMOVE:
{
static int count;
// 100번의 마우스 메세지가 발생했을 때 X,Y 좌표를 확인하고 싶다면?
int x = LOWORD(lParam);
int y = HIWORD(lParam);
++count;
if (count == 100) {
x = x;
}
hdc = GetDC(hwnd);
Rectangle(hdc, x - 20, y - 20, x + 20, y + 20);
ReleaseDC(hwnd, hdc);
}
break;
case WM_DESTROY: // x버튼 누를 때
PostQuitMessage(100); // Post : 큐에다가 저장할래요. 무엇을? QuitMessage를 (0:정상종료코드값) (wParam에 저장된다.)
break;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
디버깅 방법 중에서
트레이스 방법(OutputDebugString)
wsprintf(buf, "count : %d, x: %d, y: %d\n", count,LOWORD(lParam),HIWORD(lParam));
//writebyte string : 버퍼를 주면 문자열로 변환
내일은 4장까지 할듯. IO, 출력, 인풋, 컨트롤(에디트박스, 버튼, 리스트박스, 라디오버튼), 다이얼로그
'Win32(API)' 카테고리의 다른 글
[win32 API] DC 그리기 작업, bitmap (0) | 2019.07.02 |
---|---|
[win32 API] 콜백, Invalid 무효영역 다시그리기 등 (0) | 2019.07.02 |
[win32 api] EditBox, ListBoc Control (1) | 2018.03.17 |
[Win32] Modal dialog Modeless 대화상자 (생성 및 사용법) (0) | 2018.03.17 |
wsprintf 로 buf에 문자열 복사하기 (Win32 Api) (0) | 2018.03.16 |