개발이야기2007. 10. 18. 19:26

원본:요기 

윈도우에서 Physical Drive 직접 읽고 쓰기

  • 드라이브 열기
  1. HANDLE OpenDrive( int iPhysicalDriveNumber )
    {
        HANDLE hDevice;
        char vcDriveName[ 30 ];
  2.     // HDD를 실수로 지우는걸 방지하기 위함

        if( iPhysicalDriveNumber == 0 )
        {
            return INVALID_HANDLE_VALUE;
        }

  3.     // Physical Drive를 연다.
        sprintf( vcDriveName, "\\\\.\\PhysicalDrive%d", iPhysicalDriveNumber );
        hDevice = CreateFile( vcDriveName, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
        return hDevice;
    }

  • 드라이브 읽기
  1. BOOL ReadSector( HANDLE hDevice, DWORD dwSectorOffset, BYTE* pbBuffer,
                     int iSectorCount )
    {
        DWORD dwLow;
        DWORD dwHigh;
        DWORD dwRet;
        DWORD dwRead;
        DWORD dwErrorCode;
  2.     // 움직일 위치는 byte 단위로 되어야 한다.
        // 결국 dwSectorOffset에 512를 곱해줘야 한다.
        dwLow = ( dwSectorOffset << 9 );
        dwHigh = ( dwSectorOffset >> 23 );
  3.     // File Pointer를 이동한다.
        dwRet = SetFilePointer( hDevice, dwLow, &dwHigh, FILE_BEGIN );
        if( dwRet == INVALID_SET_FILE_POINTER )
        {
            return FALSE;
        }
  4.     // Sector를 읽는다.
        if( ReadFile( hDevice, pbBuffer, iSectorCount * SECTORSIZE, &dwRead,
            NULL ) == FALSE )
        {
            dwErrorCode = GetLastError();
            return FALSE;
        }
  5.     return TRUE;
    }
  • 드라이브 쓰기
  1. BOOL WriteSector( HANDLE hDevice, DWORD dwSectorOffset, BYTE* pbBuffer,
                     int iSectorCount )
    {
        DWORD dwLow;
        DWORD dwHigh;
        DWORD dwRet;
        DWORD dwWrite;
        DWORD dwErrorCode;
  2.     // 움직일 위치는 byte 단위로 되어야 한다.
        // 결국 dwSectorOffset에 512를 곱해줘야 한다.
        dwLow = ( dwSectorOffset << 9 );
        dwHigh = ( dwSectorOffset >> 23 );
  3.     // File Pointer를 이동한다.
        dwRet = SetFilePointer( hDevice, dwLow, &dwHigh, FILE_BEGIN );
        if( dwRet == INVALID_SET_FILE_POINTER )
        {
            return FALSE;
        }
  4.     // Sector를 쓴다.
        if( WriteFile( hDevice, pbBuffer, iSectorCount * SECTORSIZE, &dwWrite,
            NULL ) == FALSE )
        {
            dwErrorCode = GetLastError();
            return FALSE;
        }
  5.     gs_dwTotalWriteSectorCount += iSectorCount;
        return TRUE;
    }
  • 드라이브 닫기
  1. void CloseDrive( HANDLE hDevice )
    {
        CloseHandle( hDevice );
    }

Drive의 Geometry 읽기

Geometry정보에는 CHS 값이 들어있기 때문에 유용하게 쓸 수 있다.

  1. BOOL GetDriveGeometry( int iPhysicalDriveNumber, GEOMETRY* pstGeometry )
    {
        HANDLE hDevice;
        BOOL bRet;
        DWORD dwOutBytes;
        char vcDriveName[ 30 ];
        DISK_GEOMETRY stWindowGeometry;
  2.     // Physical Drive Number를 저장한다.
        sprintf( vcDriveName, "\\\\.\\PhysicalDrive%d", iPhysicalDriveNumber );
  3.     hDevice = CreateFile( vcDriveName, 0, FILE_SHARE_READ |
            FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
  4.     if( hDevice == INVALID_HANDLE_VALUE )
        {
            return FALSE;
        }
  5.     bRet = DeviceIoControl( hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY,
            NULL, 0, &stWindowGeometry, sizeof( DISK_GEOMETRY ), &dwOutBytes,
            NULL );
  6.     CloseHandle( hDevice );
  7.     // 지금은 윈도우 Geometry와 같은 Geometry를 사용한다.
        memcpy( pstGeometry, &stWindowGeometry, sizeof( DISK_GEOMETRY ) );
  8.     return bRet;
    }

Drive Descriptor 얻기

Descriptor에 보면 해당 드라이브의 제품명과 리비전 번호 같은 걸 알 수 있다.

  1. BOOL GetDeviceDescriptor( char* pcDevice, PSTORAGE_DEVICE_DESCRIPTOR pstDesc )
    {
        HANDLE hDevice;
        STORAGE_PROPERTY_QUERY  stQuery;
        DWORD dwOut;
        BOOL bRet;
  2.     memset( pstDesc, 0, sizeof(STORAGE_DEVICE_DESCRIPTOR) );
  3.     // Device를 Open한다.
        hDevice = CreateFile( pcDevice, GENERIC_READ, FILE_SHARE_READ |
            FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
        if( hDevice == INVALID_HANDLE_VALUE )
        {
            return FALSE;
        }
  4.     pstDesc->Size = sizeof( STORAGE_DEVICE_DESCRIPTOR );
  5.     // Device Io Control을 호출한다.
        stQuery.PropertyId = StorageDeviceProperty;
        stQuery.QueryType = PropertyStandardQuery;
  6.     bRet = DeviceIoControl( hDevice, IOCTL_STORAGE_QUERY_PROPERTY,
                &stQuery, sizeof( STORAGE_PROPERTY_QUERY ),
                pstDesc, pstDesc->Size, &dwOut, NULL);                
        if( bRet == FALSE )
        {
            return FALSE;
        }
  7.     CloseHandle( hDevice );
        return TRUE;
    }

Drive 문자로 Physical Index 얻기

드라이브 문자로 Physical Index를 얻는 방법은 VOLUME 정보를 이용하면 된다.

  1. int GetPhysicalDriveNumber( char cDriveName )
    {
        HANDLE hDevice;
        DWORD dwOut;
        BOOL bRet;
        char vcDriveName[ 40 ];
        VOLUME_DISK_EXTENTS* pstVolumeData;
        int iDiskNumber;
  2.     // 메모리를 할당한다.
        pstVolumeData = ( VOLUME_DISK_EXTENTS* ) malloc( VOLUMEDISKSIZE );
        if( pstVolumeData == NULL )
        {
            return -1;
        }
  3.     // 해당 Drive의 정보를 얻는다.
        sprintf( vcDriveName, "\\\\?\\%c:", cDriveName );
        // Device를 Open한다.
        hDevice = CreateFile( vcDriveName, GENERIC_READ, FILE_SHARE_READ |
            FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
        if( hDevice == INVALID_HANDLE_VALUE )
        {
            return -1;
        }
  4.     // Device Io Control을 호출한다.
        bRet = DeviceIoControl( hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
                NULL, 0, pstVolumeData, VOLUMEDISKSIZE, &dwOut, NULL );
        if( bRet == FALSE )
        {
            free( pstVolumeData );
            return -1;
        }
        CloseHandle( hDevice );
  5.     // Disk 정보가 1보다 작으면 실패
        if( pstVolumeData->NumberOfDiskExtents < 1 )
        {
            free( pstVolumeData );
            return -1;
        }
  6.     iDiskNumber = pstVolumeData->Extents[ 0 ].DiskNumber;
        free( pstVolumeData );
  7.     return iDiskNumber;
    }


Logical Drive와 Physical Drive 간의 매치방법

1. 일단 Logical Drive를 검색한다.
-> GetDriveType() 함수를 이용
2. 해당 Drvie를 열어서 Volume 정보를 얻음
-> "\\?\c" 와 같은 형태로 CreateFile() 호출
-> IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS를 날려서 Physical Drive 정보를 얻음
-> 리더기에 데이터가 연결되어있지 않으면 DeviceIoControl에서 문제 발생

00 Window I/O 관련

Posted by krsuncom