弄了一段时间Windows系统API,关于磁盘自定义设计,Windows的很多接口是不方便做软件设计的。在Windows系统上设备与文件操作区别还是非常大的。为了能够像Linux那样把存储设备当做文件来操作,我对WriteFile,ReadFile,SetFilePointerEx等函数进行了重新封装。
关于存储设备,比如硬盘,U盘,SD卡,TF卡等设备在Linux与Windows上直接操作设备的区别主要有:
1.Linux将所有存储设备都当做是文件来操作,所有存储设备都可以使用open,close,lseek,select直接操作,应用层可以进行字节为单位的读写删除操作。而Windows只能将它们当做设备来处理,应用层只能进行以扇区为单位的读写删除操作。
2.linux可以将存储分区自由的挂载到不同的目录,Windows系统只有逻辑盘符可以使用,切在Win7中,对于可移动存储设备,比如U盘,SD卡,它们只能显示出第一个分区。
注意:下面代码全部都是直接对存储设备直接进行读写操作,请确认好磁盘号正确后才编译运行,否者容易将Windows系统分区信息破毁掉导致不能开机
(一)位置偏移
Linux系统可以使用lseek,lseek64,进行文件指针的偏移,设置SEEK_SET,SEEK_CUR,SEEK_END可相对开始,当前,结束位置进行偏移,lseek与lseek64参数一样,只是lseek64支持64位可用于大容量设备。Windows系统使用SetFilePointer,SetFilePointerEx进行偏移。它们也是使用FILE_BEGIN,FILE_CURRENT,FILE_END设置相对开始,当前,结束位置进行偏移,但是这里需要非常注意,在Windows存储设备中,不能都相对于结束位置进行偏移,系统会返回错误。另外,Windows系统相对于开始位置偏移,就算偏移位置超出了实际存储设备的大小,系统也不会返回错处。
/*************************************************
Function:LCB_SetFilePointer
Description: 设置句柄读写位置
Input: handle,DistanceToMove
Output: none
Return: 成功返回0,失败返回-1
Others: !!只设置相对于句柄开始位置的距离 !!
Author: Caibiao Lee
Date: 2018-06-25
*************************************************/
int LCB_SetFilePointerEx(HANDLE handle, unsigned long long DistanceToMove)
{
unsigned long long l_u64Ret = 0;
LARGE_INTEGER liDistanceToMove = { 0 };
LARGE_INTEGER lNewFilePointer = { 0 };
PLARGE_INTEGER lpNewFilePointer = &lNewFilePointer;
liDistanceToMove.QuadPart = DistanceToMove;
l_u64Ret = SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer, FILE_BEGIN);
if (l_u64Ret)
{
if (NULL != lpNewFilePointer)
{
printf("Set Point l_s64Offset = %lld \n", lpNewFilePointer->QuadPart);
}
else
{
fprintf(stderr, "%s %d set file pointer Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
else
{
fprintf(stderr, "%s %d set file pointer Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
return 0;
}
(二)数据读
因为在Windows系统中对存储设备是按扇区大小进行操作的,所以也就是只能读取扇区大小的整数倍数据,一般一个扇区大小为512字节。也就是说在Windows系统中,如果直接读取几个字节,系统间会返回错误。这里封装一个应用层可以直接字节读取的接口,实际上也就是先按扇区读取出来然后再提取,在Linux系统中是系统完成了这些操作而Windows没有。
/*************************************************
Function:WSD_ReadFile
Description: 直接读取磁盘设备数据
Input: hFile,nNumberOfBytesToRead
Output: *pBuffer
Return: 成功返回0,失败返回-1
Others:
Author: Caibiao Lee
Date: 2018-06-25
*************************************************/
int LCB_ReadFile(HANDLE handle, unsigned long Position, unsigned char *pBuffer, unsigned long nNumberOfBytesToRead)
{
int l_s32Res = 0;
unsigned char l_arrReadBuf[ONE_SECTOR_DATA_BYTE] = { 0 };
unsigned int l_u32FirstSectorDataLen = 0; /**在读位置所在的第一扇区可读取的数据长度**/
unsigned int l_u32RemainDataLen = 0; /**剩余没有读取数据的长度**/
unsigned int l_u32ReadOffest = 0;
unsigned int l_u32WriteBufferOffest = 0;
unsigned int l_u32ReadDataLen = 0;
unsigned long l_u32Readsize = 0;
unsigned long long l_u64ReadSectorPosition = 0;
if (NULL == pBuffer)
{
printf("%s %d input error \n", __FILE__, __LINE__);
return -1;
}
/**可分布在第一扇区数据的长度**/
l_u32FirstSectorDataLen = ONE_SECTOR_DATA_BYTE - Position % ONE_SECTOR_DATA_BYTE;
/**读取扇区的开始位置**/
l_u64ReadSectorPosition = Position % ONE_SECTOR_DATA_BYTE * ONE_SECTOR_DATA_BYTE;
/**读取的偏移位置**/
l_u32ReadOffest = Position % ONE_SECTOR_DATA_BYTE;
if (nNumberOfBytesToRead > l_u32FirstSectorDataLen )
{
l_u32ReadDataLen = l_u32FirstSectorDataLen;
l_u32RemainDataLen = nNumberOfBytesToRead - l_u32FirstSectorDataLen;
}
else
{
l_u32ReadDataLen = nNumberOfBytesToRead;
l_u32RemainDataLen = 0;
}
if (0 != LCB_SetFilePointerEx(handle, l_u64ReadSectorPosition))
{
printf("Set File Error \n");
return -1;
}
l_s32Res = ReadFile(handle, l_arrReadBuf, ONE_SECTOR_DATA_BYTE, &l_u32Readsize, NULL);
if (0 != l_s32Res)
{
if (ONE_SECTOR_DATA_BYTE == l_u32Readsize)
{
memcpy(pBuffer + l_u32WriteBufferOffest, &l_arrReadBuf[l_u32ReadOffest], l_u32ReadDataLen);
l_u32ReadOffest = 0;
l_u32WriteBufferOffest += l_u32ReadDataLen;
}
else
{
fprintf(stderr, "%s %d Read data Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
while (l_u32RemainDataLen > 0)
{
l_u32ReadOffest = 0;
l_u64ReadSectorPosition += ONE_SECTOR_DATA_BYTE;
if (l_u32RemainDataLen > ONE_SECTOR_DATA_BYTE)
{
l_u32RemainDataLen -= ONE_SECTOR_DATA_BYTE;
l_u32ReadDataLen = ONE_SECTOR_DATA_BYTE;
}
else
{
l_u32ReadDataLen = l_u32RemainDataLen;
l_u32RemainDataLen = 0;
}
if (0 != WSD_SetFilePointerEx(handle, l_u64ReadSectorPosition))
{
printf("Set File Error \n");
return -1;
}
l_s32Res = ReadFile(handle, l_arrReadBuf, ONE_SECTOR_DATA_BYTE, &l_u32Readsize, NULL);
if (0 != l_s32Res)
{
if (ONE_SECTOR_DATA_BYTE == l_u32Readsize)
{
memcpy(pBuffer + l_u32WriteBufferOffest, &l_arrReadBuf[l_u32ReadOffest], l_u32ReadDataLen);
l_u32WriteBufferOffest += l_u32ReadDataLen;
}
else
{
fprintf(stderr, "%s %d Read data Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
}
return 0;
};
(三)数据写
与数据读一样,主要是封装成按字节写数据。问题点,我在实际测试中发现,在我的电脑中,只能对存储设备的前面1M大小的空间进行数据写入操作,但是读不受影响,不知道是什么原因。
/*************************************************
Function:LCB_WriteFile
Description: 直接写入磁盘设备数据
Input: handle,nNumberOfBytesToWrite,*pBuffer
Output: none
Return: 成功返回0,失败返回-1
Others:
Author: Caibiao Lee
Date: 2018-06-25
*************************************************/
int LCB_WriteFile(HANDLE handle, unsigned long long Position, unsigned char *pBuffer, unsigned long nNumberOfBytesToWrite)
{
int l_s32Res = 0;
unsigned char l_arrReadWriteBuf[ONE_SECTOR_DATA_BYTE] = { 0 };
unsigned long l_u64ReadWriteByte = 0;
unsigned long l_u64ReadWriteSize = 0;
unsigned int l_s32RemainDataLen = 0;
unsigned int l_s32WriteOffest = 0;
unsigned int l_s32FirstSectorWriteLen = 0;
unsigned int l_s32FirstSectorRemainLen = 0;
unsigned int l_s32FirstSectorOffsetLen = 0;
unsigned long long l_s32FirstWriteSectorPosition = 0;
if (NULL == pBuffer)
{
printf("%s %d input para error \n", __FILE__, __LINE__);
return -1;
}
/**开始写入的第一个扇区开始的位置(不是磁盘第一扇区)**/
l_s32FirstWriteSectorPosition = (Position / 512) * 512;
/**开始写入位置在第一扇区中实际的偏移位置**/
l_s32FirstSectorOffsetLen = Position % 512;
/**第一扇区可以写入的数据长度**/
l_s32FirstSectorRemainLen = 512 - l_s32FirstSectorOffsetLen;
/**第一扇区实际需要写入的长度**/
if (nNumberOfBytesToWrite > l_s32FirstSectorRemainLen)
{
l_s32WriteOffest = l_s32FirstSectorRemainLen;
l_u64ReadWriteByte = l_s32FirstSectorRemainLen;
l_s32RemainDataLen = nNumberOfBytesToWrite - l_u64ReadWriteByte;
}
else
{
l_s32WriteOffest = nNumberOfBytesToWrite;
l_u64ReadWriteByte = nNumberOfBytesToWrite;
l_s32RemainDataLen = 0;
}
{/**写第一扇区的数据**/
/**设置偏移量**/
if (0 != LCB_SetFilePointerEx(handle, l_s32FirstWriteSectorPosition))
{
printf("Set File Error \n");
return -1;
}
/**先把整个扇区数据读取出来**/
l_s32Res = ReadFile(handle, l_arrReadWriteBuf, ONE_SECTOR_DATA_BYTE, &l_u64ReadWriteSize, NULL);
if (0 != l_s32Res)
{
if (ONE_SECTOR_DATA_BYTE == l_u64ReadWriteSize)
{
/**只修改需要写入位置的数据**/
memcpy(l_arrReadWriteBuf + l_s32FirstSectorOffsetLen, pBuffer, l_u64ReadWriteByte);
}
else
{
fprintf(stderr, "%s %d Read data Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
/**读数据结束后,句柄指针已经偏移到了下一扇区位置
需要将句柄指针偏移回写扇区开始位置**/
if (0 != LCB_SetFilePointerEx(handle, l_s32FirstWriteSectorPosition))
{
printf("Set File Error \n");
return -1;
}
l_s32Res = WriteFile(handle, l_arrReadWriteBuf, ONE_SECTOR_DATA_BYTE, &l_u64ReadWriteSize, NULL);
if (0 == l_s32Res)
{
fprintf(stderr, "%s %d Write data Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
{/**循环写入剩下的数据**/
while (l_s32RemainDataLen > 0)
{
l_s32FirstWriteSectorPosition += ONE_SECTOR_DATA_BYTE;
if (l_s32RemainDataLen > ONE_SECTOR_DATA_BYTE)
{
l_s32RemainDataLen = l_s32RemainDataLen - ONE_SECTOR_DATA_BYTE;
l_u64ReadWriteByte = ONE_SECTOR_DATA_BYTE;
}
else
{
l_u64ReadWriteByte = l_s32RemainDataLen;
l_s32RemainDataLen = 0;
}
if (0 != LCB_SetFilePointerEx(handle, l_s32FirstWriteSectorPosition))
{
printf("Set File Error \n");
return -1;
}
l_s32Res = ReadFile(handle, l_arrReadWriteBuf, ONE_SECTOR_DATA_BYTE, &l_u64ReadWriteSize, NULL);
if (0 != l_s32Res)
{
if (ONE_SECTOR_DATA_BYTE == l_u64ReadWriteSize)
{
memcpy(l_arrReadWriteBuf, pBuffer + l_s32WriteOffest, l_u64ReadWriteByte);
l_s32WriteOffest += l_u64ReadWriteByte;
}
else
{
fprintf(stderr, "%s %d Read data Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
if (0 != LCB_SetFilePointerEx(handle, l_s32FirstWriteSectorPosition))
{
printf("Set File Error \n");
return -1;
}
l_s32Res = WriteFile(handle, l_arrReadWriteBuf, ONE_SECTOR_DATA_BYTE, &l_u64ReadWriteSize, NULL);
if (0 == l_s32Res)
{
fprintf(stderr, "%s %d Write data Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
}
}
return 0;
};
(四)磁盘容量获取
在LINUX系统中,可以直接使用文件操作函数获取磁盘的实际大小,用sleek先偏移到设备开始位置,然后再偏移到结束位置,最后的返回值就是磁盘的大小(字节单位)。但是在Windows系统中就没那么方便了,它需要调用系统IAP,先获取各种参数,然后再计算出来实际的大小。
/*************************************************
Function:WSD_GetDiskCapacity
Description:存储设备磁盘容量的获取
Input: handle设备句柄
Output: none
Return: 小于0失败;大于0实际大小
Others:
Author: Caibiao Lee
Date: 2018-06-25
*************************************************/
long long LCB_GetDiskCapacity(HANDLE handle)
{
int l_s32Ret;
DWORD junk;
DISK_GEOMETRY DiskGeometry;
unsigned long long l_u64TotalCapacity = 0;
l_s32Ret = DeviceIoControl(handle, // device to be queried
IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform
NULL, 0, // no input buffer
&DiskGeometry, sizeof(DISK_GEOMETRY), // output buffer
&junk, // # bytes returned
(LPOVERLAPPED)NULL); // synchronous I/O
if (l_s32Ret)
{
l_u64TotalCapacity = DiskGeometry.Cylinders.QuadPart * DiskGeometry.TracksPerCylinder
* DiskGeometry.SectorsPerTrack *DiskGeometry.BytesPerSector;
}
else
{
fprintf(stderr, "%s %d Get Disk Size Error: %ld\n", __FILE__, __LINE__, GetLastError());
return -1;
}
return l_u64TotalCapacity;
}
从上面可以看出,Windows还是比较适合大众办公使用而不适合做开发,反正我是不想在Windows下做开发,接口太奇葩了 O(∩_∩)O哈哈~ 当然,也有可能是因为我是Windows小白吧。