Windows硬盘等存储设备读写查询分区格式化(二)

    弄了一段时间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,先获取各种参数,然后再计算出来实际的大小。

扫描二维码关注公众号,回复: 1868373 查看本文章
/*************************************************
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小白吧。





 

猜你喜欢

转载自blog.csdn.net/li_wen01/article/details/80853576
今日推荐