第十节 进程间通信
一、剪贴板
1、数据发送
BOOL OpenClipboard(); 打开剪贴板。
HANDLE SetClipboardData(UINT uFormat,HANDLE hMem); 向剪贴板中放置数据。
参数: uFormat: 指定剪贴板格式,这个格式可以是已注册的格式,或者是任一种标志的剪贴板格式。
hMem: 具有指定格式的数据的句柄。可以是NULL,指示调用窗口直到有对剪贴板数据的请求时,才提供指定剪贴板
的数据。
HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes);
参数:uFlags: 指定分配内存的方式。
dwBytes: 指定分配的字节数。
LPVOID GlobalLock(HGLOBAL hMem); 给全局内存对象加锁。 GlobalUnLock 对全局内存对象解锁。
已被加锁的内存不能被移动,或者被废除,废除调用了GlobalRealloc函数重新分配了该内存对象。
发送数据例子:
if (OpenClipboard()) 打开剪贴板
{
CString str; 保存发送的数据。
HANDLE hClip; 保存调用GlobalAlloc函数后分配的内存对象的句柄
char *pBuf; 保存调用GlobalLock函数后返回的内存地址
EmptyClipboard(); 清空剪贴板上的数据。
str="aaaaaaa";
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
pBuf=(char*)GlobalLock(hClip);
strcpy(pBuf,str);
GlobalUnlock(hClip);
SetClipboardData(CF_TEXT,hClip);
CloseClipBoard(); 关闭剪贴板。
}
在数据放置到剪贴板之后,一定要记得调用CloseClipBoard函数关闭剪贴板,否则其他进程将无法打开剪贴板。
2、接收数据
if(OpenClipoard())
{
if(IsClipboardFormatAvailable(CF_TEXT)) 检查剪贴板中是否是我们想要的数据格式。
{
HANDLE hClip;
char *pBuf;
hClip=GetClipBoardData(CF_TEXT); 从剪贴板上获得指定格式的数据。
pBuf=(char*)GlobalLock(hClip); 获得数据的内存首地址。
GlobalUnlock(hClip);
}
CloseClipboard();
}
知识点: 当一个提供数据的进程创建了剪贴板数据之后,直到其他进程获取剪贴板数据之前,这些数据都要占据内存空间。
人工在剪贴板上放置数据过大,就会浪费内存空间,降低对资源的利用率。为了避免这种浪费,九可以采用延迟
提交技术,也九是有数据提供先提供一个指定格式的空剪贴板数据库,即把SetClipboardData函数的hMen参数设
置为NULL。当需要获取数据的进程想要从剪贴板上得到数据时,操作系统会想数据提供者发送WM_RENDERFORMAT
消息,而数据提供进程可以响应这个消息,并在此消磁的响应函数中,再一次调用SetClipboardData函数,将实
际的数据放到剪贴板上,当再次调用SetClipboardData函数时,就不再需要调用OpenClipboard函数,也不再需要
调用EmptyClipboard函数。
也就是说,为了提高资源利用率,避免浪费内存空间,可以采用延迟提交技术。第一次调用SetClipboardData函数
时,将其hMem参数设置为NULL,在剪贴板上以指定的剪贴板格式放置一个空剪贴板数据块。然后直到有其他进程需要
数据或者自身进程需要终止运行时再次调用SetClipboardData函数,这时才真正提交数据。
二、匿名管道
1、基础知识
匿名管道是ygie未命名的,单向管道,通常用来在一个父进程和一个子进程之间传输数据。匿名管道只能实现本地机器上两个
进程间的通信,而不能实现跨网络的通信。
BOOL CreatePipe(PHANDLE hReadPipe,PHANDLE hWritePipe,LPSECURITY_ATTRIBUTES lpPipeAttibutes,DWORD nSize);
参数:hReadPipe和 hWritePipe: 这两个参数都是out类型,即作为返回值来使用。前者返回管道的读取句柄,后者接收
管道的写入句柄。
lpPipeAttibutes: 一个指向SECURITY_ATTRIBUTES结构体的指针,检测返回的句柄是否能被子进程继承。
nSize: 指定管道的缓冲区大小,该大小仅仅是一个建议值,系统将使用这个值来计算一个适当
的缓冲区大小,人工此参数是0,系统则使用默认的缓冲区大小。
2、进程的创建
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bINheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
参数: lpApplicationName: 一个指向NULL终止的字符串,用来指定可执行程序的名称,是要运行程序的路径。
lpCommandLine: 一个指向NULL终止的字符串,用来指定传递给新进程的命令行字符串。我们可以在
lpApplicationName参数中传递可执行文件的名称,在lpCommandLine参数中传递命令行的参数。
lpProcessAttributes和lpThreadAttributes:
系统将为新进程创建一个进程内核对象和一个线程内核对象,后者用于进程的主线程。
bINheritHandles: 该参数用来指定父进程随后创建的子进程是否能够继承父进程的对象句柄。
dwCreationFlags: 指定控件优先级类和进程创建的附加标记。
lpEnvironment: 一个指向环境块的指针,如果此参数是NULL,那么新进程使用调用进程的环境。
lpCurrentDirectory: 一个指向空终止的字符串,用来指定子进程当前的路径,这个字符串必须是一个完整的路径名。
lpStartupInfo: 一个指向STARTUPINFO结构体的指针,用来指定新进程的主窗口将如何显式。
lpProcessInformation:返回值,是一个指向PROCESS_INFORMATION结构体的指针,用来接收关于新进程的标识信息。
3、父进程的实现
private:
HANDLE hWrite;
HANDLE hRead;
hRead=NULL;
hWrite=NULL;
/**************************************创建匿名管道******************************/
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox("创建匿名管道失败!");
return;
}
/************************************创建结束***********************************/
/************************************创建子进程*********************************/
STARTUPINFO sui;
PROCESS_INFORMATION pi;
ZeroMemory(&sui,sizeof(STARTUPINFO));
sui.cb=sizeof(STARTUPINFO);
sui.dwFlags=STARTF_USESTDHANDLES;
sui.hStdInput=hRead;
sui.hStdOutput=hWrite;
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess("..\\child\\debug\\child.exe",NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead=NULL;
hWrite=NULL;
MessageBox("创建子进程失败!");
return;
}
else
{
CloseHandle(pi.hProcess); 一定不能丢,为的是内核计数器减1.
CloseHandle(pi.hThread);
}
/***********************************子进程结束************************************/
/***********************************管道读数据************************************/
char buf[100]; 存放读取的数据
DWORD dwRead; 保存实际读取的字节数。
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
/**********************************管道读取结束**********************************/
/**********************************写入数据*************************************/
char buf[]="http://www.sunxin.org"; //用于写入的数据
DWORD dwWrite; 写入数据的字节数。
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
/**********************************写入数据结束**********************************/
if(hRead)
CloseHandle(hRead);
if(hWrite)
CloseHandle(hWrite);
4、子进程的实现
private:
HANDLE hWrite;
HANDLE hRead;
hRead=NULL;
hWrite=NULL;
/**********************************获取管道的读取和写入句柄************************/
hRead=GetStdHandle(STD_INPUT_HANDLE);
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
/********************************获取管道的读取和写入句柄结束**********************/
/***********************************管道读数据************************************/
char buf[100]; 存放读取的数据
DWORD dwRead; 保存实际读取的字节数。
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
/**********************************管道读取结束**********************************/
/**********************************写入数据*************************************/
char buf[]="http://www.sunxin.org"; //用于写入的数据
DWORD dwWrite; 写入数据的字节数。
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
/**********************************写入数据结束**********************************/
if(hRead)
CloseHandle(hRead);
if(hWrite)
CloseHandle(hWrite);
注意: 匿名管道必须是父子进程间通信。
匿名管道也可以是同一个进程间读写数据。
三、命名管道
1、基础知识
命名管道通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。命名管道不仅可以在本机子上实现两个进程间的通信,
还可以跨网络实现两个进程间的通信。对同一个命名管道的实例来说,在某一时刻,它只能和一个客户端进行通信。
HANDLE CreateNamedPipe(
LPCTSTR lpName,
DWORD dwOpenMode,
DWORD dwPipeMode,
DWORD nMaxInstances,
DWORD nOutBufferSize,
DWORD nInBufferSize,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
参数:lpName: 指向空终止的字符串,该字符串的格式必须是:"\\.\pipe\pipename".其中该字符串开始是两个连续的反斜
杠,其后的圆点标识是本地机器,如果想要与远程的服务器建立连接,那么这个圆点位置处应指定这个远程
服务器的名称。接下来是"pipe"这个固定的字符串,也就是说这个字符串的内容不能修改,但其大小写是无
所谓的。最后是创建的命名管道的名称。
dwOpenMode: 指定管道的访问方式,重叠方式,写直通方式,还是管道句柄的安全访问方式。
dwPipeMode: 指定管道句柄的类型,读取和等待方式。
nMaxInstances:指定管道能够创建实例的最大数目。
nOutBufferSize:指定输出缓冲区所保留的字节数。
nInBufferSize: 指定为输入缓冲区所保留的字节数。
nDefaultTimeOut:指定默认的超时值,单位是ms,同一个管道的不同实例必须指定同样的超时值。
lpSecurityAttributes:指定命名管道的安全描述符,并确定子进程是否可以继承这个函数返回的管道句柄。
2、服务器端程序
private:
HANDLE hPipe;
hPipe=NULL;
/********************************创建命名管道***************************************/
hPipe=CreateNamePipe("\\\\.\\pipe\\MyPipe",
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX双向模式
0,1,1024,1024,0,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("创建命名管道失败!");
hPipe=NULL;
return;
}
//创建匿名的人工重置事件对象
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
MessageBox("创建事件对象失败!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap,sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
if(!ConnectNamedPipe(hPipe,&ovlap)) 等待客户端请求的到来 允许一个服务器进程等待一个客户端
{
if(ERROR_IO_PENDING!=GetLastError())
{
MessageBox("等待客户端连接失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
}
if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
{
MessageBox("等待对象失败!");
CloseHandle(hPipe);
CloseHandle(hEvent);
hPipe=NULL;
return;
}
CloseHandle(hEvent);
/*******************************创建命名管道结束************************************/
/*******************************读取数据********************************************/
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
/******************************读取数据结束*****************************************/
/******************************写入数据*********************************************/
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
/******************************写入数据结束*****************************************/
if(hPipe)
CloseHandle(hPipe);
3、客户端程序
private:
HANDLE hPipe;
hPipe=NULL;
/***************************连接命名管道*******************************************/
//判断是否有可以利用的命名管道
if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPEAIT_WAIT_FOREVER)) 第一个参数是命名管道名称,第二个超时时间
{ 同一个命名管道超时间隔必须一样。
MessageBox("当前没有可利用的命名管道实例!");
return;
}
//打开可用的命名管道,并与服务器端进程进行通信
hPipe=CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hPipe)
{
MessageBox("打开命名管道失败!");
hPipe=NULL;
return;
}
/***************************连接命名管道结束***************************************/
/*******************************读取数据*******************************************/
char buf[100];
DWORD dwRead;
if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
/*******************************读取数据结束***************************************/
/*******************************写入数据*******************************************/
char buf[]="命名管道测试程序";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
/*******************************写入数据结束***************************************/
if(hPipe)
CloseHandle(hPipe);
四、邮槽
1、基本知识
邮槽是基于广播通信体系设计出来的。它采用无连接的不可靠数据传输。邮槽是一种单向的通信机制,创建邮槽的服务器进程
读取数据,打开邮槽的客户机进程写入数据。
HANDLE CreateMailslot(
LPCTSTR lpName,
DWORD nMaxMessageSize,
DWord lReadTimeout,
LPSECURITY_ATTRIBUTES lpSecurityAttributes);
参数:lpName: 指向一个空终止字符串的指针,该字符串指定了邮槽的名称,该名称的格式必须是:"\\.\mailslot=[path]name"
其中前两个反斜杠之后字符标识服务器所在的机器名称,圆点表示是本地主机,接着是硬编码的字符串:
"mailslot",这jgie字符不能改变,但大小写无所谓,最后的字符串([path]name)就是程序员为邮槽取的名称。
nMaxMessageSize:由来指定可用被写入到邮槽的单一消息的最大尺寸,网络可用发送任一大小的消息,可用设置为0。
lReadTimeout: 指定读取操作的超时时间间隔。以ms为单位。 如果0,没有消息,函数立即返回。如果是
MAILSLOT_WAIT_FOREVER,在该函数将一直等待。直到有消息可用。
lpSecurityAttributes:创建邮槽默认安全描述。
2、服务器端程序
HANDLE hMailslot;
hMailslot=CreateMailsLot("\\\\.\\mailslot\\Mymailslot",0,MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("创建邮槽失败!");
return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
CloseHandle(hMailslot);
return;
}
MessageBox(buf);
CloseHandle(hMailslot);
3、客户端程序
HANDLE hMailslot;
hMailslot=CreateFile("\\\\.\\mailslot\\MyMailslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
MessageBox("打开邮槽失败!");
return;
}
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
//向邮槽写入数据
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
CloseHandle(hMailslot);
return;
}
CloseHandle(hMailslot);

浙公网安备 33010602011771号