进程之间的通信

1.为什么要用到多进程:

当线程处于不同的地址空间时,也即属于不同的进程,线程之间的通信就变成了进程之间的通信。

当一个程序从一个平台移到另一个平台,如Unix不支持线程,但其进程的产生和结束的代价并不昂贵。

2.解决方案

                   低阶方案:

从“如何以消息队列权充数据转运中心”开始,看看如何产生共享内存。

Windows中定义了一个消息,名为WM_COPYDATA,专门用来在线程之间搬移数据,不论两个线程是否同属一个进程。

WM_COPYDATA 消息的使用方式如下:
SendMessage(hwndReceiver,
WM_COPYDATA,
(WPARAM)hwndSender,
(LPARAM)&cds);

LPARAM 参数 cds 必须指向一个特定的 Window s 数据结构:
typedef struct tagCOPYDATASTRUCT { // cds
DWORD dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
结构中各参数的意义:
dwData: 用户自定义值。通常用作指示lpData的内容和用途。
cbData: lpData所指数据的大小
lpData: 被传送到接收端的数据
注意:必须使用SendMessage()传送WM_COPYDATA,不能用PostMessage()和任何其他变种函数如PostThreadMessage()之流。因为要注意所传递数据的缓冲区的生命期。
http://blog.csdn.net/skyjacker/article/details/1473378中有必须使用的详细阐述。

产生COPYDATASTRUCT缓冲区
产生一个由lpData所指的COPYDATASTRUCT缓冲区时,lpData中放置一个指针,指向以下结构:
struct GoodDataBlock
{
DWORD dwNumber;
char szBuffer[80];
};
如果是下面的结构就不可以,因为szBuffer是个指针,没有真正的空间:
struct BadDataBlock
{
DWORD dwNumber;
char *szBuffer;
};
在c++中,更不能指向“某一拥有虚函数的对象”,可能造成指针指向别的进程中的函数
class BadDataClass
{
public:
BadDataClass();
virtual ~BadDataClass();
};
同样,使用拥有虚函数的内嵌类或者那些”拥有指针,指向它们自己”的内嵌类,也必须小心。如MFC中的CString,它含有一个指针指向字符数据,WM_COPYDSATA不能为它弄出一份正确的拷贝。
指针指向this作为lpData的内容也是不安全的。

COPYDATA 范例程序:
两个MFC程序,一个COPYSEND,一个COPYRECV,
在COPYRECV中,ON_MESSAGE(WM_COPYDATA, OnCopyData)响应共享消息,并跳转到OnCopyData( UINT wParam, LONG lParam)函数中。函数主题如下:
LONG CMainFrame::OnCopyData( UINT wParam, LONG lParam)
{
// HWND hwnd = (HWND)wParam; // handle of sending window
//获取缓存区
PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT) lParam;

CEditView* pView = (CEditView*)GetActiveView();
ASSERT_VALID(pView);
// Find the edit control
CEdit& ctlEdit = pView->GetEditCtrl();
switch (pcds->dwData)
{
case ACTION_DISPLAY_TEXT://display信息
    {
    // 获取共享的信息
    LPCSTR szNewString = (LPCSTR)(pcds->lpData);
    CString strTextToDisplay = szNewString;
    // Throw away any \r\n that may already be there
    strTextToDisplay.TrimRight();
    // Now add our own
    strTextToDisplay += "\r\n";

    // Set the cursor back at the end of the text
    int nEditLen = ctlEdit.GetWindowTextLength();
    ctlEdit.SetSel(nEditLen, nEditLen);
    // 展示信息
    ctlEdit.ReplaceSel(strTextToDisplay);
    ctlEdit.ShowCaret();
    break;
    }
case ACTION_CLEAR_WINDOW://清除信息
    ctlEdit.SetWindowText("");
    break;
default:
    break;

在COPYSEND中:
共享信息中lpData指向的结构体
typedef struct tagCOPYDATASTRUCT {
ULONG_PTR dwData;
DWORD cbData;
__field_bcount(cbData) PVOID lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
//再从输入框中获得要共享的信息
void CCopySendDlg::OnOk()
{
CEdit pEdit = (CEdit)GetDlgItem(IDC_EDIT_SENDTEXT);
ASSERT_VALID(pEdit);

// Get the text from the edit control
CString strDisplayText;
pEdit->GetWindowText(strDisplayText);

COPYDATASTRUCT cds;
memset(&cds, 0, sizeof(cds));
cds.dwData = ACTION_DISPLAY_TEXT;
cds.cbData = strDisplayText.GetLength() + 1; // +1 for the NULL
cds.lpData = (LPVOID)(LPCTSTR) strDisplayText;

SendToServer(cds);//调用发送函数,

}
void CCopySendDlg::SendToServer(const COPYDATASTRUCT& cds)
{
CWnd *pDisplayWnd = CWnd::FindWindow(NULL, szDisplayAppName);
if (pDisplayWnd)
{
pDisplayWnd->SendMessage(WM_COPYDATA,
(WPARAM)GetSafeHwnd(), (LPARAM)&cds);
}
else
AfxMessageBox(IDS_ERR_NOSERVER);
}
程序运行结果截图:

源码:http://download.csdn.net/detail/u011244821/8996847

                 高阶方案:

使用共享内存(Shredder Memory)
设定一块共享内存区域,需要两个步骤:
a. 产生一个所谓的file-mapping核心对象,并指定共享区域的大小。
b. 将共享区域映射到你的进程的地址空间中。

HANDLE CreateFileMapping(
HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName );
//正常而言是CreateFile()传回的,用以告诉系统将它映射到内存中。如果此参数是(HANDLE)0XFFFFFFFF,就能使用页面文件中的空间取代一般的文件。
//安全属性
文件的保护属性。可以是 PAGE_ READONLY 或PAGE_READWRITE或 PAGE_ WRITECOPY。针对 跨 进 程 的 共 享 内 存 , 你 应 该 指 定 此 参 数 为PAGE_READWRITE。
映射之文件大小的高32位。使用页面文件,此参数为0
映射区域的低32位。对于共享内存,此值即为共享的内存大小。
共享内存区域的名称。任何进程或线程根据此名称调用该file-mapping对象。不是一般的NULL。
如果成功,CreateFileMapping()传回一个handle,否则传回NULL。如果参数中所指定的文件已经存在 , CreateFileMapping() 便 会 失 败 , 这 时 候 GetLastError() 会 传 回ERROR_ALREADY_EXISTS。
有了核心对象,接着从共享内存中获得一个指针,使用MapViewOfFile() 。
LPVOID MapViewOfFile(
HANDLE hFileMappingObject, //file-mapping核心对象的handle,是CreateFileMapping()或OpenFileMapping()的传回物
DWORD dwDesiredAccess, //共享内存设置为FILE_MAP_ALL_ACCESS
DWORD dwFileOffsetHigh, //映射文件的高 32 位偏移值。如果使用页面文件( paging file ),该参数应该总是为 0,
DWORD dwFileOffsetLow, //对于共享内存而言,该参数应该总是 0 以便能够映射整个共享区域。
DWORD dwNumberOfBytesToMap //真正要被映射的字节数量。为 0表示映射整个空间。
);
成功传回一个指向被映射出来的“视图”,失败传回NULL。
找出共享内存
HANDLE OpenFileMapping(
DWORD dwDesiredAccess,对于共享内存:FILE_MAP_ALL_ACCESS
BOOL bInheritHandle, TRUE,能被子进程继承
LPCTSTR lpName 共享内存的名称
);
成功传回一个Handle,失败返回一个NULL。
在调用 OpenFileMapping() 之后,进程应该调用 MapViewOfFile() 以获得一个指针,指向共享内存。注意:第二个进程以及后续各进程调用 OpenFileMapping() 之后所获得的地址,并不保证和第一个进程所获得的地址相同。
使用完共享内存后,需要释放资源。先调用 UnmapViewOfFile(),交出原本由 MapViewOfFile() 所获得的指针,然后再调用
CloseH andle(),交出file-mapping 核心对象的 handle 。
BOOL UnmapViewOfFile(
LPCVOID lpBaseAddress 指 针 , 指 向 共 享 内 存
);成功返回TRUE,失败返回FALSE

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值