메서드 재정의(virtual, override)


virtual

부모 클래스 함수 앞에 붙는 연산자.

자식 클래스에 의해서 재정의될 수 있다는 의미를 가지고 있다.

컴파일러는 이 지정자가 붙은 함수를 비가상함수와 다르게 컴파일함으로써 재정의될 준비를 한다.


override

자식 클래스 함수 앞에 붙는 연산자.

부모로부터 상속받은 함수와는 다르게 구현한다는 의미를 가지고 있다.

재정의되는 함수는 부모의 함수와 이름, 시그니처도 일치해야함.

재정의된 함수는 부모의 함수에 의존적인 경우가 많은데 이 때 base 키워드로 부모의 원래함수를 호출 할 수 있다.


예제

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

 

class Base

{

    public virtual void echo()

    {

        Console.WriteLine("base echo~!!");

    }

}

 

class Derived : Base

{

    public override void echo()

    {

        Console.WriteLine("derived echo~!!");

    }

}

 

class Program

{

    static void Main()

    {

        Base B = new Base();

        Derived D = new Derived();

        B.echo();

        D.echo();

        Base B2 = D;

        B2.echo();

    }

}

 

실행결과



B.echo와 D.echo는 당연한 결과라고 볼 수 있지만 B2.echo호출문의 결과는

derived echo~!!라는 결과를 출력합니다. 


                                                       Base B2 = D;

위 선언에서 B2의 정적 타입은 Base

B2의 동적 타입은 Derived가 됩니다.


일반적인 비가상 메서드는 정적 타입을 따르지만 가상 메서드에서는 

호출 객체가 실제로 가리키고 있는 타입, 즉 동적 타입을 따르게 됩니다.


echo함수가 가상함수 일 때에는 B2가 비록 Base타입이지만 Derived객체를 가리키고 있기 때문에

Derived의 메서드가 호출이 됩니다.


그렇다면 위 예제를 비가상 함수로 바꾸어 출력을 해보도록 하겠습니다.


public virtual void echo()

이 부분에서 virtual을 빼고,

public override void echo()

이 부분에서 override는 new로 바꾸어 출력해보겠습니다.



실행결과




참고.

여기서 new를 안붙이고 public void echo()로 변환해도 결과는 같습니다.

new를 붙이는 이유는 상속받은 멤버를 완전히 숨겨버리고 자식 클래스가 같은 이름으로 새로운 멤버를 만든다는 뜻으로써, 의도적으로 같은 이름을 사용한다는 것을 알리는 연산자라고 볼 수 있습니다.

물론 멤버를 숨기는 것이지 상속을 안받는다는 뜻은 아닙니다.

base.메서드명()을 이용하면 부모의 숨겨진 멤버를 상속 받을 수 있게 됩니다.

신고

'Windows Programming > Windows::C#' 카테고리의 다른 글

[C#] 메서드 재정의(virtual, override)  (0) 2013.03.31
[C#] 상속 예제  (0) 2013.03.31
C# GUI 구구단 프로그램  (0) 2012.02.16
윈도우즈 폼 예제  (0) 2012.02.16
C# 콘솔 예제(성적관리 프로그램)  (0) 2012.02.16

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

 

class Human

{

    protected string Name;

    protected int Age;

    public Human(string aName, int aAge)

    {

        Name = aName;

        Age = aAge;

    }

    public virtual void Intro()

    {

        Console.WriteLine("Name : " + Name);

        Console.WriteLine("Age : " + Age);

    }

}

 

class Student : Human

{

    protected int StNum;

    public Student(string aName, int aAge, int aStNum) : base(aName, aAge)

    {

        StNum = aStNum;

    }

    public override void Intro()

    {

        base.Intro();

        Console.WriteLine("stuNumber : " + StNum);

    }

    public void Study()

    {

        Console.WriteLine("study");

    }

}

 

class Program

{

    static void Main()

    {

        Student Kim;

        Kim = new Student("Minhwa Jin", 25, 921223);

        Kim.Intro();

        Kim.Study();

    }

}

 

base(aName, aAge)

부모의 생성자를 호출하여 초기화하는 구문입니다.

기존의 StNum은 직접 자신의 생성자에서 초기화를 하게 되구요.


여기서 base를 주석처리를 하게 되면 기반 클래스(부모)의 디폴트 생성자가 호출되는데요.

Human에서는 디폴트 생성자가 없기 때문에 에러가 납니다.


이름과 나이를 출력하는 코드가 기반 클래스(부모)에 작성되어있기 때문에

Intro를 호출하면 출력이 되겠지요~


그리고 기본적으로 c#상속은 public 상속만 지원하며, 다중 상속을 지원하지 않고

상속없이 단독 클래스를 정의하는 것도 허용되지 않는다고 합니다~


실행결과





아 참고로 제나이 22 입니다...-_-;


신고

'Windows Programming > Windows::C#' 카테고리의 다른 글

[C#] 메서드 재정의(virtual, override)  (0) 2013.03.31
[C#] 상속 예제  (0) 2013.03.31
C# GUI 구구단 프로그램  (0) 2012.02.16
윈도우즈 폼 예제  (0) 2012.02.16
C# 콘솔 예제(성적관리 프로그램)  (0) 2012.02.16

#include <windows.h>

#include <iostream>

#include <fstream>

#include <string>

#include "resource.h"

 

using namespace std;

 

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

LPSTR lpszClass="Hahahia`s Notepad"

 

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR lpszCmdParam,int nCmdShow)

{

        HWND hWnd;

        MSG Message;

        WNDCLASS WndClass;

        g_hInst=hInstance;

       

        WndClass.cbClsExtra=0; // 윈도우 생성초기 설정

        WndClass.cbWndExtra=0;

        WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

        WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

        WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

        WndClass.hInstance=hInstance;

        WndClass.lpfnWndProc=(WNDPROC)WndProc;

        WndClass.lpszClassName=lpszClass;

        WndClass.lpszMenuName=NULL;

        WndClass.style=CS_HREDRAW | CS_VREDRAW;

        WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1); // Resource 생성 (메뉴)

        RegisterClass(&WndClass);

 

 

        hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_THICKFRAME, // hWnd 윈도우 생성               CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

                 NULL,(HMENU)NULL,hInstance,NULL);

        ShowWindow(hWnd,nCmdShow);

       

        while(GetMessage(&Message,0,0,0)) {

               TranslateMessage(&Message);

               DispatchMessage(&Message);

        }

        return Message.wParam;

}

 

char * drawText;

 

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

        HDC hdc;

        PAINTSTRUCT ps;

        static char text[100][1000];

        static int count, line;

        static SIZE size;

        int w_count = 0;

        OPENFILENAME OFN, SFN;

        char str[100], lpstrFile1[100] = "", lpstrFile2[100] = "";

        char filter[] = "Every File(*.*) \0*.*\0Text File\0*.txt;*.doc\0";

        int answer; // 질의

        switch(iMessage) {

        case WM_CREATE:

               CreateCaret(hWnd, NULL, 5, 15);

               ShowCaret(hWnd);

               count = 0;

               line = 0;

               drawText = new char[1024];

               strcpy(drawText, "");

               break;

        case WM_COMMAND:

               switch (LOWORD(wParam))

               {

               case ID_FILEOPEN:

                       memset(&OFN, 0, sizeof(OPENFILENAME));

                       OFN.lStructSize = sizeof(OPENFILENAME);

                       OFN.hwndOwner = hWnd;

                       OFN.lpstrFilter = filter;

                       OFN.lpstrFile = lpstrFile1;

                       OFN.nMaxFile = 100;

                       OFN.lpstrInitialDir = ".";

                       if(GetOpenFileName(&OFN) != 0){

                              wsprintf(str, "%s 파일을 열겠습니까?", OFN.lpstrFile);

                              answer = MessageBox(hWnd, str, "열기선택", MB_YESNO);

                              if(answer == IDYES){

                                      FILE *fptr = fopen(OFN.lpstrFile, "r+");

                                      line = 0;

                                      while(line < 100 && fgets(text[line], sizeof(text[0]), fptr)) // fgets 문자열을 읽어옵니다                                   {

                                                 text[line][strlen(text[line])] = '\0'; // 끝을 NULL 두져

                            count = strlen(text[line]);

                            if( text[line][strlen(text[line])-1] == 10 ) // 엔터키

                            {

                                                             line++;

                                                             count = 0;

                                                     }

                                      }

                                      fclose(fptr);

                                      InvalidateRgn(hWnd, NULL, TRUE);

                              }

                       }

                       break;

               case ID_FILESAVE:

                       memset(&SFN, 0, sizeof(OPENFILENAME));

                       SFN.lStructSize = sizeof(OPENFILENAME);

                       SFN.hwndOwner = hWnd;

                       SFN.lpstrFilter = filter;

                       SFN.lpstrFile = lpstrFile2;

                       SFN.nMaxFile = 100;

                       SFN.lpstrInitialDir = ".";

                       if(GetSaveFileName(&SFN) != 0)

                       {

                              wsprintf(str, "%s 파일로 저장하시겠습니까?", SFN.lpstrFile);

                              answer = MessageBox(hWnd, str, "저장하기 선택", MB_YESNOCANCEL);

                              if(answer == IDYES)

                              {

                                      MessageBox(hWnd, "저장합니당", "저장", MB_OK);

                                      FILE *fptr = fopen(SFN.lpstrFile, "w");    

                    while( w_count <= line  && w_count < 100)

                    {

                                             fputs(text[w_count], fptr);                                  

                                             w_count++;

                                             fputs("\n", fptr);

                                      }

                                      fclose(fptr);

                              }

                              else MessageBox(hWnd, "저장 ㄴㄴ", "취소", MB_OK);

                       }

                       break;

               case ID_FILENEW:

                       answer = MessageBox(hWnd, " 파일을 열겠습니까?", " 파일 선택", MB_OKCANCEL);

                       if(answer == IDOK){

                              memset(text, 0, sizeof(text));

                              line = 0;

                              count = 0;

                              InvalidateRgn(hWnd, NULL, true);

                       }

                       break;

               case IDHELP:

                       answer = MessageBox(hWnd, "WinAPI NodePad \nMade By Hahahia\n", "프로그램 정보", MB_OK);

                       break;

               case ID_EXIT:

                       answer = MessageBox(hWnd, "파일을 저장하고 끝내시겠습니까?", "끝내기 선택", MB_YESNOCANCEL);

                       if(answer == IDYES || answer == IDNO)

                              PostQuitMessage(0);

                       break;

               }

               break;

        case WM_PAINT:

               hdc = BeginPaint(hWnd, &ps);

               GetTextExtentPoint(hdc, text[line], strlen(text[line]), &size);

               for(int i=0; i<= line; i++)

               {

                       TextOut(hdc, 0, i*20, text[i], strlen(text[i]));

               }

               SetCaretPos(size.cx, line*20);

               EndPaint(hWnd, &ps);

               if(count == 0)

                       ShowCaret(hWnd);

               break;

        case WM_CHAR:

               if(wParam == VK_BACK) // 줄넘기기

               {

                       if(count == 0 && line == 0) break;

                       count--;

                       if(count < 0)

                       {

                              line--;

                              count = strlen(text[line]);

                       }

               }

               else if(wParam == VK_RETURN) // enter

               {

                       count = 0;

                       line += 1;

               }

               else text[line][count++] = wParam; // 키보드로 받은값 출력(배열에추가)

               text[line][count] = '\0';

               strcpy(drawText, "");

               for(int i=0;i <= line;i++)

        {

                       strcat(drawText, text[i]);

                       strcat(drawText, "\n");

        }

               InvalidateRgn(hWnd, NULL, TRUE); // 동기화

               break;

        case WM_DESTROY:

               HideCaret(hWnd);

               DestroyCaret();

               PostQuitMessage(0);

               return 0;

        }

        return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}



UI는 정말 별거없구요....대충만든거라..ㅠㅠㅠ




실행시키면 바로 메모장처럼 글을 쓸수있어요 ㅎㅎㅎㅎ




파일 -> 저장 들어가면 저장도되구요 ㅎ



저장하시겠습니까? ㅇㅋ


바탕화면에 있는 test라는 텍스트를 불러옵니다.



잘 나오죠 ㅎㅎ 

한가지 흠이라면..... 개행에서 | 글자가 꼭 나와버리네요.... 이건 추후에 수정할게요 ㅠㅠ 은근히 복잡합니당

그리고 리소스 구현 방법도 자세히 포스팅 해보도록 하겠습니다

신고

WndClass.hbrBackground=GetStockObject(WHITE_BRUSH);

BLACK_BRUSH, WHITE_BRUSH, LTGRAY_BRUSH 속성에 따라서 배경색을 지정할 수 있다.



HCURSOR LoadCursor( HINSTANCE hInstance, LPCTSTR lpCursorName );


첫번째 인수 hInstance는 커서를 가지고 있는 프로그램의 인스턴스 핸들이되 윈도우즈가 제공하는 디폴트 커서를 사용하려면 이 인수를 NULL로 지정하면 된다. 두번째 인수 lpCursorName은 사용하고자 하는 커서의 이름을 지정한다. 윈도우즈가 디폴트로 제공하는 커서에는 다음과 같은 종류가 있다.


IDC_ARROW 화살표 모양

IDC_CROSS 십자 모양

IDC_IBEAM I자 모양

IDC_NO 원안에 빗금이 쳐진 모양

IDC-WAIT 모래시계 모양


윈도우의 크기, 타이틀 바꾸기

hWnd=CreateWindow(lpszClass,"My First Program",WS_OVERLAPPEDWINDOW,

 100,100,300,200,

 NULL,(HMENU)NULL,hInstance,NULL);

// 윈도우 타이틀(2번째 인수) = "My First Program"

// (4,5,6,7번째 인수) = (100,100)의 위치에 윈도우가 나타나며 폭은 300, 높이는 200

// 윈도우 스타일(3번째 인수) => WS_OVERLAPPEDWINDOW


윈도우 스타일 속성들


WS_CAPTION 타이틀 바를 가진다.

WS_HSCROLL 수평 스크롤 바를 가진다.

WS_VSCROLL 수직 스크롤 바를 가진다.

WS_MAXIMIZEBOX 최대화 버튼을 가진다.

WS_MINIMIZEBOX 최소화 버튼을 가진다.

WS_SYSMENU 시스템 메뉴를 가진다.

WS_THICKFRAME 크기를 조절할 수 있는 경계선을 가진다.


이 값들을 or연산자로 연결하여 여러가지 속성을 한꺼번에 지정할 수 있다.

그렇다면 WS_HSCROLL | WS_VSCROLL이라 지정하면

수직, 수평 스크롤바가 달리는 윈도우가 만들어질 것이다.





참고 | winapi.co.kr

신고
소스코드 보기 => GO


WndProc은 WinMain에서 호출하는 것이 아니라 윈도우즈에 의해 호출된다. WinMain내의 메시지 루프는 메시지를 메시지 처리 함수로 보내주기만 할 뿐이며 WndProc은 메시지가 입력되면 윈도우즈에 의해 호출되어 메시지를 처리한다. 이렇게 운영체제에 의해 호출되는 응용 프로그램내의 함수를 콜백(CallBack) 함수라고 한다.


WndProc의 구조는 대체로 다음과 같은 형태를 가진다. 메시지의 종류에 따라 다중 분기하여 메시지별로 처리를 진행한다.


switch(iMessage)

{

case Msg1:

처리1;

return 0;

case Msg2:

처리2;

return 0;

case Msg3:

처리3;

return 0;

default:

return DefWindowProc(...);

}

First예제의 메시지 처리 함수=>

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

switch(iMessage) {

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}


WM_DESTROY 메시지는 사용자가 시스템 메뉴를 더블클릭하거나 Alt+F4를 눌러 프로그램을 끝내려고 할 때 발생하는 메시지이다.


메시지 처리 함수의 전체적인 순서도



참고 | winapi.co.kr


신고

소스코드 보기 => GO


WinMain 함수에서 하는 가장 중요한 일은 윈도우를 만드는 일이다. 윈도우가 있어야 사용자로부터 입력을 받을 수 있고 출력을 보여줄 수도 있기 때문이다. 윈도우를 만드려면 윈도우 클래스를 먼저 등록한 후 CreateWindow 함수를 호출해야 한다. 모든 윈도우는 클래스를 기반으로 하여 만들어지며, 윈도우 클래스는 만들어질 윈도우의 여러가지 특성을 정의한다. 윈도우 클래스는 windows.h에 다음과 같이 정의되어 있는 구조체이다.


typedef struct tagWNDCLASS

{

    UINT        style;

    WNDPROC     lpfnWndProc;

    int         cbClsExtra;

    int         cbWndExtra;

    HINSTANCE   hInstance;

    HICON       hIcon;

    HCURSOR     hCursor;

    HBRUSH      hbrBackground;

    LPCSTR      lpszMenuName;

    LPCSTR      lpszClassName;

} WNDCLASS;


이 인자들에 대한 자세한 내용은 winapi.co.kr 참고


ATOM RegisterClass( CONST WNDCLASS *lpWndClass);


RegisterClass 함수의 인수로 WndClass 구조체의 번지를 넘겨주면 된다.이런 특성을 가진 윈도우를 앞으로 사용하겠다는 등록 과정이다. 소스의 앞부분을 보면 WNDCLASS 구조체의 각 멤버에 값을 대입하고 윈도우 클래스를 등록하는 코드가 있다.


윈도우 클래스를 등록한 후에는 등록한 윈도우 클래스를 기본으로 윈도우를 실제 생성해야 한다. 윈도우를 생성할 때는 CreateWindow 함수를 사용한다.


HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam) 


이 인자들에 대한 자세한 내용은 winapi.co.kr 참고


BOOL ShowWindow(hWnd, nCmdShow); 

hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을 그대로 넘겨주면 된다. nCmdShow는 윈도우를 화면에 출력하는 방법을 지정하며 다음과 같은 매크로 상수들이 정의되어 있다.



윈도우가 만들어지는 과정을 도식화


윈도우즈 프로그램에서 메시지를 처리하는 부분을 메시지 루프라고 하며 보통 WinMain 함수의 끝에 다음과 같은 형식으로 존재한다.


while(GetMessage(&Message,0,0,0)) {

TranslateMessage(&Message);

DispatchMessage(&Message);

}


BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); 

이 함수는 시스템이 유지하는 메시지 큐에서 메시지를 읽어들인다. 메시지는 첫번째 인수가 지정하는 MSG 구조체에 저장된다. 읽어들인 메시지가 WM_QUIT일 경우 False를 리턴, 그 외의 메시지이면 WM_QUIT 메시지가 읽혀질 때까지, 즉 프로그램이 종료될 때까지 전체 while 루프가 계속 실행된다. 나머지 세 개의 인수는 읽어들일 메시지의 범위를 지정하는데 잘 사용되지 않으므로 일단 무시하기로 한다.


BOOL TranslateMessage( CONST MSG *lpMsg); 

키보드 입력 메시지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 해 준다. 이 함수는 키보드의 눌림(WM_KEYDOWN)과 떨어짐(WM_KEYUP)이 연속적으로 발생할 때 문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 한다. 예를 들어 A키를 누른 후 다시 A키를 떼면 A문자가 입력되었다는 메시지를 만들어 낸다.


LONG DispatchMessage( CONST MSG *lpmsg); 

시스템 메시지 큐에서 꺼낸 메시지를 프로그램의 메시지 처리 함수(WndProc)로 전달. 이 함수에 의해 메시지가 프로그램으로 전달되며 프로그램에서는 전달된 메시지를 점검하여 다음 동작을 결정하게 된다.



실제 메시지 처리는 별도의 메시지 처리 함수(WndProc)에서 수행한다. 메시지는 시스템의 변화에 대한 정보이며 MSG라는 구조체에 보관된다. MSG 구조체는 다음과 같이 정의되어 있다.


typedef struct tagMSG

{

    HWND        hwnd; // 어떤 종류의 메시지인가를 나타낸다

    UINT        message;

    WPARAM      wParam;

    LPARAM      lParam;

    DWORD       time;

    POINT       pt;

} MSG;


GetMessage 함수는 읽은 메시지를 MSG형의 구조체에 대입해 주며 이 구조체는 DispatchMessage 함수에 의해 응용 프로그램의 메시지 처리 함수(WndProc)로 전달된다.


참고 | winapi.co.kr


신고

/* first.cpp */


#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

LPSTR lpszClass="First";


int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

 ,LPSTR lpszCmdParam,int nCmdShow)


{

HWND hWnd;

MSG Message;

WNDCLASS WndClass;

g_hInst=hInstance;

WndClass.cbClsExtra=0;

WndClass.cbWndExtra=0;

WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);

WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);

WndClass.hInstance=hInstance;

WndClass.lpfnWndProc=(WNDPROC)WndProc;

WndClass.lpszClassName=lpszClass;

WndClass.lpszMenuName=NULL;

WndClass.style=CS_HREDRAW | CS_VREDRAW;

RegisterClass(&WndClass);


hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,

 CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

 NULL,(HMENU)NULL,hInstance,NULL);

ShowWindow(hWnd,nCmdShow);

while(GetMessage(&Message,0,0,0)) {

TranslateMessage(&Message);

DispatchMessage(&Message);

}

return Message.wParam;

}


LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

switch(iMessage) {

case WM_DESTROY:

PostQuitMessage(0);

return 0;

}

return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}



- windows.h 만 포함해 주면 stdio.h나 conio.h, graphics.h 등을 포함해 줄 필요가 없다


- 프로그램의 시작점인 엔트리 포인트(Entry Point)가 main 함수가 아니라 WinMain이다.


- hInstance : 프로그램의 인스턴스 핸들. hInstance란 프로그램 자체를 일컫는 정수값이며 API 함수에서 수시로 사용된다. 그래서 이 예제에서는 WinMain의 인수로 전달된 hInstance값을 전역 변수 g_hInst에 대입해 두었다 


WinMain은 프로그램을 시작시키기만 하며 실질적인 처리는 대부분 WndProc에서 이루어진다


출력 결과



참고

error C2440: '=' : 'LPSTR'에서 'LPCWSTR'(으)로 변환할 수 없습니다.

'CreateWindowExW' : 매개 변수 2을(를) 'LPSTR'에서 'LPCWSTR'(으)로 변환할 수 없습니다.


저 소스를 입력했을 때 위의 메시지같은 에러가 나올 경우가 있다. 이 경우에는

프로젝트의 속성->구성속성->일반->문자 집합

->유니코드 문자 집합 사용에서 멀티 바이트 문자 집합 사용으로 바꾼다.



참고 | http://winapi.co.kr/

신고

/* thread.c

        made by hahahia

*/

#include <stdio.h>

#include <conio.h>

#include <Windows.h>

 

DWORD WINAPI print2(LPVOID lpParams){

        while(1)

        {

               printf("Thread2");

               Sleep(800);

        }

}

DWORD WINAPI print1(LPVOID lpParam)

{

        while(1)

        {      

               printf("Thread1");

               Sleep(500);

        }

}

int main()

{

        int a;

        CreateThread(NULL, 0, print1, NULL, 0 , NULL);              

        CreateThread(NULL, 0, print2, NULL, 0, NULL);

        while(1)

        {

               scanf("%d", &a);

               printf("%d\n", a);

        }

}

 


신고

GetCurrentProcess 함수를 잠깐 봅시다

HANDLE GetCurrentProcess(VOID);

- 현재 실행되고 있는 프로세스의 핸들을 얻을 때 사용하는 함수입니다.
 
커널 오브젝트와 핸들의 종속 관계

- 커널 오브젝트는 Windows 운영체제에 종속적입니다

여기서 잠깐 비유(?)를 통해 종속 관계를 표현해보겠습니다
책 - 커널 오브젝트, 고객 - 프로세스, 도서 대여점 - 운영체제

- 커널 오브젝트는 프로세스에 종속적인 것이 아니라, 운영체제에 종속적인 관계커널 오브젝트의 소멸지점은 운영체제에 의해 결정된다.
-> 풀이를 해보자면 책은 고객이 마음대로 할 수 없는 것이고, 도서 대여점에서 관리를 합니다. 그리고 책을 폐기처분은 도서 대여점에서 합니다.
- 커널 오브젝트는 프로세스에 종속적인 것이 아니라 운영체제에 종속적인 관계여러 프로세스에 의해서 접근 가능(커널오브젝트 하나에서 자식프로세스, 부모 프로세스 둘다 접근 할 수 있다라는 점 알아두시기 바랍니다)
- > 책은 고객에 대해 종속적인 것이 아니라 도서 대여점에 종속적인 관계이므로 여러 고객에 책을 볼 수 있다.

흠 더 헷갈렸나요-_-;;;
어쨋든 요약을하자면 커널 오브젝트는 운영체제에 종속적이고, 커널 오브젝트는 여러 프로세스에 의해 접근이 가능하다 이겁니다....ㅋㅋ 

그리고 핸들은 운영체제에 종속적이지 않고 프로세스에 종속적입니다. 뒤에서 자세히 설명하도록 하겠습니다.

typedef struct _PROCESS_INFORMATION
{
      HANDLE hProcess;            // 프로세스의 핸들
      HANDLE hThread;   // 쓰레드 핸들
      DWORD dwProcessId;        // 프로세스의 ID
      DWORD dwThreadId;      // 쓰레드의 ID
} PROCESS_INFORMAION;

프로세스 핸들프로세스의 커널 오브젝트를 가리키는 것이고, 여기서 프로세스 ID는 커널 오브젝트가 아니라 프로세스 자체를 구분짓기 위한 것입니다.

전에 프로세스를 생성할 때 선언하던 구조체를 정의한 것입니다.
예를들어 프로세스의 우선순위를 바꾸고싶다 하면
SetPriorityClass(pi.hProcess, NORMAL_PRIORITY_CLASS); 이런식으로
표현하면 현재 pi로 정의된 프로세스의 우선순위를 낮추게 됩니다


커널 오브젝트와 Usage Count

- 프로세스가 소멸된다고 해서 커널 오브젝트가 소멸된다고 할 수 없습니다. 소멸을 시키는 것은 바로 운영체제입니다. 
이 운영체제는 해당 커널 오브젝트가 참조하는 대상이 하나도 없을 때 커널 오브젝트를 소멸 시키는데 그 참조하는 대상을 Usage Count라는 놈이 관리를하죠 
눈치가 빠르면 알겠지만 Usage Count가 0이 되는 순간 해당 커널 오브젝트는 소멸된다고 볼 수 있겠습니다.

리소스의 생성과 동시에 해당 커널 오브젝트의 Usage Count = 1 이됩니다.
생성이 완료되면 부모가 자식 리소스 커널 오브젝트의 핸들을 획득하기에 Usage Count = 2 가 됩니다.

만약 자식 리소스를 소멸하면 Usage Count 가 하나 줄어들게 되겠죠??
여기서 문제가 발생하게 됩니다. 리소스가 소멸됬음에도 그 리소스를 관리하는 커널 오브젝트는 소멸되지 않습니다. 왜냐하면 Usage Count 가 1이기 때문이죠. 이렇게되면 계속해서 커널 오브젝트가 잔류하게 되어서 속도상에 문제가 발생할 수 있겠습니다.
Usage Count = 0 을 만들기 위해선 부모의 자식의 커널 오브젝트 참조를 해제해야 Usage Count가 하나 줄어들고 0이 되겠습니다.
여기서 CloseHandle() 함수를 사용하면 자식의 커널 오브젝트 핸들을 반환하여 참조를 해제하게 됩니다.

즉, Usage Count 가 하나 줄게 되겠죠.
따라서 커널 오브젝트를 소멸하기 위해서는
부모가 소유한 자식의 커널 오브젝트 핸들을 반환해야 합니다


예)

부모 프로세스가 자식의 커널 오브젝트 핸들을 반환하지 않아 커널 오브젝트가 소멸되지 않았다고 가정할때
계산기 프로세스를 생성하면 그에 따른 커널 오브젝트가 생성이 되겠죠?
계산기 프로세스를 종료시켜도 커널 오브젝트는 소멸되지 습니다 왜냐하면 Usage Count =  1이기 때문입니다
실행-> 종료, 실행 -> 종료 를 반복한다고 할때
커널 오브젝트는 실행의 수만큼 존재할텐데
따라서 이 문제를 해결하기 위해 CloseHandle 을 통해 부모의 자식의 커널 오브젝트 참조를 해제를 합니다.
자식 프로세스가 종료될때 Usage Count = 0 이 되어 커널 오브젝트가 소멸되겠죠?

정리를 해보도록 하겠습니다. 부모 프로세스가 자식 프로세스를 생성함과 동시에 커널 오브젝트가 생성됩니다. 이 과정에서 Usage Count값이 1이 되는것 같지만, 부모 프로세스가 CreatProcess 함수 호출과정에서 자식 프로세스의 핸들을 얻기때문에 결과적으로는 Usage Count = 2 가 되는것입니다. 
그리고 프로세스(자식)를 종료할 때 Usage Count값은 2-1=1 이므로 커널 오브젝트가 지워지지 않는데 이 남은 연결고리를 끊어버리기 위해 부모프로세스에서 CloseHandle 함수를 호출해 UsageCount = 0 이 되고 커널 오브젝트는 없어지게 됩니다.
또 길어지네요.....정리를 하고싶었지만 ㅠ_ㅠ
 
참고 :

바탕화면에서 아이콘을 통해 프로세스를 생성할 경우에도 Usage Count = 2 겠죠
바탕화면 자체도 프로세스이기 때문입니다. 이 때는 바탕화면이 부모 프로세스가 되고 아이콘을 통해 생성된 프로세스는 자식프로세스입니다...
(Cmd 에서 실행하면 Cmd 가 부모 프로세스)
즉, 프로세스는 생성과 동시에 Usage Count = 2 가 됩니다.

출처 : 윈도우즈 시스템 프로그래밍 | 윤성우 저 | 한빛미디어
신고

커널(Kernel) - 컴퓨터를 운영하는 데 있어서 중심이 되는 운영체제 핵심부분

커널 오브젝트(Kernel Object) - 커널에서 관리하는 중요한 정보를 담아둔 데이터 블록

프로세스를 예를 들자면 프로세스를 생성하는 실질적인 주체는 프로그래머가 아니라 운영체제라고 해야 정확한 것이다. 
이렇게 운영체제가 프로세스를 관리하기 위해서는 프로세스에 관련된 정보들을 저장해야 한다.
그 과정에서 구조체를 하나 정의를 하는데 이 구조체가 바로 커널 오브젝트입니다. 

 - 커널 오브젝트에 대한 도식화(프로세스)

- Windows 운영체제는 프로세스, 쓰레드 혹은 파일과 같은 리소스(Resource)들을 원활히 관리하기 위해 필요한 정보를 저장해야 한다. 이 때 데이터를 저장한는 메모리 블록을 가리켜 커널 오브젝트라 한다. 
- Windows 커널에 의해서 관리되는 리소스 수만큼 커널 오브젝트도 생성됩니다.

프로그래머가 프로세스 생성을 요청합니다.
프로세스 커널 오브젝트에 실제 데이타 정보가 담기고,
그 프로세스를 관리하는것은 커널(OS)의 몫이 되겠다.
프로세스에는 상태정보 (running, block, ready, zombie 등등..)
그리고 우선순위 (프로세스가 CPU를 점거할때 순위라고 할까요~) 등
이런 정보가 "프로세스 커널 오브젝트" 로 저장되어 커널에 의해 관리가 된다.
(물론 프로세스 이외에 파일, 스레드, 파이프 등등 커널 오브젝트라는 범위는 아주 넓습니다)

그렇다면 커널 오브젝트란에 OS에 의해 간섭되는것인데..
프로세스 커널 오브젝트 안에 있는 정보들중 하나 우선순위를
프로그래머가 높이고 싶다하면 어떻게?? (프로세스에 대해서 계속 예를 들자는.. 그런 의미입니다.)
커널 오브젝트는 사용자가 직접 접근하지 못합니다. (안정성을 위해.. 그렇게 만들어짐)
직접접근하지 못한다면, 분명히 간접적으로 접근하는 방법이 있겠지요!!
그 간접적으로 접근하는 것 그것이 바로 핸들!!!!!!!!!!!
그전에 잠시 프로세스의 우선순위를 바꾸는 함수를 보자.


- 프로세스의 우선순위(Priority) 변경
BOOL SetPriorityClass(
    HANDLE hProcess, // 우선순위를 변경할 프로세스의 핸들을 전달한다
    DWORD dwPriorityClass // 새롭게 적용할 우선순위 정보를 전달한다
); 
여기서 핸들은 커널 오브젝트에 할당되는 숫자에 지나지 않는다고 생각하면 된다.(실제로 숫자에 지나지 않습니다 ㅎㅎ)
이 함수를 풀이해보자면
hProcess가 가리키는 프로세스의 우선순위를 dwPriorityClass로 변경!!

핸들(HANDLE)

운영체제가 커널 오브젝트를 관리하기 위해서는 각각의 커널 오브젝트에 번호를 줍니다. 이 번호를 Handle 정보라고 한다. 
커널 오브젝트에 직접적인 접근은 불가능 하지만, 이러한 핸들번호를 받아 간접적으로 커널 오브젝트에 접근할 수 있게 됩니다. 그러면 위에서 말하던 우선순위를 고쳐주기 위해서는 프로세스 커널 오브젝트의 핸들값을 반환하여 간접적으로 접근해주면
우선순위, 상태정보 등등 가져올 수 있고, 수정도 가능하게 됩니다. 
그 예시로 방금 위에서 설명한 SetPriorityClass를 보시면 되겠습니다. 

 - 방금 위의 설명을 도식화 시킨겁니다

 

참고 윈도우즈 시스템 프로그래밍 | 윤성우 저 | 한빛미디어 
신고

+ Recent posts