1.继续简易任务管理器项目
1.1 获取进程的路径
1.首先我们需要一个当前进程的句柄: HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID) (第一个参数是进程的权限,第二个参数是进程的继承性,第三个参数是要打开的进程ID);
2.如果进程句柄获取成功,那么使用 QueryFullProcessImageName 函数来获取进程的路径;
// 显示所有的进程信息
void CTaskManagerDlg::ShowAllProcessInfo()
{
// 创建存进程信息的结构体
PROCESSENTRY32 pe = {sizeof(pe)};
// 创建进程的快照
HANDLE h_snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(h_snapshot != INVALID_HANDLE_VALUE)
{
// 遍历快照中所有的进程
BOOL b_flag = ::Process32First(h_snapshot, &pe);
// 当有进程时,进行循环遍历
while (b_flag)
{
// =============将进程的信息放到列表中=====================
// 进程名
m_lsProcessInfo.InsertItem(m_lsProcessInfo.GetItemCount(), pe.szExeFile);
// 进程ID
CString str_process_id;
str_process_id.Format(L"%d", pe.th32ProcessID);
m_lsProcessInfo.SetItemText(m_lsProcessInfo.GetItemCount()-1, 1, str_process_id);
// 线程的数量
CString str_thread_count;
str_thread_count.Format(L"%d", pe.cntThreads);
m_lsProcessInfo.SetItemText(m_lsProcessInfo.GetItemCount()-1, 2, str_thread_count);
// ================================查找进程路径=================================
// 进程路径
HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID); // 打开一个进程
if(h_process)
{
wchar_t wsz_path[MAX_PATH] = {0}; // 接受进程的路径
DWORD dw_size = sizeof(wsz_path);
QueryFullProcessImageName(h_process, 0, wsz_path, &dw_size); // 获取进程的路径
//GetModuleFileNameEX(h_process, NULL, wsz_path, MAX_PATH);
m_lsProcessInfo.SetItemText(m_lsProcessInfo.GetItemCount()-1, 3, wsz_path);
}
::CloseHandle(h_process);
h_process = 0;
// ================================查找进程路径=================================
// =============将进程的信息放到列表中=====================
b_flag = ::Process32Next(h_snapshot, &pe); // 查找下一个进程
}
}
// 关闭句柄
::CloseHandle(h_snapshot);
h_snapshot = 0;
}
1.2 完成 结束进程 按钮函数
1.首先给 结束进程 按钮添加消息处理函数;
2.获取列表中被选中的项的索引: int index = m_lsProcessInfo.GetSelectionMark() ;
3.若有选中项,则获取进程的ID: CString str_process_id = m_lsProcessInfo.GetItemText(index, 1) ;
4.将ID转换为整型并打开该进程句柄: DWORD dw_process_id = _wtoi(str_process_id) ; HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dw_process_id) ;
5.结束进程并关闭句柄: ::TerminateProcess(h_process, 0) ; ::CloseHandle(h_process) ; h_process = 0 ;
// 结束进程
void CTaskManagerDlg::OnBnClickedButton1()
{
// 获取列表中选中的项
int index = m_lsProcessInfo.GetSelectionMark();
if(index != -1) // 当没选中项的时候返回值为-1
{
// 拿进程的ID
CString str_process_id = m_lsProcessInfo.GetItemText(index, 1);
DWORD dw_process_id = _wtoi(str_process_id); // 转换成整型
HANDLE h_process = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dw_process_id); // 打开选中的进程句柄
if(h_process)
{
// 结束进程
::TerminateProcess(h_process, 0);
::CloseHandle(h_process);
h_process = 0;
}
}
}
2.进程间通信
2.1 创建一个 其他项目->空白解决方案 ;
2.2 添加->新建项目 ,创建一个基于对话框的MFC项目,名字为 Send ;再 添加->新建项目 创建一个基于对话框的MFC项目,名字为 Recv ;
1.现在的情况是在一个工作项目中我们创建了两个子工作目录;
2.对于这两个项目 Send 和 Recv 我们可以同时运行: 右键坚决方案中的Recv -> 调试 -> 启动新实例 ; 右键坚决方案中的Send -> 调试 -> 启动新实例 即可同时开启两个对话框;
2.3 两个对话框进行通信
1.先将 Send 对话框上添加一个 Edit Control 控件和一个 Button 按钮命名为 发消息 控件;在 Recv 对话框上添加一个 Edit Control 控件;
2.现在我们想在 Send 对话框的 Edit 控件上输入一串字符,点击 发消息 按钮,这一串字符在 Recv 对话框上的 Edit 控件中进行显示;
3.首先对于 Send 对话框,我们给其上的 Edit 控件添加一个变量,类别选择 Value ,变量类型选择 CString ,名为 m_str_send ;
4.从控件中往下取数据使用的函数 UpDateData(TRUE) ,当参数为 FALSE 时是向控件上发送字符串;
5.取下来数据之后我们需要发送数据,发送消息我们需要获取要接受数据窗口的句柄: HWND h_recv_wnd = ::FindWindow(0, L"Recv") ;
6.发送消息: ::SendMessage(h_recv_wnd, UM_SEND_DATA, m_str_send.GetLength(), (LPARAM)m_str_send.GetBuffer()) ;
7. UM_SEND_DATA 是一个我们定义在总工程文件夹下的 MSG 文件夹下的 Msg.h 中定义的,要使用 UM_SEND_DATA 宏需要引用头文件: #include “…/MSG/Msg.h” ;
8.对于 Recv 对话框,我们也给其上的 Edit 控件添加一个 value 类别的 CString m_str_recv ;
9.给 RecvDlg 类添加一个自定义消息处理函数: afx_msg LRESULT OnRecvData(WPARAM wParam, LPARAM lParam) ;
10.添加消息映射表: ON_MESSAGE(UM_SEND_DATA, &CRecvDlg::OnRecvData) ;
11.在函数 CRecvDlg::OnRecvData 中进行接受数据: int str_length = wParam ; wchar_t pwsz = (wchar_t)lParam ;
// Send
void CSendDlg::OnBnClickedButton1()
{
// 获取edit控件中的字符
UpdateData(TRUE);
//TRACE(m_str_send);
// 获取Recv这个窗口的句柄
//HWND h_recv_wnd = ::FindWindow(0, L"Recv");
//::SendMessage(h_recv_wnd, UM_SEND_DATA, m_str_send.GetLength(), (LPARAM)m_str_send.GetBuffer());
}
// Recv
LRESULT CRecvDlg::OnRecvData(WPARAM wParam, LPARAM lParam)
{
int str_length = wParam;
wchar_t pwsz = (wchar_t)lParam;
return 0;
}
12.下断点进行测试,发现 str_length 的值正确,但是 pwsz 的值不正确,这是因为对于两个程序 Send 和 Recv ,它们都有自己的虚拟内存地址,我们从 Send 给 Recv 发送数据,数据的地址在 Send 程序中假设是 0x10 ,那么在 Recv 中就会自动搜索 Recv 的 0x10 处的内容,但是这两个程序的虚拟内存地址并不是相同的,相同的地址存储的内容可能是不同的,因此我们需要为发送的数据创建一块公用的地址来进行储存;
13.我们需要修改发送端发送的消息类型,不能发送自定义消息,而需要发送 WM_COPYDATA 消息;
// 在发送端
void CSendDlg::OnBnClickedButton1()
{
// 获取edit控件中的字符
UpdateData(TRUE);
// 发送 WM_COPYDATA 消息
COPYDATASTRUCT cds;
cds.dwData = 0;
cds.cbData = (m_str_send.GetLength()+1)*2;
cds.lpData = m_str_send.GetBuffer();
HWND h_recv_wnd = ::FindWindow(0, L"Recv");
::SendMessage(h_recv_wnd, WM_COPYDATA, (WPARAM)(this->m_hWnd), (LPARAM)&cds);
}
14.对于 Recv 端,我们需要给 RecvDlg 类添加一个处理 COPYDATA 的消息处理函数;
15.在 CRecvDlg::OnCopyData 函数中获取字符串并放到控件中;
BOOL CRecvDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
m_str_recv = (wchar_t*)(pCopyDataStruct->lpData);
UpdateData(FALSE);
return CDialogEx::OnCopyData(pWnd, pCopyDataStruct);
}
2.4 在 Send 对话框上添加 剪贴板Ctrl+c 按钮
1.给 Send 对话框添加 剪贴板Ctrl+c 按钮,同时给 Recv 对话框添加 剪贴板Ctrl+v 按钮;
2.给 剪贴板Ctrl+c 按钮添加消息处理函数;
void CSendDlg::OnBnClickedButton2()
{
// 1.打开 剪贴板
// 2.清空 剪贴板
// 3.分配一个全局堆空间
// 4.给全剧堆加锁
// 5.把数据放到全局堆中
// 6.给全局堆解锁
// 7.将全局堆交给剪贴板(设置剪贴板的数据格式)
// 8.关闭剪贴板
}