Windows程序设计(2) -API-01 初识

Windows 程序原理

  • 一,CPU的保护模式和windows操作系统

    • [x] windows 是多任务实现

    • [x] 虚拟内存和 各个进程的地址空间安排:2G系统空间,2G用户空间,2G用户空间是各个进程私有的,很少被其他应用打断,保证系统稳定

    • [x] 内核模式和用户模式:虽然用户模式下是独立4G空间,但是内核模式的系统和驱动共用一个2G空间;内核空间的数据只有在特定的权限下才能访问

    • [x] 当应用程序调用系统函数比如ReadFile,应用程序会从用户模式进入内核模式去执行;

      因为访问系统内部数据,必须运行在内核模式下

  • 二,内核对象

    • [x] 内核对象是系统提供的用户模式下代码与内核模式下代码交互的基本接口
    • [ ] 内核对象是内核分配的内存,只能在内核模式下运行,内核对象记录的数据在整个系统只有一份,他们也称为系统资源
    • [ ] 内核对象和普通对象的最大区别,其数据结构是隐藏的,必须调用对象服务才能使用修改。
  • 三,对象句柄

    原则上,内核对象的句柄仅对当前进程有效;当然也有手段多进程共用一个内核对象

  • 四,应用程序启动流程

    应用程序的启动过程就是应用程序的创建过程

    • 操作系统通过CreateProcess 创建新的进程,初始化引用计数,创建虚拟地址空间,加载应用程序运行时所需的代码和数据
    • 创建主线程。主线程执行C/C++运行期启动代码开始运行,然后调用main函数

初始windows编程

1.1创建Win32 Windows应用程序

Win32 Project 创建

1.2认识入口函数

#include <tchar.h>
#include <windows.h>

int WINAPI _tWinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPWSTR     lpCmdLine,
	int       nShowCmd
)
{
	 MessageBox(NULL, _T("这是第一个程序"), _T("标题"), MB_YESNOCANCEL | MB_DEFBUTTON2);
	 return 0;
}

1.3Windows常见数据类型

【Windows核心编程】Windows常见数据类型

微软使用typedef关键字,为很多常用的C/C++类型均定义了别名,这样一来,要解决源代码移植问题,只需在目标平台上定义相同的一套类型别名,

即可解决大部分问题,很是方便,这其实也是为程序的扩展性考虑的。

DWORD  实质上就是unsigned long数据类型,32位无符号整型。
HANDLE 类型实质上是void, HANDLE定义为:
typedof PVOID HANDLE;
HANDLE  实际上就是一个PVOID,那PVOID又是什么呢?
typeof void* PVOID; 
PVOID  就是指向void的指针

1.4 Windows API 调用,盘2个简单的API

1.4.1 URLDownloadToFile

【URLDownloadToFile API档案】

#include <windows.h>
#include <tchar.h>
#pragma comment(lib, "urlmon.lib")
//入口点函数
int WINAPI WinMain(
	HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpCmdLine,
	int nShowCmd
)
{
	MessageBox(NULL, _T("这是第一个程序"), _T("标题"), MB_YESNOCANCEL | MB_DEFBUTTON2);
	

	//指定网址文件下载到本地文件
	HRESULT herur=URLDownloadToFile(NULL, _T("https://s.cctry.com/images/eagle2.png"), _T("D:\123.png"), 0,NULL);
	if (herur == S_OK)
	{
		MessageBox(NULL, _T("it is done"), _T("DOWNLOAD"), NULL);
	
	}
	else {
		MessageBox(NULL, _T("it is not done"), _T("DOWNLOAD"), NULL);
	
	}
	
	//自动执行文件
	return 0;

}

1.4.2 ShellExecuteEx

[ShellExecuteEx API档案](https://www.cnblogs.com/blogpro/p/11343975.html)

打开图片

      SHELLEXECUTEINFO sei;
      ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));//使用前最好清空
      sei.cbSize = sizeof(SHELLEXECUTEINFO);//管理员权限执行cmd,最基本的使用与 ShellExecute 类似
      sei.lpFile = _T("D:\123.png");
      sei.nShow = SW_SHOW;
      ShellExecuteEx(&sei);

打开系统的 cmd

	SHELLEXECUTEINFO sei;
	ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));//使用前最好清空
	sei.cbSize = sizeof(SHELLEXECUTEINFO);//管理员权限执行cmd,最基本的使用与 ShellExecute 类似
	sei.lpFile = _T("cmd.exe");
	sei.nShow = SW_SHOW;
	sei.lpVerb = _T("runas");
	ShellExecuteEx(&sei);

打开网页

      SHELLEXECUTEINFO sei;
      ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));//使用前最好清空
      sei.cbSize = sizeof(SHELLEXECUTEINFO);//管理员权限执行cmd,最基本的使用与 ShellExecute 类似
      sei.lpFile = _T("www.baidu.com");
      sei.nShow = SW_SHOW;

      ShellExecuteEx(&sei);

打开QQ

      SHELLEXECUTEINFO sei;
      ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));//使用前最好清空
      sei.cbSize = sizeof(SHELLEXECUTEINFO);//管理员权限执行cmd,最基本的使用与 ShellExecute 类似
      sei.lpFile = _T("F:\应用程序工作目录\QQ\Bin\QQScLauncher.exe");
      sei.nShow = SW_SHOW;
      ShellExecuteEx(&sei);

1.5错误处理

	BOOL sucessDelete= DeleteFile(_T("D:\123.txt"));

	if (sucessDelete)
	{
		MessageBox(NULL, _T("删除成功"), _T("提示"), NULL);
	}
	else
	{
		DWORD error = GetLastError();
                if(error==ERROR_FILE_NOT_FOUND)
                {
                      MessageBox(NULL, _T("文件不存在"), _T("提示"), NULL);
                }
                else
                {
		      MessageBox(NULL, _T("删除失败"), _T("提示"), NULL);
                }   
	}

1.6文件操作

1.6.1 文件删除 复制与重命名

删除文件

      BOOL sucessDelete= DeleteFile(_T("D:\123.txt"));
      if (sucessDelete)
      {
	MessageBox(NULL, _T("删除成功"), _T("提示"), NULL);
      }
      else
      {
	MessageBox(NULL, _T("删除失败"), _T("提示"), NULL);
      }
      //如何使用deletefile删除一个只读属性的文件?

拷贝

      BOOL sucessCopy =CopyFile(_T("D:\456.txt"), _T("D:\789.txt"), FALSE);
      if (sucessCopy)
      {
	MessageBox(NULL, _T("拷贝成功"), _T("提示"), NULL);
      }
      else
      {
	MessageBox(NULL, _T("拷贝失败"), _T("提示"), NULL);
      }

重命名

//重命名、可对文件夹操作
//目标文件存在,重命名失败
      BOOL sucessMove = MoveFile(_T("D:\789.txt"), _T("D:\12789.txt"));
      if (sucessMove)
      {
	MessageBox(NULL, _T("move成功"), _T("提示"), NULL);
      }
      else
      {
	MessageBox(NULL, _T("move失败"), _T("提示"), NULL);
      }
      BOOL sucessMoveDirectory = MoveFile(_T("D:\新建文件夹"), _T("D:\1278"));
      if (sucessMoveDirectory)
      {
	MessageBox(NULL, _T("move文件夹成功"), _T("提示"), NULL);
      }
      else
      {
	MessageBox(NULL, _T("move文件夹失败"), _T("提示"), NULL);
      }

1.6.2 创建、打开、关闭

创建与打开都是用的一个API CreateFile,通过参数的不同来区别是创建文件还是打开已经存在的文件

CreateFile API档案

简介

创建或打开文件或 I/O 设备。常用的 I/O 设备有:文件,文件流,目录,物理磁盘,卷,控制台缓冲区,磁带驱动器,通信资源,邮筒和管道。
该函数返回一个句柄,该句柄可用于根据文件或设备以及指定的标志和属性访问文件或设备以获取各种类型的 I/O。

语法格式:

HANDLE WINAPI CreateFile(
  _In_     LPCTSTR               lpFileName,
  _In_     DWORD                 dwDesiredAccess,
  _In_     DWORD                 dwShareMode,
  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  _In_     DWORD                 dwCreationDisposition,
  _In_     DWORD                 dwFlagsAndAttributes,
  _In_opt_ HANDLE                hTemplateFile
);

参数

lpFileName

要创建或打开的文件或设备的名称

dwDesiredAccess

所请求的文件或设备访问权限。

含义
0 不希望从设备读取数据或向设备写入数据。如果只想改变设备的配置(如:修改文件的时间戳),可以传0
GENERIC_READ 允许对设备进行只读访问
GENERIC_WRITE 允许对设备进行只写访问,该选项并没有隐含 GENERIC_READ标志
dwShareMode

文件或设备的请求共享模式。

含义
0 要求独占对设备的访问。如果设备已经打开,CreateFile 调用会失败;如果成功地打开了设备,后续的 CreateFile 调用会失败
FILE_SHARE_READ 如果有其他对象要用该设备,我们要求它们不得修改设备的数据;如果设备已经以写入方式或独占方式打开,那么CreateFile调用会失败
FILE_SHARE_WRITE 如果有其他内核对象要使用该设备,则要求它们不得读取设备的数据
FILE_SHARE_DELETE 当对文件进行操作的时候,我们不关心文件是否被逻辑删除或移动。在Windows内部,系统会先将文件标记为待删除,然后当该文件所有已打开的句柄都被关闭的时候,再将其真正的删除
lpSecurityAttributes

如果此参数为NULL,则由CreateFile返回的句柄不能由应用程序可能创建的任何子进程继承,并且与返回句柄关联的文件或设备将获得默认安全描述符。一般都是直接将该参数设置为NULL

dwCreationDisposition

采取存在或不存在的文件或设备的操作。

含义
CREATE_NEW 告诉CreateFile创建一个新文件,如果同名文件已经存在,那么 CreateFile调用会失败
CREATE_ALWAYS 告诉CreateFile无论同名是否存在都创建新文件,若文件存在,则覆盖
OPEN_EXISTING 告诉CreateFile打开一个已有的文件或设备,如果文件或设备不存在,那么CreateFile调用会失败
OPEN_ALWAYS 告诉CreateFile打开一个已有的文件,如果文件存在,那么CreateFile会直接打开文件,如果不存在,则会创建一个新文件
TRUNCATE_EXISTING 告诉CreateFile打开一个已有的文件并将文件大小截断为0字节,如果文件不存在,那么CreateFile调用会失败
dwFlagsAndAttributes

该参数的用途:
允许我们设置一些标志来微调与设备之间的通信;
如果设备是一个文件,我们还能够设置文件的属性。

文件设置
FILE_ATTRIBUTE_ARCHIVE 应用程序用该标志来将文件标记为待备份或待删除。当CreateFile创建一个新文件时,会自动设置该标志
FILE_ATTRIBUTE_ENCRYPTED 文件是经过加密的
FILE_ATTRIBUTE_HIDDEN 文件是隐藏的。它不会出现在通常的目录清单中
FILE_ATTRIBUTE_NORMAL 文件没有其他属性。只有单独使用的时候,这个标志才有效
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 内容索引服务(content indexing service)不会对文件进行索引
FILE_ATTRIBUTE_OFFLINE 文件虽然存在,但文件内容已经被转移到脱机存储中
FILE_ATTRIBUTE_READONLY 文件只读
FILE_ATTRIBUTE_SYSTEM 文件是操作系统的一部分,专供操作系统使用
FILE_ATTRIBUTE_TEMPORARY 文件数据只会使用一小段时间。为了将访问时间降至最低,会尽量将文件数据保存在内存中
hTemplateFile

具有 GENERIC_READ 访问权限的模板文件的有效句柄。模板文件为正在创建的文件提供文件属性和扩展属性。该参数可以是NULL。

返回值

如果函数成功,则返回值的是指定文件、设备、命名管道或邮件插槽的句柄;
如果函数失败,则返回值为 INVALID_HANDLE_VALUE。 要获得扩展的错误信息,请调用GetLastError。

创建一个新的空白文件

	HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),//创建或打开的文件或设备的名称(这里是txt文件)。
		GENERIC_WRITE|GENERIC_READ,// 文件访问权限,可读可写
		FILE_SHARE_READ,//共享模式,这里设置0防止其他进程打开文件或设备
		NULL,//SECURITY_ATTRIBUTES结构,安全描述,这里NULL代表默认安全级别
		CREATE_ALWAYS,//对于存在或不存在的设置执行的操作,这里是始终创建
		FILE_ATTRIBUTE_NORMAL,//文件没有其他属性。只有单独使用的时候,这个标志才有效
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		
		MessageBox(NULL, _T("文件创建失败"), _T("提示"), NULL);
		DWORD error= GetLastError();
		;
	}
	else
	{
		MessageBox(NULL, _T("文件创建成功"), _T("提示"), NULL);

	}

打开一个已经存在的文件

HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_WRITE|GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,//打开已经存在的文件
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
		DWORD error= GetLastError();
		;
	}
	else
	{
		MessageBox(NULL, _T("文件打开成功"), _T("提示"), NULL);

	}

文件存在,会直接打开文件,如果不存在,则会创建一个新文件

//打开文件
	HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_WRITE|GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_ALWAYS,//文件存在,会直接打开文件,如果不存在,则会创建一个新文件
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("文件打开成功"), _T("提示"), NULL);

	}

文件存在,则清空,如果文件不存在,则创建

HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_WRITE|GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		CREATE_ALWAYS,//文件存在,会直接打开文件,如果不存在,则会创建一个新文件
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("文件打开成功"), _T("提示"), NULL);

	}

关闭CloseHandle

简介:
关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等等。
在 CreateThread 成功之后会返回一个 hThread 的线程句柄,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除该内核对象。
若在线程执行完之后,没有调用CloseHandle,在进程执行期间,将会造成内核对象的泄露,相当于句柄泄露,但不同于内存泄露,这势必会对系统的效率带来一定程度上的负面影响。但当进程结束退出后,系统会自动清理这些资源。

语法格式:

BOOL CloseHandle(
  HANDLE hObject
);

参数:
hObject:代表一个已经打开的对象句柄,例如:文件句柄,线程句柄,进程句柄等。

返回值:
TRUE:执行成功;
FALSE:执行失败,可以调用 GetLastError() 获得具体的执行失败原因。

比如创建失败,则是无效句柄,不需要CloseHandle,如果创建成功,最后不用了,则需要closeHandle

1.6.3 获取文件大小

[API档案 GetFileSize ]

简介:
该函数用于获取指定文件的大小(长度),以字节为单位!

函数声明:

 DWORD  GetFileSize (
  HANDLE hFile,
  LPDWORD lpFileSizeHigh
 );

参数:
hFile:待获取大小的文件句柄,该文件句柄必须具有 GENERIC_READ 或 GENERIC_WRITE 访问权限。

lpFileSizeHigh:指向一个 DWORD 变量的指针,该变量用于接收文件大小高端(第32-63位)部分的值。若不需获取这部分的值,该参数可以为 NULL 。

返回值:
如果函数调用成功,返回值为文件大小的低端(第0-31位)的值,如果 lpFileSizeHigh 参数不为 NULL ,该参数对应的变量包含文件大小的高端(第32-63位)部分的值。
如果函数调用失败,并且 lpFileSizeHigh 参数为 NULL ,则返回值为 INVALID_FILE_SIZE 。要获取更多错误信息,请调用 GetLastError 函数。
如果函数调用失败,并且 lpFileSizeHigh 参数不为 NULL ,返回值为 INVALID_FILE_SIZE ,调用 GetLastError 函数返回的错误代码为 NO_ERROR 以外的值。

低于4G的文件
    HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.png"),
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		MessageBox(NULL, _T("图片打开失败"), _T("提示"), NULL);
	}
	else
	{
		DWORD dwFileSize = GetFileSize(hFile, NULL);
		TCHAR szFileSize[1024] = {0};
		_stprintf_s(szFileSize, _T("文件大小是%d"), dwFileSize);
		MessageBox(NULL, szFileSize, _T("tip"), MB_OK);
		CloseHandle(hFile);

	}
>=4G的文件
    HANDLE hFile;
	hFile = CreateFile(
		_T("D:\操作系统win10.iso"),
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
	}
	else
	{
        //高32位
		DWORD dwFileSizeHigh = 0;
		DWORD dwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
        //转成longlong
		LONGLONG lFileSize = (((LONGLONG)dwFileSizeHigh) << 32) | dwFileSize;
		TCHAR szFileSize[1024] = {0};
		_stprintf_s(szFileSize, _T("文件大小是%lld"), lFileSize);
		MessageBox(NULL, szFileSize, _T("tip"), MB_OK);
		CloseHandle(hFile);

	}  

[ GetFileSizeEx API档案]

函数说明
BOOL GetFileSizeEx(
  HANDLE         hFile,
  PLARGE_INTEGER lpFileSize
);
参数:

lpFileSize: 输出参数,指向储存文件大小的一个 LARGE_INTEGER 联合体。

#if defined(MIDL_PASS)
typedef struct _LARGE_INTEGER {
#else // MIDL_PASS
typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    } DUMMYSTRUCTNAME;
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
#endif //MIDL_PASS
    LONGLONG QuadPart;
} LARGE_INTEGER;

大于小于4G文件都可以使用

    HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.png"),
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile==INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		MessageBox(NULL, _T("图片打开失败"), _T("提示"), NULL);
	}
	else
	{
		LARGE_INTEGER fileSize = { 0 };
		BOOL bRet = GetFileSizeEx(hFile, &fileSize);
		if (bRet)
		{
			TCHAR szFileSize[128] = { 0 };
			_stprintf_s(szFileSize, _T("文件大小是%lld"), fileSize.QuadPart);
			MessageBox(NULL, szFileSize, _T("tip"), MB_OK);
		}
		CloseHandle(hFile);
	}

1.6.4 文件的读写

[WriteFile 函数说明 API档案]

简介:
从文件指针指向的位置开始将数据写入到一个文件中, 且支持同步和异步操作。
如果文件打开方式没有指明 FILE_FLAG_OVERLAPPED,当程序调用成功时,它将实际写入文件的字节数保存到 lpNumberOfBytesWriten 指明的地址空间中。
如果文件要交互使用的话,当函数调用完毕时要记得调整文件指针。

声明:

 BOOL WriteFile(
  HANDLE    hFile,
  LPCVOID    lpBuffer,
  DWORD     nNumberOfBytesToWrite,
  LPDWORD    lpNumberOfBytesWritten,
  LPOVERLAPPED lpOverlapped
);

参数说明:

  • hFile:需要写入数据的已打开的文件句柄,这个句柄所关联的文件必须拥有 GENERIC_WRITE 访问权限属性的文件;
  • lpBuffer:要写入到文件的缓冲区首地址;
    nNumberOfBytesToWrite:要写入数据的字节数量。如写入零字节,表示什么都不写入,但会更新文件的“上一次修改时间”;
  • lpNumberOfBytesWritten:实际写入文件的字节数量,此变量是用来返回的 ;
  • lpOverlapped:倘若在指定 FILE_FLAG_OVERLAPPED 标志的前提下打开文件,指针不能为空,这个参数就必须引用一个特殊的结构。那个结构定义了一次异步写操作。否则,该参数应置为空;

返回值:
函数执行成功,返回TRUE,否则返回FALSE,更多错误信息还请调用 GetLastError 获取错误码。

HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
		return -1;
	}
	//写整数,(实际效果,是将123 的补码写到文件中)
	int num = 123;
	DWORD dwRealWrite = 0;
	//写入的数据的首地址
	//bool bRet = WriteFile(hFile,&num,sizeof(num),&dwRealWrite,NULL);
	//写字符
	char ch = 'g';
	//bool bRet = WriteFile(hFile, &ch, sizeof(ch), &dwRealWrite, NULL);
	//写字符串
	char szText[32] = "哈喽你好";
	//bool bRet = WriteFile(hFile, szText, sizeof(szText), &dwRealWrite, NULL);
	//写入对象(二进制数据)
	Student st;
	st.age = 10;
	st.sex = 'm';
	strcpy_s(st.name, "张三");
	bool bRet = WriteFile(hFile, &st, sizeof(st), &dwRealWrite, NULL);

	if (bRet)
	{
		MessageBox(NULL, _T("数据写入成功"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("数据写入失败"), _T("提示"), NULL);
	}
	CloseHandle(hFile);

[ReadFile 函数说明 API档案]

简介:
从文件指针指向的位置开始将文件数据读入内存中, 且支持同步和异步操作。
如果文件打开方式没有指明 FILE_FLAG_OVERLAPPED 的话,当程序调用成功时,它将实际读出文件的字节数保存到 lpNumberOfBytesRead 指明的地址空间中。
从文件中读出数据,与 C语言的 fread函数相比,这个函数要灵活的多,适用的场景也很多。该函数能够操作通信设备、管道、套接字以及邮槽。

声明:

BOOL ReadFile (
   HANDLE hFile,
   LPVOID lpBuffer,
   DWORD nNumberOfBytesToRead,
   LPDWORD lpNumberOfBytesRead,
  LPOVERLAPPED lpOverlapped
);

参数:

  • hFile:需要读入数据的文件指针,这个指针指向的文件必须是 GENERIC_READ 访问属性的文件
  • lpBuffer:接收读入文件数据的缓冲区;
  • nNumberOfBytesToRead:指定要读取的字节数;
  • lpNumberOfBytesRead:指向一个DWORD类型变量的指针,用来接收实际读取的字节数(实际读取的字节数很可能比要读取的字节数小);
  • lpOverlapped:OVERLAPPED 结构体指针,如果文件是以 FILE_FLAG_OVERLAPPED 方式打开的话,那么这个指针就不能为 NULL。FILE_FLAG_OVERLAPPED 允许对文件进行重叠操作,或者说异步操作,以后会讲解到。

返回值:
函数调用成功,返回TRUE,
调用失败,返回FALSE,如果要获得更多的错误信息,请调用 GetLastError 函数。

HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_READ| GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
		return -1;
	}
	//写整数,(实际效果,是将123 的补码写到文件中)
	int num = 123;
	DWORD dwRealWrite = 0;
	//写入的数据的首地址
	bool bRet = WriteFile(hFile,&num,sizeof(num),&dwRealWrite,NULL);
	//写字符
	char ch = 'g';
	 bRet = WriteFile(hFile, &ch, sizeof(ch), &dwRealWrite, NULL);
	//写字符串
	char szText[32] = "哈喽你好";
    bRet = WriteFile(hFile, szText, sizeof(szText), &dwRealWrite, NULL);
	//写入对象(二进制数据)
	Student st;
	st.age = 10;
	st.sex = 'm';
	strcpy_s(st.name, "zhangsan");
	bRet = WriteFile(hFile, &st, sizeof(st), &dwRealWrite, NULL);

	if (bRet)
	{
		MessageBox(NULL, _T("数据写入成功"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("数据写入失败"), _T("提示"), NULL);
	}
	//由于没有关闭句柄,所以要把内存的数据写入文件
	//FlushFileBuffers(hFile);

	//文件指针已经移动到末尾,防止在同一个程序中,测试错误
	CloseHandle(hFile);


	//文件打开

	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
		return -1;
	}
	//从文件读数据
	int ret_num = 0;
	DWORD dwRealRead;
	//读整数
	bRet= ReadFile(hFile, &ret_num, sizeof(ret_num),&dwRealRead, NULL);
	//读字符
	char cha = '0';
	bRet = ReadFile(hFile, &cha, sizeof(cha), &dwRealRead, NULL);
	//读字符字符串
	char szTextRet[32] = { 0 };
	bRet = ReadFile(hFile, szTextRet, sizeof(szTextRet), &dwRealRead, NULL);

	Student stud;
	bRet = ReadFile(hFile, &stud, sizeof(Student), &dwRealRead, NULL);


	if (bRet)
	{
		MessageBox(NULL, _T("数据读取成功"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("数据读取失败"), _T("提示"), NULL);
	}
	CloseHandle(hFile);

1.6.5 文件指针

文件指针的用途:主要用于文件的读写

打开一个文件之后,默认就与之产生了一个内部的记录文件位置的指针,用于指示 当前读写的位置,当进行文件读写之后,指针自动往后移动。

读写多少个字节,指针就往后移动多少个字节。

默认情况下可能我们也不需要关心文件的指针,因为文件指针的位置是默认自动控制的。当你想从一个文件的指定位置进行读写的时候,就用到了,比如:大文件下载的断点续传。

[SetFilePointer 函数说明 API档案]

简介:
使用 SetFilePointer 函数设置文件当前的读写位置。

声明:

DWORD SetFilePointer(
	HANDLE hFile,
    LONG  lDistanceToMove,
    PLONG lpDistanceToMoveHigh,
    DWORD dwMoveMethod
 );

参数:

  • hFile:已打开的文件句柄,必须拥有 GENERIC_READ 或者 GENERIC_WRITE 访问权限;

  • lDistanceToMove:低32位的有符号的(有负有正)的值,指定要移动文件指针的字节数。

    如果lpDistanceToMoveHigh不为NULL,则lpDistanceToMoveHigh和lDistanceToMove将形成一个指定要移动的距离的单个64位有符号值。

    如果lpDistanceToMoveHigh为NULL,则lDistanceToMove是一个32位有符号值。

    lDistanceToMove的正值在文件中向前移动文件指针,负值则向后移动文件指针。

  • lpDistanceToMoveHigh:指向要移动的有符号64位距离的高32位。 如果你不需要高位的32位,这个指针必须设置为NULL。 当非NULL时,该参数还接收文件指针新值的高位DWORD。

  • dwMoveMethod:设置文件指针的移动起点的位置:

    FILE_BEGIN=>从文件头开始往后移动

    FILE_CURRENT=>从文件的当前指针位置开始移动

    FILE_END=>从文件尾部开始往前移动。

返回值:
如果SetFilePointer函数成功并且lpDistanceToMoveHigh为NULL,则返回值是新文件指针的低位DWORD。

如果lpDistanceToMoveHigh不为NULL,则函数返回新文件指针的低位DWORD,并将新文件指针的高位DWORD放入该参数指向的LONG中。

如果函数失败并且lpDistanceToMoveHigh为NULL,则返回值为INVALID_SET_FILE_POINTER。 要获得扩展的错误信息,请调用GetLastError。

如果函数失败,并且lpDistanceToMoveHigh非空,则返回值为INVALID_SET_FILE_POINTER。但是,由于INVALID_SET_FILE_POINTER是新文件指针的低位DWORD的有效值,因此必须检查GetLastError以确定是否发生错误。 如果发生错误,GetLastError返回非NO_ERROR值。
如果新文件指针的值为负值,则函数失败,文件指针不移动,GetLastError返回的代码为ERROR_NEGATIVE_SEEK。


	HANDLE hFile;
	hFile = CreateFile(
		_T("D:\test.txt"),
		GENERIC_READ| GENERIC_WRITE,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);

	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(NULL, _T("文件打开失败"), _T("提示"), NULL);
		return -1;
	}
	//写整数,(实际效果,是将123 的补码写到文件中)
	int num = 123;
	DWORD dwRealWrite = 0;
	//写入的数据的首地址
	bool bRet = WriteFile(hFile,&num,sizeof(num),&dwRealWrite,NULL);
	//写字符
	char ch = 'g';
	 bRet = WriteFile(hFile, &ch, sizeof(ch), &dwRealWrite, NULL);
	//写字符串
	char szText[32] = "哈喽你好";
    bRet = WriteFile(hFile, szText, sizeof(szText), &dwRealWrite, NULL);
	//写入对象(二进制数据)
	Student st;
	st.age = 10;
	st.sex = 'm';
	strcpy_s(st.name, "zhangsan");
	bRet = WriteFile(hFile, &st, sizeof(st), &dwRealWrite, NULL);

	if (bRet)
	{
		MessageBox(NULL, _T("数据写入成功"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("数据写入失败"), _T("提示"), NULL);
	}
	//由于没有关闭句柄,所以要把内存的数据写入文件
	FlushFileBuffers(hFile);
	//读文件和写文件都是使用的一个文件句柄
	
	DWORD dwRealRead;

	//从文件头开始读,越过一个int和一个char
	//SetFilePointer(hFile, sizeof(int) + sizeof(char), NULL,FILE_BEGIN);
	//char szTextRet[32] = { 0 };
	//bRet = ReadFile(hFile, szTextRet, sizeof(szTextRet), &dwRealRead, NULL);
	//Student stud;
	//bRet = ReadFile(hFile, &stud, sizeof(Student), &dwRealRead, NULL);
	
	//从文件末尾计算,越过一个Student和32位字符数组
	SetFilePointer(hFile, -1*(32+ (long)sizeof(Student)) , NULL, FILE_END);
	char szTextRet[32] = { 0 };
	bRet = ReadFile(hFile, szTextRet, sizeof(szTextRet), &dwRealRead, NULL);
	Student stud;
	bRet = ReadFile(hFile, &stud, sizeof(Student), &dwRealRead, NULL); 
	
	if (bRet)
	{
		MessageBox(NULL, _T("数据读取成功"), _T("提示"), NULL);
	}
	else
	{
		MessageBox(NULL, _T("数据读取失败"), _T("提示"), NULL);
	}
	CloseHandle(hFile);

1.6.6 创建、删除 目录

创建建目

简介:

CreateDirectory 函数创建一个新的单一层级的目录。 如果底层文件系统支持文件和目录的安全性,则该函数将指定的安全描述符应用于新目录。 如需自动建立多层级目录请使用:MakeSureDirectoryPathExists 函数。

声明:

BOOL CreateDirectory(  LPCTSTR lpPathName,  LPSECURITY_ATTRIBUTES lpSecurityAttributes );

参数:

  • lpPathName:指向一个以空字符结尾的字符串,指定要创建的目录的路径。
  • lpSecurityAttributes:指向SECURITY_ATTRIBUTES结构的指针。 该结构的lpSecurityDescriptor成员为新目录指定安全描述符。 如果lpSecurityAttributes为NULL,则该目录将获取默认的安全描述符。 目标文件系统必须支持文件和目录的安全性才能使此参数有效。

返回值:

如果函数成功,返回值为TRUE。 如果函数失败,返回值为FALSE。 要获得更多的错误信息,请调用GetLastError。

	bool bRet;
	bRet = CreateDirectory(_T("D:\testDirectory"),NULL);
	if (bRet)
	{
		MessageBox(NULL, _T("创建目录成功"), _T("提示"), NULL);
	}
	else
	{
		DWORD error = GetLastError();
        //1.目录存在会创建失败
        //2.多级目录会失败 "D:\testDirectory\123"
		MessageBox(NULL, _T("创建目录失败"), _T("提示"), NULL);
	} 	

删除目录

简介:

RemoveDirectory 函数删除一个现有的不包含任何文件的空目录。要删除非空目录请调用:SHFileOperation

声明:

 BOOL RemoveDirectory(  LPCTSTR lpPathName );

参数:

lpPathName:指向一个以空字符结尾的字符串,指定要删除的目录的路径。 路径必须指定一个空目录,并且调用进程必须具有对目录的删除访问权限。

返回值: 如果函数成功,返回值为TRUE。 如果函数失败,返回值为FALSE。 要获得更多的错误信息,请调用GetLastError。

    //删除目录
	bRet= RemoveDirectory(_T("D:\testDirectory"));
	if (bRet)
	{
		MessageBox(NULL, _T("删除目录成功"), _T("提示"), NULL);
	}
	else
	{
		DWORD error = GetLastError();
		MessageBox(NULL, _T("删除目录成功"), _T("提示"), NULL);
	}

1.6.7 文件查找、遍历

[FindFirstFile 函数说明 API档案]*

简介:
根据文件名查找文件。该函数到一个文件夹(包括子文件夹)去搜索指定文件,如果要使用附加属性去搜索文件的话 可以使用FindFirstFileEx函数。

声明:

HANDLE FindFirstFile(
 	LPCTSTR        lpFileName,
 	LPWIN32_FIND_DATA lpFindFileData
);

参数:

  • lpFileName:要搜索的文件名,支持通配符:

    C:Windows*.* 
    	在C:Windows目录中查找所有文件;
    C:WindowsSystem32*.dll 
    	在 C:WindowsSystem32 目录中查找所有dll类型的文件;
    C:WindowsSystem.ini 
    	在 C:Windows 目录中查找 System.ini 文件;
    C:Windowsa???.* 
    	在 C:Windows 目录中查找所有以a开头的文件名长度为4个字符的文件;
    	Test.dat 
    在当前目录查找 Test.dat 文件
    *.* 
    	在当前目录查找所有文件
    
  • lpFindFileData:WIN32_FIND_DATA 类型,该结构用于装载与找到的文件有关的信息。该结构可用于后续的遍历与搜索。

    结构 WIN32_FIND_DATA 的成员变量里包含了以上文件的各种属性:
    结构的内容如下:

typedef struct _WIN32_FIND_DATA
 {
    DWORD dwFileAttributes;        //文件属性
   	FILETIME ftCreationTime;        //文件创建时间
  	FILETIME ftLastAccessTime;        //文件最后一次访问时间
    FILETIME ftLastWriteTime;        //文件最后一次修改时间
    DWORD nFileSizeHigh;            //文件长度高32位
 	DWORD nFileSizeLow;            //文件长度低32位
    DWORD dwReserved0;            //系统保留
    DWORD dwReserved1;            //系统保留
    TCHAR cFileName[MAX_PATH];    //长文件名
    TCHAR cAlternateFileName[14];    //8.3格式文件名
 } WIN32_FIND_DATA, *PWIN32_FIND_DATA;

​ 在使用这个结构时不能手工修改这个结构中的任何数据,结构对于开发人员来说只能作为一个只读数据,其所有的成员变量都会由系统完成填写。

返回值:
如果函数成功,返回一个 HANDLE 类型的文件搜索句柄,搜索/遍历完成后,应用 FindClose 函数关闭这个句柄;
如果函数失败,返回值为 INVALID_HANDLE_VALUE。 要获得更多的错误信息,请调用GetLastError。

[FindNextFile 函数说明 API档案]

简介:

​ 可以用来遍历目录或文件,判断当前目录下是否有下一个目录或文件。 声明:

BOOL FindNextFile
    ( HANDLE hFindFile,  
     LPWIN32_FIND_DATA lpFindFileData
    ) 

参数:

  • hFindFile:上一次查找返回的搜索文件句柄
  • lpFindFileData:WIN32_FIND_DATA 类型,该结构用于装载与找到的文件有关的信息。该结构可用于后续的遍历与搜索。

结构 WIN32_FIND_DATA 的成员变量里包含了以上文件的各种属性: 结构的内容请参考:https://www.cctry.com/thread-298658-1-1.html

返回值:

​ 如果函数成功,返回值为 TRUE;

​ 如果函数失败,返回值为 FALSE,如果不再有与指定条件相符的文件,调用 GetLastError 会返回 ERROR_NO_MORE_FILES 错误码,要获得更多的错误信息,请调用GetLastError。

#include <windows.h>
#include <tchar.h>
#include <iostream>

using namespace std;
BOOL EnumFiles(LPCTSTR lpszPath, LPCTSTR lpszType)
{
	TCHAR szPath[MAX_PATH] = { 0 };
	//路径拼接
	_stprintf_s(szPath, _T("%s\%s"), lpszPath, lpszType);
	WIN32_FIND_DATA findData = { 0 };
	HANDLE hFind=FindFirstFile(szPath, &findData);
	if (hFind == INVALID_HANDLE_VALUE) return false;

	BOOL bRet = FALSE;

	do
	{
		bRet = FindNextFile(hFind, &findData);
		if (!bRet) break;

		if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)>0)
		{
			if (_tcscmp(findData.cFileName, _T(".")) || _tcscmp(findData.cFileName, _T("..")))
			{
				continue;//跳过.和..
			}
			else 
			{
				cout << "文件夹";
			}
		}
		else
		{
			cout << "文件";
		}

		//宽字节输出
		wcout << findData.cFileName << endl;

	} while (bRet);
	

	return true;
}


//控制台应用程序入口点
int _tmain(int argc, TCHAR* argv[])
{
	EnumFiles(_T("C:\Windows"), _T("*.*"));

	return 0;
}

1.6.8获取磁盘分区信息

  1. 安装软件,动态判断所选路径是否存在
  2. 写日志信息也要判断磁盘信息
  3. 远控软件的文件管理,等等

GetLogicalDrives

简介:
该函数返回当前系统中已存在的磁盘驱动器掩码值。

声明:

DWORD GetLogicalDrives();

参数:
没有参数。

返回值:
返回值为 DWORD 类型的整形变量,该变量的二进制位标志着存在哪些驱动器。其中,位0若为1表示驱动器 A: 存在于系统中;位1若为1表示存在 B: 驱动器;以次类推。

GetLogicalDriveStrings

简介:

GetLogicalDriveStrings,获取一个字串,其中包含了当前所有逻辑驱动器的根驱动器路径。

声明:

DWORD GetLogicalDriveStrings(  DWORD nBufferLength,  LPTSTR lpBuffer ); 

参数:

nBufferLength: lpBuffer 指针指向的字符串缓冲区的以 TCHAR 字符为单位的最大长度,不包括结尾的 0 结束符。如果该参数传递0,lpBuffer 就没有用处了。

lpBuffer: 指向一个字符串缓冲区的首地址,用于接收返回的字符串内容,每个驱动器之间以1个0结束符为间隔,总体结束以2个0结束符为标志。

返回值:

如果函数执行成功,返回实际拷贝到 lpBuffer 指针指向的字符串缓冲区的字符数,不包括 0 结束符。 如果 lpBuffer 指针指向的字符串缓冲区大小不够,返回值返回实际需要的字符串缓冲区的空间长度,这个长度肯定会比传入的 nBufferLength 长。

	DWORD dwRet = GetLogicalDriveStrings(0, NULL);
	if (dwRet>0)
	{
		TCHAR*pBuffer = new TCHAR[dwRet + 2];
		ZeroMemory(pBuffer, (dwRet + 2) * sizeof(TCHAR));
		dwRet = GetLogicalDriveStrings(dwRet + 2, pBuffer);
		
		if (dwRet>0)
		{
			TCHAR *pSignal = pBuffer;
			do
			{
				wcout << pSignal << endl;
				pSignal += _tcslen(pSignal) + 1;
			} while (*pSignal);
		}
	}

输出
C:
D:
E:
F:

GetDriveType

简介:
GetDriveType 函数是用来确定磁盘驱动器的类型,是可移动的、固定的、CD-ROM、RAM磁盘还是网络驱动器。

声明:

UINT GetDriveType(
 LPCTSTR lpRootPathName
);

参数:
lpRootPathName:
分区的根目录,字符串结尾要带上反斜线 ,如果该参数传递 NULL,函数使用当前目录的根目录为准。

返回值:
返回值为该磁盘分区的类型,可能有以下值:
DRIVE_UNKNOWN(0):未知的磁盘类型
DRIVE_NO_ROOT_DIR(1):说明 lpRootPathName 是无效的
DRIVE_REMOVABLE(2):可移动磁盘
DRIVE_FIXED(3):固定磁盘
DRIVE_REMOTE(4):网络磁盘
DRIVE_CDROM(5):光驱
DRIVE_RAMDISK(6):为RAM

DWORD dwRet = GetLogicalDriveStrings(0, NULL);
	if (dwRet>0)
	{
		TCHAR*pBuffer = new TCHAR[dwRet + 10];
		ZeroMemory(pBuffer, (dwRet + 10) * sizeof(TCHAR));
		dwRet = GetLogicalDriveStrings(dwRet + 10, pBuffer);
		
		if (dwRet>0)
		{
			TCHAR *pSignal = pBuffer;
			do
			{
			
				wcout << pSignal ;
				UINT nDriveType = GetDriveType(pSignal);
				switch (nDriveType)
				{
				case DRIVE_UNKNOWN:
					cout << "未知的磁盘类型" << endl;
					break;
				case DRIVE_NO_ROOT_DIR:
					cout << "无效" << endl;
					break;
				case DRIVE_REMOVABLE:
					cout << "可移动磁盘" << endl;
					break;
				case DRIVE_FIXED:
					cout << "固定磁盘" << endl;
					break;
				case DRIVE_REMOTE:
					cout << "网络磁盘" << endl;
					break;
				case DRIVE_CDROM:
					cout << "光驱" << endl;
					break;
				case DRIVE_RAMDISK:
					cout << "RAM磁盘" << endl;
					break;
				default:
					break;
				}
				pSignal += _tcslen(pSignal) + 1;
			} while (*pSignal);
		}
	}
C:固定磁盘
D:固定磁盘
E:固定磁盘
F:固定磁盘
G:可移动磁盘
原文地址:https://www.cnblogs.com/chance0x1/p/13060295.html