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

특정 디렉터리 내에 있는 내용을 출력하는 프로그램입니다.

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

#define TYPE_FILE 1  
#define TYPE_DIR 2
#define TYPE_DOT 3

#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

BOOL TraverseDirectory(LPCTSTR, DWORD, LPBOOL);
DWORD FileType( LPWIN32_FIND_DATA);
BOOL ProcessItem( LPWIN32_FIND_DATA, DWORD, LPBOOL);
DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...);

int _tmain(int argc, LPTSTR argv[])
{
 BOOL flags[MAX_OPTIONS], ok = TRUE;
 TCHAR pathName[MAX_PATH + 1] , currPath[MAX_PATH + 1], tempPath[MAX_PATH + 1];
 LPTSTR pSlash, pFileName;
 int i, fileIndex;

 fileIndex = Options(argc, argv, _T("Rl"), &flags[0], &flags[1], NULL);

 GetCurrentDirectory(MAX_PATH, currPath);

 if( argc < fileIndex + 1)
 {
  ok = TraverseDirectory(_T("*"), MAX_OPTIONS, flags);
 }
 else 
 {
  for( i = fileIndex; i < argc; i++)
  {
   _tcscpy(pathName, argv[i]);
   _tcscpy(tempPath, argv[i]);
  
   pSlash = _tstrrchr(tempPath, '\\');

   if( pSlash != NULL )
   {
    *pSlash = '\0';
    _tcscat(tempPath, _T("\\"));
    SetCurrentDirectory(tempPath);
    pSlash = _tstrrchr(pathName, '\\');
    pFileName = pSlash + 1;
   }
   else 
   {
    pFileName = pathName;
   }

   ok = TraverseDirectory(pFileName, MAX_OPTIONS, flags) && ok;

   SetCurrentDirectory(currPath);
  }
 }
}

static BOOL TraverseDirectory(LPCTSTR pathName, DWORD numFlags, LPBOOL flags)
{
 HANDLE searchHandle;
 WIN32_FIND_DATA findData;
 BOOL recursive = flags[0];
 DWORD fType, iPass;
 TCHAR currPath[MAX_PATH + 1];

 GetCurrentDirectory(MAX_PATH, currPath);

 for( iPass = 1; iPass <= 2; iPass++)
 {
  searchHandle = FindFirstFile(pathName, &findData);

  do
  {
   fType = FileType(&findData);
   if( iPass == 1 )
   {
    ProcessItem(&findData, MAX_OPTIONS, flags);
   }

   if( fType == TYPE_DIR && iPass == 2 && recursive)
   {
    _tprintf(_T("\n%s\\%s:"), currPath, findData.cFileName);

    SetCurrentDirectory(findData.cFileName);
    TraverseDirectory(_T("*"), numFlags, flags);
    SetCurrentDirectory(_T(".."));
   }
  
  }while( FindNextFile(searchHandle, &findData));

  FindClose(searchHandle);
 }

 return TRUE;

}

static DWORD FileType(LPWIN32_FIND_DATA pFileData)
{
 BOOL isDir;
 DWORD fType;
 fType = TYPE_FILE;
 isDir =(pFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
 if (isDir)
  if (lstrcmp(pFileData->cFileName, _T(".")) == 0
    || lstrcmp(pFileData->cFileName, _T("..")) == 0)
   fType = TYPE_DOT;
  else fType = TYPE_DIR;
 return fType;
}

static BOOL ProcessItem(LPWIN32_FIND_DATA pFileData, DWORD numFlags, LPBOOL flags)
{
 const TCHAR fileTypeChar[] = {_T(' '), _T('d')};
 DWORD fType = FileType(pFileData);
 BOOL longList = flags[1];
 SYSTEMTIME lastWrite;

 if (fType != TYPE_FILE && fType != TYPE_DIR) return FALSE;

 _tprintf(_T("\n"));
 if (longList) {
  _tprintf(_T("%c"), fileTypeChar[fType - 1]);
  _tprintf(_T("%10d"), pFileData->nFileSizeLow);
  FileTimeToSystemTime(&(pFileData->ftLastWriteTime), &lastWrite);
  _tprintf(_T(" %02d/%02d/%04d %02d:%02d:%02d"),
    lastWrite.wMonth, lastWrite.wDay,
    lastWrite.wYear, lastWrite.wHour,
    lastWrite.wMinute, lastWrite.wSecond);
 }
 _tprintf(_T(" %s"), pFileData->cFileName);
 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;
}
출처 : Windows 시스템 프로그래밍 윈도우즈 API 핵심 바이블​

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

[Example] 예외 처리  (0) 2017.12.29
[Tip] 예외 처리  (0) 2017.12.29
[Tip] 파일 잠금  (0) 2017.12.29
[Tip] 문자열 처리 관련  (0) 2017.12.29
[Example] 파일 복사  (0) 2017.12.29

1. Windows 환경에서 문자열 처리 방법


  Windows 는 표준 8비트 문자(char / CHAR)와 16비트 문자( C 의 wchar_t )를 지원한다. 8비트 문자는 ASCII 라고 부르며 16비트 문자는 유니코드(Unicode) UTF-16 인코딩을 통해 지원한다.


  UTF-8 : 유니코드를 위한 가변길이 문자 인코딩 방식. 유니코드 한 문자를 표현하기 위해 1바이트에서 4바이트 까지 사용한다.

  UTF-16 : 기본 다국어 평면에 속하는 문자들은 그대로 16비트 값으로 인코딩이 되고 그 이상의 문자는 특별히 정해진 방식으로 32비트 크기로 인코딩 된다.


2. 유니코드와 ASCII 문자열 모드를 선택하여 빌드하기 위한 프로그래밍 방법


 1) 모든 문자와 문자열을 일반적 형식 TCHAR, LPTSTR, LPCTSTR을 이용해서 정의한다.

 2) 유니코드를 사용하고자 하는 경우 모든 소스 모듈에 #define UNICODE와 #define _UNICODE 를 포함시킨다. UNICODE와 _UNICODE 정의에 따라 유니코드와 ASCII 문자열이 결정된다. 이 정의는 반드시 #include <windows.h> 전에 와야 한다.

 3) 바이트 버퍼 길이 들은 sizeof(TCHAR) 를 이용해서 계산한다.

 4) <tchar.h> 에 정의된 범용 C 라이브러리 문자열 및 I/O 함수를 사용한다. _fgettc, _itot 등

 5) 상수 문자열은 다음 세가지 형태 중 하나이어야 한다.

   5.1) "이 문자열은 8비트 문자들을 사용함"

   5.2) L"이 문자열은 16비트 문자들을 사용함"

   5.3) _T("이 문자열은 전처리 구문에 따라 달라짐")

 6) 텍스트 매크로들과 범용 C 라이브러리 함수들의 정의를 위해 windows.h 다음에 tchar.h를 포함한다.


출처 : 위키백과 / 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
[Example] 파일 복사  (0) 2017.12.29

순차적으로 파일을 복사하는 프로그램을 다음 세가지 방식으로 구현해보겠습니다.


1. 표준 C 라이브러리를 이용

2. Windows API 를 이용

3. Windows API 인 CopyFile 를 이용


1. 표준 C 라이브러리를 이용

 

#include <stdio.h>
#include <errno.h>

#define BUF_SIZE 256

int main (int argc, char *argv [])
{
FILE *inFile, *outFile;
char rec[BUF_SIZE];
size_t bytesIn, bytesOut;
if (argc != 3) {
  fprintf (stderr, "Usage: cp file1 file2\n");
  return 1;
}

inFile = fopen (argv[1], "rb");
if (inFile == NULL) {
  perror (argv[1]);
  return 2;
}
outFile = fopen (argv[2], "wb");
if (outFile == NULL) {
  perror (argv[2]);
  fclose(inFile);
  return 3;
}

while ((bytesIn = fread (rec, 1, BUF_SIZE, inFile)) > 0) {
  bytesOut = fwrite (rec, 1, bytesIn, outFile);
  if (bytesOut != bytesIn) {
   perror ("Fatal write error.");
   fclose(inFile);
   fclose(outFile);
   return 4;
  }
}

fclose (inFile);
fclose (outFile);
return 0;
} 

 

 

  1) perror 자체는 전역 변수 errno에 접근하여 함수 호출 실패에 대한 정보를 알아낸다.

  2) IO는 동기적(synchronous)이다. 연산이 끝날 때까지 기다렸다가 다음으로 나아간다.

  3) Windows 뿐만 아니라 다른 시스템에서도 실행이 가능하다.



2. Windows API 를 이용


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

#define BUF_SIZE 256 

int main (int argc, char * argv[])
{
 HANDLE hIn, hOut;
 DWORD nIn, nOut;
 CHAR buffer [BUF_SIZE];
 if (argc != 3) {
  fprintf (stderr, "Usage: cp file1 file2\n");
  return 1;
 }
 hIn = CreateFileA (argv[1], GENERIC_READ, FILE_SHARE_READ, NULL,
   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 if (hIn == INVALID_HANDLE_VALUE) {
  fprintf (stderr, "Cannot open input file. Error: %x\n", GetLastError ());
  return 2;
 }

 hOut = CreateFileA (argv[2], GENERIC_WRITE, 0, NULL,
   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 if (hOut == INVALID_HANDLE_VALUE) {
  fprintf (stderr, "Cannot open output file. Error: %x\n", GetLastError ());
  CloseHandle(hIn);
  return 3;
 }
 while (ReadFile (hIn, buffer, BUF_SIZE, &nIn, NULL) && nIn > 0) {
  WriteFile (hOut, buffer, nIn, &nOut, NULL);
  if (nIn != nOut) {
   fprintf (stderr, "Fatal write error: %x\n", GetLastError ());
   CloseHandle(hIn); CloseHandle(hOut);
   return 4;
  }
 }
 CloseHandle (hIn);
 CloseHandle (hOut);
 return 0;
}

  1) ReadFile / WriteFile 함수는 BOOL 값을 돌려준다. 바이트 개수는 함수 인자를 통해 반환된다.

  2) GetLastError() 함수를 통해 시스템 오류 코드를 얻을 수 있다.

 


 

3. Windows API 인 CopyFile 를 이용

 

#include <windows.h>
#include <stdio.h>
#define BUF_SIZE 256

int main (int argc, char * argv [])
{
 if (argc != 3) {
  fprintf (stderr, "Usage: cp file1 file2\n");
  return 1;
 }
 if (!CopyFileA (argv[1], argv[2], FALSE)) {
  fprintf (stderr, "CopyFile Error: %x\n", GetLastError ());
  return 2;
 }
 return 0;
}

     1) CopyFile 함수는 파일 시간 같은 메타 데이터도 복사한다.

 


 

출처 : 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] 문자열 처리 관련  (0) 2017.12.29

+ Recent posts