布局操作
给list 控件添加一个右键单击事件
在资源文件里面添加一个菜单,在菜单里面添加下载文件、打开文件、删除文件选项,并设置对应的id
为ID_DOWNFILE、ID_OPENFILE、ID_DELETEFILE
给菜单资源文件添加id:IDR_MENU_RCLICK
弹出菜单代码实现
void CRemoteClientDlg::OnNMRClickListFile(NMHDR* pNMHDR, LRESULT* pResult) { LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); // TODO: 在此添加控件通知处理程序代码 *pResult = 0; // 获取当前点击的客户端坐标 CPoint ptMouse, ptList; GetCursorPos(&ptMouse); ptList = ptMouse; m_list.ScreenToClient(&ptList); // 获取当前选中的节点 int ListSelected = m_list.HitTest(ptList); if (ListSelected < 0) { return; } // 创建菜单对象 CMenu menu; // 从可执行文件加载菜单资源并将其附加到 CMenu 对象。 menu.LoadMenu(IDR_MENU_RCLICK); // 指定菜单中包含的弹出菜单的位置。 第一个菜单项的位置值从 0 开始。 CMenu* pPupup = menu.GetSubMenu(0); if (pPupup != NULL) { // 在指定位置显示浮动弹出菜单,并跟踪弹出菜单上项的选择情况。 pPupup->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, ptMouse.x, ptMouse.y, this); } }
文件下载功能并给大文件传输添加了状态
文件的下载是非常的耗时的,为了避免界面的卡死,选择新开一个线程来下载文件
1 开启线程下载
// 下载文件,按钮事件 void CRemoteClientDlg::OnDownfile() { _beginthread(&CRemoteClientDlg::threadEntryForDownFile, 0, this); // 将光标设置为等待状态 BeginWaitCursor(); m_statusDlg.m_info.SetWindowTextA(_T("命令正在执行中")); // 显示窗口 m_statusDlg.ShowWindow(SW_SHOW); // 窗口居中 m_statusDlg.CenterWindow(this); // 激活dlg设置到前台 m_statusDlg.SetActiveWindow(); }
2 线程入口函数
// 下载文件线程函数入口 void CRemoteClientDlg::threadEntryForDownFile(void* arg) { CRemoteClientDlg* thiz = (CRemoteClientDlg*)arg; thiz->threadDownFile(); _endthread(); }
3 下载文件线程函数
// 下载文件线程函数 void CRemoteClientDlg::threadDownFile() { CClientSocket* pclient = CClientSocket::getInstance(); // 获取文件列表中的文件,获取选中的标签 int nListSelected = m_list.GetSelectionMark(); CString strFile = m_list.GetItemText(nListSelected, 0); // 创建一个文件对话框来获取保存文件路径 CFileDialog dlg(FALSE, "*", strFile, OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY, NULL, this); // 在这里程序是阻塞的 // 用户点了确定后才继续往下执行 int ret = dlg.DoModal(); TRACE("error :%d\r\n", CommDlgExtendedError()); // 这里需要用户点击确认后才继续往下面走 if (ret == IDOK) { TRACE("enter ok\r\n"); // 通过filedlg拿到完整的路径名 FILE* pFile = fopen(dlg.GetPathName(), "wb+"); if (pFile == NULL) { AfxMessageBox("本地没有权限保存文件,或者文件无法创建"); m_statusDlg.ShowWindow(SW_HIDE); EndWaitCursor(); return; } // 获取路径 HTREEITEM hSelected = m_tree.GetSelectedItem(); strFile = GetItemPath(hSelected) + strFile; TRACE("path: %s\r\n", strFile); // 发送命令 // int ret = SendCommandPacket(4, false, (BYTE*)strFile.GetBuffer(), strFile.GetLength()); // SendCommandPacket中的Updata 会影响界面,所以不能直接用 int ret = SendMessage(WM_SEND_PACKET, 4 << 1 | 0, (LPARAM)(LPCSTR)strFile); if (ret < 0) { AfxMessageBox("执行下载命令失败"); TRACE("执行下载命令失败:ret:%d\r\n", ret); m_statusDlg.ShowWindow(SW_HIDE); EndWaitCursor(); return; } // 获取文件长度,第一个包是文件长度 long long nLength = *(long long*)pclient->GetPacket().strData.c_str(); if (nLength == 0) { AfxMessageBox("文件长度为0或无法读取文件"); m_statusDlg.ShowWindow(SW_HIDE); EndWaitCursor(); return; } TRACE("length %ld\r\n", nLength); long long nCount = 0; int count = 0; // 传输数据 while (nCount < nLength) { int ret = pclient->DealCommand(); count++; if (ret < 0) { AfxMessageBox("传输失败"); TRACE("传输失败:%d\r\n", ret); break; } fwrite(pclient->GetPacket().strData.c_str(), 1, pclient->GetPacket().strData.size(), pFile); nCount += pclient->GetPacket().strData.size(); TRACE("nCount : %d\r\n", nCount); } TRACE("recv count : %d", count); if (nCount == nLength) { AfxMessageBox("传输成功"); } fclose(pFile); } pclient->CloseSocket(); m_statusDlg.ShowWindow(SW_HIDE); EndWaitCursor(); MessageBox(_T("下载完成")); }
文件打开功能
void CRemoteClientDlg::OnOpenfile() { // 获取选中的标签 int nSelected = m_list.GetSelectionMark(); CString strFile = m_list.GetItemText(nSelected, 0); // 获取路径 HTREEITEM hSelected = m_tree.GetSelectedItem(); strFile = GetItemPath(hSelected) + strFile; int ret = SendCommandPacket(3, true, (BYTE*)strFile.GetBuffer(), strFile.GetLength()); if (ret < 0) { AfxMessageBox("打开文件命令失败"); TRACE("打开文件失败:%d", ret); } }
文件删除功能
// 删除文件 void CRemoteClientDlg::OnDeletefile() { // 获取选中的标签 int nSelected = m_list.GetSelectionMark(); CString strFile = m_list.GetItemText(nSelected, 0); // 获取路径 HTREEITEM hSelected = m_tree.GetSelectedItem(); strFile = GetItemPath(hSelected) + strFile; int ret = SendCommandPacket(5, true, (BYTE*)strFile.GetBuffer(), strFile.GetLength()); if (ret < 0) { AfxMessageBox("打开文件命令失败"); TRACE("打开文件失败:%d", ret); } LoadFileCurrent(); }
遇到问题
当在另一个线程里面使用需要操作到其它线程ui界面的函数时会不允许
解决方案使用自定义消息
自定义消息步骤
1 自定义消息名称
#define WM_SEND_PACKET (WM_USER + 1)
2 声明实现消息函数
// 2 自定义消息响应函数 afx_msg LRESULT OnSendPacket(WPARAM wParam, LPARAM lParam); // 自定义消息函数 LRESULT CRemoteClientDlg::OnSendPacket(WPARAM wParam, LPARAM lParam) { CString strFile = (LPCSTR)lParam; int ret = SendCommandPacket(wParam >> 1, wParam & 1, (BYTE*)strFile.GetBuffer(), strFile.GetLength()); return ret; }
3 将消息函数和消息绑定起来
// 3 自定义消息,添加到消息映射表里 ON_MESSAGE(WM_SEND_PACKET, &CRemoteClientDlg::OnSendPacket)