由于最近工作比较忙,也没有时间把自己的点点滴滴记录下来,今天把自己认为比较重要的东西分享给大家
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权限的,他可以创建全局的共享内存文件映射。我们只需要在创建的时候降低它的访问权限就可以了。
下面看代码:
点击(此处)折叠或打开
- 服务端代码:
- TCHAR YourFileMappingName[] = "Global\\YourFileMappingName"; //共享内存文件名
- HANDLE hFile = NULL;
-
- //设置访问权限
- SECURITY_ATTRIBUTES sa;
- SECURITY_DESCRIPTOR sd;
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.bInheritHandle = FALSE;
- sa.lpSecurityDescriptor = &sd;
- if(!InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION) || !SetSecurityDescriptorDacl(&sd,TRUE,NULL,FALSE))
- //All access is allowed.
- {
- TRACE("Create access set Dacl failed %d\n",GetLastError());
- }
- else
- {
- //创建共享内存文件映射
- CreateFileMapping(INVALID_HANDLE_VALUE,
- &sa,PAGE_READWRITE,0,MAX_PATH,YourFileMappingName) ;//PAGE_READWRITE设置其他进程可读写,重要!
-
- }
- CloseHandle(hFile);
看应用程序端的代码:
点击(此处)折叠或打开
- TCHAR YourFileMappingName[] = "Global\\YourFileMappingName";
- HANDLE hFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, 0, YourFileMappingName);
- if (hFile != INVALID_HANDLE_VALUE)
- {
- char* lpFile= (char *)MapViewOfFile(hFile,FILE_MAP_READ | FILE_MAP_WRITE,0,0,0);
- if (lpFile != NULL)
- {
- //设置共享内存
- sprintf(lpFile,"%s",YourWriteData);//设置需要写的数据
- UnmapViewOfFile( lpFile);
- }
- }
-
- CloseHandle(hFile);

假设我们服务端起一个线程主要是接受由服务程序提供的消息,那么就相当于应用程序可以让服务程序干应用程序干不成的事,相对安全性而言提升应用程序的权限不如此方法安全,毕竟应用程序也是自己写的,有什么操作都只有自己知道。这样我们跨越session 编程还有那么复杂么???
二、使用命名管道(NamedPipe):
除了使用上述的方法外,我们还可以使用命名管道的方法,但是命名管道的方法相对比较麻烦,相对占用资源也是比较多的。
我写管道是给大家做个例子,因为好多人写的例子我觉得好像都不好用。
同样我还是用服务程序去创建一个管道,单独起一个线程去做这些操作,废话不多说了,直接上代码:
服务端:
点击(此处)折叠或打开
- AfxBeginThread(GetAppData,this);//服务开始出开启线程
- UINT CYourDlg::GetAppData(LPVOID pParam)
- {
- //设置访问对象权限
- SECURITY_ATTRIBUTES sa;
- SECURITY_DESCRIPTOR sd;
- sa.nLength = sizeof( SECURITY_ATTRIBUTES );
- sa.bInheritHandle = FALSE;
- sa.lpSecurityDescriptor = &sd;
- InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
- SetSecurityDescriptorDacl( &sd,TRUE,NULL,FALSE );
-
- HANDLE hPipe = NULL;
- while(!Stop)
- {
- hPipe = CreateNamedPipe(
- _T("\\\\.\\pipe\\SharePipe"),
- PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
- PIPE_UNLIMITED_INSTANCES,
- MAX_PATH,
- MAX_PATH,
- INFINITE,
- &sa);
- if(hPipe == INVALID_HANDLE_VALUE)
- {
- Sleep(1000);
- continue;//create pipe filed!
- }
- BOOL bConnected = ConnectNamedPipe(hPipe,NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
- if(bConnected)
- {
- AfxBeginThread(GetPipeValue,hPipe);
- }
- else
- {
- CloseHandle(hPipe);
- }
-
- }
-
- return 0;
- }
- //start thread to get data of Pipe.
- UINT CYourDlg::GetPipeValue(LPVOID pParam)
- {
- HANDLE hPipe = static_cast<HANDLE>(pParam);
- TCHAR buffer[MAX_PATH] = {0};
- DWORD cbBytesRead = 0;
-
- if(ReadFile(hPipe,buffer , MAX_PATH , &cbBytesRead , NULL) == FALSE)
- {
- DisconnectNamedPipe(hPipe);
- CloseHandle(hPipe);
- return 0;
- }
- else
- {
- CString m_Pipe_data;
- m_Pipe_data.Format(_T("%s"),buffer);
-
- //拿到m_Pipe_data你想要的数据、处理
- DisconnectNamedPipe(hPipe);
- CloseHandle(hPipe);
- }
-
- return 0;
- }
上述是服务程序的管道处理方式,下面介绍下应用程序的写管道数据方法:
Client:
点击(此处)折叠或打开
- TCHAR buffer[MAX_PATH] = {0};
- DWORD cbWritten = 0;
- HANDLE hPipe = NULL,hShareEvent = NULL;
- if(WaitNamedPipe(_T("\\\\.\\pipe\\SharePipe"),NMPWAIT_USE_DEFAULT_WAIT) == FALSE)
- {
- return;
- }
- hPipe = CreateFile(
- _T("\\\\.\\pipe\\SharePipe"),
- GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ,
- NULL,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_TEMPORARY,
- NULL);
- if(hPipe == INVALID_HANDLE_VALUE)
- {
- //Open pipe
-
- return;
- }
- else{
- CString Write_to_Pipe;//需要写的数据
- _tcscpy(buffer,Write_to_Pipe);//使用函数拷贝CString到TCHAR中去。
- if(WriteFile(hPipe , buffer , MAX_PATH ,&cbWritten , NULL) == FALSE)
- {
- //write filed.
- CloseHandle(hPipe);
- return;
- }
-
- FlushFileBuffers(hPipe);
- DisconnectNamedPipe(hPipe);
- CloseHandle(hPipe);
- }
这样也可以达到跨越session0实现数据共享的目的。