windows进程通信之共享内存那点事

第一次做服务程序,碰到不少问题,稍作总结,留作后用。

 

程序结构

一个服务程序(A),每当有新用户登录,启动一个托盘(B),服务程序可以发数据给托盘,托盘也可以发数据给服务程序。因为二者都是在本机由此想到了使用共享内存。

新建两块共享内存,一块用作A写B读,另一块B写A读。因为server操作系统支持多用户登录,所以可能存在多个B程序。

 

A的大概代码:

//关于填充子进程命令行参数要多说一句,B写好数据之后要通知A来读,所以要发送一个消息给A,此时调用PostThreadMessage,第一个参数便是目的地的主线程ID

char szCmdLine[MAX_PATH];
sprintf_s(szCmdLine, MAX_PATH, "%ld",oGlobalConfig.dwMainThreadId

LPTSTR szCmdline = _tcsdup((LPCTSTR)szCmdLine);

//创建子进程,创建之后要保留子进程的主线程ID,从pi取得。这个也是A写完数据后发消息给B,通知B来读取共享内存的数据

if (!CreateProcessAsUser( pExplorerToken[i] ,strTrayName.c_str(), szCmdLine ,
      NULL,NULL,FALSE , NORMAL_PRIORITY_CLASS| CREATE_NEW_CONSOLE ,NULL,NULL,&si,&pi ) )
     {
      printf("打开失败 %s\n",strTrayName.c_str());
     }

 //写完数据后通知B来取数据

bool WriteToShareMem(char *pTactics)
{
 if (!hShareMem || !pShareMem || !pTactics)
  return false;
 //写入共享区
 LPSEND_HEAD lpSendHead = (LPSEND_HEAD)pTactics;
 memcpy(pShareMem, pTactics, lpSendHead->PacketLength);
 //对vecChildThread加锁,因为当托盘程序退出时,需要从列表中清除对应ID
 EnterCriticalSection(&oGlobalConfig.cs_ChildThread);
 //写完后给托盘发送消息,托盘开始读数据
 if (0 == oGlobalConfig.vecChildThread.size())
 {
  LeaveCriticalSection(&oGlobalConfig.cs_ChildThread);
  return true;
 }

//此向量里保存的全是子进程的主线程ID
 vector<DWORD>::iterator iter = oGlobalConfig.vecChildThread.begin();
 for (; iter != oGlobalConfig.vecChildThread.end(); ++iter)
 {
  BOOL bRet = PostThreadMessage(*iter, WM_TRAYGETDATA, 0, 0);
 }
 LeaveCriticalSection(&oGlobalConfig.cs_ChildThread);
 return true;
}

 

至此,写数据完成,接下来是读取B写入的数据。这里用一个消息循环,来接收B发送的消息。这个消息循环一定要写在自winthread的主线程中,详见msdn

void AnalysisMsg()
{
 MSG msg;
 char *pMonitorData = NULL;//接收托盘发送的数据
 while (GetMessage(&msg, NULL, WM_USER, WM_USER+3))
 {
  if (msg.message == WM_MONITORGETDATA)//自定义消息
  {
   //读取共享内存数据

   pMonitorShare = (char *)MapViewOfFile(hMonitorShare, FILE_MAP_ALL_ACCESS, 0, 0, MONITOR_SHARE_SIZE);
   if (NULL == pMonitorShare)
   {
    continue;
   }
   else
   {
    LPSEND_HEAD lpHead = (LPSEND_HEAD)pMonitorShare;
    //判断操作类型

    switch (lpHead->TransactionNumber)
    {
    case TRAY_APP_UDISK_REGISTER:
     {

     数据处理

     }
     break;
    case TRAY_APP_UDISK_TACTICS:
     {
      数据处理

     }
     break;
    }
   }
  }
 }
}

 

B程序从命令行参数获取父进程的主线程ID

//通过命令行获取父进程的主线程ID
 LPSTR lpstr = GetCommandLine();
 string cmdline = lpstr;
 m_uiParentThreadId = DWORD(atol(cmdline.c_str()));

其他与A程序相差不多,只是创建共享内存之类的改为open即可。有一点要注意,假如加入同步互斥控制,则需要用到EVENT或MUTEX之类,服务程序中创建的这些内核对象,在子进程中打开时会提示“拒绝访问”,错误代号:5。因为服务程序的安全级别较高,所以在创建的时候要传入第一个参数,而不是NULL。假如是用NULL创建的,在子进程也可以访问,只是在open中的第一个参数传入SYNCHRONIZE即可,但这时候获得的句柄不能对其做重置操作,例如:ResetEvent。此时如果有需求,还是在创建时传入安全属性,因我没这种需求,所以暂时没去研究这个安全属性怎样设置。

 

总结:

1、有名内核对象如果在服务程序中创建,则要注意安全属性

2、共享内存时,用消息通知的方式通知对方来读数据。读写要进行加锁操作,我用的是mutex

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用共享内存进行进程通信的客户端和服务端的示例代码,代码中包括了创建和映射共享内存,以及读写共享内存的基本操作。 服务端代码: ``` #include <windows.h> #include <stdio.h> int main() { HANDLE hMapFile; LPCTSTR pBuf; // 创建共享内存 hMapFile = CreateFileMapping( INVALID_HANDLE_VALUE, // 使用无效的句柄 NULL, // 默认安全级别 PAGE_READWRITE, // 可读写 0, // 高位字节数 1024, // 低位字节数 TEXT("MyFileMappingObject")); // 共享内存名称 if (hMapFile == NULL) { printf("CreateFileMapping failed (%d)\n", GetLastError()); return 1; } // 映射共享内存 pBuf = (LPTSTR)MapViewOfFile( hMapFile, // 共享内存句柄 FILE_MAP_ALL_ACCESS,// 可读写 0, 0, 1024); if (pBuf == NULL) { printf("MapViewOfFile failed (%d)\n", GetLastError()); CloseHandle(hMapFile); return 1; } // 从共享内存中读取数据 printf("Buf: %s\n", pBuf); // 写入数据到共享内存 CopyMemory((LPVOID)pBuf, "Hello from server!", sizeof("Hello from server!")); // 解除映射 UnmapViewOfFile(pBuf); // 关闭句柄 CloseHandle(hMapFile); return 0; } ``` 客户端代码: ``` #include <windows.h> #include <stdio.h> int main() { HANDLE hMapFile; LPCTSTR pBuf; // 打开共享内存 hMapFile = OpenFileMapping( FILE_MAP_ALL_ACCESS, // 可读写 FALSE, // 不继承 TEXT("MyFileMappingObject")); // 共享内存名称 if (hMapFile == NULL) { printf("OpenFileMapping failed (%d)\n", GetLastError()); return 1; } // 映射共享内存 pBuf = (LPTSTR)MapViewOfFile( hMapFile, // 共享内存句柄 FILE_MAP_ALL_ACCESS,// 可读写 0, 0, 1024); if (pBuf == NULL) { printf("MapViewOfFile failed (%d)\n", GetLastError()); CloseHandle(hMapFile); return 1; } // 写入数据到共享内存 CopyMemory((LPVOID)pBuf, "Hello from client!", sizeof("Hello from client!")); // 从共享内存中读取数据 printf("Buf: %s\n", pBuf); // 解除映射 UnmapViewOfFile(pBuf); // 关闭句柄 CloseHandle(hMapFile); return 0; } ``` 此代码演示了如何创建一个共享内存对象,并分别在服务端和客户端中映射到进程地址空间中,然后进行数据读写。在实际应用中,需要根据具体需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值