Windows平台为我们提供了多种进程间通信的机制,主要包括:注册表方式、共享文件方式、共享内存方式、共享数据段、映射文件方式、管道方式、 剪贴板方式、消息方式。其中注册表方式需要增加注册表表项,而注册表一般不应删改,所以此种方式不被推荐;共享数据段需要借助动态链接库,实现起来比较麻 烦,这种方式也不被推荐。下面重点介绍一下其它几种进程间通信的实现方式。

1.共享文件方式

(1)数据发送

数据发送进程为通过Cfile类创建一个共享文件,然后调用Write()方法想文件中写入数据,具体如下:

void CSendDlg::OnSend()

{

//TODO: 在此添加控件通知处理程序代码

UpdateData(TRUE);                              //更新数据

CFile file;

CString filename = _T("C:\\test.txt");

if (file.Open(filename , CFile::modeCreate | CFile::modeWrite | CFile::shareDenyRead))

{

char * buf = (char*)(LPCTSTR)m_strsend;

file.Write(buf, strlen(buf));

file.Close();

}

else

{

MessageBox(_T("创建文件失败!"));

}

}

(2)数据接收

数据在接收进程中,通过Cfile类打开以上创建的共享文件,然后调用Read方法读取数据,具体代码如下:

void CRecieveDlg::Onrecieve()

{

//TODO: 在此添加控件通知处理程序代码

CFile file;

CString filename = _T("C:\\test.txt");

if (file.Open(filename , CFile::modeRead|CFile::shareDenyWrite))

{

char Buf[100]={0};

file.Read(Buf,100);

m_strrecieve=Buf;

file.Close();

}

else

{

MessageBox(_T("打开文件失败!"));

}

UpdateData(FALSE);                                  //更新数据

}

2.共享内存方式

通 过内存来传递数据,必须在内存中申请一定的空间。可以调用GlobalAlloc()或者VirtualAllocEx()来实现内存空间分配,使用内存 读写函数ReadProcessMemory()和WriteProcessMemory()来读写进程的内存。要使接收程序获得发送程序的内存地址,可 以通过发送消息方法来实现,即通过消息把内存地址从发送程序传递到接收程序。

(1)数据发送

首先要使用发送消息的方法来传递指针,就需要定义一个用户消息。可用如下的自定义消息来传递指针:

const UINT wm_nMemMsg=RegisterWindowMessage("mem_data");

寻 找接收数据的程序Recieve的窗口指针pWnd和进程句柄hProcess,用VirtualAllocEx()函数在这个进程中申请虚拟内存空间。 然后通过WriteProcessMemory()把字符串m_strsend存放入虚拟内存中,并且通过消息wm_nMemMsg把所申请的内存空间起 始地址发送给数据接收程序。最后,当数据接收程序接收到数据后,用VirtualFreeEx()释放所申请的虚拟内存。

数据发送函数具体代码如下:

void CSendDlg::OnSend()

{

//TODO: 在此添加控件通知处理程序代码

UpdateData(TRUE);                                       //更新数据

CWnd *pWnd=CWnd::FindWindow(NULL,_T("Recieve"));  //查找Recieve进程

if(pWnd==NULL){

MessageBox(_T("寻找接收消息窗口失败!"));

return;

}

DWORD PID;                                           //获取进程号

GetWindowThreadProcessId(pWnd->m_hWnd, (DWORD*)&PID );

HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS,FALSE,PID);

LPVOID lpBaseAddress;                                    //分配虚拟内存

lpBaseAddress = VirtualAllocEx(hProcess, 0, BUFFER_SIZE,

MEM_COMMIT, PAGE_READWRITE);

char [BUFFER_SIZE];

strcpy(data,m_strsend);

//把字符串写入hProcess进程的内存

WriteProcessMemory(hProcess, lpBaseAddress, data, BUFFER_SIZE, NULL);

//发送基址给Recieve进程

pWnd->SendMessage(wm_nMemMsg,NULL,(LPARAM)lpBaseAddress);

Sleep(100);                                             //等待接收程序接收数据

VirtualFreeEx(hProcess,lpBaseAddress, 0, MEM_RELEASE); //释放虚拟内存

}

(2)数据接收

首先需要定义一个用户消息,如下代码所示:

const UINT wm_nMemMsg=RegisterWindowMessage("mem_data");

然后在头文件中添加消息映射函数定义:

afx_msg void OnRegMemMsg(WPARAM wParam,LPARAM lParam);

接着需要定义wm_nMemMsg消息映射,它在消息映射表中的表示方法如下:

BEGIN_MESSAGE_MAP(CDataRecvDlg, CDialog)

ON_REGISTERED_MESSAGE(wm_nMemMsg,OnRegMemMsg)

END_MESSAGE_MAP()

最后在源文件中添加实现消息映射函数,具体代码如下:

LRESULT CRecieveDlg::OnRegMemMsg(WPARAM wParam,LPARAM lParam)

{

//TODO: 在此添加控件通知处理程序代码

LPVOID lpBaseAddress=(LPVOID)lParam;

HANDLE hProcess=GetCurrentProcess(); //把字符串写入hProcess进程的内存

char data[BUFFER_SIZE];

ReadProcessMemory(hProcess, lpBaseAddress, data,BUFFER_SIZE, NULL);

m_strrecieve=data;

UpdateData(FALSE);                   //更新数据

return 0;

}

3.映射文件方式

在Windows中,单个计算机上共享数据的底层机制是内存映射文件。如果互相通信的进程都在同一台计算机上,上面提到的所有机制均使用内存映射文件实现。如果想要达到较高的性能和较小的开销,内存映射文件将是最佳的实现机制。

内 存映射文件是通过两个或多个进程映射同一个文件映射对象的视图来实现的,这意味着它们将共享物理存储器的同一个页面。因此,当一个进程将数据写入一个共享 文件映射对象的视图时,其他进程可以立即看到它们视图中的数据变更情况。如果多个进程共享单个文件映射对象,那么所有进程必须使用相同的名字来表示该文件 映射对象。

(1)数据发送

在数据发送进程中,程序应首先调用CreateFileMapping()函数创建一个命名的内存 映射对象,得到相应内存起始位置指针lhShareMemory。如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,得到指向 映射到内存的第一个字节的指针lpBuffer并通过该指针读写共享的内存区域。最后使用UnmapViewOfFile()函数来解除视图映射,传入参 数为lpBuffer,具体代码如下:

void CSendDlg::OnSend()

{

UpdateData(TRUE);

HANDLE lhShareMemory;

char* lpBuffer = NULL;

//创建一个有名的共享内存

lhShareMemory = CreateFileMapping(

HANDLE(0xFFFFFFFF), //0xFFFFFFFF表示创建一个进程间共享的对象

NULL,

PAGE_READWRITE,             //读写共享

0,

100,                            //共享区间大小

"mySharedMemory");           //映射文件名,即共享内存的名称

if (NULL == lhShareMemory)

{

if (ERROR_ALREADY_EXISTS == GetLastError())

{

MessageBox(_T("Already exists!"));

}

else

{

MessageBox(_T("Create Sheared Memory unsuccessfully!"));

}

return;

}

//映射到本进程的地址空间

lpBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_WRITE, 0, 0, 100);

if (NULL == lpBuffer)

{

MessageBox(_T("Get Share memory unsuccessfully!"));

return;

}

strcpy(lpBuffer, (char*)(LPCTSTR)m_strsend);

UnmapViewOfFile(lpBuffer);            //取消本进程地址空间的映射

lpBuffer = NULL;

}

(2)数据接收

在 数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的内存映射文件对象,得到相应内存起始位置指针 lhShareMemory。如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,得到指向映射到内存的第一个字节的指针 lpcBuffer并通过该指针读写共享的内存区域。最后调用UnmapViewOfFile()函数来解除视图映射,传入参数为lpcBuffer,调 用CloseHandle()函数来关闭内存映射文件,传入参数为lhShareMemory,具体代码如下:

void CRecieveDlg::Onrecieve()

{

HANDLE lhShareMemory;

char* lpcBuffer;       //获得共享内存句柄

lhShareMemory = OpenFileMapping(FILE_MAP_READ, false, "mySharedMemory");

if (NULL == lhShareMemory)

{

MessageBox(_T("Open share memory unsuccessfully!"));

return;

}

lpcBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_READ, 0, 0, 100);

if (NULL == lpcBuffer)

{

MessageBox(_T("Open share memory unsuccessfully!"));

return;

}

m_strrecieve = lpcBuffer;

UnmapViewOfFile(lpcBuffer)            //取消本进程地址空间的映射;

lpcBuffer=NULL;

CloseHandle(lhShareMemory)

UpdateData(FALSE);

}

4.管道方式

管 道的类型有两种:匿名管道和命名管道。匿名管道是不命名的,它最初用于本地系统中父进程与它启动的子进程之间的通信。命名管道则高级一些,通过一个名字进 行标识,使客户端和服务端应用程序可以通过该管道进行通信。Win32命名管道甚至可以在不同系统的进程间使用,这使它成为许多客户/服务器应用程序的理 想之选。

现在我们用命名管道实现进程间的通信,具体实现过程如下。

(1)创建命名管道,具体代码如下

void CSendDlg::OnPipeCreate()

{

hPipe=CreateNamedPipe("\\\\.\\pipe\\MyPipe",

PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,

0,1,1024,1024,0,NULL);

if(INVALID_HANDLE_VALUE==hPipe)

{

MessageBox("创建命名管道失败!");

hPipe=NULL;

return;

}

HANDLE hEvent;

hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);

if(!hEvent)

{

MessageBox("创建事件对象失败!");

CloseHandle(hPipe);

hPipe=NULL;

return;

}

OVERLAPPED ovlap;

ZeroMemory(&ovlap,sizeof(OVERLAPPED));

ovlap.hEvent=hEvent;

if(!ConnectNamedPipe(hPipe,&ovlap))

{

if(ERROR_IO_PENDING!=GetLastError())

{

MessageBox("等待客户端连接失败!");

CloseHandle(hPipe);

CloseHandle(hEvent);

hPipe=NULL;

return;

}

}

if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))

{

MessageBox("等待对象失败!");

CloseHandle(hPipe);

CloseHandle(hEvent);

hPipe=NULL;

return;

}

CloseHandle(hEvent);

}

(2)读取数据,对于命名管道的数据读取操作,与上面匿名管道的读取操作是一样的,代码如下

void CSendDlg::Onrecieve()

{

char buf[100];

DWORD dwRead;

if(!ReadFile(hPipe,buf,100,&dwRead,NULL))

{

MessageBox(_T("读取数据失败!"));

return;

}

m_strrecieve=buf;

UpdateData(FALSE);

}

(3)写入数据,对于命名管道的数据写入操作,与上面匿名管道的写入操作也是相同的,代码如下

void CSendDlg::OnSend()

{

UpdateData(TRUE);

char * buf = (char*)(LPCTSTR)m_strsend;

DWORD dwWrite;

if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))

{

MessageBox("写入数据失败!");

return;

}

}

(4) 连接命名管道。客户端在连接服务器端程序创建的命名管道之前,首先应判断一下,是否有可以利用的命名管道,这可以通过调用WaitNamedPipe() 函数实现,该函数会一直等待,直到指定的超时间隔已过,或者指定了命名管道的实例可以用来连接了,也就是说该管道的服务器进程有了一个未决的 ConnectNamedPipe操作。如果当前命名管道的实例可以使用,那么客户端就可以调用CreateFile函数打开这个命名管道,与服务端进程 进行通信了。客户端的连接命名管道的代码如下

void CRecieveDlg::OnPipeConnect()

{

if(!WaitNamedPipe(_T("\\\\.\\pipe\\MyPipe"),NMPWAIT_WAIT_FOREVER))

{

MessageBox(_T("当前没有可利用的命名管道实例!"));

return;

}

hPipe=CreateFile(_T("\\\\.\\pipe\\MyPipe"),GENERIC_READ | GENERIC_WRITE,

0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

if(INVALID_HANDLE_VALUE==hPipe)

{

MessageBox(_T("打开命名管道失败!"));

hPipe=NULL;

return;

}

}

(5)读取数据。如果客户端成功打开了指定的命名管道,那么就可以进行读取和写入操作了,具体代码如下

void CRecieveDlg::Onrecieve()

{

char buf[100];

DWORD dwRead;

if(!ReadFile(hPipe,buf,100,&dwRead,NULL))

{

MessageBox(_T("读取数据失败!"));

return;

}

m_strrecieve=buf;

UpdateData(FALSE);

}

(6)写入数据。客户端向命名管道写入数据与上面服务器端向命名管道写入数据一样,具体代码如下

void CRecieveDlg::OnSend()

{

UpdateData(TRUE);

char * buf = (char*)(LPCTSTR)m_strsend;

DWORD dwWrite;

if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))

{

MessageBox("写入数据失败!");

return;

}

}

5.剪贴板方式

Windows 剪贴板是一种比较简单同时也是开销比较小的IPC(进程间通信)机制。Windows系统支持剪贴板IPC的基本机制是由系统预留的一块全局共享内存,用 来暂存各个进程间进行交换的数据。提供数据的进程创建一个全局内存块中,并将要传送的数据移到或复制到该内存块;而接受数据的进程(也可以是提供数据的进 程本身)获取此内存块的句柄,并完成对该内存块数据的读取。

(1)数据发送

在数据放到剪贴板前,首先需要打开剪切板,这里可 以用CWnd类的OpenClipboard()成员函数实现。打开剪切板后,调用EmptyClipboard()函数让打开剪切板的当前窗口拥有剪切 板。调用SetClipboardData()函数向剪切板中放置数据。调用SetClipboardData()函数之后,系统就拥有了hMen参数所 标识的数据对象,该应用程序可以读取这个数据对象。如果hMen参数标识了一个内存对象,那么这个对象必须是利用GMEM_MOVEABLE标志调用 GlobalAlloc函数为其分配内存的。向剪切板发送数据的具体代码如下

void CSendDlg::OnSend()

{

UpdateData(TRUE);

if(OpenClipboard())

{

HANDLE hClip;

char *pBuf;

EmptyClipboard();

hClip=GlobalAlloc(GMEM_MOVEABLE,m_strsend.GetLength()+1);

pBuf=(char*)GlobalLock(hClip);       //将句柄转换为指针

strcpy(pBuf,m_strsend);

GlobalUnlock(hClip);

SetClipboardData(CF_TEXT,hClip);

CloseClipboard();

}

else

{

MessageBox(_T("打开剪切板失败!"));

}

}

(2)数据接收

从剪切板中接收数据的具体代码如下

void CRecieveDlg::Onrecieve()

{

if(OpenClipboard())

{

if(IsClipboardFormatAvailable(CF_TEXT))

{

HANDLE hClip;

char *pBuf;

hClip=GetClipboardData(CF_TEXT);

pBuf=(char*)GlobalLock(hClip);

GlobalUnlock(hClip);

m_strrecieve = pBuf;

CloseClipboard();

}

}

else

{

MessageBox(_T("打开剪切板失败!"));

}

UpdateData(FALSE);

}

6.消息方式

消息是Windows操作系统提供的一种驱动机制。利用消息进行进程通信,就是使用消息激活某种操作的过程。对于进程间的通信,一般采用用户自定义的消息来完成;如果要实现的是Windows定义的消息功能,则可以使用已定义的消息。

(1)数据发送

在数据发送进程中,调用FindWindow()函数根据窗口的标题或者接收窗体的类名搜索窗口,然后给COPYDATASTRUCT结构赋值,最后调用SendMessage()函数发送消息,具体代码如下

void CSendDlg::OnSend()

{

UpdateData(TRUE);

CWnd *pWnd=CWnd::FindWindow(NULL,_T("Recieve"));        //查找DataRecv进程

if(pWnd==NULL)

{

MessageBox(_T("寻找接收窗口失败!"));

return;

}

COPYDATASTRUCT cpd;                                      //给COPYDATASTRUCT结构赋值

cpd.dwData = 0;

cpd.cbData = m_strsend.GetLength();

cpd.lpData = (void*)m_strsend.GetBuffer(cpd.cbData);

pWnd->SendMessage(WM_COPYDATA,NULL,(LPARAM)&cpd);   //发送

}

(2)数据接收

实现数据接收的代码如下

BOOL CRecieveDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)

{

m_strrecieve=(LPSTR)pCopyDataStruct->lpData;

m_strrecieve=m_strrecieve.Left(pCopyDataStruct->cbData); //获得实际长度的字符串

UpdateData(FALSE);                                               //更新数据

return CDialog::OnCopyData(pWnd, pCopyDataStruct);

}

windows 进程间通讯方法的更多相关文章

  1. Windows进程间通讯(IPC)----消息队列

    消息队列 windows系统是通过消息驱动的,每移动一下鼠标,点击一下屏幕都会产生一个消息.这些消息会先被放在windows的一个系统消息队列(先进先出)中,windows系统会为每一个GUI线程创建 ...

  2. Windows进程间通讯(IPC)----共享内存

    Windows中同一个EXE文件多次加载过程 Windows中EXE文件加载是基于内存映射文件的. 当EXE文件第一次被加载. 首先系统会先创建一个进程内核对象,并创建一个新的进程地址空间. 系统调用 ...

  3. Windows进程间通讯(IPC)----内存映射文件

    内存映射文件原理 内存映射文件是通过在虚拟地址空间中预留一块区域,然后通过从磁盘中已存在的文件为其调度物理存储器,访问此虚拟内存空间就相当于访问此磁盘文件了. 内存映射文件实现过程 HANDLE hF ...

  4. Windows进程间通讯(IPC)----WM_COPYDATA

    WM_COPYDATA通讯思路 通过向其他进程的窗口过程发送WM_COPYDATA消息可以实现进程间通讯. 只能通过SendMessage发送WM_COPYDATA消息,而不能通过PostMessag ...

  5. windows进程间通讯的方法

    版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者“tingsking18”和主站点地址,方便其他朋友提问和指正.   1.使用共享内存 代码如下: void FileMapp ...

  6. Windows进程间通讯(IPC)----管道

    管道的分类 管道其实际就是一段共享内存,只不过Windows规定需要使用I/O的形式类访问这块共享内存,管道可以分为匿名管道和命名管道. 匿名管道就是没有名字的管道,其支持单向传输数据,如果需要双向传 ...

  7. Windows进程间通讯(IPC)----信号量

    线程同步内核对象 操作系统进行进程间同步是利用信号量机制.对于windows系统而言,可以利用一些内核对象进行线程同步,因为这些内核对象可以命名并且属于系统内核,所以可以支持不同进程间的线程同步进而实 ...

  8. Windows进程间通讯(IPC)----套接字

    Windows套接字 Windows套接字即socket,通过socket可以实现在不同的进程间通信,甚至这两个进程可以不在同一个计算机中. Winsock使用步骤 服务端 socket初始化 创建套 ...

  9. UNIX环境高级编程——进程间通讯方法整理

    一.无名管道pipe #include <unistd.h> int pipe(int fd [2]) 二.fifo #include <sys/stat.h> int mkf ...

随机推荐

  1. 【BZOJ 2693】jzptab(莫比乌斯+分块)

    2693: jzptab Description Input 一个正整数T表示数据组数 接下来T行 每行两个正整数 表示N.M Output T行 每行一个整数 表示第i组数据的结果 Sample I ...

  2. 2.5.5 使用DatePickerDialog, TimePickerDialog

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout ...

  3. The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

    The server is temporarily unable to service your request due to maintenance downtime or capacity pro ...

  4. 为虚拟机搭建MacOSX系统

    发现一篇正确的安装文章,转载自:http://blog.imqyc.com/2013/05/29/vritual-MacOS/ 今年新买了一个笔记本电脑,本打算购入心仪已久的Macbook Pro 1 ...

  5. 电源管理之pmu驱动分析

    电源管理芯片可以为多设备供电,且这些设备电压电流有所不同.为这些设备提供的稳压器代码模型即为regulator. 说白了regulator就是稳压器,它提供电源供给.简单的可以gpio操作,高电平开电 ...

  6. 【基于spark IM 的二次开发笔记】第二天 树形结构

    MessageTreePlugin.java final MessageTreeTab messageTreeTab = new MessageTreeTab(); /** * Adds a tab  ...

  7. HW4.17

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  8. CF 294C(Java大数做计数)

    题目链接:http://codeforces.com/contest/294/problem/C 代码: import java.util.*; import java.math.*; public ...

  9. HOWTO: Setup XCode 6.1 to work with OpenCV3 libraries

    HOWTO: Setup XCode 6.1 to work with OpenCV3 libraries Overview This post demonstrates how to setup y ...

  10. oracle Imp和exp以及导入常见的错误

    一 1) 数据库对象已经存在 一般情况, 导入数据前应该彻底删除目标数据下的表, 序列, 函数/过程,触发器等; 数据库对象已经存在, 按缺省的imp参数, 则会导入失败 如果用了参数ignore=y ...