当前位置:   article > 正文

Windows下进程间通信_windows进程间通信

windows进程间通信

1. WM_COPYDATA


这种方法仅适合于Windows的GUI程序。利用WM_COPYDATA消息来进行进程间通信。其原理是如下:

在x86下,由于保护模式下进程都具有虚拟内存。所以每个进程都拥有4GB的虚存空间,并通过分页机制进行寻址管理。其中2GB的内核空间是进程共享的而2GB的用户空间则是每个进程独立拥有。

WM_COPYDATA消息会在内核空间开辟一段缓冲区。然后把要输入的内容传递进去。由于内核空间是共享的,所以其他进程可以直接获取。

这种方法比较消耗资源,不推荐携带大量数据。

看一下演示程序:

这是发送消息程序Send的主要代码:

 这是接受消息程序Recv的主要代码

 结果如下:

 2. DLL共享段


当DLL被映射到多个进程空间时,其中包含的变量数据各个都是不受影响的。也就是说数据是不共享的。如果这些数据是共享的那就可以达到进程间通信的目的。可以通过添加一个共享段并把数据丢在里面达到共享数据的目的

 接下来使用这个dll的进程就可以共享这个g_iNum的变量了。

3. 文件映射


文件映射是利用把磁盘上的文件映射到各个进程中。虽然多个进程空间内都有该映射,但实际上内存中只有一份。对映射到内存的文件进行修改也会影响到磁盘上的文件。

直接看代码:

  1. #include <windows.h>
  2. #include <cstdio>
  3. #define FILENAME "E:\\Projects\\FileMapping\\FileMapping\\MsgSnd.exe"
  4. VOID ShowError(const char *pcszErrFuncName)
  5. {
  6. char szErrInfoBuf[MAX_PATH] = { 0 };
  7. if (NULL == pcszErrFuncName)
  8. {
  9. return;
  10. }
  11. wsprintf(szErrInfoBuf,
  12. "Error Information: %s Error code: %d\r\n",
  13. pcszErrFuncName,
  14. GetLastError());
  15. OutputDebugString(szErrInfoBuf);
  16. }
  17. // 含有文件的文件映射
  18. int main()
  19. {
  20. HANDLE hFile = INVALID_HANDLE_VALUE;
  21. HANDLE hFileMapping = NULL;
  22. LPVOID lpAddr = NULL;
  23. do
  24. {
  25. hFile = CreateFile(FILENAME,
  26. GENERIC_READ | GENERIC_WRITE,
  27. FILE_SHARE_READ,
  28. NULL,
  29. OPEN_EXISTING,
  30. FILE_ATTRIBUTE_NORMAL,
  31. NULL
  32. );
  33. if (INVALID_HANDLE_VALUE == hFile)
  34. {
  35. ShowError("CreateFile");
  36. break;
  37. }
  38. hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, "Test");
  39. if (NULL == hFileMapping)
  40. {
  41. ShowError("CreateFileMapping");
  42. break;
  43. }
  44. lpAddr = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0);
  45. if (NULL == lpAddr)
  46. {
  47. ShowError("MapViewOfFile");
  48. break;
  49. }
  50. system("pause");
  51. UnmapViewOfFile(lpAddr);
  52. } while (FALSE);
  53. if (INVALID_HANDLE_VALUE != hFile)
  54. {
  55. CloseHandle(hFile);
  56. hFile = INVALID_HANDLE_VALUE;
  57. }
  58. if (NULL != hFileMapping)
  59. {
  60. CloseHandle(hFileMapping);
  61. hFileMapping = NULL;
  62. }
  63. system("pause");
  64. return(0);
  65. }

接下来使用另一个进程打开该内存映射, 即可实现进程间通信:

  1. #include <windows.h>
  2. #include <cstdio>
  3. #define FILENAME "E:\\Projects\\FileMapping\\FileMapping\\MsgSnd.exe"
  4. VOID ShowError(const char *pcszErrFuncName)
  5. {
  6. char szErrInfoBuf[MAX_PATH] = { 0 };
  7. if (NULL == pcszErrFuncName)
  8. {
  9. return;
  10. }
  11. wsprintf(szErrInfoBuf,
  12. "Error Information: %s Error code: %d\r\n",
  13. pcszErrFuncName,
  14. GetLastError());
  15. OutputDebugString(szErrInfoBuf);
  16. }
  17. // 含有文件的文件映射
  18. int main()
  19. {
  20. HANDLE hFileMapping = NULL;
  21. LPVOID lpAddr = NULL;
  22. do
  23. {
  24. hFileMapping = OpenFileMapping(FILE_MAP_WRITE, FALSE, "Test");
  25. if (NULL == hFileMapping)
  26. {
  27. ShowError("CreateFileMapping");
  28. break;
  29. }
  30. lpAddr = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0);
  31. if (NULL == lpAddr)
  32. {
  33. ShowError("MapViewOfFile");
  34. break;
  35. }
  36. system("pause");
  37. UnmapViewOfFile(lpAddr);
  38. } while (FALSE);
  39. if (NULL != hFileMapping)
  40. {
  41. CloseHandle(hFileMapping);
  42. hFileMapping = NULL;
  43. }
  44. system("pause");
  45. return(0);
  46. }

代码都差不多唯一的差别就是CreateFile和CreateFileMapping直接替换成OpenFileMapping打开对应内存文件映射即可。

有一点需要注意的。就是MapViewOfFile这个API。来看一下对应的MSDN文档:

 其中的dwFileOffsetLow参数中指出,映射的偏移位置必须是对齐粒度的整数倍。我的计算机的粒度目前是0x10000即64KB。所以这里需要时0x10000的倍数才行

经过调试也可以发现MapViewOfFile的返回基址是0x02d30000也是0x10000的整数倍。

接下来看一种无文件的内存映射,这种情况的下不需要CreateFile,其会在内存中开辟一段空间,这段空间会被进程之间共享。

  1. #include <windows.h>
  2. #include <cstdio>
  3. VOID ShowError(const char *pcszErrFuncName)
  4. {
  5. char szErrInfoBuf[MAX_PATH] = { 0 };
  6. if (NULL == pcszErrFuncName)
  7. {
  8. return;
  9. }
  10. wsprintf(szErrInfoBuf,
  11. "Error Information: %s Error code: %d\r\n",
  12. pcszErrFuncName,
  13. GetLastError());
  14. OutputDebugString(szErrInfoBuf);
  15. }
  16. // 无文件内存映射
  17. int main()
  18. {
  19. HANDLE hFileMapping = NULL;
  20. LPVOID lpAddr = NULL;
  21. do
  22. {
  23. hFileMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 0x1000, "Test");
  24. if (NULL == hFileMapping)
  25. {
  26. ShowError("CreateFileMapping");
  27. break;
  28. }
  29. lpAddr = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0);
  30. if (NULL == lpAddr)
  31. {
  32. ShowError("MapViewOfFile");
  33. break;
  34. }
  35. system("pause");
  36. UnmapViewOfFile(lpAddr);
  37. } while (FALSE);
  38. if (NULL != hFileMapping)
  39. {
  40. CloseHandle(hFileMapping);
  41. hFileMapping = NULL;
  42. }
  43. system("pause");
  44. return(0);
  45. }

另一个进程去打开这段内存空间然后即可实现进程间通讯。

  1. #include <windows.h>
  2. #include <cstdio>
  3. VOID ShowError(const char *pcszErrFuncName)
  4. {
  5. char szErrInfoBuf[MAX_PATH] = { 0 };
  6. if (NULL == pcszErrFuncName)
  7. {
  8. return;
  9. }
  10. wsprintf(szErrInfoBuf,
  11. "Error Information: %s Error code: %d\r\n",
  12. pcszErrFuncName,
  13. GetLastError());
  14. OutputDebugString(szErrInfoBuf);
  15. }
  16. // 无文件内存映射
  17. int main()
  18. {
  19. HANDLE hFileMapping = NULL;
  20. LPVOID lpAddr = NULL;
  21. do
  22. {
  23. hFileMapping = OpenFileMapping(FILE_MAP_WRITE, FALSE, "Test");
  24. if (NULL == hFileMapping)
  25. {
  26. ShowError("CreateFileMapping");
  27. break;
  28. }
  29. lpAddr = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0);
  30. if (NULL == lpAddr)
  31. {
  32. ShowError("MapViewOfFile");
  33. break;
  34. }
  35. system("pause");
  36. UnmapViewOfFile(lpAddr);
  37. } while (FALSE);
  38. if (NULL != hFileMapping)
  39. {
  40. CloseHandle(hFileMapping);
  41. hFileMapping = NULL;
  42. }
  43. system("pause");
  44. return(0);
  45. }

在利用文件映射进程共享数据的过程中,记得不要关闭CreateFileMapping生成的句柄和MapViewOfFile返回的地址。

4. 管道


管道分为匿名管道和命名管道。其中匿名管道主要用于进程间通信但只能是父子进程之间。

利用这个特性可以写出一些有趣的小程序。比如:

 该程序可以输入dos命令并执行对应命令。看上去像是一个shell一样。实际上其只是利用管道借用了cmd.exe来执行命令后把结果返回到edit控件中。

贴核心代码:

  1. DWORD WINAPI RecvInfoThread(LPVOID lpThreadParameter)
  2. {
  3. CString strResult, strTmp, strTmp1;
  4. DWORD dwTotalAvail = 0;
  5. DWORD dwReaded = 0;
  6. char szBuf[129] = { 0 };
  7. BOOL fOk = FALSE;
  8. BOOL fFirst = TRUE;
  9. BOOL fOriginalFirst = TRUE;
  10. INT iIdx = 0;
  11. while (!g_fExit)
  12. {
  13. // 检测到管道内有数据
  14. if ((fOk = PeekNamedPipe(g_hReadPipe_, NULL, 0, NULL, &dwTotalAvail, NULL)) &&
  15. dwTotalAvail > 0)
  16. {
  17. fFirst = TRUE;
  18. // 读取管道内的数据并显示
  19. do
  20. {
  21. // 每次从管道内读取128个字节
  22. RtlZeroMemory(szBuf, sizeof(szBuf));
  23. fOk = ReadFile(g_hReadPipe_, szBuf, sizeof(szBuf) - 1, &dwReaded, NULL);
  24. if (!fOk)
  25. {
  26. break;
  27. }
  28. strTmp = szBuf;
  29. // 截取左边3个字符判定是否是cls命令或者exit命令
  30. strTmp1 = strTmp.Left(5);
  31. if (!strTmp1.Compare("cls\r\n"))
  32. {
  33. iIdx = strTmp.GetLength() - strTmp.Find("\f") - 1;
  34. strTmp = strTmp.Right(iIdx);
  35. }
  36. strTmp1 = strTmp.Left(6);
  37. // 如果是exit命令则退出自身进程
  38. if (!strTmp1.Compare("exit\r\n"))
  39. {
  40. // 退出进程
  41. TerminateProcess(GetCurrentProcess(), 0);
  42. }
  43. if (!fOriginalFirst)
  44. {
  45. if (fFirst)
  46. {
  47. int iIdx = strTmp.GetLength() - strTmp.Find("\n") - 1;
  48. strTmp = strTmp.Right(iIdx);
  49. fFirst = FALSE;
  50. }
  51. }
  52. else
  53. {
  54. fOriginalFirst = FALSE;
  55. }
  56. strResult += strTmp;
  57. dwTotalAvail -= dwReaded;
  58. } while (dwTotalAvail > 0);
  59. SetDlgItemTextA(g_MainWnd, IDC_EDIT_CONTENT, strResult);
  60. strResult = "";
  61. dwTotalAvail = 0;
  62. }
  63. Sleep(100);
  64. }
  65. return(0);
  66. }
  67. BOOL Init()
  68. {
  69. SECURITY_ATTRIBUTES sa = { 0 };
  70. BOOL fOk = FALSE;
  71. STARTUPINFO si = { sizeof(si) };
  72. PROCESS_INFORMATION pi = { 0 };
  73. HANDLE hThread = NULL;
  74. char szBuf[] = "cmd.exe";
  75. sa.bInheritHandle = TRUE;
  76. sa.lpSecurityDescriptor = NULL;
  77. sa.nLength = sizeof(sa);
  78. // 创建2条匿名管道
  79. fOk = CreatePipe(&g_hReadPipe, &g_hWritePipe, &sa, 0);
  80. if (!fOk)
  81. {
  82. return(FALSE);
  83. }
  84. fOk = CreatePipe(&g_hReadPipe_, &g_hWritePipe_, &sa, 0);
  85. if (!fOk)
  86. {
  87. return(FALSE);
  88. }
  89. si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  90. si.wShowWindow = SW_HIDE;
  91. si.hStdInput = g_hReadPipe;
  92. si.hStdOutput = g_hWritePipe_;
  93. si.hStdError = g_hWritePipe_;
  94. // 创建隐秘cmd进程
  95. fOk = CreateProcess(NULL, szBuf, &sa, NULL, TRUE, 0, NULL, NULL, &si, &pi);
  96. if (!fOk)
  97. {
  98. return(FALSE);
  99. }
  100. g_hProcess = pi.hProcess;
  101. // 创建管道监视线程
  102. hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RecvInfoThread, NULL, 0, NULL);
  103. if (NULL != hThread)
  104. {
  105. CloseHandle(hThread);
  106. hThread = NULL;
  107. }
  108. return(TRUE);
  109. }
  110. void CEasyTrojanDlg::OnBnClickedBtnSendcmd()
  111. {
  112. // TODO: 在此添加控件通知处理程序代码
  113. CString strCmd;
  114. CEdit *pCEdit = NULL;
  115. GetDlgItemText(IDC_EDIT_CMD, strCmd);
  116. if (strCmd.IsEmpty())
  117. {
  118. return;
  119. }
  120. // 先把命令转成小写简化
  121. strCmd.MakeLower();
  122. strCmd += "\r\n";
  123. DWORD dwWritten = 0;
  124. WriteFile(g_hWritePipe,
  125. strCmd.GetBuffer(0),
  126. strCmd.GetLength(),
  127. &dwWritten,
  128. NULL);
  129. SetDlgItemText(IDC_EDIT_CMD, "");
  130. GetDlgItem(IDC_EDIT_CMD)->SetFocus();
  131. }
  132. BOOL CEasyTrojanDlg::OnInitDialog()
  133. {
  134. CDialogEx::OnInitDialog();
  135. BOOL fOk = FALSE;
  136. // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
  137. // 执行此操作
  138. SetIcon(m_hIcon, TRUE); // 设置大图标
  139. SetIcon(m_hIcon, FALSE); // 设置小图标
  140. // TODO: 在此添加额外的初始化代码
  141. g_MainWnd = GetSafeHwnd();
  142. if (!g_MainWnd)
  143. {
  144. return(FALSE);
  145. }
  146. fOk = Init();
  147. if (!fOk)
  148. {
  149. return(FALSE);
  150. }
  151. GetDlgItem(IDC_EDIT_CMD)->SetFocus();
  152. return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
  153. }

(完)

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号