ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파일 관련 API
    프로그래밍/Windows API 2005. 6. 24. 05:36

    파일을 다루는데 사용되는 API함수엔 다음과 같은 함수가 있습니다.

     

    CreateFile, WriteFile, ReadFile

     

    우선 파일을 열거나 생성할때는 CreateFile 함수가 사용됩니다.

     

    함수원형

    HANDLE CreateFile(

        LPCTSTR lpFileName,

        DWORD dwDesiredAccess,

        DWORD dwShareMode,

        LPSECURITY_ATTRIBUTE lpSecurityAttributes,

        DWORD dwCreationDispostion,

        DWORD dwFlagsAndAttributes,

        HANDLE hTemplateFile );

     

    lpFileName : 생성 또는 오픈할 파일의 경로와 파일이름( NULL로 끝나는 문자열 )

     

    dwDesiredAccess : 파일 접근 모드. GENERIC_READ 와 GENERIC_WRITE 가 있으며 읽고쓰기를 병행할땐 두 플래그를 비트연산자로 조합해서 사용할 수 있습니다.

     

    dwShareMode : 파일을 공유할지의 여부를 결정하는 플래그 입니다. 0 을 대입하면 다른쪽에서 이 파일에 접근할 수 없게 됩니다.

    FILE_SHARE_READ 나 FILE_SHARE_WRITE 플래그를 주면 다른 객체가 이 파일에 대한 읽기 쓰기 접근을 할 수 있습니다.

     

    lpSecurityAttributes : 보안속성을 지정하는 인수. 대게 NULL을 대입합니다.

     

    dwCreationDispostion : 파일을 어떤 모드로 열것인지 결정하는 플래그. 다음과 같은 값을 줄 수 있습니다.

    CREATE_NEW : 파일을 새로 생성함. 이미 존재할 경우엔 에러를 리턴

    CREATE_ALWAYS : 파일을 새로 생성. 이미 존재할 경우엔 그 파일을 지우고 새로 쓴다.

    OPEN_EXISTING : 이미 존재하는 파일을 오픈. 파일이 존재하지 않을땐 에러를 리턴

     

    dwFlagsAndAttributes : 대게 FILE_ATTRIBUTE_NORMAL 을 대입합니다.

     

    hTemplateFile : 대게 잘 쓰이지 않아 NULL을 대입합니다.

     

    파일을 정상적으로 열었을땐 파일에 대한 핸들을 리턴하고 실패할 경우엔 INVALID_HANDLE_VALUE 를 리턴합니다. 파일 핸들은 사용한 뒤엔 반드시 닫아주어야 하는데 이때 CloseHandle 함수를 사용합니다. 인수로 파일 핸들을 넘겨주면 되죠.

     

     

     

     

    파일을 읽고 쓰는 함수엔 다음 함수들이 사용됩니다.

     

    BOOL WriteFile(

        HANDLE hFile,

        LPCVOID lpBuffer,

        DWORD nNumberOfBytesToWrite,

        LPDWORD lpNumberOfBytesWritten,

        LPOVERLAPPED lpOverlapped );

     

    hFile : 파일의 핸들. CreateFile의 리턴값을 대입

     

    lpBuffer : 파일에 쓸 데이터가 들어있는 버퍼

     

    nNumberOfBytesToWrite : lpBuffer데이터의 얼마만큼을 파일에 쓸건지 대입(바이트단위)

     

    lpNumberOfBytesWritten : 파일이 얼마만큼 쓰여졌는가를 받을 dword의 포인터변수

     

    lpOverlapped : 비동기 파일연산시에 사용. 대개 잘 사용되지 않으므로 NULL을 대입

     

     

     

    BOOL ReadFile(

        HANDLE hFile,

        LPVOID lpBuffer,

        DWORD nNumberOfBytesToRead,

        LPDWORD lpNumberOfBytesRead,

        LPOVERLAPPED lpOverlapped );

     

    hFile : 파일의 핸들.

     

    lpBuffer : 파일의 내용을 읽어들일 버퍼

     

    nNumberOfBytesToRead : 파일의 얼마만큼의 내용을 읽어들일지에 대한 바이트단위의 수.

     

    lpNumberOfBytesRead : 파일의 얼마만큼의 데이터를 읽어들였는지에 대한 출력용 인수

     

    lpOverlapped : 대게 NULL을 대입

     

     

    위 두 함수는 성공하면 TRUE를 리턴하고 그렇지 않을땐 FALSE를 리턴합니다.

     

    파일을 다룰때 알아야 할것이 FP(File Pointer) 라는 개념인데 이는 커서와 비슷한 개념입니다. 메모장에서 글씨를 쓸때 쓴만큼 커서가 이동되는것과 마찬가지로 전체 파일에서 절반을 읽어들였다면 FP는 읽어들인 곳 바로 다음을 가리키게 됩니다.

     

    아래는 새로 텍스트 파일을 생성해서 데이터를 저장한뒤 다시 읽어들여 화면에 출력하는 간단한 코드입니다.

     

     

    void main( void )

    {

        HANDLE hFile;

        DWORD dwWritten, dwRead;

        char pszBuf[] = "This is file test code source";

     

        // test.txt 파일을 새로 생성한다

        hFile =CreateFile( "test.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL,

                                       CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL );

        if(INVALID_HANDLE_VALUE== hFile )

            return;

     

        // text.txt파일에 데이터를 씀

        if( FALSE ==WriteFile( hFile, pszBuf, strlen( pszBuf ) + 1, &dwWritten, NULL ) )

        {

            CloseHandle( hFile );

            return;

        }

     

        // 데이터가 들어있는 버퍼를 비우고

        memset( pszBuf, 0, sizeof( pszBuf ) );

     

        // 파일로부터 내용을 읽어들임

        if( FALSE ==ReadFile( hFile, pszBuf, dwWritten, &dwRead, NULL ) )

        {

            CloseHandle( hFile );

            return;

        }

        // 파일 핸들을 닫고

        CloseHandle( hFile );

        // 파일 내용을 출력

        puts( pszBuf );

    }

     

     

    WriteFile, ReadFile 함수는 블록함수라 데이터가 큰 파일을 접근할때는 시스템이 파일 크기에 비례해 멈춰버리게 됩니다. 이를 위해 비동기 입출력 방법이 있지만 효율적인 방법은 아니며 대게 스레드를 사용합니다. 비동기 입출력에 대해선 레퍼런스를 참고하시고.. 여기서는 다만 스레드를 사용하는게 아니라 시스템이 다운되지 않았다는걸 사용자가 알 수 있도록 파일을 조금씩 읽어들여서 상태를 출력하는 코드를 짜보았습니다. 아래는 코드입니다.

     

    void main( void )

    {

        HANDLE hFile;

        DWORD dwWritten, dwRead, dwTotalSize, dwState = 0;

        char *pszBuf, Msg[256];

        BOOL bResult;

     

       // 큰 데이터를 준비한다

        pszBuf = (char *)malloc( 30000 );

        if( NULL == pszBuf )

            return;

     

        hFile =CreateFile( "test.txt", GENERIC_WRITE, 0, NULL,

                                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

        if( INVALID_HANDLE_VALUE == hFile )

        {

            free( pszBuf );

            return;

        }

     

        for( i = 0; i <= 20000; i++ )

        {

            wsprintf( pszBuf, "%d data\r\n", i );

           WriteFile( hFile, pszBuf, strlen( pszBuf ), &dwWritten, NULL );

        }

        CloseHandle( hFile );

     

        // 읽기 전용으로 파일을 다시 연다

        hFile =CreateFile("test.txt", GENERIC_READ, 0, NULL,

                                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

        if( INVALID_HANDLE_VALUE == hFile )

        {

            free( pszBuf );

            return;

        }

     

        // 파일 전체 크기를 읽어옴

        dwTotalSize = GetFileSize( hFile, 0 );

     

        // 루프를 돌면서 파일을 조금씩 읽어들이고 중간중간 상태를 출력

        while( 1 )

        {

            bResult =ReadFile( hFile,pszBuf + dwState, 1024, &dwRead, NULL );

           if( ( TRUE == bResult ) && ( 0 == dwRead ) )

            {

                // 파일 읽기 완료

                CloseHandle( hFile );

                puts( "Success read file !!" );

               break;

            }

            else

            {

                // 진행중이면 현재 상태를 출력

                dwState += dwRead;

                wsprintf( Msg, "%d 퍼센트 완료..", ( 100 * dwState ) / dwTotalSize );

                system( "cls" );        puts( Msg );

            }

        }

     

        free( pszBuf );

    }

     

     

    루프문 안에서 파일의 끝을 검사하는 if문을 주의깊게 살펴보세요.

    단지 리턴값만 검사하는것이 아니라 얼마만큼 데이터를 기록했는지의 출력용 인수인 dwWritten 과 함께 검사해 주고 있습니다. 참을 리턴하고 0만큼 데이터를 기록했다면 파일의 끝이므로 끝 처리 루틴을 넣어주면 됩니다. 어렵지 않죠? 아니.. 조금 어려운가요^^;

    댓글