typedef struct _IMAGE_EXPORT_DIRECTORY
{
    DWORD Characteristics
    DWORD TimeDateStamp
    WORD MajorVersion
    WORD MinorVersion
    DWORD Name
    DWORD Base
    DWORD NumberOfFunctions
    DWORD NumberOfNames
    DWORD AddressOfFunctions   // 함수 주소 배열(EAX)
    DWORD AddressOfNames    //함수명 배열
    DWORD AddressOfNameOrdinals  //함수 서수 배열
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY

 

 

1) 함수명 배열로 이동하여 원하는 함수의 이름과 해당 인덱스를 찾는다.
2) Ordinals 배열에서 인덱스에 해당하는 서수 인덱스 값을 찾는다.
3) EAT 배열에서 서수 인덱스에 해당하는 함수 offset을 확인한다.
4) DLL Base 주소와 offset을 합해 함수의 실제 주소 값을 구한다. 

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Example] 생산자 | 소비자  (0) 2017.12.29
[Tip] 동기화  (0) 2017.12.29
[Tip] Thread 사용시 주의 사항  (0) 2017.12.29
[Tip] 프로세스 관리  (0) 2017.12.29
[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <io.h>
#include <process.h>
#include <time.h>

 

#define DATA_SIZE 256

 

#ifdef _UNICODE
#define _tstrrchr wcsrchr
#else
#define _tstrrchr strrchr
#endif

 

#ifdef _UNICODE
#define _tstrstr wcsstr
#else
#define _tstrstr strstr
#endif

 

#ifdef _UNICODE
#define _memtchr wmemchr
#else
#define _memtchr memchr
#endif


typedef struct MSG_BLOCK_TAG
{
 CRITICAL_SECTION mGuard;
 DWORD fReady, fStop;
 volatile DWORD nCons, mSequence;
 DWORD nLost;
 time_t mTimestamp;
 DWORD mChecksum;
 DWORD mData[DATA_SIZE];

} MSG_BLOCK;

 

MSG_BLOCK mBlock = {0,0,0,0,0};

 

unsigned int WINAPI Produce (void *);
unsigned int WINAPI Consume(void *);
void MessageFill (MSG_BLOCK *);
void MessageDisplay( MSG_BLOCK *);

 

DWORD _tmain( DWORD argc, LPTSTR argv[] )
{
 DWORD status;
 HANDLE hProduce, hConsume;

 InitializeCriticalSection( &mBlock.mGuard );
 
 hProduce = (HANDLE) _beginthreadex( NULL, 0, Produce, NULL, 0, NULL );
 hConsume = (HANDLE) _beginthreadex( NULL, 0, Consume, NULL, 0, NULL );

 status = WaitForSingleObject( hConsume, INFINITE );
 status = WaitForSingleObject( hProduce, INFINITE );

 DeleteCriticalSection ( &mBlock.mGuard );

 _tprintf(_T("Producer and consumer thread have terminated \n"));
 _tprintf(_T("Message produced : %d, Consumed : %d Lost : %d \n"), mBlock.mSequence, mBlock.nCons, mBlock.mSequence - mBlock.nCons );
 
 return 0;

}

 

unsigned int WINAPI Produce (void * arg)
{
 srand ( (unsigned int) time(NULL) );
 
 while( !mBlock.fStop )
 {
  Sleep(rand() / 100 );

  EnterCriticalSection( &mBlock.mGuard );

  __try
  {
   if( !mBlock.fStop )
   {
    mBlock.fReady = 0;
    MessageFill( &mBlock );
    mBlock.fReady = 1;
    InterlockedIncrement( &mBlock.mSequence );
   }
  }
  __finally
  {
   LeaveCriticalSection ( &mBlock.mGuard );
  }

  
 }

 return 0;
}

 

unsigned int WINAPI Consume( void * arg )
{
 CHAR command, extra;

 while( !mBlock.fStop )
 {
  _tprintf(_T("\n ** Enter 'c' for Consume; 's' to Stop : "));
  _tscanf("%c%c", &command, &extra);

  if( command == 's' )
  {
   mBlock.fStop = 1;
  }
  else if( command == 'c' )
  {
   EnterCriticalSection( &mBlock.mGuard );
   
   __try
   {
    if( mBlock.fReady == 0)
    {
     _tprintf(_T("No New Message. Try Again later \n"));
    }
    else
    {
     MessageDisplay(&mBlock );
     mBlock.nLost = mBlock.mSequence - mBlock.nCons + 1;
     mBlock.fReady = 0;
     InterlockedIncrement( &mBlock.nCons );
    }
   }
   __finally
   {
    LeaveCriticalSection( &mBlock.mGuard );
   }
  }
  else
  {
   _tprintf(_T("Illegal Command, Try Again \n"));
  }

 }

 return 0;
}

 

void MessageFill( MSG_BLOCK * msgBlock)
{
 DWORD i;
 msgBlock->mChecksum = 0;

 for( i = 0; i< DATA_SIZE; i++)
 {
  msgBlock->mData[i] = rand();
  msgBlock->mChecksum ^= msgBlock->mData[i];
 }

 msgBlock->mTimestamp = time( NULL );

 return;

}

 

void MessageDisplay( MSG_BLOCK * msgBlock )
{
 DWORD i, tcheck = 0;

 for( i = 0; i < DATA_SIZE; i++)
 {
  tcheck ^= msgBlock->mData[i];
 }

 _tprintf(_T("\n Message number %d generated at : %s \n"), msgBlock->mSequence, _tctime( &( msgBlock->mTimestamp )));
 _tprintf(_T("First and last entries : %x %x \n"), msgBlock->mData[0], msgBlock->mData[DATA_SIZE - 1 ]);

 if( tcheck == msgBlock->mChecksum )
 {
  _tprintf(_T("GOOD -> mChecksum was validated. \n"));
 }
 else
 {
  _tprintf(_T("BAD -> mCheckSum failed. message Corrupted \n"));
 }

 return;

}

다중 스레드 환경에서 코드가 정상적으로 동작하기 위한 기본 지침은 다음과 같다.
 1) 각 스레드에서만 사용할 변수는 전역 변수가 아니어야 한다. 개별 스레드만 접근 할 수 있도록 자료구조 또는 TLS에 두어야 한다.
 2) 한 함수를 여러 스레드들이 호출하여 어떠한 스레드 고유의 상태 값이 여러 호출들에 걸쳐서 유지되어야 하는 경우 그 상태 값을 전역 변수나 전역 자료구조에 저장하지 말고 TLS나 해당 스레드의 고유한 자료구조에 저장해야 한다.
 3) 스레드를 일시 정지 상태로 생성하지 않는 경우 초기화되지 않은 변수들 같은 경쟁 조건을 피해야 한다.
 4) 스레드가 표준 입력, 출력 핸들들을 설정한다거나 환경 변수들을 변경하는 일은 하지 말아야 한다. 기본 스레드가 다른 어떤 스레드들을 생성하기 전에 환경을 변경한다면 모든 스레드는 동일한 관경을 고유하게 된다. 
 5) 모든 스레드가 공유하는 변수는 정적 또는 전역 저장소에 있어야 하며, 메모리 장벽을 생성하는 동기화 또는 상호잠금 메커니즘들로 변수를 보호해야한다.

1. CRITICAL_SECTION 
  CRITICAL_SECTION 객체는 초기화 되고 삭제되지만 핸들을 가지지는 않으며, 다른 프로세스들과 공유할 수도 없다. CRITICAL_SECTION 형식의 한 변수는 하나의 임계코드 영역을 대표한다. 스레드들은 이 변수를 통해 임계 코드 영역으로 들어가거나 영역에서 벗어난다. 한 시점에서 특정 CRITICAL_SECTION 에는 하나의 스레드만 존재할 수 있다. CRITICAL_SECTION 변수와 관련 자원들을 초기화 하고 삭제할때는 각각 InitializeCriticalSection 함수와 DeleteCriticalSection 함수를 사용한다. EnterCriticalSection 호출이 반환되면 스레드는 임계 영역에 진입한 것이며 이를 스레드가 CRITICAL_SECTION 을 소유했다라고 한다. LeaveCriticialSection 을 호출하면 CRITICAL_SECTION 에 대한 소유권이 해제되고 스레드가 임계영역에서 벗어난다. CRITICAL_SECTION 의 소유권은 반드시 반환해야 한다. 그렇지 않으면 다른 스레드가 영원히 기다리게 된다. 소유한 스레드가 종료되더라고 소유권은 자동으로 반환되지 않으며 __finally 블록을 이용하여 정리 하는 방법을 권장한다. CRITICAL_SECTION 은 커널 객체가 아니라는 장점과 사용자 공간에서 관리된다는 장점이 있다. 비슷한 기능을 가진 Windows 뮤텍스 커널 객체에 비해 거의 항상 성능상의 이점을 제공한다.

2. 뮤텍스(Mutex)
  Mutual Exclusion(상호 배제) 의 준말인 Mutex 는 CRITICAL_SECTION 이상의 잠금 기능성을 제공한다. 뮤텍스에는 이름을 붙일 수 있고 핸들도 있기 때문에 서로 다른 프로세스의 스레드들 사이의 프로세스 간 동기화에 이용할 수 있다. CRITICAL_SECTION 에 비해 뮤텍스는 만료시간을 지정할 수 있으며 프로세스가 종료되었을때 신호될 수 있다는 장점이 있다.

3. 뮤텍스 vs CRITICAL_SECTION 
 1) 두 객체 모두 한 스레드가 객체를 소유하고 있으면 다른 스레드들은 객체가 해제될 때까지 차단된다.
 2)  스레드 종료에 의해 폐기된 뮤텍스는 신호된 상태가 되므로 다른 스레드들이 영원히 차단되는 일이 없다. 하지만 폐기된 뮤텍스가 있다면 프로그램에 심각한 버그나 실패 상황이 존재함이 거의 확실하다.
 3) 뮤텍스는 대기 만료 시간을 지정할 수 있다. CRITICAL_SECTION 은 주기적으로 점검해야한다.
 4) 뮤텍스는 이름을 가질 수 있으며 서로 다른 프로세스의 스레드들이 공유할 수 있다.
 5) 뮤텍스를 생성할때 스레드가 그 뮤텍스를 즉시 소유하게 할 수 있다.
 6) CRITICAL_SECTION 은 거의 항상 뮤텍스 보다 훨씬 빠르다.

4. 세마포어
  하나의 세마포어는 하나의 횟수가 부여된다. 그 횟수가 0보다 크면 세마포 객체는 신호된 상태이고 횟수가 0이면 신호되지 않은 상태이다. 스레드는 두 가지 대기 함수들 중 하나를 사용해서 보통의 방식대로 세마포어를 기다린다. 대기 스레드가 해제되면 세마포어의 횟수가 1 감소한다. 세마포어 함수는 CreateSemaphore, CreateSemaphoreEx, OpenSemaphore, ReleaseSemaphore 가 있다. 세마포어어 횟수를 한번에 2 감소시키는 방법은 존재하지 않기 때문에 CRITICAL_SECTION 을 이용하여 구현해야 한다.

5. 이벤트
  이벤트는 어떠한 조건( 사용 가능한 메시지가 있음 ) 이 이제 성립함을 다른 스레드들에게 신호하는데 쓰인다. 한 이벤트가 신호되었을 때 그것을 기다리는 여러 개 스레드들이 동시에 해제된다. 이벤트는 수동 재설정 이벤트와 자동 재 설정 이벤트로 나뉜다.
  1) 수동 재설정 이벤트는 이벤트를 기다리는 여러 스레드들에게 신호할 수 있다.
  2) 자동 재설정 이벤트는 이벤트를 기다리는 하나의 스레드에게 신호한다. 이벤트는 자동으로 재설정 된다.
  이벤트를 다루는 함수는 CreateEvent, CreateEventEx, OpenEvent, SetEvent, ResetEvent, PulseEvent 이다. SetEvent 는 스레드가 이벤트에 신호를 할 때 쓰인다. 지정된 이벤트가 자동 재설정이면 하나의 대기 스레드가 해제 되며, 이벤트는 자동적으로 신호되지 않은 상태로 돌아간다.

  C 라이브러리는 단일 스레드 프로세스를 염두에 두고 작성되었으며 일부 함수들이 중간 결과를 저장하기 위해 전역 저장소를 사용한다. 이런 라이브러리는 스레드에 안전하지 않다. 예를 들어 서로 다른 두 스레드가 전역 저장소를 각자 수정하게 되면 이상한 결과가 나올수 있기 때문이다.
 C 라이브러리 함수들이 스레드에 안전하지 않은 이유를 잘 보여주는 함수로 strtok 가 있다. strtok는 문자열을 토큰단위로 분리하는 함수로 한번 호출 할 때마다 다음 토큰의 위치를 돌려준다. 이를 위해 함수는 일련의 함수 호출들 사이에서 영속적 상태를 유지하는데 그 상태를 함수를 호출하는 스레드가 공유하는 정적 저장소에 저장한다는 것이 문제이다. 
 Microsoft C는 LIBCMT.LIB 라는 스레드에 안전한 C 라이브러리 구현을 통해 이러한 문제를 해결한다. 스레드를 생성하고자 하는 경우 CreateThread 함수 대신 _beginthreadex 함수를 사용할 것을 권장한다. _beginthreadex 함수는 스레드 고유 작업 장소를 생성한다. 스레드 종료 시에는 ExitThread 함수 대신 _endthreadex 함수를 사용한다. 이 함수를 사용하려면 헤더 파일들을 포함하기 전에 반드시 전처리 변수 _MT를 정의해야 한다.
 Visual Studio 경우 다중 스레드에서 C 라이브러리를 제대로 사용하려면 다음과 같은 사항을 지켜야한다.
  1) 프로젝트 속성 -> 구성 속성 -> C/C++ -> 코드 생성 -> 런타임 라이브러리에서 다중 스레드 DLL(/MD) 를 선택한다.
  2) 스레드 생성시 _beginthreadex 함수를 종료시에는 _endthreadex 함수 또는 스레드 루틴 끝에서 return 문으로 실행을 끝나게 한다.

출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Example] 생산자 | 소비자  (0) 2017.12.29
[Tip] 동기화  (0) 2017.12.29
[Tip] 프로세스 관리  (0) 2017.12.29
[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29

모든 프로세스는 하나나 그 이상의 스레드들을 가진다. 스레드는 실행의 기본 단위이다. 


Windows 프로세스는 다음과 같은 구성 요소를 가진다.
 1) 하나 이상의 스레드들
 2) 다른 프로세스의 주소 공간과 구별되는 가상 주소 공간
 3) 하나나 그 이상의 코드 구역( Code Segment )들 ( DLL 코드 포함 )
 4) 전역 변수들을 담은 하나나 그 이상의 자료 구역들 ( Data Segment )
 5) 환경 변수 정보 / 프로세스 힙 / 핸들 및 자원 정보
한 프로세스 안의 각 스레드는 코드, 전역변수, 환경 문자열, 자원들을 공유한다.

스레드는 다음과 같은 구성 요소를 가진다.
 1) 프로시저 호출, 인터럽트, 예외 처리부 등을 위한 스택
 2) TLS(Thread Local Storage) 각 스레드 만의 고유한 저장소
 3) 스레드 생성시 지정된 스택에 대한 인수 등
 4) 커널이 관리하는 Context 구조체

 1. 프로세스 생성


  가장 기본적인 Windows 프로세스 관리 함수는 스레드 하나를 가진 프로세스를 생성하는 CreateProcess 함수이다. 함수의 원형은 다음과 같다. 
 

 

 CreateProcess 함수는 HANDLE 값을 반환하지 않고 성공 실패 여부만 반환한다. 생성된 프로세스 관련 정보는 PROCESS_INFORMATION 구조체를 통해 전달 받는다.  

 

 

 

 

 파일 핸들과 마찬가지로 프로세스 핸들이나 스래드 핸들도 더 이상 필요하지 않게 되면 닫아주어야 한다.

2. 상속 가능 핸들
  프로세스를 생성 할때 부모 프로세스의 객체들에 대한 핸들을 통해 자식 프로세스에서도 객체들에 접근하고 싶은 상황이 발생한다. 이런 경우 부모프로세스가 가지고 있는 핸들 정보를 자식 프로세스에게 상속 할수 있다. 핸들 값을 상속 하기 위해서는 SECURITY_ATTRIBUTES 구조체의 보안 서술자 필드를 설정해주어야 한다. 상속된 핸들은 원래의 핸들과 개별적인 복사본이다. 부모와 자식이 같은 파일에 서로 다른 파일 포인터를 이용하여 접근한다. 또한 두 프로세스는 자신의 핸들을 각자 닫을 수 있으며 실제로 각자 닫아야 한다.



 

 

 

 

출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Tip] 동기화  (0) 2017.12.29
[Tip] Thread 사용시 주의 사항  (0) 2017.12.29
[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29
[Example] 예외 처리  (0) 2017.12.29
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <io.h>
#include <process.h>

#define KEY_SIZE 8

#define EMPTY _T ("")
#define YES _T ("y")
#define NO _T("n")
#define CR 0x0D
#define LF 0x0A
#define TSIZE sizeof (TCHAR)

#ifdef _UNICODE 
#define _tstrrchr wcsrchr
#else
#define _tstrrchr strrchr
#endif

#ifdef _UNICODE 
#define _tstrstr wcsstr
#else
#define _tstrstr strstr
#endif

#ifdef _UNICODE 
#define _memtchr wmemchr
#else
#define _memtchr memchr
#endif

#define GetExceptionCode            _exception_code
#define exception_code              _exception_code
#define GetExceptionInformation     (struct _EXCEPTION_POINTERS *)_exception_info
#define exception_info              (struct _EXCEPTION_POINTERS *)_exception_info
#define AbnormalTermination         _abnormal_termination
#define abnormal_termination        _abnormal_termination

unsigned long __cdecl _exception_code(void);
void *        __cdecl _exception_info(void);
int           __cdecl _abnormal_termination(void);

typedef struct _TREENODE
{
 struct _TREENODE *Left, *Right;
 TCHAR key[KEY_SIZE];
 LPTSTR pData;

} TREENODE, *LPTNODE, **LPPTNODE;

#define NODE_SIZE sizeof( TREENODE )
#define NODE_HEAP_ISIZE 0x8000
#define DATA_HEAP_ISIZE 0x8000
#define MAX_DATA_LEN 0x1000
#define TKEY_SIZE KEY_SIZE * sizeof( TCHAR )
#define STATUS_FILE_ERROR 0xE0000001

VOID ReportError (LPCTSTR userMessage, DWORD exitCode, BOOL printErrorMessage);
VOID ReportException (LPCTSTR userMessage, DWORD exceptionCode);
DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...);

LPTNODE FillTree( HANDLE, HANDLE, HANDLE);
BOOL Scan( LPTNODE );
int KeyCompare(LPCTSTR, LPCTSTR);
BOOL InsertTree( LPPTNODE, LPTNODE );

int _tmain(int argc, LPTSTR argv[])
{
 HANDLE hIn = INVALID_HANDLE_VALUE, hNode = NULL, hData = NULL;
 LPTNODE pRoot;
 BOOL noPrint;
 CHAR errorMessage[0x100] = {0x0, };
 
 int iFirstFile = Options( argc, argv, _T("n"), &noPrint, NULL);
 int iFile = 0;

 for( iFile = iFirstFile; iFile < argc; iFile ++ )
 {
  __try
  {
   hIn = CreateFile( argv[iFile], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

   if( hIn == INVALID_HANDLE_VALUE )
   {
    RaiseException( STATUS_FILE_ERROR, 0, 0, NULL );
   }

   __try
   {
    hNode = HeapCreate( HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE, NODE_HEAP_ISIZE, 0);
    hData = HeapCreate( HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE, DATA_HEAP_ISIZE, 0);

    pRoot = FillTree(hIn, hNode, hData);

    _tprintf(_T("Sorted file : %s \n"), argv[iFile]);

    Scan( pRoot );

   }
   __finally
   {
    if( hNode != NULL )
    {
     HeapDestroy( hNode );
    }

    if( hNode != NULL )
    {
     HeapDestroy( hData );
    }

    hNode = NULL;
    hData = NULL;

    if( hIn != INVALID_HANDLE_VALUE )
     CloseHandle(hIn);

    hIn = INVALID_HANDLE_VALUE;
    
   }

  }
  __except( (GetExceptionCode() == STATUS_FILE_ERROR || GetExceptionCode() == STATUS_NO_MEMORY) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH )
  {
   _stprintf( errorMessage, _T("\n %s %s "), _T("sortBT error onfile : "), argv[iFile]);
   ReportError( errorMessage, 0, TRUE );
  }
 }


 return 0;
}

LPTNODE FillTree (HANDLE hIn, HANDLE hNode, HANDLE hData)
{
 LPTNODE pRoot = NULL, pNode;
 DWORD nRead, i;
 BOOL atCR;
 TCHAR dataHold[MAX_DATA_LEN];
 LPTSTR pString;
     
 while (TRUE) {
  pNode = HeapAlloc (hNode, HEAP_ZERO_MEMORY, NODE_SIZE);
  pNode->pData = NULL;
  (pNode->Left) = pNode->Right = NULL;

  if (!ReadFile (hIn, pNode->key, TKEY_SIZE,
    &nRead, NULL) || nRead != TKEY_SIZE)
  
   return pRoot; 
  atCR = FALSE;   

  for (i = 0; i < MAX_DATA_LEN; i++) {
   ReadFile (hIn, &dataHold[i], TSIZE, &nRead, NULL);
   if (atCR && dataHold[i] == LF) break;
   atCR = (dataHold[i] == CR);
  }
  dataHold[i - 1] = _T('\0');


  pString = HeapAlloc (hData, HEAP_ZERO_MEMORY,
    (SIZE_T)(KEY_SIZE + _tcslen (dataHold) + 1) * TSIZE);
  memcpy (pString, pNode->key, TKEY_SIZE);
  pString[KEY_SIZE] = _T('\0');
  _tcscat (pString, dataHold);
  pNode->pData = pString;

  InsertTree (&pRoot, pNode);

 } 
 return NULL;
}

BOOL InsertTree (LPPTNODE ppRoot, LPTNODE pNode)
{
 if (*ppRoot == NULL) {
  *ppRoot = pNode;
  return TRUE;
 }
 if (KeyCompare (pNode->key, (*ppRoot)->key) < 0)
  InsertTree (&((*ppRoot)->Left), pNode);
 else
  InsertTree (&((*ppRoot)->Right), pNode);
 return TRUE;
}

int KeyCompare (LPCTSTR pKey1, LPCTSTR pKey2)
{
 return _tcsncmp (pKey1, pKey2, KEY_SIZE);
}

static BOOL Scan (LPTNODE pNode)
{
 if (pNode == NULL)
  return TRUE;
 Scan (pNode->Left);
 _tprintf (_T ("%s\n"), pNode->pData);
 Scan (pNode->Right);
 return TRUE;
}


DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...)
{
 va_list pFlagList;
 LPBOOL pFlag;
 int iFlag = 0, iArg;

 va_start (pFlagList, OptStr);

 while ((pFlag = va_arg (pFlagList, LPBOOL)) != NULL
    && iFlag < (int)_tcslen (OptStr)) {
  *pFlag = FALSE;
  for (iArg = 1; !(*pFlag) && iArg < argc && argv [iArg] [0] == _T('-'); iArg++)
   *pFlag = _memtchr (argv [iArg], OptStr [iFlag],
     _tcslen (argv [iArg])) != NULL;
  iFlag++;
 }

 va_end (pFlagList);

 for (iArg = 1; iArg < argc && argv [iArg] [0] == _T('-'); iArg++);

 return iArg;
}

VOID ReportException (LPCTSTR userMessage, DWORD exceptionCode)
{ 
 if (lstrlen (userMessage) > 0)
  ReportError (userMessage, 0, TRUE);

 if (exceptionCode != 0) 
  RaiseException (
   (0x0FFFFFFF & exceptionCode) | 0xE0000000, 0, 0, NULL);
 
 return;
}

VOID ReportError (LPCTSTR userMessage, DWORD exitCode, BOOL printErrorMessage)
{
 DWORD eMsgLen, errNum = GetLastError ();
 LPTSTR lpvSysMsg;
 _ftprintf (stderr, _T("%s\n"), userMessage);
 if (printErrorMessage) {
  eMsgLen = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL, errNum, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR) &lpvSysMsg, 0, NULL);
  if (eMsgLen > 0)
  {
   _ftprintf (stderr, _T("%s\n"), lpvSysMsg);
  }
  else
  {
   _ftprintf (stderr, _T("Last Error Number; %d.\n"), errNum);
  }

  if (lpvSysMsg != NULL) LocalFree (lpvSysMsg); 
 }

 if (exitCode > 0)
  ExitProcess (exitCode);

 return;
}
출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블​

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Tip] Thread 사용시 주의 사항  (0) 2017.12.29
[Tip] 프로세스 관리  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29
[Example] 예외 처리  (0) 2017.12.29
[Tip] 예외 처리  (0) 2017.12.29

Windows 는 프로세스가 사용할 메모리를 Heap로 관리 한다. 하나의 프로세스는 여러개의 힙들을 가질 수 있으며 프로그램은 그 힙들로 부터 메모리를 할당한다. Heap는 Windows 객체이며 메모리 할당을 하기 위해서는 핸들이 필요하다. 기본 Heap의 핸들은 다음 할수를 통해 얻을 수 있다.

  HANDLE GetProcessHeap(VOID) // 성공시 프로세스 힙의 핸들, 실패시 NULL 반환


 한 프로그램이 기본 Heap 이외의 Heap 들을 생성 하는것이 가능하며 개별 Heap를 가질때에는 다음과 같은 장점이 있다.

 1) 공평함

 2) 다중 스레드 성능 향상

 3) 할당의 효율성

 4) 할당 해제의 효율성

 5) Locality of reference에 의한 효율성


 1. Heap 생성

   새로운 Heap 를 생성하고자 하는 경우 초기 Heap의 크기를 정하여 HeapCreate 함수를 호출한다. HeapCreate 함수의 원형은 다음과 같다.

HANDLE HeapCreate(DWORD flOption, SIZE_T dwInitialSize, SIZE_T dwMaximumSize)

// 성공시 Heap 핸들 실패 시에는 NULL 반환


 초기 Heap 의 크기는 페이지 크기의 배수로 상향 조정 된다. flOption 은 HEAP_GENERATE_EXCEPTIONS / HEAP_NO_SERIALIZE / HEAP_CREATE_ENABLE_EXECUTE 등으로 설정 할수 있다.  Heap 의 최대 크기는 dwMaximumSize 로 결정된다. dwMaximumSize를 0이 아닌 값으로 설정한 경우 가상 주소 공간이 그 크기에 맞게 할당된다. 0이 아닌 값이 Heap 의 최대 크기가 된다. 0으로 설정 하는 경우 사용 가능한 가상 메모리 주소 공간에 의해 결정된다.


2. Heap 제거

 Heap를 제거 하는 경우 HeapDestory 함수를 이용한다. HeapDestory 함수의 원형은 다음과 같다.


 BOOL HeapDestory( HANDLE hHeap )


 Heap 를 통째로 해제하면 다음과 같은 장점이 있다.

 1) 자료구조 운행 코드를 작성할 필요가 없다.

 2) 개별 요소를 일일이 해제하지 않아도 된다.

 3) 한 번의 호출로 자료구조의 모든 요사가 해제되므로, 시스템이 Heap 를 관리하는데 시간을 낭비하지 않는다.


3. Heap 메모리 할당

 Heap 영역에 메모리 블록을 얻을때에는 Heap 핸들, 블록 크기, 그리고 여러 플래그들을 지정해서 HeapAlloc 함수를 호출한다.


LPVOID HeapAlloc( HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes)

// 성공시 할당된 메모리 블록을 가리키는 포인터 실패시 NULL 반환


  hHeap : 할당 하고자 하는 Heap 의 핸들 값

  dwBytes : 할당할 메모리 블록의 크기


 4. Heap 메모리 해제

 Heap 영역의 메모리를 해제 할때는 HeapFree 함수를 사용한다.


 BOOL HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)


5. Heap 메모리 재 할당

 Heap 영역의 메모리 크기를 변경 하고 싶은 경우 HeapReAlloc 함수를 이용한다.


LPVOID HeapReAlloc( HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes)

// 성공시 재할당된 블록을 가리키는 포인터, 실패 시에는 NULL 또는 예외가 반환


6. Heap 크기 확인

 Heap 영역의 크기를 알고 싶은 경우 HeapSize 함수를 이용한다.


SIZE_T HeapSize( HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem)


7. 요약

 1) CreateHeap 나 GetProcessHeap 함수를 통해 Heap 핸들을 얻는다.

 2) HeapAlloc 으로 메모리 블록들을 할당한다.

 3) HeapFree 로 개별 블록들 일부 또는 전체를 해제한다.

 4) HeapDestory 로 Heap 을 파괴하고 Heap 핸들을 닫는다.


출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Tip] 프로세스 관리  (0) 2017.12.29
[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29
[Example] 예외 처리  (0) 2017.12.29
[Tip] 예외 처리  (0) 2017.12.29
[Tip] 파일 잠금  (0) 2017.12.29
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <io.h>
#include <process.h>

#define MAX_OPTIONS 20 
#define MAX_ARG 1000 
#define MAX_COMMAND_LINE MAX_PATH+50 
#define MAX_PATH_LONG 32767 
#define MAX_NAME 256    

#ifdef _UNICODE 
#define _tstrrchr wcsrchr
#else
#define _tstrrchr strrchr
#endif

#ifdef _UNICODE 
#define _tstrstr wcsstr
#else
#define _tstrstr strstr
#endif

#ifdef _UNICODE 
#define _memtchr wmemchr
#else
#define _memtchr memchr
#endif

VOID ReportError (LPCTSTR userMessage, DWORD exitCode, BOOL printErrorMessage);
VOID ReportException (LPCTSTR userMessage, DWORD exceptionCode);

int _tmain(int argc, LPTSTR argv[])
{
 HANDLE hIn = INVALID_HANDLE_VALUE, hOut = INVALID_HANDLE_VALUE;
 DWORD nXfer, iFile, j;
 CHAR outFileName[256] = {0x0, }, *pBuffer = NULL;
 OVERLAPPED ov = {0, 0, 0, 0, NULL};
 LARGE_INTEGER fSize;

 for( iFile = 1; iFile < argc; iFile++)
 {
  __try
  {
   if( _tcslen(argv[iFile]) > 250 )
   {
    ReportException(_T("The File name is Too Long"), 1);
   }

   _stprintf(outFileName, "UC_%s", argv[iFile]);

   __try
   {
    hIn = CreateFile(argv[iFile], GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

    if( hIn == INVALID_HANDLE_VALUE )
    {
     ReportException(argv[iFile], 1);
    }

    if( !GetFileSizeEx(hIn, &fSize) || fSize.HighPart > 0)
    {
     ReportException(_T("This file is to Large. \n"), 1);
    }

    hOut = CreateFile(outFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);

    if( hOut == INVALID_HANDLE_VALUE )
    {
     ReportException(outFileName, 1);
    }

    pBuffer = ( CHAR * ) malloc(fSize.LowPart);

    if( pBuffer == NULL )
     ReportException(_T("Memory allocation error \n"), 1);

    if( !ReadFile(hIn, pBuffer, fSize.LowPart, &nXfer, NULL ) || (nXfer != fSize.LowPart) )
    {
     ReportException(_T("ReadFile error \n"), 1);
    }

    for(j = 0; j < fSize.LowPart; j++)
    {
     if (isalpha(pBuffer[j]))
      pBuffer[j] = toupper(pBuffer[j]);
    }

    if( !WriteFile(hOut, pBuffer, fSize.LowPart, &nXfer, NULL) || (nXfer != fSize.LowPart))
    {
     ReportException(_T("WriteFile Error \n"), 1);
    }

   }
   __finally
   {
    if( pBuffer != NULL )
    {
     free(pBuffer);
     pBuffer = NULL;
    }

    if( hIn != INVALID_HANDLE_VALUE )
    {
     CloseHandle(hIn);
     hIn = INVALID_HANDLE_VALUE;
    }

    if( hOut != INVALID_HANDLE_VALUE )
    {
     CloseHandle(hOut);
     hOut = INVALID_HANDLE_VALUE;
    }

    _tcscpy(outFileName, _T(""));
   }
  }
  __except (EXCEPTION_EXECUTE_HANDLER)
  {
   _tprintf(_T("Error Processing File %s \n"), argv[iFile]);
   DeleteFile(outFileName);
  }
 }

 return 0;
}

VOID ReportException (LPCTSTR userMessage, DWORD exceptionCode)
{ 
 if (lstrlen (userMessage) > 0)
  ReportError (userMessage, 0, TRUE);

 if (exceptionCode != 0) 
  RaiseException (
   (0x0FFFFFFF & exceptionCode) | 0xE0000000, 0, 0, NULL);
 
 return;
}

VOID ReportError (LPCTSTR userMessage, DWORD exitCode, BOOL printErrorMessage)
{
 DWORD eMsgLen, errNum = GetLastError ();
 LPTSTR lpvSysMsg;
 _ftprintf (stderr, _T("%s\n"), userMessage);
 if (printErrorMessage) {
  eMsgLen = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL, errNum, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR) &lpvSysMsg, 0, NULL);
  if (eMsgLen > 0)
  {
   _ftprintf (stderr, _T("%s\n"), lpvSysMsg);
  }
  else
  {
   _ftprintf (stderr, _T("Last Error Number; %d.\n"), errNum);
  }

  if (lpvSysMsg != NULL) LocalFree (lpvSysMsg); 
 }

 if (exitCode > 0)
  ExitProcess (exitCode);

 return;
}
​출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Example] 이진 검색 트리를 이용한 파일 정렬  (0) 2017.12.29
[Tip] Heap Memory  (0) 2017.12.29
[Tip] 예외 처리  (0) 2017.12.29
[Tip] 파일 잠금  (0) 2017.12.29
[Example] 디렉터리 내용 출력  (0) 2017.12.29

 Windows 의 구조적 예외 처리( Structured Exception Handling, SEH) 는 주소 접근 위반이나 산술 실패, 시스템 오류 같은 예기치 않은 비 동기적 사건에 대해 응용 프로그램이 대처할 수 있도록 하는 안정적인 메커니즘을 제공한다. 또한 SEH를 이용하면 코드 블록 안의 어느 곳에서도 프로그램을 종류할 수 있으며 그런 종료 상황에서 프로그래머가 지정한 처리 및 오류 복구가 수행되게 할수 있다. 예외 처리 구조가 없다면 NULL 포인터 참조라든가 0으로 나누기 같은 의도치 않은 예외 상황이 발생했을 때 프로그램이 즉시 종료된다. 예외 처리 절차는 다음과 같다.


 1) 예외가 발생한다. 이번 예에서는 0으로 나누기에 의한 예외가 발생했다고 가정한다.

 2) 제어권이 예외 처리부로 넘어간다. GetExceptionCode가 먼저 호출 되고, 반환값이 함수 필터의 인수가 된다.

 3) 필터 함수는 주어진 오류 코드 값에 근거해서 행동을 결정한다.

 4) 이 예에서 예외 코드는 EXCEPTION_INT_DIVIDE_BY_ZERO 이다.

 5) 필터 함수는 예외 처리부를 수행해야 한다고 결정하고, 그에 맞게 EXCEPTION_EXECUTE_HANDLER를 돌려준다.

 6) __except 절에 있는 예외 코드가 수행된다.

 7) 제어권이 __try __except 블록을 벗어난다.


출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Tip] Heap Memory  (0) 2017.12.29
[Example] 예외 처리  (0) 2017.12.29
[Tip] 파일 잠금  (0) 2017.12.29
[Example] 디렉터리 내용 출력  (0) 2017.12.29
[Tip] 문자열 처리 관련  (0) 2017.12.29

 여러 개의 프로세스들을 동시에 실행하는 컴퓨터에서는 파일 같은 공유 자원에 대한 접근 중재와 동기화가 중요한 문제가 된다.

Windows 는 파일 전체 또는 일부를 잠글 수 있다. 일단 파일 또는 파일의 일부분을 잠그면 다른 프로세스 또는 같은 프로세스의 다른 스레드는 그 파일 또는 파일의 일부분에 접근 할 수 없다. 잠금은 프로세스 수준에서 강제되는 것이기 때문에 어떠한 프로세스가 기존의 잠금을 위반하면서 파일의 일부에 대해 접근하려 하면 그 접근은 실패한다.

 파일 잠금에서 가장 일반적인 함수는 LockFileEx이다. 확장 I/O 함수군에 속하는 것으로 잠글 64비트 파일 위치와 파일 영역의 범위를 지정하기 위해 중첩 구조체가 필요하다.


 LockFileEx 함수의 원형은 다음과 같다.

 

  BOOL LockFileEx(

HANDLE hFile, // 열린 파일 핸들

DWORD dwFlags, // 잠금 모드와 자물쇠가 풀릴 때까지 기다릴지의 여부를 결정

// LOCKFILE_EXCLUSIVE_LOCK => 읽기/ 쓰기 자물쇠가 된다.

// LOCKFILE_FAIL_IMMEDIATELY => 자물쇠를 얻을 수 없는 경우 즉시 FALSE를 반환한다.

DWORD dwReserved, // 반드시 0으로 설정

DWORD nNumberOfBytesToLockLow, // 잠글 바이트 구간의 길이

DWORD nNumberOfBytesToLockHigh, // 잠글 바이트 구간의 길이

LPOVERLAPPED lpOverlapped);


 UnlockFileEx 함수의 원형은 다음과 같다. dwFlags를 제외한 매개변수는 LockFileEx와 동일하다.


 BOOL UnlockFileEx(

HANDLE hFile, // 열린 파일 핸들

DWORD dwReserved, // 반드시 0으로 설정

DWORD nNumberOfBytesToLockLow, // 잠글 바이트 구간의 길이

DWORD nNumberOfBytesToLockHigh, // 잠글 바이트 구간의 길이

LPOVERLAPPED lpOverlapped);



 파일 잠금을 사용할 때 주의사항이 몇가지 있다.


 1) 자물쇠를 해제할 때에는 파일을 잠갔을 때와 정확히 동일한 구간을 지정해야 한다. 이전의 두 잠금 구간들을 결합해서 한번에 해제한다거나 잠긴 구간의 일부만을 해제하는 것은 불가능 하다.

 2) 새 자물쇠가 기존의 잠긴 영역과 겹쳐서 충돌이 생기면 잠금이 실패한다.

 3) 파일의 끝을 넘는 영역을 잠그는 것이 가능하다.

 4) 자물쇠들이 새로 생성된 프로세스로 상속되지 않는다.


출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블

'0x001 Programming > 01. C | C++' 카테고리의 다른 글

[Example] 예외 처리  (0) 2017.12.29
[Tip] 예외 처리  (0) 2017.12.29
[Example] 디렉터리 내용 출력  (0) 2017.12.29
[Tip] 문자열 처리 관련  (0) 2017.12.29
[Example] 파일 복사  (0) 2017.12.29

+ Recent posts