一、基础知识
1.匿名管道
匿名管道是一个未命名的、单向管道,通常用来在一个父进程和一个子进程之间传输数据。匿名管道只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。
二、从代码进行分析
1.父进程
//管道的创建
void CParentView::OnPipeCreate()
{
// TODO: Add your command handler code here
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
//此处创建管道,hRead和hWrite分别为 从管道读取数据的句柄 和 向管道写入数据的句柄
if (!CreatePipe(&hRead,&hWrite,&sa,0))
{
::MessageBox(m_hWnd,_T("创建管道失败!"),NULL,MB_OK);
return;
}
STARTUPINFO sui;
ZeroMemory(&sui,sizeof(STARTUPINFO));//此函数用来给 STARTUPINFO数据结构开辟内存
sui.dwFlags = STARTF_USESTDHANDLES;
sui.hStdInput = hRead;
sui.hStdOutput = hWrite;
sui.cb = sizeof(STARTUPINFO);
sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);
PROCESS_INFORMATION pi;
//访问指定文件夹中的exe文件,利用此文件来启动子进程
if (!CreateProcess(_T("..\\Debug\\Child.exe"),NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead = NULL;
hWrite = NULL;
::MessageBox(m_hWnd,_T("创建子进程失败!"),NULL,MB_OK);
return;
}
else
{
//系统首先为新创建的进程创建一个进程内核对象和一个线程内核对象,但是在CreateProcess函数返回之前
//此函数会去访问新的进程内核对象和线程内核对象,在其进行访问的时候,就给这两个内核对象的内核计数
//加一,所以先用 CloseHandle给这两个对象各减一,等进程停止运行的时候,需要系统再为这两个对象减一
//此时这两个内核对象的计数都为0,就可以被释放了
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
//从管道读取数据
void CParentView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if (!ReadFile(hRead,buf,100,&dwRead,NULL))
{
::MessageBox(m_hWnd,_T("读取数据失败!"),NULL,MB_OK);
return;
}
::MessageBox(m_hWnd,(LPCTSTR)buf,NULL,MB_OK);
}
//向管道写入数据
void CParentView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[] = "http://www.sunxin.org";
DWORD dwWrite;
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
::MessageBox(m_hWnd,_T("写入数据失败!"),NULL,MB_OK);
return;
}
}
2.子进程
//从管道读取数据
void CChildView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if (!ReadFile(hRead,buf,100,&dwRead,NULL))
{
::MessageBox(m_hWnd,_T("读取数据失败!"),NULL,MB_OK);
return;
}
::MessageBox(m_hWnd,(LPCTSTR)buf,NULL,MB_OK);
}
//向管道写入数据
void CChildView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[] = "子进程写入数据";
DWORD dwWrite;
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
::MessageBox(m_hWnd,_T("写入数据失败!"),NULL,MB_OK);
return;
}
}
//得到管道的读写句柄
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
hRead = GetStdHandle(STD_INPUT_HANDLE);
hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
}
1.父进程
//管道的创建
void CParentView::OnPipeCreate()
{
// TODO: Add your command handler code here
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
//此处创建管道,hRead和hWrite分别为 从管道读取数据的句柄 和 向管道写入数据的句柄
if (!CreatePipe(&hRead,&hWrite,&sa,0))
{
::MessageBox(m_hWnd,_T("创建管道失败!"),NULL,MB_OK);
return;
}
STARTUPINFO sui;
ZeroMemory(&sui,sizeof(STARTUPINFO));//此函数用来给 STARTUPINFO数据结构开辟内存
sui.dwFlags = STARTF_USESTDHANDLES;
sui.hStdInput = hRead;
sui.hStdOutput = hWrite;
sui.cb = sizeof(STARTUPINFO);
sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);
PROCESS_INFORMATION pi;
//访问指定文件夹中的exe文件,利用此文件来启动子进程
if (!CreateProcess(_T("..\\Debug\\Child.exe"),NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead = NULL;
hWrite = NULL;
::MessageBox(m_hWnd,_T("创建子进程失败!"),NULL,MB_OK);
return;
}
else
{
//系统首先为新创建的进程创建一个进程内核对象和一个线程内核对象,但是在CreateProcess函数返回之前
//此函数会去访问新的进程内核对象和线程内核对象,在其进行访问的时候,就给这两个内核对象的内核计数
//加一,所以先用 CloseHandle给这两个对象各减一,等进程停止运行的时候,需要系统再为这两个对象减一
//此时这两个内核对象的计数都为0,就可以被释放了
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
//从管道读取数据
void CParentView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if (!ReadFile(hRead,buf,100,&dwRead,NULL))
{
::MessageBox(m_hWnd,_T("读取数据失败!"),NULL,MB_OK);
return;
}
::MessageBox(m_hWnd,(LPCTSTR)buf,NULL,MB_OK);
}
//向管道写入数据
void CParentView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[] = "http://www.sunxin.org";
DWORD dwWrite;
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
::MessageBox(m_hWnd,_T("写入数据失败!"),NULL,MB_OK);
return;
}
}
2.子进程
//从管道读取数据
void CChildView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if (!ReadFile(hRead,buf,100,&dwRead,NULL))
{
::MessageBox(m_hWnd,_T("读取数据失败!"),NULL,MB_OK);
return;
}
::MessageBox(m_hWnd,(LPCTSTR)buf,NULL,MB_OK);
}
//向管道写入数据
void CChildView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[] = "子进程写入数据";
DWORD dwWrite;
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
::MessageBox(m_hWnd,_T("写入数据失败!"),NULL,MB_OK);
return;
}
}
//得到管道的读写句柄
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
hRead = GetStdHandle(STD_INPUT_HANDLE);
hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
}
三、OnInitialUpdate函数的引申
关于构造函数,Create,OnCreate,
时间上,调用先后顺序不同,构造函数生成本类的对象,但没有产生窗口,Create产生了窗口但没产生窗口里的内容,
Create,OnCreate,OnInitalUpdate调用时机。
Create实际创建窗口
Create函数创建窗口的实质是,产生WM_CREATE消息,而响应WM_CREATE消息的是OnCreate函数。
Create函数先调用,OnCreate函数后调用。
在Create调用完成后将产生视图基本结构,主窗口将向所有子窗口发送WM_INITIALUPDATE消息,次消息将引起视图类虚函数OnInitialUpdate调用。
四、需要掌握的函数
(1)CreateProcess
(2)CreatePipe
(3)ZeroMemory
(4)GetStdHandle
(5)CloseHandle
(6)ReadFile
(7)WriteFile