/* 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를 보시면 되겠습니다. 

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

 

참고 윈도우즈 시스템 프로그래밍 | 윤성우 저 | 한빛미디어 
신고
- 부모 프로세스에서 받은 두개의 인자를 자식 프로세스에서 받아
  두 개의 합을 출력하는 프로그램입니다....

/* Process.cpp */
// 자식 프로세스 
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(int argc, TCHAR* argv[])
{
DWORD val1, val2;
val1 = _ttoi(argv[1]);
val2 = _ttoi(argv[2]);

_tprintf(_T("%d + %d = %d\n"), val1, val2, val1+val2);

_gettchar(); // 프로그램의 실행을 잠시 멈추기 위해
return 0;
}

/* CreateProcess.cpp */
// 부모 프로세스(자식 프로세스를 생성)

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#define DIR_LEN MAX_PATH+1

int _tmain(int argc, TCHAR * argv[])
{
STARTUPINFO si={0,};
PROCESS_INFORMATION pi;

si.cb = sizeof(si); // 구조체 변수의 크기
si.dwFlags = STARTF_USEPOSITION | STARTF_USESIZE;
si.dwX = 100;
si.dwY = 200;
si.dwXSize = 300;
si.dwYSize = 200;
si.lpTitle = _T("i am a boy"); // 콘솔 윈도우의 타이틀 바 제목
TCHAR command[] = _T("Process.exe 10 20"); // 생성할 프로세스의 이름정                                                                                보및 인수 전달
TCHAR cDir[DIR_LEN];
BOOL state;

GetCurrentDirectory(DIR_LEN, cDir); // 현재 디렉터리 확인
_fputts(cDir, stdout);
_fputts(_T("\n"), stdout);

SetCurrentDirectory(_T("C:\\WinSystem"));
GetCurrentDirectory(DIR_LEN, cDir); // 현재 디렉터리 확인
_fputts(cDir, stdout);
_fputts(_T("\n"), stdout);

state = CreateProcess( // 프로세스 생성
NULL, // 실행파일의 이름
command, // main 함수에 전달될 문자열(첫번째 인자를 NULL, 두번째                                         인자를 통해 생성하려는 
                 // 프로세스 이름 정보까지 함께 전달
NULL, NULL, TRUE, 
CREATE_NEW_CONSOLE, // 새롭게 생성하는 프로세스를 위한 콘솔 윈                                                         도우가 독립적으로 생성
NULL, NULL, &si, &pi); 
if(state!=0)
_fputts(_T("Creation OK! \n"), stdout);
else
_fputts(_T("Creation Error! \n"), stdout);

return 0;
}

- 참고로 C드라이브에 Winsystem 폴더를 생성한 후 process.cpp에서 컴파일해 만들어진 process.exe 파일을 Winsystem 폴더에 넣어야 한다.
- 현재 디렉터리에 있는 process.exe파일을 CreateProcess를 통해 실행시키는 과정이다. 

출력화면
- CreateProcess.exe 실행시....
- i am a boy라는 타이틀의 콘솔이 하나 더 실행되는 것을 볼 수 있다.

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

/* WinSysDir.cpp */

#include <stdio.h>
#include <tchar.h>
#include <windows.h>

#define DIR_LEN MAX_PATH+1

int _tmain(int argc, TCHAR** argv)
{
TCHAR sysDir[DIR_LEN];
TCHAR winDir[DIR_LEN];

GetSystemDirectory(sysDir, DIR_LEN); // 시스템 디렉터리 정보 추출

GetWindowsDirectory(winDir, DIR_LEN); // 윈도우즈 디렉터리 정보 추출

_tprintf(_T("System Dir : %s\n"), sysDir);
_tprintf(_T("Windows Dir : %s\n"), winDir);
return 0;
}
신고

Process A                    --------------->             Process B
(부모 프로세스)      CreateProcess에 의한 생성     (자식 프로세스)

CreateProcess의 구조
Reference : http://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx 

BOOL CreateProcess (

    LPCTSTR lpApplicationName, // 생성될 프로세스의 이름   

LPTSTR lpCommandLine, // 생성될 프로세스에 인자 전달(변수만 가능)

    LPSECURITY_ATTRIBUTES lpProcessAttributes, // 프로세스의 보안 속성 지정    LPSECURITY_ATTRIBUTES lpThreadAttributes,   // 쓰레드의 보안 속성 지정

    BOOL bInheritHandles,      // TRUE : 부모 프로세스가 소유하는 상속 가능한 핸들을 상속한다.    DWORD dwCreationFlags,    // 생성하는 프로세스의 특성을 결정짓는 옵션(우선순위)   

LPVOID lpEnvironment,     // 생성하는 프로세스의 Environment Block 지정 NULL : 부모 프로세스의  환경 블록 복사   

LPCTSTR lpCurrentDirectory,      // 생성하는 프로세스의 현재 디렉터리 설정 NULL : 부모 프로세스의 현재 디렉터리

    LPSTARTUPINFO lpStartupInfo, // STARTUPINFO 구조체 변수 초기화한                                                      변수의 포인터를 인자로 전달   

LPPROCESS_INFORMATION lpProcessInformation

          // 생성하는 프로세스의 정보를 얻기 위한 인자

         // PROCESS_INFORMATION 구조체 변수의 주소값을 인자로 전달);

 

- 첫번째 인자에 실행파일을 전달할 경우 현재 디렉터리를 기준으로 실행파일을 검색한다.
- 두번째 인자에 실행파일을 전달할 경우(첫번째 인자 : NULL) 표준 검색경로(밑에 참고) 순서대로 실행파일을 검색한다.

물론 저 10개의 매개변수를 외울필요는 없습니다. 외워서 쓰는 사람도 없구요 
굵게 표시된 매개변수 정도만 이해해도 좋습니다. 적당히 레퍼런스 하셔서 ㅎㅎ

STARTUPINFO의 구조
Reference : http://msdn.microsoft.com/en-us/library/ms686331(VS.85).aspx

typedef struct _STARTUPINFO {

        DWORD cb; // 구조체 변수의 크기

        LPTSTR lpReserved;

        LPTSTR lpDesktop;

        LPTSTR lpTitle; // 콘솔 윈도우의 타이틀 제목

        DWORD dwX; // 프로세스 윈도우의 x좌표

        DWORD dwY; // y 좌표

        DWORD dwXSize; // 프로세스 윈도우의 가로길이

        DWORD dwYSize; // 세로길이

        DWORD dwXCountChars;

        DWORD dwYCountChars;

        DWORD dwFillAttribute;

        DWORD dwFlags; // 설정된 멤버의 정보

        WORD wShowWindow;

        WORD cbReserved2;

        LPBYTE lpReserved2;

        HANDLE hStdInput;

        HANDLE hStdOutput;

        HANDLE hStdError;

}; STARTUPINFO, *LPSTARTUPINFO;

 


- 첫번째 인자 cb가 중요한 이유는 CreateProcess함수의 9번째 들어갈 프로세스 정보 구조체에 들어가는 구조체가 혹시나 바뀔 수 있고, 다양한 구조체가 들어가기 위해서 둔 것인데 정보를 전달하는 구조체가 무엇인기 구분짓겠다는 의도로 해석.

- 현재 디렉터리(Current Directory)의 설정

현재 디렉터리 확인 함수(GetCurrentDirectory)

DWORD GetCurrentDirectory(

        DWORD nBufferLength,  // 현재 디렉터리 정보가 저장될 메모리 버퍼 크기

        LPTSTR lpBuffer // 현재 디렉터리 정보가 저장될 메모리 버퍼의 pointer

);

현재 디렉터리 변경 함수(SetCurrentDirectory)

BOOL SetCurrentDirectory(

        LPCTSTR lpPathName // 변경하고자 하는 현재 디렉터리 경로명

);
 
표준 검색경로(두 번째 전달인자(lpCommandLine)를 통해 실행파일 이름을 전달할 경우)
1. 표준 검색경로 : 실행 중인 프로세스의 실행파일이 존재하는 디렉터리
2. 표준 검색경로 : 실행 중인 프로세스의 현재 디렉터리(Current Directory)
3. 표준 검색경로 : Windows의 시스템 디렉터리(System Directory)
4. 표준 검색경로 : Windows의 디렉터리(Windows Directory)
5. 표준 검색경로 : 환경변수 PATH에 의해 지정되어 있는 디렉터리
3,4 표준 검색경로(시스템, 윈도우즈 디렉터리) 확인 예제 => LINK

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


신고

프로세스란?
- 실행 중에 있는 프로그램을 의미함.(메인 메모리로 이동하여)

프로세스를 구성하는 요소
- 메모리 구조 + Register Set

프로세스의 스케줄링(Scheduling)
- 프로세스의 CPU 할당 순서 및 방법을 결정짓는 일
- 이 때 사용되는 알고리즘을 스케줄링 알고리즘(Scheduling Algorithms)이라 한다.

스케쥴러
- 스케줄링 알고리즘을 적용해 실제로 프로세스를 관리하는 운영체제 요소(모듈)

프로세스의 상태 변화
- 멀티 프로세스 운영체제에서는 프로세스 하나가 계속해서 실행되는 것이 아니고, 여러 개의 프로세스들이 돌아가면서 실행되기 때문에, 프로세스 각각의 상태는 시간 흐름에 따라 변화한다.


1. S(Start) -> Ready 상태로의 전이

- 프로세스가 생성이 되면 스케줄러에 의해 선택되기를 바라며 Ready상태가 되어 실행을 기다리며 존재한다.

2. Ready -> Running 상태로의 전이

- 스케줄러는 Ready 상태에 있는 프로세스 중 하나를 선택해 CPU에 의해 실행되게 함(스케줄링 알고리즘 기반)
- 스케줄러에 의해 선택된 프로세스는 Running 상태가 되어 실행되는 것이다.

3. Running -> Ready 상태로의 전이

- 일의 중요정도에 따라 우선순위가 매겨진다.
- 예를 들어 높은 우선순위의 프로세스가 생성되었고 Ready 상태가 되었다. 그리고 낮은 우선순위의 Running상태의 프로세스는 Ready 상태로 내리고 높은 순위의 프로세스가 Running 상태로 변한다

4. Running -> Blocked 상태로의 전이

- 데이터 입, 출력에 관련된 일을 하는 경우 발생.
- 데이터 입, 출력시 CPU에 의해서 프로세스가 실행될수 없다.
- 따라서 데이터 입, 출력을 진행중인 프로세스를 잠시 내려오게 하고(Blocked) Ready 상태의 프로세스 중 하나를 실행시킴(CPU를 효율적으로 사용)

5. Blocked -> Ready 상태로의 전이

- Blocked 상태는 스케줄러에 의해 선택될 수 없다.
- 따라서  다시 Running 하기 위해서는 Ready 상태를 거쳐야만 한다.
- ex) 입, 출력이 완료된 프로세스 

6. Blocked -> Exit

- 프로세스 종료 

컨텍스트 스위칭(Context Switching)
-
CPU 내에 존재하는 레지스터들은 현재 실행 중에 있는 프로세스 관련 데이터들로 채워진다.
- 스케줄러에 의해 실행된 프로세스가 변경될 시 CPU 내에 존재하는 레지스터들은 컨텍스트 스위칭에 의해 데이터를 변경한다.

 
- 실행되는 프로세스의 변경과정에서 발생하는 컨텍스트 스위칭은 시스템에 많은 부담을 준다. 따라서 멀티 프로세스 기반의 프로그램 실행은 많은 부분 성능 향상에 도움을 줄 수도 있지만 컨텍스트 스위칭이 미치는 영향을 고려할 경우 오히려 성능 저하를 가져올 수 있다.

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

/* CommandPrmpt_One.cpp */

#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <locale.h>
#include <string.h>
#include <windows.h>

#define STR_LEN 256
#define CMD_TOKEN_NUM 10

TCHAR ERROR_CMD[] = _T("'%s'은(는) 실행할 수 있는 프로그램이 아닙니다. \n");

int CmdProcessing();
TCHAR * StrLower(TCHAR *);
int _tmain(int agrc, TCHAR * argv[])
{
// 한글 입력 가능하게끔
_tsetlocale(LC_ALL, _T("Korean"));

DWORD isExit;
while(1)
{
isExit = CmdProcessing();
if(isExit == TRUE)
{
_fputts(_T("명령어 처리를 종료합니다. \n"), stdout);
break;
}
}
return 0;
}

TCHAR cmdString[STR_LEN];
TCHAR cmdTokenList[CMD_TOKEN_NUM][STR_LEN];
TCHAR seps[] = _T(" , \t \n ");

int CmdProcessing(void) // 명령어를 입력받아 해당 명령어에 지정되어 있는 기능을 수행함.
{ // exit가 입력되면 TRUE를 반환하고 이는 프로그램의 종료로 이어진다.
_fputts(_T("Best command prompt>> "), stdout);
_getts(cmdString);
TCHAR * token = _tcstok(cmdString, seps);
int tokenNum = 0;
while(token != NULL)
{
_tcscpy(cmdTokenList[tokenNum++], StrLower(token));
token = _tcstok(NULL, seps);
}

if(!_tcscmp(cmdTokenList[0], _T("exit")))
{
return TRUE;
}
else if(!_tcscmp(cmdTokenList[0], _T("추가 되는 명령어 1")))
{
}
else if(!_tcscmp(cmdTokenList[0], _T("추가 되는 명령어 2")))
{
}
else
{
_tprintf(ERROR_CMD, cmdTokenList[0]);
}
return 0;
}

TCHAR * StrLower(TCHAR *pStr) // 문자열 내의 존재하는 모든 대문자를 소문자로 변경
{ // 변경된 문자열의 포인터를 반환한다.
TCHAR *ret = pStr;
while(*pStr)
{
if(_istupper(*pStr))
*pStr = _totlower(*pStr);
pStr++;
}
return ret;
}

위 코드는 프로젝트를 쉽게 이해하기 위해 간결하게 제시된 코드이다.
주목해서 봐야할 사항
- 명령어를 추가할 때 변경되는 부분
- 명령어의 대,소문자를 구분하지 않기 위해서 고려된 부분
- 명령어 EXIT가 입력되었을 때 프로그램 종료방식
 
출처. 윈도우즈 프로그래밍 | 윤성우 저  | 한빛미디어 
신고
  1. 김멋져 2012.03.26 05:30 신고

    매우 많은 도움되었습니다. 감사합니다.

  2. 김민식 2016.11.20 18:41 신고

    인풋을 받고, 다음 명령을 실행하니 버퍼가 입력되 있어서, 자동으로 명령어를 계속 실행합니다. T.T
    cmdString이 원래 코드에서 자동으로 비워지나요?


GetLastError 함수와 에러코드

- 오류가 발생했을 때, 이어서 바로 GetLastError 함수를 호출하면 오류의 원인에 해당하는 에러코드를 얻을 수 있다.

DWORD GetLastError(void); // NULL값으로 반환하면 오류가 발생했음을 알 수 있지                                            만 원인을 알 수 없다.

MSDN을 참조하면 시스템 에러코드의 종류와 해당 에러코드가 의미하는 바를 알 수 있다. 예제를 통해 시스템 에러코드를 얻는 방법을 보자.

ex1)
/* GetLastError.cpp */
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(void)
{
HANDLE hFile = CreateFile(_T("ABC.DAT"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 
FILE_ATTRIBUTE_NORMAL, NULL); // Windows system 함수
     // CreateFile함수는 함수호출이 실패할 경우 INVALID_HANDLE_VALUE를 반환 
if(hFile == INVALID_HANDLE_VALUE)  
{
_tprintf(_T("error code: %d\n"), GetLastError());
return 0;
}
return 0;

실행결과
error code : 2 // 이 에러코드를 MSDN에서 확인해보면 
                    // "The system cannot find the file specified." 파일이 없음

ex2)
/* ErrorStateChange.cpp */
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(void)
{
HANDLE hFile = CreateFile(_T("ABC.DAT"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING
FILE_ATTRIBUTE_NORMAL, NULL);
_tprintf(_T("error code : %d\n"), GetLastError()); //파일을 개방. 존재하지 않으므로 오류발생

hFile = CreateFile(_T("ABC2.DAT"), GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_NEW
FILE_ATTRIBUTE_NORMAL, NULL); //ABC2.DAT 파일 생성
_tprintf(_T("error code : %d\n"), GetLastError()); // 오류확인 이상 무
return 0;

실행결과
error code : 2 //  "The system cannot find the file specified." 파일이 없음
error code : 0 // "The operation completed successfully" 이상없음.

위의 ErrorStateChange.cpp 파일을 다시한번 실행해보자..
그렇게되면 실행결과는
error code : 2 
error code : 80 // "The file exists." 생성하고자 하는 파일 ABC2.DAT파일이 이미 존                          재하기 때문에 함수 호출이 실패하였다.(생성하고자 하는 파일이 이                          미 존재하면 함수 호출이 실패하도록 전달인자를 설정함)

출처. 윈도우즈 프로그래밍 | 윤성우 저  | 한빛미디어 
신고
1. 64비트와 32비트

나누는 기준
- I/O BUS를 통하여 한번에 송신 및 수신할 수 있는 데이터의 크기
- 데이터 처리능력

32비트에서 64비트로
- 더 넓은 메모리 공간 활용가능(32비트 : 표현할 수 있는 주소 4GB -> 64비트 : 표현할 수 있는 주소 16TB)
- 연산속도가 빨라진다(32비트 : 최대 32비트 데이터 처리 -> 64비트 : 최대 64비트 데이터 처리) 

2. 64bit 기반 프로그래밍

- LLP64, LP64

 운영체제 모델  char  short  int  long  포인터 
Windows  LLP64 1byte  2byte  4byte  4byte  8byte 
Unix LP64  1byte  2byte  4byte  8byte  8btye  

Windows에서는 32비트 시스템과의 호환성을 중시하기 때문에 포인터만 8바이트로 표현한다.

- 64비트와 32비트 공존의 문제점

#include <stdio.h>
int main()
{
int arr[10] = {0,};
int arrVal = (int)arr; // 64비트 시스템에서는 포인터의 크기와 int형의 크기가 다                                         르기 때문에 형변환 과정에서 데이터 손실이 발생한다
printf("pointer = %d\n", arrVal);
return 0;


"64비트 시스템에서는 포인터가 지니고 있는 주소값을 4바이트 정수형으로 형 변환하지 말자."
 
-64비트 시스템 Windows 스타일 자료형 
 Windows 자료형 의미  정의 형태 
 BOOL  Boolean variable   typedef int BOOL 
 DWORD  32-bit unsigned integer   typedef unsigned long DWORD; 
 DWORD32  32-bit unsigned integer   typedef unsigned int DOWRD32 
 DWORD64  64-bit unsigned integer   typedef unsigned __int64 DWORD64 
 INT  32-bit signed integer   typedef int INT; 
 INT32  32-bit signed integer   typedef signed int INT32 
 INT64  64-bit signed integer   typedef signed __int64 INT64 
 LONG  32-bit signed integer   typedef long LONG
 LONG32  32-bit signed integer   typedef signed int LONG32
 LONG64  64-bit signed integer   typedef signed __int64 LONG64
 UINT  Unsigned INT   typedef unsigned int UINT 
 UINT32  Unsigned INT32   typedef unsigned int UINT32 
 UINT64    Unsigned INT64   typedef unsigned __int64 UINT 64 
 ULONG  Unsigned LONG   typedef unsigned int ULONG 
 ULONG32  Unsigned LONG32   typedef unsinged int ULONG32 
 ULONG64  Unsigned LONG64   typedef unsigned __int64 UNLOG64 

프로그래밍을 통해 변수 2개의 거리차이가 얼마나 나는지
확인 할 때 포인터를 이용해 주소값의 차를 계산하는데 64비트와 32비트의 포인터의 크기가 다르기 때문에 정확한 계산이 불가능할 때가 있다.
#if defined(_WIN64)

typedef __int64 LONG_PTR;
typedef unsigned __int64 ULONG_PTR;
typedef __int64 INT_PTR;
typedef unsigned __int64 UINT_PTR;

#else
typedef long LONG_PTR;
typedef unsigned long ULONG_PTR;

typedef int INT_PTR;
typedef unsigned int UNIT_PTR;

#endif 
이름에 PTR이 붙어서 자료형을 포인터라고 생각하기 쉬운데, 실제로 보면 포인터가 아닙니다. PTR이라 이름을 붙인 이유는 포인터값 기반의 산술연산을 위해 정의된 자료형이기 때문입니다.
32비트 시스템과 64비트 시스템의 포인터의 크기가 다르기 때문에 생기는 문제를 해결하기위해 만들어진 자료형이다.
#define _WIN64를 통해 운영체제를 확인하여 운영체제에 따른 포인터의 크기의 호환성을 맞춰주는 역할을 합니다.
 
ex)

/* PolymorphicType2.cpp */
 
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

UINT_PTR CalDistance(UINT_PTR a, UINT_PTR b)
{
return a-b ;
}

int _tmain(void)
{
INT32 val1 = 10;
INT32 val2 = 20;

_tprintf(_T("Distance : %d\n"), CalDistance((UINT_PTR)&val1, (UINT_PTR)&val2)); // 12
return 0;

출처. 윈도우즈 프로그래밍 | 윤성우 저  | 한빛미디어 
저작자 표시
신고

+ Recent posts

티스토리 툴바