Chinaunix首页 | 论坛 | 博客
  • 博客访问: 84873
  • 博文数量: 19
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 281
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-07 14:42
个人简介

业精于勤,荒于嬉;凡事用心,事事皆成。

文章分类

全部博文(19)

文章存档

2017年(1)

2016年(9)

2015年(2)

2014年(2)

2013年(5)

我的朋友

分类: C/C++

2013-12-12 18:24:04

由于最近工作比较忙,也没有时间把自己的点点滴滴记录下来,今天把自己认为比较重要的东西分享给大家

windows平台开发的人肯定非常头疼vista之后高低进程权限之间通信和交换数据的问题,之前在网上也有很多人讨论这个话题

             但是我今天要说的和他们的可能不同,让穿越session0更为简单,只需要逆向思维一下下就可以了。好了废话不多说了,看正文。

通常我们开发中肯定会有进程之间数据共享的问题,常用的方法有以下几种:
            1)使用命名管道(NamedPipe);
            2)使用内存映射(CreateFileMapping);
            3)使用WM_COPYDATA自定义消息(ChangeWindowsMessageFilter);

一、使用内存映射(CreateFileMapping):


在vista和win7之前我们通常用CreateFileMapping就可以了,但是在win7中不是所有的进程有创建内存文件映射的权限。必须要有SeCreateGlobalPrivilege权限才可以创建全局(Global)的共享内存名称。
          
我今天主要讲两种方法去解决不同权限进程交换数据的问题第一种:内存映射、第二种:命名管道。

            相信有经验的同学肯定是在创建内存映射(CreateFileMapping)的时候出现了拒绝访问的问题。如果在win7中创建带有Global前缀的共享内存文件的时候必须有上述的权限,不然是没办法创建的。
只有超级管理员、system和具有SeCreateGlobalPrivilege的进程才可以创建Global前缀的共享内存文件映射。其他的应用程序只能创建以Local开头的共享内存文件映射。

            什么时候用“Global\\YourFileMappingName”?
            什么时候用“Local\\YourFileMappingName”?
            它们有什么区别?


           1、 如果你有一个服务程序(Session 0)和一个应用程序(Session 1),它们之间通信或者交换数据,那么可以使用全局(Global)的共享内存文件映射。
           2、如果你有两个应用进程(Session 1)(Application),它们之间通信或者交换数据,可以使用局部(Local)的共享内存文件映射。

          第2种方法很简单,不在赘述,主要说一下第1种问题的解决方案。

           在网上看到有很多人在解决上述问题的时候采用的是CreateFileMapping的方法,但是都失败了。有的采用了写物理文件的方法,有的是用的是获取用户令牌的方式,还有的是我开始写的上面的第3)中方法(自定义消息)。

           那么我们转化一下思路,如果我需要服务程序和应用程序交换数据,那么必须穿越Session 0,通常我们都是用应用程序去创建共享内存文件映射的方法,但是

涉及到返回拒绝访问的问题。于是我们都放弃了使用这种方法!!!

          Why?我们除了用应用程序去创建还可以用服务程序去创建啊!     服务程序是有system权限的,他可以创建全局的共享内存文件映射。我们只需要在创建的时候降低它的访问权限就可以了。

          下面看代码:


点击(此处)折叠或打开

  1. 服务端代码:
  2.         TCHAR YourFileMappingName[] = "Global\\YourFileMappingName"; //共享内存文件名    
  3.         HANDLE hFile = NULL;
  4.     
  5.         //设置访问权限
  6.         SECURITY_ATTRIBUTES sa;
  7.         SECURITY_DESCRIPTOR sd;
  8.         sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  9.         sa.bInheritHandle = FALSE;
  10.         sa.lpSecurityDescriptor = &sd;
  11.         if(!InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION) || !SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE))    
  12.             //All access is allowed.
  13.         {
  14.             TRACE("Create access set Dacl failed %d\n",GetLastError());
  15.         }
  16.         else
  17.         {
  18.             //创建共享内存文件映射
  19.              CreateFileMapping(INVALID_HANDLE_VALUE,
  20.                 &sa,PAGE_READWRITE,0,MAX_PATH,YourFileMappingName) ;//PAGE_READWRITE设置其他进程可读写,重要!
  21.             
  22.         }
  23.         CloseHandle(hFile);
在服务程序初始化的时候创建此共享内存文件映射。那么应用程序就可以对此“Global\\YourFileMappingName”进行读写操作!

看应用程序端的代码:

点击(此处)折叠或打开

  1.     TCHAR YourFileMappingName[] = "Global\\YourFileMappingName";
  2.     HANDLE hFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, 0, YourFileMappingName);

  3.     if (hFile != INVALID_HANDLE_VALUE)
  4.                 {
  5.                     char* lpFile= (char *)MapViewOfFile(hFile,FILE_MAP_READ | FILE_MAP_WRITE,0,0,0);
  6.                     if (lpFile != NULL)
  7.                     {
  8.                         //设置共享内存
  9.                         sprintf(lpFile,"%s",YourWriteData);//设置需要写的数据
  10.                         UnmapViewOfFile( lpFile);
  11.                     }    
  12.                 }
  13.     
  14.     CloseHandle(hFile);
由此可见,我们应用程序可以对此命名的一块文件内存映射进行读写操作,那么如果有什么我们应用程序做不了的操作我们都可以由此传递给服务程序去做,可想而知是多么的强大啊。

假设我们服务端起一个线程主要是接受由服务程序提供的消息,那么就相当于应用程序可以让服务程序干应用程序干不成的事,相对安全性而言提升应用程序的权限不如此方法安全,毕竟应用程序也是自己写的,有什么操作都只有自己知道。这样我们跨越session 编程还有那么复杂么???



二、使用命名管道(NamedPipe):


         除了使用上述的方法外,我们还可以使用命名管道的方法,但是命名管道的方法相对比较麻烦,相对占用资源也是比较多的。

         我写管道是给大家做个例子,因为好多人写的例子我觉得好像都不好用。
         同样我还是用服务程序去创建一个管道,单独起一个线程去做这些操作,废话不多说了,直接上代码:
  服务端:
       

点击(此处)折叠或打开

  1. AfxBeginThread(GetAppData,this);//服务开始出开启线程


  2. UINT CYourDlg::GetAppData(LPVOID pParam)
  3. {
  4.     //设置访问对象权限
  5.     SECURITY_ATTRIBUTES sa;
  6.     SECURITY_DESCRIPTOR sd;
  7.     sa.nLength = sizeof( SECURITY_ATTRIBUTES );
  8.     sa.bInheritHandle = FALSE;
  9.     sa.lpSecurityDescriptor = &sd;
  10.     InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
  11.     SetSecurityDescriptorDacl( &sd,TRUE,NULL,FALSE );
  12.     

  13.     HANDLE hPipe = NULL;

  14.     while(!Stop)
  15.     {
  16.         hPipe = CreateNamedPipe(
  17.         _T("\\\\.\\pipe\\SharePipe"),
  18.         PIPE_ACCESS_DUPLEX,
  19.         PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
  20.         PIPE_UNLIMITED_INSTANCES,
  21.         MAX_PATH,
  22.         MAX_PATH,
  23.         INFINITE,
  24.         &sa);

  25.         if(hPipe == INVALID_HANDLE_VALUE)
  26.         {
  27.             Sleep(1000);
  28.             continue;//create pipe filed!
  29.         }

  30.         BOOL bConnected = ConnectNamedPipe(hPipe,NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

  31.         if(bConnected)
  32.         {
  33.             AfxBeginThread(GetPipeValue,hPipe);
  34.         }
  35.         else
  36.         {
  37.             CloseHandle(hPipe);
  38.         }
  39.         
  40.     }
  41.     
  42.     return 0;
  43. }

  44. //start thread to get data of Pipe.
  45. UINT CYourDlg::GetPipeValue(LPVOID pParam)
  46. {
  47.     HANDLE hPipe = static_cast<HANDLE>(pParam);
  48.     TCHAR buffer[MAX_PATH] = {0};
  49.     DWORD cbBytesRead = 0;
  50.     
  51.     if(ReadFile(hPipe,buffer , MAX_PATH , &cbBytesRead , NULL) == FALSE)
  52.     {
  53.         DisconnectNamedPipe(hPipe);
  54.         CloseHandle(hPipe);
  55.         return 0;
  56.     }
  57.     else
  58.     {
  59.         CString m_Pipe_data;
  60.         m_Pipe_data.Format(_T("%s"),buffer);
  61.         
  62.         //拿到m_Pipe_data你想要的数据、处理
  63.         DisconnectNamedPipe(hPipe);
  64.         CloseHandle(hPipe);
  65.     }

  66.     
  67.     return 0;
  68. }

上述是服务程序的管道处理方式,下面介绍下应用程序的写管道数据方法:
Client:

点击(此处)折叠或打开

  1.     TCHAR buffer[MAX_PATH] = {0};
  2.     DWORD cbWritten = 0;
  3.     HANDLE hPipe = NULL,hShareEvent = NULL;
  4.     if(WaitNamedPipe(_T("\\\\.\\pipe\\SharePipe"),NMPWAIT_USE_DEFAULT_WAIT) == FALSE)
  5.     {
  6.         return;
  7.     }
  8.     hPipe = CreateFile(
  9.         _T("\\\\.\\pipe\\SharePipe"),
  10.         GENERIC_READ | GENERIC_WRITE,
  11.         FILE_SHARE_READ,
  12.         NULL,
  13.         CREATE_ALWAYS,
  14.         FILE_ATTRIBUTE_TEMPORARY,
  15.         NULL);
  16.     if(hPipe == INVALID_HANDLE_VALUE)
  17.     {
  18.         //Open pipe
  19.         
  20.         return;
  21.     }
  22.     else{
  23.         CString Write_to_Pipe;//需要写的数据
  24.         _tcscpy(buffer,Write_to_Pipe);//使用函数拷贝CString到TCHAR中去。
  25.         if(WriteFile(hPipe , buffer , MAX_PATH ,&cbWritten , NULL) == FALSE)
  26.         {
  27.             //write filed.
  28.             CloseHandle(hPipe);
  29.             return;
  30.         }
  31.         
  32.             FlushFileBuffers(hPipe);
  33.             DisconnectNamedPipe(hPipe);
  34.             CloseHandle(hPipe);        
  35.     }

这样也可以达到跨越session0实现数据共享的目的。


怎么样?我觉得还是第一种方法比较省资源,所以奇思妙想还是可以碰撞出一些比较好的方法的。我写下来分析给大家,希望大家能够明白,对win7不同session间进程之间的传递数据有个深刻的了解。




阅读(2290) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~