C语言 :进程通信


前言

进程间通信就是在不同进程之间传播或交换信息,进程的用户空间是相互独立的,一般而言是不可以互相访问(除共享内存区外),系统空间却是“公众场所”,通过通信方法:管道(pipe)(有名管道FIFO)、消息队列、信号量(semaphore)、共享内存(shared memory)、套接字(socket),邮槽(MailSlot)等实现功能。更多详细信息点击官方连接:进程间通讯


一、邮槽(MailSlot)

1: Mailslots 概述:

邮件槽是一种单向进程间通信(IPC)机制。应用程序可以在邮件槽中存储消息。邮件槽的所有者可以检索存储在那里的消息。这些消息通常通过网络发送到指定的计算机或指定域中的所有计算机。域是共享组名称的一组工作站和服务器。

  • 邮件槽是驻留在内存中的伪文件,您可以使用标准文件函数来访问它。邮件槽消息中的数据可以是任何形式,但在计算机之间发送时不能大于424字节。与磁盘文件不同,邮件槽是临时的。当一个邮件槽的所有句柄关闭时,该邮件槽及其包含的所有数据将被删除。
  • 邮件槽服务器是创建并拥有邮件槽的进程。当服务器创建一个邮件槽时,它将接收一个邮件槽句柄。当进程从邮件槽读取消息时,必须使用此句柄。只有创建邮件槽或通过其他机制(如继承)获得句柄的进程才能从邮件槽中读取。所有邮件槽对于创建它们的进程都是本地的。进程无法创建远程邮件槽。
  • 邮件槽客户端是将消息写入邮件槽的进程。任何具有邮件槽名称的进程都可以将消息放在那里。新消息紧跟在邮件槽中任何现有消息之后。
  • 邮件插槽可以在一个域内广播消息。如果一个域中的几个进程都使用相同的名称创建了一个邮件槽,那么参与的进程将接收发送到该邮件槽并发送到该域的每个消息。由于一个进程可以同时控制服务器邮件槽句柄和在打开邮件槽进行写操作时检索到的客户端句柄,因此应用程序可以在域中轻松实现简单的消息传递工具。
  • 要在计算机之间发送大于424字节的消息,可以使用命名管道Windows套接字

2: Mailslot Names(邮槽的命名):

当进程创建邮件槽时,邮件槽名称必须具有以下形式:

\\.\mailslot\[path\]name

邮槽名称需要以下元素:开始名称的两个反斜杠、句点、句点后面的反斜杠、单词“mailslot”和后面的反斜杠。名称不区分大小写。邮槽名称前面可以有一个路径,该路径由一个或多个目录的名称组成,中间用反斜杠分隔。例如,如果用户期望收到Bob、Pete和Sue关于税收的消息,那么用户的邮槽应用程序可能允许用户为每个发送者创建单独的邮槽,如下所示:

\\.\mailslot\taxes\bobs_comments
\\.\mailslot\taxes\petes_comments
\\.\mailslot\taxes\sues_comments

若要将消息放入邮槽中,进程将按名称打开邮槽。若要写入本地计算机上的邮槽,进程可以使用与创建邮槽相同的表单的邮槽名称。然而,这种情况相对少见。更常见的情况是,您可以使用以下表单写入特定远程计算机上的邮槽:

\\ComputerName\mailslot\[path\]name

若要使用给定名称将消息放入指定域的每个邮件槽中,请使用以下方式:

\\DomainName\mailslot\[path\]name

要将消息放入系统主域中具有给定名称的每个邮件槽中,请使用以下方式:

\\*\mailslot\[path\]name

3 : 实例:

服务端进程:创建邮槽,并读取数据

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>


#define MAILSLOT "\\\\.\\mailslot\\envelope" //定义名称
#define SIZET unsigned int

int main(void)
{
	//创建邮槽句柄 参数:( 名称,数量,等待时间,安全属性[默认为NULL] )
	HANDLE hmailslot = CreateMailslotA(MAILSLOT, 0, MAILSLOT_WAIT_FOREVER, NULL);
	
	if (hmailslot == NULL)
	{
		printf("创建失败!\n");
	}
	else
	{
		while (1)
		{
			system("pause");
			DWORD dxNextSize = 0; //标识下一个
			DWORD dxMsgCount = 0; //消息数量
			DWORD dxReadCount = 0;//读取的数量
			//读取数据           文件名称          标记下一个字符串长度    多少条消息
			if (GetMailslotInfo(hmailslot, NULL, &dxNextSize,           &dxMsgCount, NULL))
			{
				for (SIZET i = 0; i < dxMsgCount; i++)
				{
					LPBYTE lpBuf = malloc(dxNextSize + 1);//分配内存
					//读取              首地址  大小         读取的个数
					ReadFile(hmailslot, lpBuf, dxNextSize, &dxReadCount, NULL);
					printf("%s\n", lpBuf);
					free(lpBuf);
				}
			}
			else
			{
				printf("Read Error!\n");
			}
		}
	}
	system("pause");
	return 0;
}

客户端进程:打开油槽写入内容:

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>


#define MAILSLOT "\\\\.\\mailslot\\envelope" //定义名称

int main(void)
{
	int n = 10;
	int wFlag = 0;
	//打开邮槽句柄 参数:( 名称,安全属性[模式],共享文件读,NULL,打开已经存在的文件模式,0,NULL )
	HANDLE hmailslot = CreateFileA(MAILSLOT, GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_EXISTING,0,NULL);

	if (hmailslot == INVALID_HANDLE_VALUE)
	{
		printf("打开失败!\n");
	}
	else
	{
		while (n--)
		{
			printf("点击写入数据\n");
			system("pause");
			char msg[50] = "ABCDEFG;12345";
			LPBYTE lpmsg = (LPBYTE)msg;//转换指针类型。
			
			//写入数据 参数:( 句柄名称,数据内存首地址,数据长度,写入成功数据数量,NULL )
			BOOL bl= WriteFile(hmailslot, lpmsg, strlen(msg)+1, &wFlag, NULL);
		}
	}

	CloseHandle(hmailslot);//关闭句柄
	system("pause");
	return 0;
}

4:相关 Win32 API :

(1)CreateMailslotA函数 与 CreateMailslot函数(winbase.h)区别是使用字节不同。

创建具有指定名称的邮筒,并返回邮筒服务器可用于对邮筒执行操作的句柄。邮槽对于创建它的计算机而言是本地的。如果具有指定名称的邮筒已经存在,则会发生错误。

API:

WINBASEAPI
HANDLE
WINAPI
CreateMailslotA(
    _In_     LPCSTR lpName,
    _In_     DWORD nMaxMessageSize,
    _In_     DWORD lReadTimeout,
    _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );

参数:

变量参数含义
lpNameLPCSTR 类型 等同于 char* 类型。邮槽名称,必须是唯一的,该名称可能包含由反斜杠分隔的多级伪目录。
nMaxMessageSizeDWORD 类型 等同于 unsigned long 类型。可以写入邮槽的单个消息的最大大小,以字节为单位。要指定消息可以是任何大小,请将此值设置为0。
lReadTimeout读取操作在超时之前等待消息写入邮件槽的时间,以毫秒为单位。以下值具有特殊含义。 0 : 如果没有消息,则立即返回。MAILSLOT_WAIT_FOREVER (系统定义常量(DWORD)-1):永远等待消息。此超时值适用于所有后续读取操作和所有继承的邮槽句柄。
lpSecurityAttributes指向SECURITY_ATTRIBUTES结构的指针 。结构的bInheritHandle成员确定子进程是否可以继承返回的句柄。如果lpSecurityAttributes为NULL,则不能继承该句柄。

返回值:
如果函数成功,则返回值是邮槽的句柄,用于服务器邮槽操作。此函数返回的句柄是异步的或重叠的。

如果函数失败,则返回值为INVALID_HANDLE_VALUE。要获取扩展的错误信息,请调用 GetLastError

(2) CreateFileA函数(fileapi.h)打开邮槽:

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

HANDLE CreateFileA(
  LPCSTR                lpFileName,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwCreationDisposition,
  DWORD                 dwFlagsAndAttributes,
  HANDLE                hTemplateFile
);

参数:

参数含义
lpFileName要创建或打开的文件或设备的名称。您可以在此名称中使用正斜杠(/)或反斜杠(\)。
dwDesiredAccess所请求的对文件或设备的访问,可以概括为读,写,两者均为零或都不为零 。最常用的值是 GENERIC_READ, GENERIC_WRITE, or both (GENERIC_READ , GENERIC_WRITE). 有关更多信息,请参见 通用访问权限 文件安全性和访问权限文件访问权限常量ACCESS_MASK
dwShareMode文件或设备的请求共享模式,可以读取,写入,两者,删除,全部或全部不共享(请参阅下表1)。对属性或扩展属性的访问请求不受此标志的影响。
lpSecurityAttributes指向SECURITY_ATTRIBUTES 结构的指针,该结构包含两个单独但相关的数据成员:一个可选的安全描述符,以及一个布尔值,该值确定子进程是否可以继承返回的句柄。此参数可以为NULL。如果此参数为NULL,则CreateFile返回的句柄 不能被应用程序可能创建的任何子进程继承,并且与返回的句柄关联的文件或设备将获得默认的安全描述符。
dwCreationDisposition对存在或不存在的文件或设备执行的操作。对于文件以外的设备,此参数通常设置为OPEN_EXISTING。(请参阅下表2)
dwFlagsAndAttributes文件或设备属性和标志,FILE_ATTRIBUTE_NORMAL是文件的最常见默认值。此参数可以包括可用文件属性(FILE_ATTRIBUTE_ *)的任意组合。所有其他文件属性将覆盖 FILE_ATTRIBUTE_NORMAL。此参数还可以包含标志的组合(FILE_FLAG_),用于控制文件或设备的缓存行为,访问模式和其他特殊用途的标志。这些与任何 FILE_ATTRIBUTE_值组合。通过指定SECURITY_SQOS_PRESENT标志,此参数还可以包含安全服务质量(SQOS)信息 。在属性和标志表之后的表中提供了其他与SQOS相关的标志信息。
hTemplateFile具有GENERIC_READ访问权限的模板文件的有效句柄。模板文件为正在创建的文件提供文件属性和扩展属性。 此参数可以为NULL。打开现有文件时,CreateFile会忽略此参数。

文件或设备的请求共享模式 表1:

含义
0(0x00000000)阻止其他进程在请求删除,读取或写入访问时打开文件或设备。
FILE_SHARE_DELETE(0x00000004)在文件或设备上启用后续打开操作以请求删除访问。否则,如果其他进程请求删除访问,则它们将无法打开文件或设备。如果未指定此标志,但是已打开文件或设备以进行删除访问,则该功能将失败。注意 删除访问权限允许删除和重命名操作。
FILE_SHARE_READ(0x00000001)在文件或设备上启用后续打开操作,以请求读取访问权限。否则,其他进程如果请求读取访问权限,则无法打开文件或设备。如果未指定此标志,但是已打开文件或设备以进行读取访问,则该功能将失败。
FILE_SHARE_WRITE(0x00000002)在文件或设备上启用后续打开操作以请求写访问权限。否则,其他进程如果请求写访问权,则无法打开文件或设备。如果未指定此标志,但是已打开文件或设备以进行写访问,或具有具有写访问权限的文件映射,则该功能将失败。

表2:

含义
CREATE_ALWAYS(2)始终创建一个新文件。如果指定的文件存在并且可写,则该函数将覆盖该文件,该函数将成功,并且上一个错误代码设置为ERROR_ALREADY_EXISTS(183)。如果指定的文件不存在并且是有效路径,则创建一个新文件,该函数成功,并且最后一个错误代码设置为零。
CREATE_NEW(1)仅在尚不存在时创建一个新文件。如果指定的文件存在,该函数将失败,并且上一个错误代码将设置为 ERROR_FILE_EXISTS(80)。如果指定的文件不存在并且是可写位置的有效路径,则会创建一个新文件。
OPEN_ALWAYS(4)始终打开文件。如果指定的文件存在,则函数成功,并且最后一个错误代码设置为 ERROR_ALREADY_EXISTS(183)。如果指定的文件不存在,并且是可写位置的有效路径,则该函数将创建一个文件,并且上一个错误代码将设置为零。
OPEN_EXISTING(3)仅打开文件或设备(如果存在)。如果指定的文件或设备不存在,该功能将失败,并且上一个错误代码将设置为 ERROR_FILE_NOT_FOUND(2)。
TRUNCATE_EXISTING(5)打开文件并将其截断,以使其大小为零字节(仅存在时)。如果指定的文件不存在,该函数将失败,并且上一个错误代码将设置为 ERROR_FILE_NOT_FOUND(2)。调用进程必须使用dwDesiredAccess参数的一部分设置GENERIC_WRITE位来打开文件。

(3)GetMailslotInfo函数(winbase.h)

检索有关指定邮槽的信息。
语法:

BOOL GetMailslotInfo(
  HANDLE  hMailslot,
  LPDWORD lpMaxMessageSize,
  LPDWORD lpNextSize,
  LPDWORD lpMessageCount,
  LPDWORD lpReadTimeout
);

参数:

参数含义
hMailslot邮槽的句柄。该 CreateMailslot功能必须创建这个句柄。
lpMaxMessageSize此邮槽允许的最大邮件大小(以字节为单位)。该值可以大于或等于在创建邮槽的CreateMailslot函数的cbMaxMsg参数中 指定的值。此参数可以为NULL。
lpNextSize下一条消息的大小,以字节为单位。以下值具有特殊含义。(MAILSLOT_NO_MESSAGE :没有下一条消息)此参数可以为NULL。
lpMessageCount函数返回时等待读取的消息总数。此参数可以为NULL。
lpReadTimeout在超时发生之前,读取操作可以等待的时间(以毫秒为单位)将消息写入邮件槽。函数返回时将填写此参数。此参数可以为NULL。

二、共享内存 (shared memory),文件映射

1.共享文件和内存概述 官方链接

文件映射可用于在两个或多个进程之间共享文件或内存。要共享文件或内存,所有进程都必须使用相同文件映射对象的名称或句柄。

  • 若要共享文件,第一个过程使用CreateFile函数创建或打开文件。接下来,它使用CreateFileMapping函数创建文件映射对象,并指定文件句柄和文件映射对象的名称。事件,信号量,互斥量,可等待计时器,作业和文件映射对象的名称共享相同的名称空间。因此,如果CreateFileMapping和OpenFileMapping函数指定另一个类型的对象正在使用的名称,则它们将失败。
  • 要共享与文件不相关的内存,进程必须使用CreateFileMapping函数并将INVALID_HANDLE_VALUE指定为hFile参数,而不是现有的文件句柄。相应的文件映射对象访问由系统页面文件支持的内存。在对CreateFileMapping的调用中指定INVALID_HANDLE_VALUE的hFile时,必须指定大于零的大小。
  • 其他进程获取第一个进程创建的文件映射对象的句柄的最简单方法是使用OpenFileMapping函数并指定对象的名称。这称为命名共享内存。如果文件映射对象没有名称,则该进程必须通过继承或复制来获取其句柄。有关继承和复制的更多信息,请参见继承。
  • 共享文件或内存的进程必须使用MapViewOfFile或MapViewOfFileEx函数创建文件视图。他们必须使用信号量,互斥量,事件或其他互斥技术来协调访问。有关更多信息,请参见同步。
  • 共享文件映射对象只有在使用它的所有进程通过使用CloseHandle函数关闭它们的句柄之后,才会被销毁。
  • 有关文件映射对象安全性的信息,请参阅文件映射安全性和访问权限

2. 实例:

代码如下(示例):

service.c

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

#define  MEM_NAME "SHARED_MEMORY"
#define  SIZE_MEM 4096

LPVOID lpData = NULL;//内存首地址

int main(void)
{
	if (lpData!=NULL)
	{
		printf("内存空间已存在\n");
	}
	else
	{
		//创建共享内存
		HANDLE hmapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE | SEC_COMMIT, 0, SIZE_MEM, MEM_NAME);
		if (hmapFile == NULL)
		{
			printf("创建失败!\n");
		}
		else
		{
			//映射内存文件到指针
			lpData = MapViewOfFile(hmapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
			if (lpData==NULL)
			{
				printf("内存指针映射失败!\n");
			}
			else
			{
				//写入内容
				//char str[256] = "You can have a mix of common and independent paging space devices in a shared memory pool.";
				//memcpy(lpData, str, strlen(str) + 1);//写入
				int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
				memcpy(lpData, arr, sizeof(arr));


				system("pause");

				UnmapViewOfFile(lpData);//解除映射
			}
			CloseHandle(hmapFile);//关闭句柄。
		}

	}

	system("pause");
	return 0;
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

#define  MEM_NAME "SHARED_MEMORY"

int main(void)
{
	//打开共享内存;参数:(只读模式,是否锁定到当前线程内,名称)
	HANDLE hmapFile = OpenFileMappingA(FILE_MAP_READ, FALSE, MEM_NAME);
	if (hmapFile == NULL)
	{
		printf("打开失败!\n");
	}
	else
	{
		//创建指针,映射内存地址
		LPVOID lptr = MapViewOfFile(hmapFile, (DWORD)FILE_MAP_READ, 0, 0, 0);
		if (lptr == NULL)
		{
			printf("映射失败!\n");
		}
		else
		{
			//printf("%s \n", (char*)lptr);
			int *ptr = lptr;
			for (int i = 0; i < 10;i++)
			{
				printf("%d \n", ptr[i]);
			}
			UnmapViewOfFile(hmapFile);//解除映射
		}
		CloseHandle(hmapFile);//关闭句柄。
	}

	system("pause");
	return 0;
}

运行实例图

关于文件映射 链接

3:相关 Win32 API:

(1)OpenFileMappingA函数(winbase.h)

句法:官方链接

HANDLE OpenFileMappingA(
  DWORD  dwDesiredAccess,
  BOOL   bInheritHandle,
  LPCSTR lpName
);
参数含义
dwDesiredAccess对文件映射对象的访问。将根据目标文件映射对象上的任何安全描述符检查此访问。有关值的列表,请参见“ 文件映射安全性和访问权限”。
bInheritHandle如果此参数为TRUE,则由CreateProcess函数创建的进程 可以继承该句柄;否则,该句柄不能被继承。
lpName要打开的文件映射对象的名称。如果使用该名称存在文件映射对象的打开句柄,并且映射对象上的安全描述符不与dwDesiredAccess参数冲突 ,则打开操作成功。该名称可以具有“ Global \”或“ Local \”前缀,以在全局或会话名称空间中显式打开对象。名称的其余部分可以包含除反斜杠字符(\)之外的任何字符。有关更多信息,请参见 内核对象命名空间。使用终端服务会话可实现快速的用户切换。第一个登录的用户使用会话0,下一个登录的用户使用会话1,依此类推。内核对象名称必须遵循为“终端服务”概述的准则,以便应用程序可以支持多个用户。

(2)MapViewOfFile函数(memoryapi.h) 官方链接

将文件映射的视图映射到调用进程的地址空间。
要为视图指定建议的基址,请使用 MapViewOfFileEx函数。但是,不建议这种做法。
句法:

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,
  DWORD  dwDesiredAccess,
  DWORD  dwFileOffsetHigh,
  DWORD  dwFileOffsetLow,
  SIZE_T dwNumberOfBytesToMap
);
参数含义
hFileMappingObject文件映射对象的句柄。该 的CreateFileMapping和 OpenFileMapping函数返回该句柄。
dwDesiredAccess对文件映射对象的访问类型,它确定页面的页面保护。该参数可以是以下值之一,也可以是多个值的按位“或”组合。(参见下表1)对于使用SEC_IMAGE属性创建的文件映射对象, dwDesiredAccess参数无效,应将其设置为任何有效值,例如 FILE_MAP_READ。
dwFileOffsetHigh视图开始处的文件偏移量的高阶DWORD。
dwFileOffsetLow视图开始处的文件偏移量的低阶DWORD。高偏移量和低偏移量的组合必须在文件映射中指定偏移量。它们还必须匹配系统的内存分配粒度。即,偏移量必须是分配粒度的倍数。要获取系统的内存分配粒度,请使用 GetSystemInfo函数,该函数将填充SYSTEM_INFO结构的成员。
dwNumberOfBytesToMap映射到视图的文件映射的字节数。所有字节必须在CreateFileMapping指定的最大大小内。如果此参数为0(零),则映射将从指定的偏移量扩展到文件映射的末尾。

表1:对文件映射对象的访问类型,它确定页面的页面保护。该参数可以是以下值之一,也可以是多个值的按位“或”组合。

含义
FILE_MAP_ALL_ACCESS映射文件的读/写视图。必须已使用PAGE_READWRITE或PAGE_EXECUTE_READWRITE保护创建了文件映射对象 。与MapViewOfFile函数一起使用时, FILE_MAP_ALL_ACCESS等效于FILE_MAP_WRITE。
FILE_MAP_READ映射文件的只读视图。尝试写入文件视图会导致访问冲突。必须使用PAGE_READONLY, PAGE_READWRITE,PAGE_EXECUTE_READ或 PAGE_EXECUTE_READWRITE保护创建文件映射对象。
FILE_MAP_WRITE映射文件的读/写视图。必须已使用PAGE_READWRITE或PAGE_EXECUTE_READWRITE保护创建了文件映射对象 。与MapViewOfFile一起使用时,(FILE_MAP_WRITE

使用按位或,可以将上面的值与这些值组合。

含义
FILE_MAP_COPY映射文件的写时复制视图。必须使用PAGE_READONLY,PAGE_READ_EXECUTE, PAGE_WRITECOPY,PAGE_EXECUTE_WRITECOPY, PAGE_READWRITE或PAGE_EXECUTE_READWRITE保护创建文件映射对象 。当流程写入写时复制页面时,系统会将原始页面复制到该流程专用的新页面。新页面由分页文件支持。新页面的保护从写时复制更改为读/写。当指定了写时复制访问权限时,由于整个调用过程都可能写入视图中的每个页面,从而使所有页面变为私有,因此对整个视图收取的系统和进程提交费用。新页面的内容永远不会写回到原始文件,并且在取消映射视图时会丢失。
FILE_MAP_EXECUTE映射文件的可执行视图(映射的内存可以作为代码运行)。必须已使用PAGE_EXECUTE_READ, PAGE_EXECUTE_WRITECOPY或PAGE_EXECUTE_READWRITE 保护创建了文件映射对象。
FILE_MAP_LARGE_PAGES从Windows 10版本1703开始,此标志指定应使用大页面支持来映射视图。视图的大小必须是GetLargePageMinimum函数报告的大页面的大小的倍数,并且文件映射对象必须已使用SEC_LARGE_PAGES选项创建。如果为lpBaseAddress提供非空值,则该值必须是GetLargePageMinimum的倍数。
FILE_MAP_TARGETS_INVALID将映射文件中的所有位置设置为Control Flow Guard(CFG)的无效目标。该标志类似于PAGE_TARGETS_INVALID。将此标志与执行访问权限FILE_MAP_EXECUTE结合使用。对这些页面中位置的任何间接调用都将失败CFG检查,并且该过程将终止。分配的可执行页的默认行为将被标记为CFG的有效调用目标。

(3)CreateFileMappingA函数(winbase.h)官方链接

句法:

HANDLE CreateFileMappingA(
  HANDLE                hFile,
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  DWORD                 flProtect,
  DWORD                 dwMaximumSizeHigh,
  DWORD                 dwMaximumSizeLow,
  LPCSTR                lpName
);

参数:

参数含义
hFile从中创建文件映射对象的文件句柄。如果hFile为INVALID_HANDLE_VALUE,则调用过程还必须在dwMaximumSizeHigh和 dwMaximumSizeLow参数中指定文件映射对象的大小。在这种情况下, CreateFileMapping创建指定大小的文件映射对象,该文件映射对象由系统页面文件而不是文件系统中的文件支持。
lpFileMappingAttributes指向SECURITY_ATTRIBUTES 结构的指针,该结构确定子进程是否可以继承返回的句柄。SECURITY_ATTRIBUTES结构的 lpSecurityDescriptor成员 为新文件映射对象指定安全描述符。如果lpFileMappingAttributes为NULL,则不能继承该句柄,并且文件映射对象将获取默认的安全描述符。文件映射对象的默认安全描述符中的访问控制列表(ACL)来自创建者的主要或模拟令牌。
flProtect指定文件映射对象的页面保护。对象的所有映射视图都必须与此保护兼容。参见下表2
dwMaximumSizeHigh文件映射对象最大大小的高阶DWORD。
dwMaximumSizeLow文件映射对象最大大小的低阶DWORD。如果此参数和dwMaximumSizeHigh为0(零),则文件映射对象的最大大小等于hFile标识的文件的当前大小 。尝试映射长度为0(零)的文件失败,错误代码为 ERROR_FILE_INVALID。应用程序应测试长度为0(零)的文件,并拒绝这些文件。
lpName文件映射对象的名称。 如果此参数与现有映射对象的名称匹配,则该函数使用flProtect指定的保护来请求访问该对象。如果此参数为NULL,则创建的文件映射对象不带名称。如果lpName与现有事件,信号量,互斥量,可等待计时器或作业对象的名称匹配,则该函数将失败,并且GetLastError 函数将返回ERROR_INVALID_HANDLE。发生这种情况是因为这些对象共享相同的名称空间。

表2:

含义
PAGE_EXECUTE_READ(0x20)允许将视图映射为只读,写时复制或执行访问。由hFile参数指定的文件句柄必须使用 GENERIC_READ和GENERIC_EXECUTE访问权限创建。
PAGE_EXECUTE_READWRITE(0x40)允许将视图映射为只读,写时复制,读/写或执行访问。必须使用GENERIC_READ,GENERIC_WRITE和 GENERIC_EXECUTE访问权限创建hFile参数指定 的文件句柄。
PAGE_EXECUTE_WRITECOPY(0x80)允许将视图映射为只读,写时复制或执行访问。该值等效于 PAGE_EXECUTE_READ。必须使用GENERIC_READ和GENERIC_EXECUTE访问权限创建hFile参数指定 的文件句柄。
PAGE_READONLY(0x02)允许将视图映射为只读或写时复制访问。尝试写入特定区域会导致访问冲突。必须使用GENERIC_READ访问权限创建hFile参数指定的文件句柄。
PAGE_READWRITE(0x04)允许将视图映射为只读,写时复制或读/写访问。必须使用GENERIC_READ和 GENERIC_WRITE访问权限创建hFile参数指定 的文件句柄。
PAGE_WRITECOPY(0x08)允许将视图映射为只读或写时复制访问。此值等效于 PAGE_READONLY。必须使用GENERIC_READ访问权限创建hFile参数指定 的文件句柄。

应用程序可以通过将文件映射对象与前面的页面保护值之一组合在一起,来指定文件映射对象的以下一个或多个属性。

含义
SEC_COMMIT(0x8000000)如果文件映射对象由操作系统页面文件支持( hfile参数为INVALID_HANDLE_VALUE),则指定当文件视图映射到进程地址空间时,将提交而不是保留整个页面范围。系统必须具有足够的可提交页面以容纳整个映射。否则, CreateFileMapping将失败。此属性对由可执行映像文件或数据文件支持的文件映射对象无效(hfile参数是文件的句柄)。SEC_COMMIT不能与SEC_RESERVE结合使用。如果未指定属性,则假定为SEC_COMMIT。
SEC_IMAGE(0x1000000)指定 hFile参数指定的文件是将不会执行的可执行映像文件,并且装入的映像文件将不会运行强制完整性检查。此外,映射使用SEC_IMAGE_NO_EXECUTE属性创建的文件映射对象的视图 将不会调用使用PsSetLoadImageNotifyRoutine 内核API注册的驱动程序回调。该SEC_IMAGE_NO_EXECUTE属性必须与组合 PAGE_READONLY页面保护价值。没有其他属性对SEC_IMAGE_NO_EXECUTE有效 。
SEC_LARGE_PAGES(0x80000000)允许将大页面用于由操作系统页面文件支持的文件映射对象(hfile参数为INVALID_HANDLE_VALUE)。由可执行映像文件或数据文件支持的文件映射对象不支持此属性(hFile参数是可执行映像或数据文件的句柄)。文件映射对象的最大大小必须是GetLargePageMinimum函数返回的大页面的最小大小的倍数。如果不是,CreateFileMapping将失败。映射使用SEC_LARGE_PAGES创建的文件映射对象的视图时,基地址和视图大小也必须是最小大页面大小的倍数。SEC_LARGE_PAGES要求 在调用者的令牌中启用SeLockMemoryPrivilege特权。
SEC_NOCACHE(0x10000000)将所有页面设置为不可缓存。除非设备明确要求,否则应用程序不应使用此属性。将互锁的功能与通过SEC_NOCACHE映射的内存一起 使用会导致 EXCEPTION_ILLEGAL_INSTRUCTION异常。SEC_NOCACHE要求设置SEC_RESERVE或 SEC_COMMIT属性。
SEC_RESERVE(0x4000000)如果文件映射对象由操作系统页面文件支持( hfile参数为INVALID_HANDLE_VALUE),则指定当文件视图映射到进程地址空间时,将保留整个页面范围供以后供进程使用而不是承诺。保留的页面可以在对VirtualAlloc函数的后续调用中提交 。提交页面后,无法使用VirtualFree函数释放或取消使用页面 。此属性对由可执行映像文件或数据文件支持的文件映射对象无效(hfile参数是文件的句柄)。SEC_RESERVE不能与SEC_COMMIT结合使用。
SEC_WRITECOMBINE(0x40000000)将所有页面设置为写合并。除非设备明确要求,否则应用程序不应使用此属性。将互锁的函数与SEC_WRITECOMBINE映射的内存一起 使用会导致 EXCEPTION_ILLEGAL_INSTRUCTION异常。SEC_WRITECOMBINE需要设置SEC_RESERVE或 SEC_COMMIT属性。

组合使用时注意 Windows 系统版本兼容性。


三、 管道通信(Pipe)

1 概述:

管道是共享存储器的部分,其处理用于通信。创建管道的过程是管道服务器。连接到管道的进程是管道客户端。一个进程将信息写入管道,然后另一个进程从管道中读取信息。管道有两种:匿名管道和命名管道。匿名管道比命名管道需要更少的开销,但是提供的服务有限(使用时着重命名管道)。详细信息参见连接:关于管道

  • 匿名管道 :一个匿名管道是一个未命名的,单向管,其通常父进程和子进程之间传输的数据。匿名管道始终位于本地;它们不能用于网络通信。
  • 命名管道:一个命名管道是一个命名的,单向或双面管道的管道服务器和一个或多个管道客户端之间的通信。命名管道的所有实例共享相同的管道名称,但是每个实例都有其自己的缓冲区和句柄,并提供用于客户端/服务器通信的单独管道。使用实例可使多个管道客户端同时使用相同的命名管道。任何进程都可以访问命名管道,但需要进行安全检查,从而使命名管道成为相关或不相关进程之间的一种简单通信方式。命名管道可用于提供同一计算机上的进程之间或整个网络上不同计算机上的进程之间的通信。如果服务器服务正在运行,则可以远程访问所有命名管道。如果打算仅在本地使用命名管道,请拒绝访问NT AUTHORITY \ NETWORK或切换到本地RPC。

2 命名管道的管道名称:

每个命名管道都有一个唯一的名称,以区别于系统命名对象列表中的其他命名管道。管道服务器在调用CreateNamedPipe函数以创建一个或多个命名管道实例时为其指定管道名称。管道客户端在调用CreateFileCallNamedPipe函数以连接到命名管道的实例时指定管道名称。

  1. 在CreateFile,WaitNamedPipe或CallNamedPipe函数中指定管道名称时,请使用以下格式:\\ServerName\pipe\PipeName 其中,ServerName是远程计算机的名称或句点,用于指定本地计算机。PipeName指定的管道名称字符串可以包含反斜杠以外的任何字符,包括数字和特殊字符。整个管道名称字符串最多可以包含256个字符。管道名称不区分大小写。
  2. 管道服务器无法在另一台计算机上创建管道,因此CreateNamedPipe必须使用句点作为服务器名称,如以下示例所示:\\.\pipe\PipeName 管道服务器可以向其管道客户端提供管道名称,以便它们可以连接到管道。管道客户端从某个持久性源(例如注册表项,文件或其他应用程序)发现管道名称。否则,客户端必须在编译时知道管道名称。

3 单一管道连接实例:

service.c

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

#define  PIPE_NAME  "\\\\.\\PIPE\\PIPELINE_COMMUNICATION" //映射设备
#define  MUTEX_NAME "CHECK_MUTEX_STATE"       //互斥量
#define  PIPE_SIZE  4096

HANDLE   h_pipe = NULL;



void check_mutex();
void create_pipeline();
void pipe_read();
void pipe_write();



void main()
{
	check_mutex();
	create_pipeline();
	puts("管道服务启动成功!\n");
	system("pause");
	pipe_write(); //相当于发送信息
	system("pause");
	pipe_read(); //相当于读取信息


	system("pause");
}


void pipe_read()
{
	char msg_buf[PIPE_SIZE] = { 0 };
	int buf_count = 0;
	if (!ReadFile(h_pipe, msg_buf, PIPE_SIZE, &buf_count, NULL))
	{
		printf("管道信息读取失败!\n");
		return;
	}
	msg_buf[buf_count] = '\0';
	printf("ReadInfo= %s\n", msg_buf);
}


void pipe_write()
{
	char msg[256] = "服务端,写入数据信息:CreateNamedPipe创建一个命名管道。返回的句柄由管道的服务器端使用。";
	int buf_count = 0;
	BOOL res = WriteFile(h_pipe, msg, strlen(msg) + 1, &buf_count, NULL);
	if (!res)
	{
		printf("管道信息写入失败!\n");
	}
	else
	{
		printf("管道信息写入成功!\n");
	}
}

void create_pipeline()
{
	h_pipe = CreateNamedPipeA(  PIPE_NAME,   //管道名称
								PIPE_ACCESS_DUPLEX, //管道读写属性
								PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,//消息模式,消息读模式,等待模式阻塞
								PIPE_UNLIMITED_INSTANCES, //最大个数
								PIPE_SIZE, //输出缓冲区大小
								PIPE_SIZE, //输入缓冲区大小
								0,         //超时 ,0:无限等待
								NULL //指定一个SECURITY_ATTRIBUTES结构,或者传递零值(将参数声明为ByVal As Long,并传递零值),以便使用不允许继承的一个默认描述符
							 );
	if (h_pipe==NULL)
	{
		printf("管道创建失败\n");
	}
	else
	{
		//链接管道
		BOOL is_connect = ConnectNamedPipe(h_pipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
		if (is_connect)
		{
			MessageBoxA(0, (LPCSTR)("管道链接成功!"), (LPCSTR)("提示"), 0);
		}
		else
		{
			MessageBoxA(0, (LPCSTR)("管道链接失败!"), (LPCSTR)("提示"), 0);
		}
	}
}

void check_mutex()
{
	HANDLE mutex = OpenMutexA(MUTEX_ALL_ACCESS, TRUE, MUTEX_NAME);
	if (mutex == NULL)
	{
		mutex = CreateMutexA(NULL, TRUE, MUTEX_NAME);
		if (mutex == NULL)
		{
			MessageBoxA(0, (LPCSTR)("句柄创建失败,程序将退出。"), (LPCSTR)("提示"), 0);
			exit(0);
		}
		printf("启动成功!\n");
	}
	else
	{
		MessageBoxA(0, (LPCSTR)("句柄已经创建,程序将退出。"), (LPCSTR)("提示"), 0);
		exit(0);
	}
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

#define  PIPE_NAME  "\\\\.\\PIPE\\PIPELINE_COMMUNICATION"
#define  PIPE_SIZE  4096

HANDLE   h_pipe = NULL;

void pipe_read();
void pipe_write();

void main()
{
	//等待连接管道
	if (!WaitNamedPipeA(PIPE_NAME,NMPWAIT_USE_DEFAULT_WAIT))
	{
		MessageBoxA(0, (LPCSTR)("管道连接失败!"), (LPCSTR)("提示"), 0);
	}
	else
	{
		printf("管道连接成功!\n");
		//连接成功,打开管道
		h_pipe = CreateFileA(   PIPE_NAME, //管道名称
								GENERIC_READ | GENERIC_WRITE, //对文件或设备的访问 读写模式
								FILE_SHARE_READ, //共享读属性(单一链接)
								NULL,			//默认安全属性
								OPEN_EXISTING,  //打开已经存在的设备
								FILE_ATTRIBUTE_NORMAL, //默认值
								NULL				   //默认值	
							);


		system("pause");
		pipe_read();
		system("pause");
		pipe_write();
	}

	system("pause");
}

void pipe_read()
{
	char msg_buf[PIPE_SIZE] = { 0 };
	int buf_count = 0;
	if (!ReadFile(h_pipe, msg_buf, PIPE_SIZE, &buf_count, NULL))
	{
		printf("管道信息读取失败!\n");
		return;
	}
	msg_buf[buf_count] = '\0';
	printf("ReadInfo= %s\n", msg_buf);
}


void pipe_write()
{
	char msg[256] = "客户端,写入数据信息:CreateFileA函数 创建或打开文件或I/O设备,返回值句柄。";
	int buf_count = 0;
	BOOL res = WriteFile(h_pipe, msg, strlen(msg) + 1, &buf_count, NULL);
	if (!res)
	{
		printf("管道信息写入失败!\n");
	}
	else
	{
		printf("管道信息写入成功!\n");
	}
}

运行:
在这里插入图片描述

在这里插入图片描述

4 并发多线程管道通信:

service.c

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

#define  PIPE_NAME  "\\\\.\\PIPE\\GENERIC_PIPELINE" //管道名称
#define  PIPE_CACHE_SIZE  0x0000 
#define  PIPE_SIZE 1024
#define  MAX_PIPE_CONNECT 128

HANDLE   h_pipe = NULL;
int		 start_begin_thread_num = 10;

typedef struct Pipe_Info
{
	HANDLE  h_thread; //线程
	HANDLE  h_pipe;   //管道
	HANDLE  h_event;  //事件

}PIPE_INFO;
PIPE_INFO pipe_st_init [MAX_PIPE_CONNECT];

BOOL start_pipe_services();
BOOL end_pipe_services();
DWORD WINAPI service_thread_fun(void *lp);

void main()
{

	start_pipe_services();


	system("pause");
}

BOOL start_pipe_services()
{
	for (int i = 0; i < start_begin_thread_num;i++)
	{
		//1 创建管道
		pipe_st_init[i].h_pipe = CreateNamedPipeA(PIPE_NAME,   //管道名称
												  PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, //管道读写属性|实时
												  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,//字节模式,字节读模式,等待模式阻塞
												  start_begin_thread_num, //最大个数
												  PIPE_CACHE_SIZE, //输出缓冲区大小 实时生效
												  PIPE_CACHE_SIZE, //输入缓冲区大小 实时生效
												  1000,         //超时 (毫秒),0:无限等待
												  NULL //指定一个SECURITY_ATTRIBUTES结构,或者传递零值(将参数声明为ByVal As Long,并传递零值),以便使用不允许继承的一个默认描述符
												 );
		if (pipe_st_init[i].h_pipe==INVALID_HANDLE_VALUE)
		{
			printf("管道【%d】创建失败\n");
			return FALSE;
		}
		//2 创建事件
		pipe_st_init[i].h_event = CreateEventA(NULL, FALSE, FALSE, FALSE);
		if (pipe_st_init[i].h_event == INVALID_HANDLE_VALUE)
		{
			printf("事件【%d】创建失败\n");
			return FALSE;
		}
		//3 创建线程
		pipe_st_init[i].h_thread = CreateThread(NULL, 0, service_thread_fun, &pipe_st_init[i], 0, NULL);
		if (pipe_st_init[i].h_thread==NULL)
		{
			printf("线程【%d】创建失败\n");
			return FALSE;
		}
	}
	puts("服务开启成功!\n");
	return TRUE;
}

DWORD WINAPI service_thread_fun(void *lp)
{
	DWORD n_read = 0;
	DWORD n_weite = 0;
	DWORD dw_byte = 0;
	char buff[PIPE_SIZE] = { 0 };
	PIPE_INFO curr_pipe = *((PIPE_INFO*)lp); //转换当前结构体参数值

	OVERLAPPED over_lap;
	{ over_lap.Internal = 0;
	  over_lap.InternalHigh = 0; 
	  over_lap.Offset = 0;
	  over_lap.OffsetHigh = 0;
	  over_lap.Pointer = 0;
	  over_lap.hEvent = curr_pipe.h_event;
	}//初始化系统结构体(包含了用于异步输入输出的信息的结构体。)


	while (TRUE)
	{
		memset(buff, 0x00, sizeof(buff));
		BOOL is_conn= ConnectNamedPipe(curr_pipe.h_pipe, &over_lap);
		WaitForSingleObject(curr_pipe.h_event, INFINITE);
		if (!GetOverlappedResult (curr_pipe.h_pipe,&over_lap,&dw_byte,TRUE))//检测IO 是否完成,完成跳出循环
		{
			break;
		}
		if (!ReadFile (curr_pipe.h_pipe,buff,PIPE_SIZE,&n_read,NULL))//读取
		{
			printf("管道数据读取失败!\n");
			break;
		}
		int n1, n2;
		sscanf(buff, "%d %d", &n1, &n2);
		memset(buff, 0x00, sizeof(buff));
		sprintf(buff, "%d", n1 + n2);

		WriteFile(curr_pipe.h_pipe, buff, strlen(buff), &n_weite, NULL);//写入
		DisconnectNamedPipe(curr_pipe.h_pipe);//断开管道连接
	}
	return 0;
}

BOOL end_pipe_services()
{
	return TRUE;
}

client.c

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>

#define  PIPE_NAME  "\\\\.\\PIPE\\GENERIC_PIPELINE"
#define  PIPE_SIZE  4096
#define  MSG_SIZE   512

HANDLE   h_pipe = NULL;
int num1 = 0, num2 = 0;

void randomizer();





void main()
{
	//等待连接管道
	if (!WaitNamedPipeA(PIPE_NAME, NMPWAIT_USE_DEFAULT_WAIT))
	{
		MessageBoxA(0, (LPCSTR)("管道连接失败!"), (LPCSTR)("提示"), 0);
	}
	else
	{
		printf("管道连接成功!\n");
		//连接成功,打开管道
		h_pipe = CreateFileA( PIPE_NAME, //管道名称
							  GENERIC_READ | GENERIC_WRITE, //对文件或设备的访问 读写模式
							  0, //共享读属性(多链接)
							  NULL,			//默认安全属性
							  OPEN_EXISTING,  //打开已经存在的设备
							  FILE_ATTRIBUTE_NORMAL, //默认值
							  NULL				   //默认值	
							);

		if (h_pipe==INVALID_HANDLE_VALUE) // INVALID_HANDLE_VALUE 无效的句柄 值=0xFFFFFFFF (-1)
		{
			printf("管道打开失败!\n");
		}
		else
		{
			int n_read = 0;
			int n_write = 0;
			randomizer();
			char msg[MSG_SIZE] = { 0 };
			sprintf(msg, "%d %d", num1, num2);
			//写入数据
			BOOL is_write= WriteFile(h_pipe, msg, strlen(msg), &n_write, NULL);
			if (!is_write)
			{
				printf("写入数据失败:num1=%d, num2=%d \n", num1, num2);
			}
			else
			{
				printf("写入数据成功:num1=%d, num2=%d \n", num1, num2);
			}

			//读取
			memset(msg, 0x00, MSG_SIZE);
			BOOL is_read= ReadFile(h_pipe, msg, MSG_SIZE, &n_read, NULL);
			if (!is_read)
			{
				printf("读取数据失败! \n");
			}
			else
			{
				int res = 0;
				sscanf(msg, "%d", &res);
				printf("读取数据成功: %d = %d + %d \n", res,num1,num2);
			}

		}
	
	}


	system("pause");
}

void randomizer()
{
	time_t ts;
	unsigned int num = (unsigned int)time(&ts);
	srand(num);
	num1 = rand() % 1000;
	num2 = rand() % 1000;

}

5 相关 Win32 API:

(1)CreateNamedPipeA函数(winbase.h). 函数连接

创建命名管道的实例,并返回用于后续管道操作的句柄。命名管道服务器进程使用此功能来创建特定命名管道的第一个实例并建立其基本属性,或者创建现有命名管道的新实例。
句法:

HANDLE CreateNamedPipeA(
  LPCSTR                lpName,
  DWORD                 dwOpenMode,
  DWORD                 dwPipeMode,
  DWORD                 nMaxInstances,
  DWORD                 nOutBufferSize,
  DWORD                 nInBufferSize,
  DWORD                 nDefaultTimeOut,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

参数:

变量含义
lpName唯一的管道名称 。名称的管道名称部分可以包含反斜杠以外的任何字符,包括数字和特殊字符。整个管道名称字符串最多可以包含256个字符。管道名称不区分大小写。
dwOpenMode打开模式。详情见下表1
dwPipeMode管道模式。详情见下表2
nMaxInstances可以为此管道创建的最大实例数。管道的第一个实例可以指定此值。必须为管道的其他实例指定相同的编号。可接受的值在1到PIPE_UNLIMITED_INSTANCES(255)之间。如果此参数为PIPE_UNLIMITED_INSTANCES,则可以创建的管道实例数仅受系统资源的可用性限制。如果nMaxInstances大于PIPE_UNLIMITED_INSTANCES,则返回值为INVALID_HANDLE_VALUE,而GetLastError返回ERROR_INVALID_PARAMETER。
nOutBufferSize要为输出缓冲区保留的字节数。
nInBufferSize为输入缓冲区保留的字节数。
nDefaultTimeOut如果WaitNamedPipe函数指定NMPWAIT_USE_DEFAULT_WAIT,则默认超时值(以毫秒为单位) 。命名管道的每个实例必须指定相同的值。零值将导致默认超时50毫秒。
lpSecurityAttributes指向SECURITY_ATTRIBUTES结构的指针,该 结构为新的命名管道指定安全描述符,并确定子进程是否可以继承返回的句柄。如果lpSecurityAttributes为NULL,则命名管道将获取默认的安全描述符,并且无法继承该句柄。命名管道的默认安全描述符中的ACL将完全控制权授予LocalSystem帐户,管理员和创建者所有者。他们还向所有人组和匿名帐户的成员授予读取权限。

相关表1:
如果dwOpenMode指定的值不是0或下表中列出的标志,则该函数将失败。
此参数必须指定以下管道访问模式之一。必须为管道的每个实例指定相同的模式。

值(模式)含义
PIPE_ACCESS_DUPLEX(0x00000003)管道是双向的;服务器和客户端进程都可以从管道读取和写入管道。此模式使服务器具有对管道的GENERIC_READ和GENERIC_WRITE访问权限。当客户端使用CreateFile函数连接到管道时,客户端可以指定GENERIC_READ或GENERIC_WRITE或两者 。
PIPE_ACCESS_INBOUND(0x00000001)管道中的数据流仅从客户端流向服务器。此模式使服务器具有对管道的GENERIC_READ访问权限。客户端在连接到管道时必须指定GENERIC_WRITE访问。如果客户端必须通过调用GetNamedPipeInfo或GetNamedPipeHandleState函数读取管道设置,则客户端在连接到管道时必须指定GENERIC_WRITE和FILE_READ_ATTRIBUTES访问。
PIPE_ACCESS_OUTBOUND(0x00000002)管道中的数据流仅从服务器流向客户端。此模式使服务器等效于对管道的GENERIC_WRITE访问。客户端在连接到管道时必须指定GENERIC_READ访问。如果客户端必须通过调用SetNamedPipeHandleState函数来更改管道设置,则客户端必须在连接到管道时指定GENERIC_READ和FILE_WRITE_ATTRIBUTES访问。

此参数还可以包括以下一个或多个标志,这些标志启用直写和重叠模式。对于同一管道的不同实例,这些模式可以不同。

值(模式)含义
FILE_FLAG_FIRST_PIPE_INSTANCE(0x00080000)如果尝试使用此标志创建管道的多个实例,则第一个实例的创建成功,但是下一个实例的创建失败,并带有ERROR_ACCESS_DENIED。
FILE_FLAG_WRITE_THROUGH(0x80000000)启用直写模式。此模式仅影响字节类型管道上的写操作,并且仅在客户端和服务器进程位于不同计算机上时才起作用。如果启用此模式,则只有在通过网络传输写入的数据并将其存储在远程计算机上的管道缓冲区中之后,写入命名管道的函数才会返回。如果未启用此模式,则系统将通过缓冲数据直到累积最少的字节数或经过最大的时间来提高网络操作的效率。
FILE_FLAG_OVERLAPPED(0x40000000)启用重叠模式。如果启用此模式,则执行读,写和连接操作可能需要很长时间才能完成的功能可以立即返回。此模式使启动操作的线程可以执行其他操作,而耗时的操作则在后台执行。例如,在重叠模式下,线程可以在管道的多个实例上处理同时的输入和输出(I / O)操作,或在同一管道句柄上执行同时的读取和写入操作。如果未启用重叠模式,则在操作完成之前,不返回在管道手柄上执行读取,写入和连接操作的功能。该 ReadFileEx和 WriteFileEx函数只能与重叠模式下的管道手柄一起使用。的 ReadFile的, WriteFile的, ConnectNamedPipe和 TransactNamedPipe功能可以执行同步或作为重叠操作。

此参数可以包括以下安全访问模式的任意组合。对于同一管道的不同实例,这些模式可以不同。

值(模式)含义
WRITE_DAC(0x00040000L)调用方将具有对命名管道的任意访问控制列表(ACL)的写访问权。
WRITE_OWNER(0x00080000L)调用方将具有对命名管道所有者的写访问权。
ACCESS_SYSTEM_SECURITY(0x01000000L)调用方将具有对命名管道的SACL的写访问权。有关更多信息,请参见 访问控制列表(ACL)和 SACL访问权限。

相关表2:
如果dwPipeMode指定的值不是0或下表中列出的标志,则该函数将失败。可以指定以下类型模式之一。必须为管道的每个实例指定相同的类型模式。

值(模式)含义
PIPE_TYPE_BYTE(0x00000000)数据作为字节流写入管道。此模式不能与PIPE_READMODE_MESSAGE一起使用。管道不区分在不同写入操作期间写入的字节。
PIPE_TYPE_MESSAGE(0x00000004)数据作为消息流写入管道。管道将在每次写操作期间写的字节视为消息单元。当未完全读取消息时,GetLastError函数将返回ERROR_MORE_DATA。此模式可以与PIPE_READMODE_MESSAGE或PIPE_READMODE_BYTE一起使用。

可以指定以下读取模式之一。同一管道的不同实例可以指定不同的读取模式。

值(模式)含义
PIPE_READMODE_BYTE(0x00000000)从管道读取数据作为字节流。此模式可以与PIPE_TYPE_MESSAGE或PIPE_TYPE_BYTE一起使用。
PIPE_READMODE_MESSAGE(0x00000002)从管道读取数据作为消息流。仅当还指定了PIPE_TYPE_MESSAGE时,才可以使用此模式。

可以指定以下等待模式之一。同一管道的不同实例可以指定不同的等待模式。

值(模式)含义
PIPE_WAIT(0x00000000)阻止模式已启用。当在ReadFile,WriteFile或 ConnectNamedPipe函数中指定了管道句柄时 , 直到有要读取的数据,写入的所有数据或连接了客户端后,操作才能完成。使用此模式可能意味着在某些情况下将无限期等待客户端进程执行操作。
PIPE_NOWAIT(0x00000001)非阻塞模式已启用。在这种模式下,ReadFile,WriteFile和 ConnectNamedPipe总是立即返回。

可以指定以下远程客户端模式之一。同一管道的不同实例可以指定不同的远程客户端模式。

值(模式)含义
PIPE_ACCEPT_REMOTE_CLIENTS(0x00000000)可以接受来自远程客户端的连接,并根据管道的安全描述符进行检查。
PIPE_REJECT_REMOTE_CLIENTS(0x00000008)来自远程客户端的连接将被自动拒绝。

返回值:
如果函数成功,则返回值是命名管道实例的服务器端的句柄。如果函数失败,则返回值为INVALID_HANDLE_VALUE。要获取扩展的错误信息,请调用 GetLastError

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值