Windows进程间通信-共享内存

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 07:48   16   0

做项目时要用到进程间的通信,把服务程序和普通界面程序建立通信,记录一下用到的方法防止忘记

首先这里用的是共享内存的通信方式

共享内存的方式原理就是将一份物理内存映射到不同进程各自的虚拟地址空间上,这样每个进程都可以读取同一份数据,从而实现进程通信。因为是通过内存操作实现通信,因此是一种最高效的数据交换方法。

共享内存在 Windows 中是用 FileMapping 实现的,从具体的实现方法上看主要通过以下几步来实现:

1、调用 CreateFileMapping 创建一个内存文件映射对象;

复制代码
HANDLE CreateFileMapping(
  HANDLE hFile,              // handle to file to map
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
                             // optional security attributes
  DWORD flProtect,           // protection for mapping object
  DWORD dwMaximumSizeHigh,   // high-order 32 bits of object size
  DWORD dwMaximumSizeLow,    // low-order 32 bits of object size
  LPCTSTR lpName             // name of file-mapping object
);
复制代码

通过这个API函数 将创建一个内存映射文件的内核对象,用于映射文件到内存。与虚拟内存一样,内存映射文件可以用来保留一个地址空间的区域,并将物理存储器提交
给该区域。它们之间的差别是,物理存储器来自一个已经位于磁盘上的文件,而不是系统的页文件。

复制代码
hFile用于标识你想要映射到进程地址空间中的文件句柄。该句柄可以通过调用C r e a t e F i l e函数返回。这里,我们并不需要一个实际的文件,所以,就不需要调用 CreateFile 创建一个文件, hFile 这个参数可以填写 INVALID_HANDLE_VALUE; 

lpFileMappingAttributes:参数是指向文件映射内核对象的 SECURITY_ATTRIBUTES结构的指针,通常传递的值是 N U L L;

flProtect:对内存映射文件的安全设置(PAGE_READONLY 以只读方式打开映射;PAGE_READWRITE 以可读、可写方式打开映射;PAGE_WRITECOPY 为写操作留下备份)

dwMaximumSizeHigh:文件映射的最大长度的高32位。

dwMaximumSizeLow:文件映射的最大长度的低32位。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度。

lpName:指定文件映射对象的名字,别的进程就可以用这个名字去调用 OpenFileMapping 来打开这个 FileMapping 对象。

如果创建成功,返回创建的内存映射文件的句柄,如果已经存在,则也返回其句柄,但是调用 GetLastError()返回的错误码是:183(ERROR_ALREADY_EXISTS),如果创建失败,则返回NULL;
复制代码

2、调用 MapViewOfFile 映射到当前进程的虚拟地址上;

如果调用CreateFileMapping成功,则调用MapViewOfFile函数,将内存映射文件映射到进程的虚拟地址中;

复制代码
LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,  // file-mapping object to map into 
                              // address space
  DWORD dwDesiredAccess,      // access mode
  DWORD dwFileOffsetHigh,     // high-order 32 bits of file offset
  DWORD dwFileOffsetLow,      // low-order 32 bits of file offset
  DWORD dwNumberOfBytesToMap  // number of bytes to map
);

hFileMappingObject:CreateFileMapping()返回的文件映像对象句柄。
dwDesiredAccess: 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。
dwFileOffsetHigh: 表示文件映射起始偏移的高32位.
dwFileOffsetLow: 表示文件映射起始偏移的低32位.
dwNumberOfBytesToMap :文件中要映射的字节数。为0表示映射整个文件映射对象。
复制代码

3、在接收进程中打开对应的内存映射对象

在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的文件映射内核对象,得到相应的文件映射内核对象句柄hFileMapping;如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,将文件映射内核对象hFileMapping映射到当前应用程序的进程地址,进行读取操作。(当然,这里如果用CreateFileMapping也是可以获取对应的句柄)

复制代码
HANDLE OpenFileMapping(
  DWORD dwDesiredAccess,  // access mode
  BOOL bInheritHandle,    // inherit flag
  LPCTSTR lpName          // pointer to name of file-mapping object
);

dwDesiredAccess:同MapViewOfFile函数的dwDesiredAccess参数
bInheritHandle :如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE。
lpName :指定要打开的文件映射对象名称。
复制代码

4、进行内存映射文件的读写

一旦MapViewOfFile调用成功,就可以像读写本进程地址空间的内存区一样,进行内存的读写操作了。

复制代码
//读操作:
if ( m_pViewOfFile  )
{
        // read text from memory-mapped file
        TCHAR s[dwMemoryFileSize];
        
        lstrcpy(s, (LPCTSTR) m_pViewOfFile);
}
//写操作:
if ( m_pViewOfFile )
 {
        TCHAR s[dwMemoryFileSize];
        m_edit_box.GetWindowText(s, dwMemoryFileSize);
            
        lstrcpy( (LPTSTR) m_pViewOfFile, s);
            
        // Notify all running instances that text was changed
        ::PostMessage(HWND_BROADCAST, 
            wm_Message,     
            (WPARAM) m_hWnd,
            0);    
}
复制代码

5、清理内核对象

在用完后,要取消本进程地址空间的映射,并释放内存映射对象。

    //取消本进程地址空间的映射;   
    UnmapViewOfFile(pLocalMem);  
      
    pLocalMem=NULL;   
    //关闭文件映射内核文件  
    CloseHandle(hFileMapping);
以上为参考,原地址http://www.cnblogs.com/xiekeli/p/4018579.html

下面放自己写的例子,因为我的是与服务程序进行通信,略微不同

1.写消息的函数

int main()
{
     int nRetCode = 0;
 //SECURITY_ATTRIBUTES sa={0};
    char szBuffer[] = "Shine";
    HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,1024,L"Global\\ShareMemory");//加Global\\代表全局session,不然检测不到
    if(ERROR_FILE_INVALID==GetLastError())
        printf("ERROR_FILE_INVALID");
    else if(ERROR_INVALID_HANDLE==GetLastError())
        printf("ERROR_INVALID_HANDLE");
    else if(ERROR_ALREADY_EXISTS   ==GetLastError())
        printf("ERROR_ALREADY_EXISTS");

    LPVOID lpBase = MapViewOfFile(hMapping,FILE_MAP_WRITE|FILE_MAP_READ,0,0,0);
    strcpy((char*)lpBase,szBuffer);
    Sleep(500);//如果此处不挂起一会,共享内存还没被另一个检测到就被下面两条指令关闭
    UnmapViewOfFile(lpBase);//停止当前内存映射
    CloseHandle(hMapping);//关闭句柄
    return 0;
}
这里要对文件映射对象的名称加Global\\ ,因为每个不同的客户进程,都有各自独立的对象命名空间,独立的命名空间能够保证在同时运行多个客户程序的时候不会发生冲突。在客户Session下,系统默认给客户程序使用的是Session命名空间。要想让任何会话中的程序都能访问到内核对象,需要加上“Global\”前缀。

win7和visa后服务程序运行在session0区,和桌面程序不在一个会话层中,所以要添加。另外如果添加后还不能通信,可以把CreateFileMapping的第二个参数换成第二行注释掉的代码试一下,这涉及到权限的问题。

2.读消息的函数

int CATLdemoModule::Share_Memory()
{
 int nRetCode = 0;
 while(1){
    HANDLE hMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,L"Global\\ShareMemory");
    if (hMapping)
    {
        LogEvent(L"Success");
        LPVOID lpBase = MapViewOfFile(hMapping,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
        char szBuffer[20] = {0};
        strcpy(szBuffer,(char*)lpBase);
        LogEvent(L"%s",szBuffer);
        UnmapViewOfFile(lpBase);
        CloseHandle(hMapping);
  nRetCode=1;
  break;
    }
    else
    {
        LogEvent(L"OpenMapping Error");
    }
 }
    return nRetCode;
}




分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP