几种Windows进程通信

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代码

#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;
}

进程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;
}

*邮件槽的实现和命名管道大同小异,都是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;
}

进程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;
}

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

打开文件-->计算文件大小-->创建内存映射对象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;
}

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的操作系统空间内核对象进行传递。

剩下的套接字,RPC,DDE等也可用来进行进程间通信,但总有种杀鸡用牛刀的感觉。我并没有再进行整理,有兴趣的可以在进行了解了解。

时间: 2024-07-30 13:45:23

几种Windows进程通信的相关文章

Windows进程通信 -- 共享内存

享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Windows 中是用 FileMapping 实现的,从具体的实现方法上看主要通过以下几步来实现: 1.调用 CreateFileMapping 创建一个内存文件映射对象: HANDLE CreateFileMapping( HANDLE hFile, // handle to file to map

Windows进程通信 -- 共享内存(1)

共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信.因为是通过内存操作实现通信,因此是一种最高效的数据交换方法. 共享内存在 Windows 中是用 FileMapping 实现的,从具体的实现方法上看主要通过以下几步来实现: 1.调用 CreateFileMapping 创建一个内存文件映射对象: HANDLE CreateFileMapping( HANDLE hFile, // handle to file to ma

Windows进程通信-共享内存空间

三个模块 1,game.exe,三个方法,控制台输入指令('A','B','R')分别控制三个方法的调用: 2,WGDll.dll,要注入到game进程中的dll文件: 3,myconsole.exe,用来注入dll文件的程序: 先开启game进程,然后用myconsole把dll注入到game,dll模块和myconsole模块利用共享内存实现进程通信,在myconsole的控制台输入指令,dllmokuai接受指令,调用game模块的方法,达到控制game的目的 game模块 #includ

windows进程通信 -- WM_COPYDATA消息

WM_COPYDATA消息,在win32中用来进行进程间的数据传输. typedef struct tagCOPYDATASTRUCT { // cds DWORD dwData; DWORD cbData; PVOID lpData; } COPYDATASTRUCT; 其中dwData为32位的自定义数据, lpData为指向数据的指针,cbData为lpData指针指向数据的大小(字节数). 一般推荐用SendMessage函数进行发送,这样就能确保在接收方复制数据前避免发送方能修改或删除

【朝花夕拾】一篇文章搞懂Android跨进程通信

前言 只要是面试中高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Android开发高级工程师必须要跨过的一道坎.如果您还对这方面的知识还做不到如数家珍,那就和我一起来攻克它吧! 本文主要包含了如下内容: 其行文脉络大致如下,希望能加深读者对这方面内容的记忆:(1)Android基于Linux系统,所以先说系统进程相关知识和Linux IPC.(2)总结Android的IPC

windows系统调用 利用事件对象实现进程通信

1 #include "iostream" 2 #include "windows.h" 3 #include "cstring" 4 using namespace std; 5 6 static LPCTSTR g_szContinueEvent="w2kdg.EventDemo.event.Continue"; 7 8 BOOL CreateChild(){ 9 TCHAR szFilename[MAX_PATH]; 1

Linux进程通信的几种方式总结

进程通信的目的 数据传输 一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据,一个进程对共享数据 通知事 一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程). 资源共享 多个进程之间共享同样的资源.为了作到这一点,需要内核提供锁和同步机制. 进程控制 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变.

*Android跨进程通信的四种方式

由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于android系统中4种应用程序组件:Activity.Content Provider.Broadcast和Service.其中Activity可以跨进程调用其他应用程序的Activity:Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应

Qt的内部进程通信机制 [转]

Qt 作为一种跨平台的基于 C++ 的 GUI系统,能够提供给用户构造图形用户界面的强大功能.自从 1996 年 Qt 被 Trolltech公司发布以来,该系统成为世界上很多成功的图形用户应用所使用的主要系统.更为重要的是,Linux 操作系统的桌面环境系统 KDE 也是基于 Qt构造的.目前,Qt 已经提供了对包括 MS/Windows.Unix/X11 和嵌入式平台的支持,得到了越来越广泛的应用. 在 Qt 系统中,不仅有着构造完善的系统结构,而且为了满足用户对编写图形用户界面应用的种种需