设为首页 收藏本站
查看: 679|回复: 0

[经验分享] 几种Windows进程通信

[复制链接]

尚未签到

发表于 2017-6-27 16:29:13 | 显示全部楼层 |阅读模式
  32位Windows采用虚拟内存技术使每个进程虚拟4G内存,在逻辑上实现了对进程之间数据代码的分离与保护。那么相应的进程之间的通信也就有必要整理掌握一下。
  Windows进程间通讯的方法有很多:管道、邮件槽、剪切板、共享内存、消息、套接字、RPC、DDE等。
  但是他们大部分拥有一个共同的本质:利用Windows操作系统高2GB内核共享空间进行数据传递的桥梁,所以他们都是内核对象!
  所以他们大部分都要遵循:A创建对象-->A写入数据-->B打开A创建的对象-->B读入数据的规则
  下面着重通过一些代码Demo来加深下对进程间通信的理解
  0X01
  命名管道
  进程A代码


DSC0000.gif DSC0001.gif


#define READ_PIPE   L"\\\\.\\pipe\\ReadPipe"
#define WRITE_PIPE  L"\\\\.\\pipe\\WritePipe"      //   管道命名

typedef struct _USER_CONTEXT_
{
HANDLE hPipe;
HANDLE hEvent;
}USER_CONTEXT,*PUSER_CONTEXT;

USER_CONTEXT  Context[2] = {0};
HANDLE hThread[2] = {0};
BOOL  WritePipe();
BOOL  ReadPipe();
BOOL  bOk = FALSE;
DWORD WINAPI WritePipeThread(LPVOID LPParam);
DWORD WINAPI ReadPipeThread(LPVOID LPParam);
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HANDLE hPipe = NULL;
if (WritePipe()==FALSE)
{
return -1;
}
if (ReadPipe()==FALSE)
{
return -1;
}
int iIndex = 0;
while (TRUE)
{   
if (bOk==TRUE)
{
SetEvent(Context[0].hEvent);
SetEvent(Context[1].hEvent);
Sleep(1);
}
iIndex = WaitForMultipleObjects(2,hThread,TRUE,5000);
if (iIndex==WAIT_TIMEOUT)
{
continue;
}
else
{
break;
}
}
int i = 0;
for (i=0;i<2;i++)
{
CloseHandle(Context.hEvent);
CloseHandle(Context.hPipe);
}
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
cout<<"Exit"<<endl;
return nRetCode;
}

BOOL  WritePipe()
{
HANDLE hWritePipe = NULL;
hWritePipe = CreateNamedPipe(
WRITE_PIPE,            
PIPE_ACCESS_DUPLEX,      
PIPE_TYPE_MESSAGE |   
PIPE_READMODE_MESSAGE |  
PIPE_WAIT,               
PIPE_UNLIMITED_INSTANCES,
MAX_PATH,         
MAX_PATH,
0,                     
NULL);            

if (hWritePipe==INVALID_HANDLE_VALUE)
{
return FALSE;
}
HANDLE hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
Context[0].hEvent = hEvent;
Context[0].hPipe  = hWritePipe;
hThread[0] = CreateThread(NULL,0,WritePipeThread,NULL,0,NULL);
return TRUE;
}

BOOL  ReadPipe()
{
HANDLE hReadPipe = NULL;
hReadPipe = CreateNamedPipe(
READ_PIPE,            
PIPE_ACCESS_DUPLEX,      
PIPE_TYPE_MESSAGE |   
PIPE_READMODE_MESSAGE |  
PIPE_WAIT,               
PIPE_UNLIMITED_INSTANCES,
MAX_PATH,         
MAX_PATH,
0,                     
NULL);            
if (hReadPipe==INVALID_HANDLE_VALUE)
{
return FALSE;
}
HANDLE hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
Context[1].hEvent = hEvent;
Context[1].hPipe  = hReadPipe;
hThread[1] = CreateThread(NULL,0,ReadPipeThread,NULL,0,NULL);
return TRUE;
}
DWORD WINAPI ReadPipeThread(LPVOID LPParam)
{
HANDLE hEvent     = Context[1].hEvent;
HANDLE hReadPipe  = Context[1].hPipe;
DWORD  dwReturn   = 0;
char szBuffer[MAX_PATH] = {0};
int  iIndex = 0;
while (TRUE)
{
iIndex = WaitForSingleObject(hEvent,30);
iIndex = iIndex-WAIT_OBJECT_0;
if (iIndex==WAIT_FAILED||iIndex==0)
{
break;
}
if (ReadFile(hReadPipe,szBuffer,MAX_PATH,&dwReturn,NULL))
{
szBuffer[dwReturn] = '\0';
cout<<szBuffer<<endl;
}
else
{
if (GetLastError()==ERROR_INVALID_HANDLE)
{
break;
}               
}
}
return 0;
}

DWORD WINAPI WritePipeThread(LPVOID LPParam)
{
HANDLE hEvent     = Context[0].hEvent;
HANDLE hWritePipe = Context[0].hPipe;
DWORD  dwReturn   = 0;
char szBuffer[MAX_PATH] = {0};
int  iIndex = 0;
while (TRUE)
{
iIndex = WaitForSingleObject(hEvent,30);
iIndex = iIndex-WAIT_OBJECT_0;
if (iIndex==WAIT_FAILED||iIndex==0)
{
break;
}
cin>>szBuffer;   
if (WriteFile(hWritePipe,szBuffer,strlen(szBuffer),&dwReturn,NULL))
{

}
else
{
if (GetLastError()==ERROR_INVALID_HANDLE)
{
break;
}   
}
}
return 0;
}
View Code  进程B代码





#define WRITE_PIPE   L"\\\\.\\pipe\\ReadPipe"
#define READ_PIPE  L"\\\\.\\pipe\\WritePipe"
HANDLE hThread[2] = {0};
DWORD WINAPI  ReadPipeThread(LPARAM LPParam);
DWORD WINAPI  WritePipeThread(LPARAM LPParam);
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{

HANDLE hReadPipe  = NULL;
HANDLE hWritePipe = NULL;
hThread[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ReadPipeThread,NULL,0,NULL);
hThread[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WritePipeThread,NULL,0,NULL);
WaitForMultipleObjects(2,hThread,TRUE,INFINITE);
CloseHandle(hReadPipe);
CloseHandle(hWritePipe);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
cout<<"Exit"<<endl;

return -1;
}

DWORD WINAPI WritePipeThread(LPARAM LPParam)
{
HANDLE hWritePipe = NULL;
char  szBuffer[MAX_PATH] = {0};
DWORD dwReturn = 0;
while(TRUE)
{
hWritePipe = CreateFile(WRITE_PIPE,GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,0,NULL);

if (hWritePipe==INVALID_HANDLE_VALUE)
{
continue;
}
break;
}
while (TRUE)
{
cin>>szBuffer;
if (WriteFile(hWritePipe,szBuffer,MAX_PATH,&dwReturn,NULL))
{
}
else
{
if (GetLastError()==ERROR_NO_DATA)
{
cout<<"Write Failed"<<endl;
break;
}
}

}
return 0;
}

DWORD WINAPI  ReadPipeThread(LPARAM LPParam)
{
HANDLE hReadPipe = NULL;
char  szBuffer[MAX_PATH] = {0};
DWORD dwReturn = 0;

while(TRUE)
{
hReadPipe = CreateFile(READ_PIPE,GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,0,NULL);

if (hReadPipe==INVALID_HANDLE_VALUE)
{
continue;
}
break;
}
while (TRUE)
{
if (ReadFile(hReadPipe,szBuffer,MAX_PATH,&dwReturn,NULL))
{
szBuffer[dwReturn] = '\0';
cout<<szBuffer;
}
else
{
cout<<"Read Failed"<<endl;
break;
}
}
return 0;
}
View Code  *其中进程A创建了管道内核对象,以及用于读写管道的双线程。B进程通过对象名打开了A创建的内核对象,同时也创建了双线程进行命名管道的读与写。
  对于管道需要多说的是有一种管道是匿名管道,也就是不需要创建对象管道的名字。那么其他进程又是如何知道这个管道对象,从而实现对信息的传递的呢?
  原来它是通过内核对象的可继承性进行的,也就是说匿名管道只能作用于父子进程之间,在父进程创建子进程的时候通过对CreateProcess函数中传参,即可让子进程获得父进程的内核对象句柄。
  具体实现细节,请参考《Windows核心编程》内核对象一章。
  0X02
  邮件槽
  进程A代码





#define  MAIL_SLOT_NAME  L"\\\\.\\mailslot\\Name"
HANDLE  hReadMailSlot = INVALID_HANDLE_VALUE;
DWORD WINAPI ReadMail();
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

HANDLE hReadThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ReadMail,NULL,0,NULL);
Sleep(INFINITE);
if (hReadMailSlot!=INVALID_HANDLE_VALUE)
{
CloseHandle(hReadMailSlot);
}
Sleep(10);
return nRetCode;
}

DWORD WINAPI ReadMail()
{
hReadMailSlot = CreateMailslot(MAIL_SLOT_NAME,0,0,NULL);

if (hReadMailSlot==INVALID_HANDLE_VALUE)
{
return -1;
}
//查看油槽的信息

DWORD cbMessage = 0;
DWORD cMessage  = 0;
BOOL bOk  = FALSE;
char*  szBuffer = NULL;
DWORD  dwReturn = 0;
while (TRUE)
{
bOk =  GetMailslotInfo(hReadMailSlot,NULL,&cbMessage,&cMessage,NULL);
if (bOk==FALSE)
{
break;
}
if (cMessage==0)
{
continue;
}
else
{
if (szBuffer!=NULL)
{
free(szBuffer);
szBuffer = NULL;
}
szBuffer = (char*)malloc(sizeof(char)*cbMessage+1);

if (ReadFile(hReadMailSlot,
szBuffer,
cbMessage,
&dwReturn,
NULL)==TRUE)
{
szBuffer[dwReturn] = '\0';
if (strcmp(szBuffer,"Exit")==0)
{
break;
}
cout<<szBuffer<<endl;
}        
}
}

cout<<"ReadThread Exit"<<endl;
}
View Code  进程B代码





#define  MAIL_SLOT_NAME  L"\\\\.\\mailslot\\Name"
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HANDLE hWriteMailSlot = NULL;

while(TRUE)
{
hWriteMailSlot = CreateFile(MAIL_SLOT_NAME,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,NULL);

if (hWriteMailSlot==INVALID_HANDLE_VALUE)
{
continue;
}
else
{
break;
}
}
DWORD dwReturn = 0;
char szBuffer[1024] = {0};
while (TRUE)
{
cin>>szBuffer;
if (strcmp(szBuffer,"Exit")==0)
{
break;
}
WriteFile(hWriteMailSlot,szBuffer,strlen(szBuffer),&dwReturn,NULL);
}
WriteFile(hWriteMailSlot,szBuffer,strlen(szBuffer),&dwReturn,NULL);
CloseHandle(hWriteMailSlot);
return nRetCode;
}
View Code  *邮件槽的实现和命名管道大同小异,都是A创建对象-->A写入数据-->B打开A创建的对象-->B读入数据。以前一直认为邮件槽是Windows与Linux共有的机制,自从某次上Liunx课和老师讨论了一会进程间通信的问题,
  才愚蠢的知道Linux并没有邮件槽这个机制。
  0X03
  共享内存
  进程A代码





using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
char szBuffer[] = "Shine";
HANDLE hMapping = CreateFileMapping(NULL,NULL,PAGE_READWRITE,0,4096,L"ShareMemory");

LPVOID lpBase = MapViewOfFile(hMapping,FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);

strcpy((char*)lpBase,szBuffer);

Sleep(20000);

UnmapViewOfFile(lpBase);
CloseHandle(hMapping);

return nRetCode;
}
View Code  进程B代码





int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

HANDLE hMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,L"ShareMemory");
if (hMapping)
{
wprintf(L"%s\r\n",L"Success");

LPVOID lpBase = MapViewOfFile(hMapping,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
char szBuffer[20] = {0};
strcpy(szBuffer,(char*)lpBase);

printf("%s",szBuffer);

UnmapViewOfFile(lpBase);
CloseHandle(hMapping);
}
else
{
wprintf(L"%s",L"OpenMapping Error");
}
return nRetCode;
}
View Code  说道共享内存不得不说下内存映射:如何将一个文件映射到自己的缓冲区中。
  打开文件-->计算文件大小-->创建内存映射对象Mapping-->mapofviewfile映射到自己的缓冲区中
  通过文件映射来进行读写文件操作较为方便。
  文件映射代码





int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HANDLE hFile = CreateFile(L"D:\\Demo.txt",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_WRITE|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
DWORD dwHigh = 0;
DWORD dwLow = 0;
dwLow = GetFileSize(hFile,&dwHigh);   
dwLow = ((dwLow + 4095)/4096)*4096;
if (hFile==INVALID_HANDLE_VALUE)
{
return -1;
}
HANDLE hMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE,dwHigh,dwLow,NULL);
if (hMapping==NULL)
{
CloseHandle(hFile);
}
char* szBuffer = NULL;
szBuffer = (char*)MapViewOfFile(hMapping,FILE_MAP_ALL_ACCESS,0,0,0);

if (szBuffer!=NULL)
{
cout<<szBuffer<<endl;
}
*(szBuffer+1) = 'w';

UnmapViewOfFile(szBuffer);
CloseHandle(hMapping);
CloseHandle(hFile);

return nRetCode;
}
View Code  0X04
  消息
  进程A代码





void CServerDlg::OnBnClickedOk()
{
CString  strBuffer;
m_Edit.GetWindowText(strBuffer);
if (strBuffer.GetLength()==0)
{
return;
}
COPYDATASTRUCT  Temp;
Temp.dwData = 0;      
Temp.cbData = strBuffer.GetLength()*sizeof(WCHAR);      //  sizeof    没有算  '\0'
Temp.lpData = strBuffer.GetBuffer();           

HWND hFindWindow = ::FindWindow(NULL,L"Client");
if (hFindWindow==NULL)
{
return;
}
::SendMessage(hFindWindow,WM_COPYDATA,NULL,(LPARAM)&Temp);
}
View Code  进程B代码
  进程B需要添加WM_COPYDATA消息





BOOL CClientDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (pCopyDataStruct->lpData==NULL||pCopyDataStruct->cbData==0)
{
return FALSE;
}

int nSize = 0;                           //字节20
int nLen = pCopyDataStruct->cbData+sizeof(WCHAR);    //字符HelloWorld10     加了个'\0'

WCHAR* szBuffer =  new WCHAR[nLen>>1];      //   右移一位   除以二    申请  同样大的内存
if (szBuffer==NULL)
{
return FALSE;
}
memset(szBuffer,0,sizeof(WCHAR)*(nLen>>1));
memcpy(szBuffer,pCopyDataStruct->lpData,pCopyDataStruct->cbData);
m_Edit.SetWindowText(szBuffer);

delete szBuffer;
szBuffer = NULL;
return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}
View Code  这种方式是由操作系统负责给目标窗口传递 ,所以目标进程必须需要窗口,不然A得不到窗口句柄就无法传递。这种方式是通过Windows消息队列传递,看起来与之前的内核对象传递消息有悖,
  那是因为操作系统把相关细节都屏蔽掉了,如果深究起来还是通过Ring0的操作系统空间内核对象进行传递。
  剩下的套接字,RPC,DDE等也可用来进行进程间通信,但总有种杀鸡用牛刀的感觉。我并没有再进行整理,有兴趣的可以在进行了解了解。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-388824-1-1.html 上篇帖子: Windows Server 2008 R2 IIS重装 下篇帖子: 背水一战 Windows 10 (43)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表