如何实现历程间数据通讯技能
副标题#e#
1、引言
在Windows措施中,各个历程之间经常需要互换数据,举办数据通讯。WIN32 API提供了很多函数使我们可以或许利便高效地举办历程间的通讯,通过这些函数我们可以节制差异历程间的数据互换,就如同在WIN16中对当地历程举办读写操纵一样。
典范的WIN16两历程可以通过共享内存来举办数据互换:(1)历程A将GlobalAlloc(GMEM_SHARE…)API分派必然长度的内存;(2)历程A将GlobalAlloc函数返回的句柄通报给历程B(通过一个登录动静);(3)历程B对这个句柄挪用GlobalLock函数,并操作GlobalLock函数返回的指针会见数据。这种要领在WIN32中大概失败,这是因为GlobalLock函数返回指向的是历程A的内存,由于历程利用的是虚拟地点而非实际物理地点,因此这一指针仅与A历程有关,而于B历程无关。
本文探讨了几种WIN32下历程之间通讯的几种实现要领,读者可以利用差异的要领以到达措施运行高效靠得住的目标。
2、Windows95中历程的内存空间打点
WIN32历程间通讯与Windows95的内存打点有密切干系,领略Windows95的内存打点对我们如下的措施设计将会有很大的辅佐,下面我们接头以下Windows95中历程的内存空间打点。
在WIN16下,所有Windows应用措施共享单一地点,任何历程都可以或许对这一空间中属于共享单一的地点空间和属于其他历程的内存举办读写操纵,甚至可以存取操纵系统自己的数据,这样就大概粉碎其他措施的数据段代码。
在WIN32下,每个历程都有本身的地点空间,一个WIN32历程不能存取另一个地点的私有数据,两个历程可以用具有沟通值的指针寻址,但所读写的只是它们各自的数据,这样就淘汰了历程之间的彼此滋扰。另一方面,每个WIN32历程拥有4GB的地点空间,但并不代表它真正拥有4GB的实际物理内存,而只是操纵系统操作CPU的内存分派成果提供的虚拟地点空间。在一般环境下,绝大大都虚拟地点并没有物理内存与它对应,在真正可以利用这些地点空间之前,还要由操纵系统提供实际的物理内存(这个进程叫“提交”commit)。在差异的环境下,系统提交的物理内存是差异的,大概是RAM,也大概是硬盘模仿的虚拟内存。
3、WIN32中历程间的通讯
在Windows 95中,为实现历程间平等的数据互换,用户可以有如下几种选择:
* 利用内存映射文件
* 通过共享内存DLL共享内存
* 向另一历程发送WM_COPYDATA动静
* 挪用ReadProcessMemory以及WriteProcessMemory函数,用户可以发送由GlobalLock(GMEM_SHARE,…)函数挪用提取的句柄、GlobalLock函数返回的指针以及VirtualAlloc函数返回的指针。
–3.1、操作内存映射文件实现WIN32历程间的通讯
Windows95中的内存映射文件的机制为我们高效地操纵文件提供了一种途径,它答允我们在WIN32历程中保存一段内存区域,把方针文件映射到这段虚拟内存中。在措施实现中必需思量各历程之间的同步。详细实现步调如下:
首先我们在发送数据的历程中需要通过挪用内存映射API函数CreateFileMapping建设一个有名的共享内存:
HANDLE CreateFileMapping(
HANDLE hFile, // 映射文件的句柄,
//设为0xFFFFFFFF以建设一个历程间共享的工具
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // 安详属性
DWORD flProtect, // 掩护方法
DWORD dwMaximumSizeHigh, //工具的巨细
DWORD dwMaximumSizeLow,
LPCTSTR lpName // 必需为映射文件定名
);
与虚拟内存雷同,掩护方法可以是PAGE_READONLY或是PAGE_READWRITE。假如多历程都对同一共享内存举办写会见,则必需保持彼此间同步。映射文件还可以指定PAGE_WRITECOPY符号,可以担保其原始数据不会遭到粉碎,同时答允其他历程在须要时自由地操纵数据的拷贝。
在建设文件映射工具后利用可以挪用MapViewOfFile函数映射到本历程的地点空间内。
下面说明建设一个名为MySharedMem的长度为4096字节的有名映射文件:
HANDLE hMySharedMapFile=CreateFileMapping((HANDLE)0xFFFFFFFF),
NULL,PAGE_READWRITE,0,0x1000,“MySharedMem”);
并映射缓存区视图:
LPSTR pszMySharedMapView=(LPSTR)MapViewOfFile(hMySharedMapFile,
FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
其他历程会见共享工具,需要得到工具名并挪用OpenFileMapping函数。
HANDLE hMySharedMapFile=OpenFileMapping(FILE_MAP_WRITE,
FALSE,“MySharedMem");
一旦其他历程得到映射工具的句柄,可以像建设历程那样挪用MapViewOfFile函数来映射工具视图。用户可以利用该工具视图来举办数据读写操纵,以到达数据通讯的目标。
当用户历程竣事利用共享内存后,挪用UnmapViewOfFile函数以打消其地点空间内的视图:
if (!UnmapViewOfFile(pszMySharedMap
View))
{ AfxMessageBox(“could not unmap view of file"); }
#p#副标题#e#
–3.2、操作共享内存DLL
#p#分页标题#e#
共享数据DLL答允历程以雷同于Windows 3.1 DLL共享数据的方法会见读写数据,多个历程都可以对该共享数据DLL举办数据操纵,到达共享数据的目标。在WIN32中为成立共享内存,必需执行以下步调:
首先建设一个有名的数据区。这在Visual C++中是利用data_seg pragma宏。利用data_seg pragma宏必需留意数据的初始化:
#pragma data_seg(“MYSEC")
char MySharedData[4096]={0};
#pragma data_seg()
然后在用户的DEF文件中为有名的数据区设定共享属性。
LIBRARY TEST
DATA READ WRITE
SECTIONS
.MYSEC READ WRITE SHARED
这样每个隶属于DLL的历程都将接管到属于本身的数据拷贝,一个历程的数据变革并不会反应到其他历程的数据中。
在DEF文件中适内地输出数据。以下的DEF文件项说明白如何故常数变量的形式输出MySharedData。
EXPORTS
MySharedData CONSTANT
最后在应用措施(历程)按外部变量引用共享数据。
extern _export"C"{char * MySharedData[]}
历程中利用该变量应留意间接引用。
m_pStatic=(CEdit*)GetDlgItem(IDC_SHARED);
m_pStatic->GetLine(0,*MySharedData,80);
–3.3、用于传输只读数据的WM_COPYDATA
传输只读数据可以利用Win32中的WM_COPYDATA动静。该动静的主要目标是答允在历程间通报只读数据。Windows95在通过WM_COPYDATA动静通报期间,不提供担任同步方法。SDK文档推荐用户利用SendMessage函数,接管方在数据拷贝完成前不返回,这样发送方就不行能删除和修改数据:
SendMessage(hwnd,WM_COPYDATA,wPara
m,lParam);
个中wParam配置为包括数据的窗口的句柄。lParam指向一个COPYDATASTRUCT的布局:
typedef struct tagCOPYDATASTRUCT{
DWORD dwData;//用户界说数据
DWORD cbData;//数据巨细
PVOID lpData;//指向数据的指针
}COPYDATASTRUCT;
该布局用来界说用户数据。
–3.4、直接挪用ReadProcessMemory和WriteProcessMemory函数实现历程间通讯
通过挪用ReadProcessMemory以及WriteProcessMemory函数用户可以按雷同与Windows3.1的要领实现历程间通讯,在发送历程中分派一块内存存放数据,可以挪用GlobalAlloc可能VirtualAlloc函数实现:
pApp→m_hGlobalHandle=GlobalAlloc(GMEM_SHARE,1024);
可以获得指针地点:pApp→mpszGlobalHandlePtr=(LPSTR)GlobalLock(pApp→m_hGlobalHandle);
在吸收历程中要用到用户但愿影响的历程的打开句柄。为了读写另一历程,应按如下方法挪用OpenProcess函数:
HANDLE hTargetProcess=OpenProcess(
STANDARD_RIGHTS_REQUIRED|
PROCESS_VM_REDA|
PROCESS_VM_WRITE|
PROCESS_VM_OPERATION,//会见权限
FALSE,//担任干系
dwProcessID);//历程ID
为担保OpenProcess函数挪用乐成,用户所影响的历程必需由上述符号建设。
一旦用户得到一个历程的有效句柄,就可以挪用ReadProcessMemory函数读取该历程的内存:
BOOL ReadProcessMemory(
HANDLE hProcess, // 历程指针
LPCVOID lpBaseAddress, // 数据块的首地点
LPVOID lpBuffer, // 读取数据所需缓冲区
DWORD cbRead, // 要读取的字节数
LPDWORD lpNumberOfBytesRead
);
利用同样的句柄也可以写入该历程的内存:
BOOL WriteProcessMemory(
HANDLE hProcess, // 历程指针
LPVOID lpBaseAddress, // 要写入的首地点
LPVOID lpBuffer, // 缓冲区地点
DWORD cbWrite, // 要写的字节数
LPDWORD lpNumberOfBytesWritten
);
如下所示是读写另一历程的共享内存中的数据:
ReadProcessMemory((HANDLE)hTargetProcess,(LPSTR)lpsz,m_strGlobal.GetBuffer(_MAX_FIELD),
_MAX_FIELD,&cb);
WriteProcessMemory((HANDLE)hTargetProcess,(LPSTR)lpsz,(LPSTR)STARS,
m_strGlobal.GetLength(),&cb);
4、历程之间的动静发送与吸收
在实际应用中历程之间需要发送和吸收Windows动静来通知历程间彼此通讯,发送方发送通讯的动静以通知吸收方,吸收方在收到发送方的动静后就可以对内存举办读写操纵。
我们在措施设计中回收Windows注册动静进动作静通报,首先在发送历程初始化进程中进动作静注册:
m_nMsgMapped=::RegisterWindowsMessage(“Mapped”);
m_nMsgHandle=::RegisterWindowsMessage(“Handle”);
m_nMsgShared=::RegisterWindowsMessage(“Shared”);
在措施运行中向吸收历程发送动静:
CWnd* pWndRecv=FindWindow(lpClassName,“Receive”);
pWndRecv→SendMessage(m_MsgMapped,0,0);
pWndRecv→SendMessage(m_nMsgHandle,(UINT)GetCurrentProcessID(),(LONG)pApp→m_hGlobalHandle);
pWndRecv→SendMessage(m_nMsgShared,0,0);
可以按如下方法发送WM_COPYDATA动静:
static COPYDATASTRUCT cds;//用户存放数据pWnd→SendMessage(WM_COPYDATA,NULL,(LONG)&cds);
吸收方历程初始化也必需进动作静注册:
UNIT CRecvApp:: m_nMsgMapped=::RegisterWindowsMessage(“Mapped”);
UNIT CRecvApp::m_nMsgHandle=::RegisterWindowsMessage(“Handle”);
UNIT CRecvApp::m_nMsgShared=::RegisterWindowsMessage(“Shared”);
同时映射动静函数如下:
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgMapped,OnRegMsgMapped)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgHandle,OnRegMsgHandle)
ON_REGISTERED_MASSAGE(CRecvApp::m_nMsgShared,OnRegMsgShared)
有这些动静函数我们就可以回收上述技能实现吸收历程中数据的读写操纵了。
5、竣事语
#p#分页标题#e#
从以上阐明中我们可以看出Windows95的内存打点对历程之间的通讯有较为严格的限制。这就确保了任何妨碍措施无法意外地写入用户的地点空间,而用户则可按照实际环境机动地举办历程间的数据通讯,从这一点上来讲Windows95加强了应用措施的强壮性。