几种Windows进程通信

来源:互联网 发布:java 更改数据库 编辑:程序博客网 时间:2024/06/08 20:04

32位Windows采用虚拟内存技术使每个进程虚拟4G内存,在逻辑上实现了对进程之间数据代码的分离与保护。那么相应的进程之间的通信也就有必要整理掌握一下。

Windows进程间通讯的方法有很多:管道、邮件槽、剪切板、共享内存、消息、套接字、RPC、DDE等。

但是他们大部分拥有一个共同的本质:利用Windows操作系统高2GB内核共享空间进行数据传递的桥梁,所以他们都是内核对象!

所以他们大部分都要遵循:A创建对象-->A写入数据-->B打开A创建的对象-->B读入数据的规则

 

下面着重通过一些代码Demo来加深下对进程间通信的理解

 

0X01

命名管道

进程A代码

复制代码
#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[i].hEvent);        CloseHandle(Context[i].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;}
复制代码

进程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;}
复制代码

 

*其中进程A创建了管道内核对象,以及用于读写管道的双线程。B进程通过对象名打开了A创建的内核对象,同时也创建了双线程进行命名管道的读与写。

对于管道需要多说的是有一种管道是匿名管道,也就是不需要创建对象管道的名字。那么其他进程又是如何知道这个管道对象,从而实现对信息的传递的呢?

原来它是通过内核对象的可继承性进行的,也就是说匿名管道只能作用于父子进程之间,在父进程创建子进程的时候通过对CreateProcess函数中传参,即可让子进程获得父进程的内核对象句柄。

具体实现细节,请参考《Windows核心编程》内核对象一章。

 

0X02

邮件槽

进程A代码

 View Code

进程B代码

 View Code

 

*邮件槽的实现和命名管道大同小异,都是A创建对象-->A写入数据-->B打开A创建的对象-->B读入数据。以前一直认为邮件槽是Windows与Linux共有的机制,自从某次上Liunx课和老师讨论了一会进程间通信的问题,

才愚蠢的知道Linux并没有邮件槽这个机制。

 

0X03

共享内存

进程A代码

 View Code

 

进程B代码

 View Code

 

说道共享内存不得不说下内存映射:如何将一个文件映射到自己的缓冲区中。

打开文件-->计算文件大小-->创建内存映射对象Mapping-->mapofviewfile映射到自己的缓冲区中

通过文件映射来进行读写文件操作较为方便。

文件映射代码

 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);}
复制代码

进程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);}
复制代码

 

这种方式是由操作系统负责给目标窗口传递 ,所以目标进程必须需要窗口,不然A得不到窗口句柄就无法传递。这种方式是通过Windows消息队列传递,看起来与之前的内核对象传递消息有悖,

那是因为操作系统把相关细节都屏蔽掉了,如果深究起来还是通过Ring0的操作系统空间内核对象进行传递。

0 0