一、剪贴板
1、基础知识
剪贴板实际上是系统维护管理的一块内存区域,当在一个进程中复制数据时,是将这个数据放到该块内存区域中,当在另一个进程中粘贴数据时,是从该内存区域中取出数据。
2、函数说明:
(1)、BOOL OpenClipboard( )
CWnd类的OpenClipboard函数用于打开剪贴板。若打开剪贴板成功,则返回非0值。若其他程序或当前窗口已经打开了剪贴板,则该函数返回0值,表示打开失败。若某个程序已经打开了剪贴板,则其他应用程序将不能修改剪贴板,直到前者调用了CloseClipboard函数。
(2)、BOOL EmptyClipboard(void)
EmptyClipboard函数将清空剪贴板,并释放剪贴板中数据的句柄,然后将剪贴板的所有权分配给当前打开剪贴板的窗口。
(3)、HANDLE SetClipboardData(UINT uFormat, HANDLE hMem)
SetClipboardData函数是以指定的剪贴板格式向剪贴板上放置数据。uFormat指定剪贴板格式,这个格式可以是已注册的格式,或是任一种标准的剪贴板格式。CF_TEXT表示文本格式,表示每行数据以回车换行(0x0a0x0d)终止,空字符作为数据的结尾。hMem指定具有指定格式的数据的句柄。hMem参数可以是NULL,指示采用延迟提交技术,则该程序必须处理WM_RENDERFORMA T和WM_RENDERALLFORMATS消息。应用程序在调用SetClipboardData函数之后,就拥有了hMem参数所标识的数据对象,该应用程序可以读取该数据对象,但在应用程序调用CloseClipboard函数之前,它不能释放该对象的句柄,或者锁定这个句柄。若hMem标识了一个内存对象,那么这个对象必须是利用GMEM_MOVEABLE标志调用GlobalAlloc函数为其分配内存。
注意:调用SetClipboardData函数的程序必须是剪贴板的拥有者,且在这之前已经打开了剪贴板。
延迟提交技术:当一个提供数据的进程创建了剪贴板数据之后,直到其他进程获取剪贴板数据之前,这些数据都要占据内存空间。若在剪贴板上放置的数据过大,就会浪费内存空间,降低对资源的利用率。为了避免这种浪费,就可以采用延迟提交计数,也就是由数据提供进程先提供一个指定格式的空剪贴板数据块,即把SetClipboardData函数的hMem参数设置为NULL。当需要获取数据的进程想要从剪贴板上得到数据时,操作系统会向数据提供进程发送WM_RENDERFORMA T消息,而数据提供进程可以响应这个消息,并在此消息的响应函数中,再一次调用SetClipboardData函数,将实际的数据放到剪贴板上。当再次调用SetClipboardData函数时,就不再需要调用OpenClipboard函数,也不再需要调用Empty
Clipboard函数。也就是说,为了提高资源利用率,避免浪费内存空间,可以采用延迟提交技术。第一次调用SetClipboardData函数时,将其hMem参数设置为NULL,在剪贴板上以指定的剪贴板格式放置一个空剪贴板数据块。然后直到有其他进程需要数据或自身进程需要终止运行时再次调用SetClipboardData函数,这时才真正提交数据。
(4)、HGLOBAL GlobalAlloc( UINT uFlags,SIZE_T dwBytes);
GlobalAlloc函数从堆上分配指定数目的字节。uFlags是一个标记,用来指定分配内存的方式,uFlags为0,则该标记就是默认的GMEM_FIXED。dwBytes指定分配的字节数。
存中从来不被移动,但可在一个默认堆中被移动。创建一个
进程时,系统为应用程序分配一块默认堆。返回值是一块内
存对象句柄,若想将这个句柄转换为一个指针,可以使用
GlobalLock函数。这个标志不能和GMEM_FIXED标志一起
使用。
GMEM_ZEROINIT 初始化内存的内容为0
GPTR GMEM_FIXED和GMEM_ZEROINIT的组合
(5)、LPVOID GlobalLock(HGLOBAL hMem);
GlobalLock函数是对全局内存对象加锁,然后返回该对象内存块第一个字节的指针。hMem 指一个全局内存对象句柄。每个内存对象的内部数据结构中都包含了一个初始值为0的锁计数,对于可移动的内存对象来说,GlobalLock函数将其锁计数加1,而GlobalUnlock函数将锁计数减1。被锁定的内存对象的内存块将保持锁定,直到它的锁计数为0,这时,该内存块才能被移动,或者被废弃。另外,已被加锁的内存不能被移动,或者被废弃,除非调用了GlobalRealloc函数重新分配了该内存对象。对于一个进程来说,每一次调用GlobalLock函数后,最后一定要记住调用GlobalUnlock函数。使用GMEM_FIXED标志分配的内存对象其锁计数总是0。GMEM_FIXED与GMEM_MOVEABLE标志的区别:若指定的是前者,那么GlobalAlloc函数返回的句柄值就是分配的内存地址;若指定的是后者,那么GlobalAlloc 函数返回的不是实际内存的地址,而是指向该进程中句柄表条目的指针,该条目中包含有实际分配的内存指针。若一个函数的返回值为HGLOBAL类型,那么我们应该假定它的内存是采用GMEM_MOVEABLE标志来分配的,这就意味值必须调用GlobalLock函数对该全局内存对象加锁,并且返回该内存的地址。若一个函
数的参数类型为HGLOBAL,我们就应该用GMEM_MOVEABLE标志调用GlobalAlloc函数来生成这个参数值。
3、实例讲解,利用剪贴板实现通信
(1)、新建一个基于对话框的MFC程序Clipboard,设计ID为IDD_CLIPBOARD_DIALOG 的对话框资源如下:
(2)、为Send按钮添加单击命令响应函数,将发送编辑框中的内容发送到剪贴板:
void CClipboardDlg::OnBtnSend()
{
if(OpenClipboard())
{//打开剪贴板成功
CString str;//用于保存发送编辑框中的数据
HANDLE hClip;//用于保存GlobalAlloc函数分配的内存对象的句柄
char *pBuf;//用于保存调用GlobalLock函数后返回的内存地址
EmptyClipboard();//清空剪贴板,并获得剪贴板所有权
GetDlgItemText(IDC_EDIT_SEND,str);//将发生编辑框中的数据保存到str
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);//分配内存对象,若设定的是文本数据,那么该数据是以空字符结尾,所以多分配一个字节
pBuf=(char*)GlobalLock(hClip);//对内存对象加锁,将句柄转换为指针
strcpy(pBuf,str);//将str中的数据复制到pBuf指向的内存中
GlobalUnlock(hClip);//对内存块解锁
SetClipboardData(CF_TEXT,hClip);\\向剪贴板上放置数据
CloseClipboard();//关闭剪贴板
}
}
注意:在把数据放置到剪贴板之后,一定要记得调用CloseClipboard函数关闭剪贴板,否则其他进程将无法打开剪贴板。
(3)、为Recv按钮添加单击命令响应函数,从剪贴板上取出数据显示到接受编辑框中:void CClipboardDlg::OnBtnRecv()
{
if (OpenClipboard())
{//打开剪贴板成功
if (IsClipboardFormatAvailable(CF_TEXT))
{//若剪贴板中的数据是CF_TEXT格式的数据
HANDLE hClip;
char *pBuf;
hClip=GetClipboardData(CF_TEXT);//返回CF_TEXT格式存在的剪贴板对象的句柄
pBuf=(char*)GlobalLock(hClip);
GlobalUnlock(hClip);
SetDlgItemText(IDC_EDIT_RECV,pBuf);
}
CloseClipboard();
}
}
二、匿名管道
1、基础知识
匿名管道是一个未命名的单向管道,通常用来在一个父进程和一个子进程之间传输数据。匿名管道只能实现本地机器上两个父子进程间的通信,而不能实现跨网络的通信。另外,利用匿名管道还可以实现同一个进程内数据的读取和写入。
2、函数说明
(1)、BOOL CreatePipe(PHANDLE hReadPipe,
PHANDLE hWritePipe,
LPSECURITY_ATTRIBUTES lpPipeAttributes,
DWORD nSize)
CreatePipe创建一个匿名管道。hReadPipe返回管道的读取句柄,hWritePipe返回管道的写入句柄。lpPipeAttributes指向SECURITY_ATTRIBUTES结构体的指针,检测返回的句柄是否能被子进程继承,若此参数为NULL,则句柄不能被继承。nSize指定管道的缓冲区大小,该大小仅仅是一个建议值,系统将使用该值计算一个适当的缓冲区大小,若此参数为0,系统则使用默认的缓冲区大小。
(2)、typedef struct _SECURITY_A TTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
进程间通信最快的方式
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_A TTRIBUTES;
SECURITY_ATTRIBUTES结构体的nLength指定该结构体的大小。lpSecurityDescriptor指向安全描述符的指针,若该参数为NULL,则系统为创建的匿名管道赋予默认的安全描述符。bInheritHandle指定所返回的句柄是否能被一个新的进程所继承,若该参数设为TRUE,则返回的句柄能被新进程继承。
(3)、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 );
CreateProcess创建一个进程。lpApplicationName用来指定可执行程序的名称,该名称可以是程序的完整路径和文件名,也可以是部分名称,若是部分名称,CreateProcess函数就在当前路径下搜索可执行文件名,但不会使用搜索路径进行搜索。注意:一定要加上扩展名,系统将不会自动假设文件名有一个.exe扩展名。lpApplicationName可以是NULL,这时,文件名必须是lpCommandLine指向的字符串中的第一个空格界定的标记,若使用了包含空格的长文件名,那么应该使用引号将该名称包含起来,以表明文件名的结束和参数的开始,若未指定文件扩展名,它假设扩展名为exe。lpCommandLine用来指定传递给新进程的命令行字符串,系统会在该字符串的最后增加一个NULL字符,若有必要,它会去掉首尾空格,要注意的是,若在lpCommandLine中传递了一个可执行的文件名,并且没有包含路径,那么这时CreateProcess将按以下顺序搜索可执行文件:应用程序被装载的目录、父进程的目录、Windows Me/98/95的Windows目录或Windows NT/2000(32位)的System32目录或Windows NT/2000(16位)的System目录、Windows目录、PATH环境变量中列出的目录。若文件名包含全路径名,那么系统就不再搜索上述目录。lpCommandLine也可以是NULL,这时,CreateProcess函数将使用lpApplicationName
参数作为命令行。lpProcessAttributes和lpThreadAttributes用来设置新进程对象和线程对象的安全性,以及指定父进程创建的其他子进程是否可以继承这两个对象的句柄。bInheritHandles指定父进程随后创建的子进程是否能够继承父进程的对象句柄,若为TRUE,那么父进程的每个可继承的打开句柄都能被子进程继承,继承的句柄与原始的句柄拥有同样的值和访问特权。dwCreationFlags指定控件优先
级类和进程创建的附加标记,若只是为了启动子进程,并不需要设置它创建的标记,可以直接将此参数设置为0。lpEnvironment指向环境块的指针,若此参数为NULL,那么新进程使用调用进程的环境。lpCurrentDirectory指定子进程当前的路径,这个字符串必须是一个完整的路径名,包括驱动器和标识符,若此参数为NULL,那么新的子进程将与调用进程,即父进程拥有相同的驱动器和目录。lpStartupInfo指向STARTUPINFO结构体的指针,用来指定新进程的主窗口将如何显示。lpProcessInformation指向PROCESS_INFORMATION结构体的指针,用来接收关于新进程的标识信息。当启动一个进程时,系统会为此进程分配一个标识符,同时这个进程中的线程也会分配一个标识符,在一个进程运行时,该进程的标识符和线程的标识符是唯一的,但应注意,当该进程停止运行时,该进程的标识符和其线程的标识符可能会被系统分配给另一个进程和另一个线程使用,若一个函数调用依赖于进程的标识符或者线程的标识符,那么就要确保该进程当前是处于运行状态。
3、实例讲解,匿名管道实现进程间通信
(1)、新建单文档类型的MFC工程Parent。该工程实现父进程,首先为该工程添加一个子菜单,名称为Pipe。接着,为该子菜单添加三个菜单项:CreatePipe(ID为IDM_PIPE_CREATE)、PipeRead(ID为IDM_PIPE_READ)、PipeWrite(ID为IDM_PIPE_WRITE),并为它们添加相应的命令响应函数,让CParentView类接收这些命令并进行处理。
(2)、为CParentView类添加以下两个私有成员变量,它们分别为匿名管道的读写句柄:
HANDLE hWrite;
HANDLE hRead;
(3)、在CParentView类初始化hWrite和hRead为NULL。
(4)、在CParentView类的析构函数中,关闭hWrite和hRead句柄:
CParentView::~CParentView()
{
if (hRead)//NULL值一般为0
{//若hRead不为空,即hRead不为NULL值,则关闭句柄。
CloseHandle(hRead);
}
if (hWrite)
{
CloseHandle(hWrite);
}
}
(5)、为CreatePipe菜单项编写命令响应函数,用于创建匿名管道:
void CParentView::OnPipeCreate()
{
SECURITY_ATTRIBUTES sa;//定义一个安全结构体
sa.bInheritHandle=TRUE;//让子进程可以继承父进程的匿名管道的读写句柄
sa.lpSecurityDescriptor=NULL;//匿名管道赋予默认的安全描述符
sa.nLength=sizeof(SECURITY_A TTRIBUTES);//获取安全结构体的长度
if (!CreatePipe(&hRead,&hWrite,&sa,0))//创建匿名管道,并获取该管道的读写句柄,使用默认的缓冲区大小
{//创建匿名管道失败
MessageBox("Fail");
return;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论