进程间的数据交换就会涉及到进程的通信 我们所知道的网络编程就是网络上的2个进程间的通信
一般进程间通信有4种方式 这里我们简绍其中2中
1.剪切板(Clipboard)
犹如我们可以再记事本中复制一段数据 我们可以再 MS Word中粘贴一样 剪切板就可以实现我们这样的功能 来实现进程的通信
当然 我们在复制数据时的 复制/粘贴操作也是同样的原理
我们先介绍一下有关剪切板的相关函数(函数的介绍可以详见MSDN)
OpenClipboard() 此函数用来打开一个剪切板
EmptyClipboard() 此函数用来清空打开的剪切板的内存区域
GlobalAlloc(//为剪切板分配空间
UINT uFlags, // 分配内存的标志 一般用GMEM_FIXED固定大小和 GMEM_MOVEABLE可变大小
SIZE_T dwBytes // 分配的内存大小
);
//如果我们用GMEM_MOVEABLE标志的话 我们就需要调用一下2个函数来讲数据填入剪切板
GlobalLock( HGLOBAL hMem )给剪切板加锁 参数为剪切板的句柄 返回值为指向内存地址的字符指针
GlobalUnlock( HGLOBAL hMem )剪切板解锁 参数为剪切板的句柄
SetClipboardData(//设置剪切板的数据
UINT uFormat, // 数据的模式 详见MSDN
HANDLE hMem //剪切板句柄
)
CloseClipboard()关闭剪切板 注意在使用OpenClipboard后记得调用这个函数
IsClipboardFormatAvailable(UINT uFormat) 此函数用来查看当前剪切板的数据类型 我们可以用这个函数来查看剪切板中数据是否为我们想要的类型 以作取舍
下面的代码是在剪切板中写入数据
-----------------------------------------------------------------------------------------------
//MFC源码
if (OpenClipboard())//打开剪切板
{
CString str;
HANDLE hClip;//剪切板句柄
char *pBuf;
EmptyClipboard();//清空剪切板 因为我们要给它重新赋值
GetDlgItemText(IDC_EDIT_SEND,str);//获得编辑框内容
hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);//为剪切板分配空间
pBuf=(char*)GlobalLock(hClip);//给剪切板加锁 将句柄转化为字符指针
strcpy(pBuf,str);//拷贝数据岛内存中[剪切板]
GlobalUnlock(hClip);//给剪切板解锁
SetClipboardData(CF_TEXT,hClip);//设置剪切板数据
CloseClipboard();//将数据放入剪切板后 记得调用此函数关闭剪切板 不调用这个函数的话 当前窗口就拥有了 //这个剪切板他进程无法法打开 一定要记得这2个函数配合使用
}
--------------------------------------------------------------------------------------------------
下面的代码是在剪切板中获得上面写入的数据
----------------------------------------------------------------------------------------------------------
//mfc源码
if (OpenClipboard())
{
if (IsClipboardFormatAvailable(CF_TEXT))//这里我们先判断剪切板中数据时候可以被使用(这里判断是否//为文本数据)
{
HANDLE hClip;
char *pBuf;
hClip=GetClipboardData(CF_TEXT);//获得剪切板数据
pBuf=(char*)GlobalLock(hClip);//锁定剪切板
GlobalUnlock(hClip);//解锁剪切板
CloseClipboard();//关闭剪切板 理由同上
SetDlgItemText(IDC_EDIT_RECV,pBuf);
}
2.进程间通信的第二种方式:匿名管道(Pipe)
需要注意的是 匿名管道的使用只能是父子进程之间
由于这种关系 我们先看一下如何创建一个进程
BOOL CreateProcess(
LPCTSTR lpApplicationName, // name of executable module 可执行模块的名字
LPTSTR lpCommandLine, // command line string 命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD 进程的默认安全性(是否能被继承)
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD 进程中主线程的默认安全性
BOOL bInheritHandles, // handle inheritance option 此进程是否可以继承创建其的进程的访问权限(这里用TRUE)
DWORD dwCreationFlags, // creation flags 创建标记
LPVOID lpEnvironment, // new environment block 新环境块的 如果为NULL 这是用创建进程的环境块 环境块的构造详见MSDN
LPCTSTR lpCurrentDirectory, // current directory name 当前路径名 为NULL则和创建进程一样
LPSTARTUPINFO lpStartupInfo, // startup information 新进程主窗口如何出现
LPPROCESS_INFORMATION lpProcessInformation // process information 返回进程信息
);
利用CreateProcess函数我们可以创建一个进程 第一个参数和第二个参数都可以用来指定执行的可执行文件
下面介绍和匿名通道相关的几个函数
BOOL CreatePipe(
PHANDLE hReadPipe, // read handle
PHANDLE hWritePipe, // write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes, // security attributes
DWORD nSize // pipe size
);
创建匿名管道 第一,二个参数为读写句柄
//第三个参数为安全标志 最后为管道缓冲区大小 为0这系统默认大小
//由于匿名管道只能用于父子进程间进行通信 安全性必须可以被继承
HANDLE GetStdHandle( //这个函数用来获得调用进程的标准输入 输出错误句柄
DWORD nStdHandle // input, output, or error device
);
ReadFile(),WriteFile()函数用来读出和写入数据
下面是相关的2个结构体:
SECURITY_ATTRIBUTES 安全描述符
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; //安全结构体的大小
LPVOID lpSecurityDescriptor; //安全描述符
BOOL bInheritHandle; //安全性可以被继承
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
下面是源码
/创建匿名管道
HANDLE hRead;
HANDLE hWrite;
SECURITY_ATTRIBUTES sa;//创建安全标志
sa.bInheritHandle=TRUE;//安全性可以被继承
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
//创建匿名管道 第一个参数为读写句柄
//第三个参数为安全标志 最后为管道缓冲区大小 为0这系统默认大小
//由于匿名管道只能用于父子进程间进行通信 安全性必须可以被继承
if(!CreatePipe(&hRead,&hWrite,&sa,0)){
MessageBox("匿名管道创建失败!");
return;
}
//下面创建子进程
STARTUPINFO sui;
PROCESS_INFORMATION pi;//进程信息 包含进程ID 进程主线程id 进程和主线程的句柄
ZeroMemory(&sui,sizeof(STARTUPINFO));//由于我们只用到了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))
{
MessageBox("创建子进程失败!");
CloseHandle(hWrite);
CloseHandle(hRead);
hWrite=NULL;
hRead=NULL;
return;
}else
{
CloseHandle (pi.hProcess);//关闭子进程和 子进程主线程的句柄 以减少进程的使用计数
CloseHandle(pi.hThread);
}
///冲匿名管道中读出数据
char buf[100];
DWORD dwRead;//实际读取数据数目
//读取数据
if (!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
}
MessageBox(buf);
/写入数据到匿名管道
char buf[]="www.gouou.com";
DWORD dwWrite;//实际写入的数据量
//写入数据
if (!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
}
相关代码会在后面给出