网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读

源代码在线查看: 17.2.3 父进程的实现.txt

软件大小: 956 K
上传用户: kzdai22
关键词: TXT VC 目录
下载地址: 免注册下载 普通下载 VIP

相关代码

				17.2.3 父进程的实现
				下面,我们就利用匿名管道实现进程间的通信。首先实现父进程,新建一个单文档类型的MFC应用程
				序,工程取名为: Parent。首先为该工程增加一个子菜单,名称为"匿名管道"。接着,为该子菜单
				添加三个菜单项,井分别为它们添加相应的命令响应函数,本例选择CParentView类接收这些命令响
				应函数。各菜单项的E、名称,以及响应函数如表 
				17.4所示。
				表17.4添加的菜单项及相应的晌应函数
				
				ID  菜单名称 响应函数  
				IDM_PIPE_CREATE  创建管道  OnPipeCreate  
				IDM_PIPE_READ  读取数据  OnPipeRead  
				IDM_PIPE_WRITE  写入数据  OnPipeWrite  
				
				接下来,为CParentView类增加以下两个私有成员变量,即两个句柄,它们将分别作 为匿名管道的
				读写句柄来使用。
				private: 
				HANDLE hWrite, 
				HANDLE hRead, 
				
				并在CParentView类的构造函数中将它们都初始化为NULL: 
				CParentView::CParentView() 
				// TODO: add construction code here 
				hRead=NULL, 
				hWrite=NULL, 
				
				然后在CParentView类的析构函数中,如果判断出这两个变量有值,则调用CloseHandle函数关闭这
				两个变量。 
				CParentView::-CParentView() 
				if(hRead) 
				CloseHandle(hRead) ; 
				if(hWrite) 
				CloseHandle(hWrite) , 
				
				1.创建匿名管道
				现在就可以在【创建管道】菜单项命令响应函数OnPipeCreate中调用CreatePipe创建匿名管道,返
				回管道的读写句柄。代码如例 17-3所示。 
				例17-3 
				
				void CParentView: :OnPipeCreate() 
				1. SECURITY_ATTRIBUTES sa; 
				2. sa.bInheritHandle=TRUE; 
				
				3. sa.lpSecurityDescriptor=NULL; 
				
				4. sa.nLength=sizeof(SECURITY_ATTRIBUTES) ; 
				
				5. if(!CreatePipe(&hRead, &hWrite , &sa , 0)) 
				
				6. { 
				
				
				7. MessageBox ( "创建匿名管道失败!"); 
				8. return; 
				
				9. } 
				
				
				10. STARTUPINFO sui;
				11. PROCESS_INFORMATION pi; 
				12. ZeroMemory(&sui, sizeof(STARTUPINFO) ) ; 
				13. sui.cb=sizeof(STARTUPINFO); 
				
				14. sui.dwFlags=STARTF_USESTDHANDLES; 
				
				15. sui.hStdInput=hRead; 
				
				16. sui.hStdOutput=hWrite; 
				
				17. sui.hStdError=GetStdHandle(STD_ERROR_HANDLE); 
				
				18. 
				
				19. if(!CreateProcess("..\\Child\\Debug\\Child.exe" , NULL , NULL , NULL , 
				
				
				20. TRUE, 0, NULL , NULL , &sui , &pi)) 
				
				21. { 
				
				22. CloseHandle(hRead); 
				
				23. CloseHandle(hWrite); 
				
				
				24 . hRead=NULL ; 
				25. hWrite=NULL; 
				26. MessageBox("创建子进程失败!"); 
				27. return; 
				
				28. } 
				
				29. else 
				
				30. { 
				
				31. CloseHandle(pi.hProcess); 
				
				32. CloseHandle(pi.hThread); 
				
				33. } 
				
				
				在如例 17-3所示代码中,首先定义了一个安全结构体 (SECURITY_ATTRIBUTES) 类型的变量: sa,
				并对其成员分别赋值。这里需要将 bInheritHandle成员设置为 TRUE,让子进程可以继承父进程创
				建的匿名管道的读写句柄:将、安全描述符成员 ( lpSecurityDescriptor)设置为 NULL,让系统为
				创建的匿名管道赋予默认的安全描述符:长度成员(nLeng也)可以利用 sizeof函数得到 SECURITY_ 
				ATIRmUIES结构体的长度。
				然后,就调用 CreatePipe函数创建匿名管道(第 5行代码),前两个参数就是返回的管道的读取和写
				入句柄,第三个参数就是刚刚定义的安全结构的地址,最后一个参数设置为 0,让系统使用默认的
				缓冲区大小。 CreatePipe函数如果调用失败,将返回一个 0值,这时提示用户:"创建匿名管道失
				败!",并立即返回。
				如果创建匿名管道成功,就启动子进程,并将匿名管道的读写句柄传递给子进程。为了启动一个进
				程,可以调用 CreateProcess函数,根据前面的介绍,我们知道该函数的第九
				个参数需要一个 STARTUPIOFO结构体类型的值,因此上述代码中定义了一个这种结构体类型的变量: 
				sui (第 10行代码 ),因为该结构体中有多个成员,而我们只用到了其中的一小部分,那么其他的
				成员,如果没有将它们置为 0之前,它们都只是一些随机的值,如果将这些随机的值传递给 Create 
				Process函数,可能会影响该函数执行的结果,所以首先应该调用ZeroMemory函数将 sui变量中所有
				成员都设置为 0(第 12行代码)。接下来,为 sui变量的几个必要成员赋值。正如前面所说,通常在
				使用结构体变量时,都会为该结构体中表示结构体本身大小的成员赋值,因此,这里首先用 
				STARTUPIOFO结构体长度为 sui变量的 cb成员赋值:接着,设定标志成员 : dwFlags的值为 STARTF_ 
				USESTDHANDLES , 当采用此标志时,就表示当前 STARTUPIOF。这个结构体变量中的标准输入、标准
				输出和标准错误句柄这三个成员是有用的,本例将子进程的标准输入句柄、输出句柄分别设置为管
				道的读、写句柄。当调用 CreateProcess函数启动一个子进程时,它将继承父进程中所有可继承的
				己打开的句柄,但在子进程中无法知道它所继承的句柄中哪一个是管道的读句柄,哪一个是管道的
				写句柄,因为管道的读写句柄并不是通过参数,或者其他方式传递进来的,它们只是子进程从其父
				进程中继承的众多句柄中的两个。为了让子进程从众多继承的句柄中区分出管道的读、写句柄,就
				必须将子进程的特殊句柄设置为管道的读写句柄,因此这里就将子进程的标准输入和输出句柄分别
				设置为管道的读、写句柄。这样,在子进程中,只要得到了标准输入和标准输出句柄,就相当于得
				到了这个管道的读、写句柄。接下来,还要设置子进程的标准错误设备句柄,这可以通过 
				GetStdHandle函数得到,该函数可以获得标准输入、标准输出,或者一个标准错误输出句柄。该函
				数的原型声明如下: 
				HANDLE GetStdHandle(DWORD nStdHandlel; 
				GetStdHandle函数有一个 DWORD类型的参数,通过为该参数指定不同的值来得到不同的句柄。该参
				数的取值如表 17.5所示。
				表 17.5 nStdHandle参数取值
				
				STD_INPUT_HANDLE  标准输入设备句柄
				STD_OUTPUT_HANDLE  标准输出设备句柄
				STD_ERROR_HANDLE     标准错误设备句柄  
				
				由表 17.5可知,如果将 GetStdHandle函数的参数指定为 STD_INPUT_HANDLE,那么该函数返回的就
				是一个标准的错误设备句柄。应注意,这里得到的是父进程的标准错误句柄,也就是说,将子进程
				的标准错误句柄设置为父进程的标准错误句柄。虽然这个标准错误设备句柄在本程序中没有被使用,
				但读者应该清楚这里得到的是父进程的错误句柄。
				接下来,如例 17-3所示的 OnPipeCreate函数就调用 CreateProcess函数创建子进程 (第 19行代
				码)。将该函数的第一个参数设置为子进程的应用程序的文件名,这里先指定为"..\\Child\\Debug
				\\Child.exe",下面我们再编写 Child这个子进程程序,并把这个子进程程序的目录与本程序所在
				目录保持平级:第二个参数指定命令行参数,本例不需要指定,所以设置为 NULL;第三个参数和第四
				个参数分别是进程安全属性和线程安全属性,均设置为 NULL,使用默认的安全属性;第五个参数设
				置为 TRUE,让父进程的每个可继承的打
				开句柄都能被子进程继承:第六个参数是创建标志,本例并不需要指定,所以设置为 0;第七个参数
				设置为NULL,让新进程使用调用进程的环境:第八个参数把当前路径设置为 NULL,让子进程与父进
				程拥有同样的驱动器和路径:第九个参数就是刚刚定义的 STARUPINFO结构体变量:sui的地址:第
				十个参数是PROCESS_INFORMATION结构体变量指针,同样,先定义一个该结构体类型的变量: pi (第 
				11行代码),然后将该变量的地址传递给这个参数。
				程序需要对CreateProcess函数的返回值进行判断,如果该函数调用失败,将返回0值,这时就调用
				两次CloseHandle函数关闭管道的读、写句柄,并将这两个句柄设置为NULL(这是为了避免在
				CParentView类的析构函数再次调用CloseHandle函数关闭这些句柄,将它们设置为 NULL),然后提
				示用户:"创建子进程失败!"并返回:如果 CreateProcess函数调用成功,则调用 CloseHandle函数关
				闭所返回的子进程的句柄和子进程中主线程的句柄。为什么要这么做呢?前面已经提到,在创建一个
				新进程时,系统会为该进程建立一个进程内核对象和一个线程内核对象,而内核对象都有一个使用
				计数,系统会为这两个对象赋予初始的使用计数: 1,在 CreateProcess函数返回之前,它将打开创
				建的进程对象和线程对象,并将每个对象与进程和线程相关的句柄放在其最后一个参数: PROCESS_ 
				INFORMATION结构体变量的相应成员中。当 CreateProcess函数在其内部打开这些对象时,每个对象
				的使用计数就变为 2,如果在父进程中不需要使用子进程的这两个句柄,则可以调用 CloseHandle
				函数关闭它们,系统会将于进程的进程内核对象和线程内核对象的
				』
				计数减1,当子进程终止运行时,系统会再将这些使用计数减1,这时子进程的进程内核
				对象和线程内核对象的计数都变为0了,这两个内核对象就能够被释放了。所以在编程时,
				
				当不需要这些内核对象时,总是应该调用CloseHandle函数关闭它们。
				以上就是在父进程中创建匿名管道的实现代码。当子进程启动之后,父子进程间就可以通过创建的
				匿名管道来读取数据和写入数据。对于管道的读取和写入实际上是通过调用 ReadFile和WriteFile
				这两个函数完成的。前面已经提到过,这两个函数不仅能够完成对文件的读写,还可以完成对像控
				制台和管道这类对象的读写操作。 
				2.读取数据
				下面在OnPipeRead函数中实现匿名管道的读取操作,结果如例17-4所示。 
				
				例17-4 
				void CParentView::OnPipeRead(} 
				char buf [100] ; 
				DWORD dwRead; 
				if(!ReadFile(hRead,buf, 100 , &dwRead, NULL)) 
				MessageBox("读取数据失败!"}; 
				return; 
				MessageBox(buf}i 
				在实现读取操作时,首先定义了一个 char类型的字符数组: buf,用来存放将要读取到的数据。接
				着,定义了一个 DWORD类型的变量: dwRead,用来保存实际读取的字节数。接下来就是调用 ReadFile
				函数利用匿名管道的读句柄从管道中读取数据。该函数如果调用失败,将返回 0值,这时提示用户:"
				读取数据失败!";如果 ReadFile函数调用成功,则调用 MessageBox函数显示读取到的数据。 
				3.写入数据
				下面在 OnPipeWrite函数中实现医名管道的写入操作,结果如例 17-5所示。
				例 17-5 
				
				void CParentView::OnPipeWrite() 
				char buf []= " http://www. sunxin .org" ; 
				DWORD dwWrite; 
				if(!WriteFile(hWrite,buf , strlen(buf)+1 , &dwWrite, NULL)) 
				{ 
				
				MessageBox("写入数据失败!··); 
				return; 
				同样地,在实现写入操作时,首先定义了一个 char类型的字符数组: buf,其内容是一个网址:http://www.sunxin.org",就是我们将要写入的数据。接着,定义了一个 DWORD类型的变量: dwWrite,
				用来保存实际写入的字节数。接下来就是调用 WriteFile函数利用匿名管道的写入句柄向管道写入
				数据。该函数如果调用失败,将返回 O值,这时提示用户:"写入数据失败!",然后调用 retum语句
				返回。
				最后,需要利用 Build命令生成 Parent程序。
				以上就是利用匿名管道实现父子进程间通信的父进程程序的实现,在父进程中创建匿名管道,返回
				该管道的读、写句柄,然后调用 CreateProcess函数启动子进程,并且将子进程的标准输入和标准
				输出句柄设置为匿名管道的读、写句柄,相当于将该管道的读写句柄作上了一个标记,传递给子进
				程,然后在子进程中得到自己的标准输入和标准输出句柄时,相当于得到了匿名管道的读、写句柄。 			

相关资源