본문 바로가기

IT/WindowsProgramming

윈도우(Windows) 파일 접근 API

/* 
written by kaspy (kaspyx@gmail.com)
*/ 


이번 포스팅에서는 윈도우 (NTFS) 상에서 파일 조작과 관련된 API를 다루도록 하겠습니다.

* 윈도우(Windows) 에서 제공하는 파일 읽기/쓰기용 API


모듈 

함수(prototype)

해당 라이브러리 함수

열기

CreateFile

fopen

읽기

ReadFile

fread

쓰기

WriteFile

fwrite

닫기

CloseHandle

fclose

 

파일을 읽고 쓰는 기본적인 순서는 다음과 간다.


(1) 파일을 연다 (open)

(2) 실제로 데이터를 읽고 쓴다(read/write)

(3) 파일을 닫는다 (close)


아래는 CreateFile에대한 API의 프로토 타입이다.


  1. HANDLE CreateFile(                      //반환값: 파일 핸들
  2.         LPCTSTR lpFileName,             //첫번째 인자, 파일명
  3.         DWORD dwDesiredAccess,          //두번째 인자, 접근모드 
  4.         DWORD dwShareMode,              //세번째 인자, 배타적 제어 지정
  5.         LPSECURITY_ATTRIBUES lpSecurityAttributes,      //네번째 인자 보안 디스크립터
  6.         DWORD dwCreationDisposition,    // 다섯번째 인자, 파일을 여는 방법
  7.         DWORD dwFlagsAndAttributes,     // 여섯번째 인자, 파일 속성 플래그
  8.         HANDLE hTemplate                // 마지막 인자, 템플릿 파일 핸들
  9. );


 실패시 INVALID_HANDLE_VALUE 를 리턴한다.

 첫번째 인자에는 생성하거나 열려고 하는 파일의 이름을 지정한다. 두번째 인자에는 읽기/쓰기 중 어느 쪽을 선택할지 지정한다. 읽을 경우에는 GENERIC_READ를, 쓰기일 경우에는 상수 GENERIC_WRITE를 준다. 둘다하려면 논리연산자 OR('|')를 주면 된다.

 세번째 인자는 배타적 제어에 관한 것으로 주로 0을 주지만, 다른 프로세스와 공유를 한다거나 할때 설정해준다.  아래 예제코드 위에는 한번 파일을 열었을경우 다른 프로세스에서 접근시에 에러가 발생하지만 두번째 코드는 에러가 발생하지 않고 사용할수있다.


  1. CreateFile("file.dat",GENERIC_WRITE | GENERIC_READ ,
  2.                 0,
  3.                 NULL, OPEN_ALWAYS, 0NULL);
  4.  
  5. CreateFile("file.dat",GENERIC_WRITE | GENERIC_READ ,
  6.                 FILE_SHARE_READ,
  7.                 NULL, OPEN_ALWAYS, 0NULL);


 네번째 인자 보안 디스크립터는 파일 접근 권한을 설정할 경우에 사용하며 NULL을 주면 프로그램을 실행한 사용자나 파일을 생성하려는 폴더의 기본 설정이 사용된다. (기본적으로 NULL을 준다고 보면될듯싶다.)


 다섯번째 인자는 파일을 생성할것인지 기존 파일을 열것인지에 대한 값으로 속성값은 아래와 같다.

 

설정 

설명

CREATE_NEW

신규 파일을 생성한다같은 파일명이 있을 경우에는 에러

CREATE_ALWAYS

같은 파일명이 있어서 새로 생성하면 기존 파일의 내용은 사라짐

OPEN_EXISTING

기존 파일을 연다존재하지 않으면 에러

OPEN_ALWAYS

기존 파일을 연다존재하지 않으면 새로 생성

TRUNCATE_EXISTING

기존 파일을 연다기존 파일의 내용을 소거한다.

 

 여섯번째 인자에는 파일 속성과 플래그를 지정한다. 지정가능한 속성에는 '숨겨진파일'이나 '읽기 전용'등이 있다. 속성은 생성한 뒤에도 SetFileAttribute API로 변경할수 있다. 

 마지막 인자에는 윈도우(Windows) NT/2000/XP 에 파일속성을 지정하기위한 템플릿 파일이 있을때 핸들을 넘기는데 거의 사용하지 않고 NULL로 지정한다.


아래는 ReadFile과 WriteFile API에 대해서 설명하였다.


  1. BOOL ReadFile(                          // 반환값 : 성공이면 TRUE
  2.         HANDLE hFile,                   // 파일 핸들
  3.         LPVOID lpBuffer,                // 읽은 데이터를 저장할 버퍼
  4.         DWORD nNumberOfBytesToRead,     // 읽을 최대 바이트 수
  5.         LPVOID lpNumberOfBytesRead,     // 실제로 읽은 바이트 수를 받을 변수의 주소
  6.         LPOVERLAPPED lpOverlapped       // OVERLAPPED 구조체 주소
  7. );
  8.  
  9. BOOL WriteFile(                         // 반환값 : 성공이면 TRUE
  10.         HANDLE hFile,                   // 파일 핸들
  11.         LPCVOID lpBuffer,               // 기록할 데이터가 저장된 버퍼
  12.         DWORD nNumberOfBytesToWrite,    // 기록할 최대 바이트수
  13.         LPDWORD lpNumberOfBytesWritten, // 실제로 기록한 바이트 수를 받을 변수의 주소
  14.         LPOVERLAPPED lpOverlapped       // OVERLAPPED 구조체 주소
  15. );


아래는 설명한 파일 접근 API를 사용한 간단한 예제샘플이다. (파일 복사, test.txt를 CopiedFile로 복사함)


  1. #include <stdio.h>
  2. #include <Windows.h>
  3.  
  4. int main()
  5. {
  6.         HANDLE hSrcFile,hDstFile;
  7.         char buf[512];
  8.         DWORD nread,nwrite;
  9.         hSrcFile = CreateFile("test.txt", GENERIC_READ, 0NULL, OPEN_EXISTING, 0NULL);
  10.         hDstFile = CreateFile("CopiedFile", GENERIC_WRITE, 0NULL, TRUNCATE_EXISTING,0,NULL);
  11.  
  12.         if ((hSrcFile == INVALID_HANDLE_VALUE) || (hDstFile == INVALID_HANDLE_VALUE)){
  13.                 printf("File Open Failed\n");
  14.                 return -1;
  15.         }
  16.  
  17.         while(1)
  18.         {
  19.                 ReadFile(hSrcFile,buf,sizeof(buf),&nread,NULL);
  20.                 if (nread <= 0)
  21.                         break;
  22.                 printf("%d byte read\n",nread);
  23.                 WriteFile(hDstFile,buf,nread,&nwrite,NULL);
  24.                 printf("%d byte wrote\n",nwrite);
  25.         }
  26.  
  27.         CloseHandle(hSrcFile);
  28.         CloseHandle(hDstFile);
  29.  
  30. }


아래는 SetFilePointer API에 대해서 설명한다. 하드 디스크에 저장된 일반 파일을 접근해야할 경구는 파일포인터로 몇번째 위치로 이동해서 복사 등을 할것인지 지정해줘야하는 경우가 있다. 이러한 조작을 SEEK 이라고 한다. 여기서 유심히 볼것은 LONG타입을 두개를 사용하는데 2GB 이상의 파일도 접근하기 위해서이다.


  1. DWORD SetFilePointer(           //반환값: 이동후 파일 포인터의 하위 32비트
  2.         HANDLE hFile,           // 파일 핸들
  3.         LONG lDistanceToMove,   // 이동할 거리의 하위 32비트
  4.         PLONG lpDistacneToMoveHigh,     // 이동할 상대거리의 상위 32비트를 넣은 변수의 주소. (이동후의 32비트가 반환)
  5.         DWORD dwMoveMethod              // 기존 기준 위치
  6. );


 이동할 거리를 음수를 주게되면 반대로 접근한다. 예를들어 파일을 연뒤 기준 위치를 FILE_END로 지정하고 -100을 하면 끝에서 100번 지점에서부터 접근을 하게 된다. 아래는 파일을 하나 만든뒤 2바이트 오프셋하여 5바이트를 읽어서 출력하는 간단한 예제이다.


  1. #include <stdio.h>
  2. #include <Windows.h>
  3.  
  4. int main()
  5. {
  6.         HANDLE hf = CreateFile("test.txt",GENERIC_WRITE | GENERIC_READ ,0NULL, CREATE_ALWAYS, 0NULL);
  7.         if ( hf == INVALID_HANDLE_VALUE)
  8.         {
  9.                 return -1;
  10.         }
  11.         char *data = "This is test data\n abcdefg";
  12.         char buf[256];
  13.         DWORD nread,nwrite;
  14.  
  15.         WriteFile(hf,data, strlen(data)&nwrite, NULL);
  16.  
  17.         SetFilePointer(hf, 2NULL, FILE_BEGIN);
  18.         ReadFile(hf,buf,5,&nread,NULL);
  19.         buf[nread] = NULL;
  20.  
  21.         printf("%s\n",buf);
  22.  
  23.         CloseHandle(hf);
  24. }