幾種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;
}
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等也可用來進行進程間通信,但總有種殺雞用牛刀的感覺。我並沒有再進行整理,有興趣的可以在進行了解了解。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM