Files
SimpleRemoter/server/2015Remote/ShellDlg.cpp
2026-04-19 22:55:21 +02:00

305 lines
9.7 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ShellDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "2015Remote.h"
#include "ShellDlg.h"
#include "afxdialogex.h"
#define EDIT_MAXLENGTH 30000
BEGIN_MESSAGE_MAP(CAutoEndEdit, CEdit)
ON_WM_CHAR()
END_MESSAGE_MAP()
void CAutoEndEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// 获取当前光标位置
int nStart, nEnd;
GetSel(nStart, nEnd);
// 如果光标在最小可编辑位置之前,移动到末尾
if (nStart < (int)m_nMinEditPos) {
int nLength = GetWindowTextLength();
SetSel(nLength, nLength);
}
if (nStart == 30000){
static int hasNotify = 0;
if (hasNotify++ % 10 == 0) {
THIS_APP->PostNotify(_TR("需要清理终端"), _TR("达到字符数限制时,需执行\"clear\"命令"));
}
}
// 调用父类处理输入字符
CEdit::OnChar(nChar, nRepCnt, nFlags);
}
// CShellDlg 对话框
IMPLEMENT_DYNAMIC(CShellDlg, CDialog)
CShellDlg::CShellDlg(CWnd* pParent, Server* IOCPServer, CONTEXT_OBJECT *ContextObject)
: DialogBase(CShellDlg::IDD, pParent, IOCPServer, ContextObject, IDI_ICON_SHELL)
{
m_brBackground.CreateSolidBrush(RGB(0, 0, 0)); // 黑色背景
}
CShellDlg::~CShellDlg()
{
}
void CShellDlg::DoDataExchange(CDataExchange* pDX)
{
__super::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT, m_Edit);
}
BEGIN_MESSAGE_MAP(CShellDlg, CDialog)
ON_WM_CLOSE()
ON_WM_CTLCOLOR()
ON_WM_SIZE()
END_MESSAGE_MAP()
// CShellDlg 消息处理程序
BOOL CShellDlg::OnInitDialog()
{
__super::OnInitDialog();
m_nCurSel = 0;
m_nReceiveLength = 0;
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon,FALSE);
CString str;
str.FormatL("%s - 远程终端", m_IPAddress);
SetWindowText(str);
BYTE bToken = COMMAND_NEXT;
m_ContextObject->Send2Client(&bToken, sizeof(BYTE));
m_Edit.SetWindowTextA(">>");
m_nCurSel = m_Edit.GetWindowTextLengthA();
m_nReceiveLength = m_nCurSel;
m_Edit.m_nMinEditPos = m_nReceiveLength; // 设置最小可编辑位置
m_Edit.SetSel((int)m_nCurSel, (int)m_nCurSel);
m_Edit.PostMessage(EM_SETSEL, m_nCurSel, m_nCurSel);
m_Edit.SetLimitText(EDIT_MAXLENGTH);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
VOID CShellDlg::OnReceiveComplete()
{
if (m_ContextObject==NULL) {
return;
}
AddKeyBoardData();
m_nReceiveLength = m_Edit.GetWindowTextLength();
m_Edit.m_nMinEditPos = m_nReceiveLength; // 更新最小可编辑位置
}
#include <regex>
std::string removeAnsiCodes(const std::string& input)
{
// Match all common ANSI escape sequences:
// CSI sequences: \x1B[...X where X is a letter
// OSC sequences: \x1B]...(\x07|\x1B\\)
// Simple escapes: \x1B[=>] or single char after \x1B
std::regex ansi_regex(
"\x1B\\[[0-9;?]*[A-Za-z]" // CSI: \x1B[...m, \x1B[...H, \x1B[...J, etc.
"|\x1B\\][^\x07]*\x07" // OSC: \x1B]...\x07
"|\x1B\\][^\x1B]*\x1B\\\\" // OSC: \x1B]...\x1B\\ [*]
"|\x1B[=>]" // \x1B= or \x1B>
"|\x1B[78]" // Save/restore cursor
"|\x1B\\([AB0-2]" // Character set selection
);
return std::regex_replace(input, ansi_regex, "");
}
// UTF-8 → ANSI(GBK) 转换,如果输入不是合法 UTF-8 则原样返回
static std::string Utf8ToLocal(const std::string& text)
{
if (text.empty()) return text;
// 尝试以 UTF-8 解码MB_ERR_INVALID_CHARS 会让非法 UTF-8 失败
int wLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.c_str(), -1, NULL, 0);
if (wLen <= 0) return text; // 不是合法 UTF-8原样返回Windows 客户端 GBK 数据走这里)
std::wstring wstr(wLen, 0);
MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, &wstr[0], wLen);
int aLen = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
if (aLen <= 0) return text;
std::string ansi(aLen, 0);
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &ansi[0], aLen, NULL, NULL);
if (!ansi.empty() && ansi.back() == '\0') ansi.pop_back();
return ansi;
}
VOID CShellDlg::AddKeyBoardData(void)
{
// 最后填上0
//Shit\0
m_ContextObject->InDeCompressedBuffer.WriteBuffer((LPBYTE)"", 1); //从被控制端来的数据我们要加上一个\0
Buffer tmp = m_ContextObject->InDeCompressedBuffer.GetMyBuffer(0);
bool firstRecv = tmp.c_str() == std::string(">");
std::string cleaned = removeAnsiCodes(tmp.c_str());
std::string converted = Utf8ToLocal(cleaned); // Linux 客户端 UTF-8 → GBKWindows 客户端原样通过
CString strResult = firstRecv ? "" : CString("\r\n") + converted.c_str();
//替换掉原来的换行符 可能cmd 的换行同w32下的编辑控件的换行符不一致 所有的回车换行
strResult.Replace("\n", "\r\n");
if (strResult.GetLength() + m_Edit.GetWindowTextLength() >= EDIT_MAXLENGTH) {
CString text;
m_Edit.GetWindowTextA(text);
auto n = EDIT_MAXLENGTH - strResult.GetLength() - 5; // 留5个字符输入clear清屏
if (n < 0) {
strResult = strResult.Right(strResult.GetLength() + n);
}
m_Edit.SetWindowTextA(text.Right(max(n, 0)));
}
//得到当前窗口的字符个数
int iLength = m_Edit.GetWindowTextLength(); //kdfjdjfdir
//1.txt
//2.txt
//dir\r\n
//将光标定位到该位置并选中指定个数的字符 也就是末尾 因为从被控端来的数据 要显示在 我们的 先前内容的后面
m_Edit.SetSel(iLength, iLength);
//用传递过来的数据替换掉该位置的字符 //显示
m_Edit.ReplaceSel(strResult);
//重新得到字符的大小
m_nCurSel = m_Edit.GetWindowTextLength();
//我们注意到,我们在使用远程终端时 ,发送的每一个命令行 都有一个换行符 就是一个回车
//要找到这个回车的处理我们就要到PreTranslateMessage函数的定义
}
void CShellDlg::OnClose()
{
CancelIO();
// 等待数据处理完毕
if (IsProcessing()) {
ShowWindow(SW_HIDE);
return;
}
DialogBase::OnClose();
}
CString ExtractAfterLastNewline(const CString& str)
{
int nPos = str.ReverseFind(_T('\n'));
if (nPos != -1) {
return str.Mid(nPos + 1);
}
nPos = str.ReverseFind(_T('\r'));
if (nPos != -1) {
return str.Mid(nPos + 1);
}
return str;
}
BOOL CShellDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN) {
// 屏蔽VK_ESCAPE、VK_DELETE
if (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_DELETE)
return true;
//如果是可编辑框的回车键
if (pMsg->wParam == VK_RETURN && pMsg->hwnd == m_Edit.m_hWnd) {
//得到窗口的数据大小
int iLength = m_Edit.GetWindowTextLength();
CString str;
//得到窗口的字符数据
m_Edit.GetWindowText(str);
//加入换行符
str += "\r\n";
//得到整个的缓冲区的首地址再加上原有的字符的位置,其实就是用户当前输入的数据了
//然后将数据发送出去
LPBYTE pSrc = (LPBYTE)str.GetBuffer(0) + m_nCurSel;
#ifdef _DEBUG
TRACE("[Shell]=> %s", (char*)pSrc);
#endif
if (0 == strcmp((char*)pSrc, "exit\r\n")) { // 退出终端
return PostMessage(WM_CLOSE);
} else if (0 == strcmp((char*)pSrc, "clear\r\n")) { // 清理终端
str = ExtractAfterLastNewline(str.Left(str.GetLength() - 7));
m_Edit.SetWindowTextA(str);
m_nCurSel = m_Edit.GetWindowTextLength();
m_nReceiveLength = m_nCurSel;
m_Edit.m_nMinEditPos = m_nReceiveLength; // 更新最小可编辑位置
m_Edit.SetSel(m_nCurSel, m_nCurSel);
return TRUE;
}
int length = str.GetLength() - m_nCurSel;
m_ContextObject->Send2Client(pSrc, length);
m_nCurSel = m_Edit.GetWindowTextLength();
}
// 限制VK_BACK
if (pMsg->wParam == VK_BACK && pMsg->hwnd == m_Edit.m_hWnd) {
if (m_Edit.GetWindowTextLength() <= m_nReceiveLength)
return true;
}
// 限制VK_LEFT - 不能移动到历史输出区域
if (pMsg->wParam == VK_LEFT && pMsg->hwnd == m_Edit.m_hWnd) {
int nStart, nEnd;
m_Edit.GetSel(nStart, nEnd);
if (nStart <= (int)m_nReceiveLength)
return true;
}
// 限制VK_UP - 禁止向上移动到历史输出
if (pMsg->wParam == VK_UP && pMsg->hwnd == m_Edit.m_hWnd) {
return true;
}
// 限制VK_HOME - 移动到当前命令行开始位置而不是文本开头
if (pMsg->wParam == VK_HOME && pMsg->hwnd == m_Edit.m_hWnd) {
m_Edit.SetSel((int)m_nReceiveLength, (int)m_nReceiveLength);
return true;
}
}
return __super::PreTranslateMessage(pMsg);
}
HBRUSH CShellDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if ((pWnd->GetDlgCtrlID() == IDC_EDIT) && (nCtlColor == CTLCOLOR_EDIT)) {
pDC->SetTextColor(RGB(255, 255, 255)); // 白色文本
pDC->SetBkColor(RGB(0, 0, 0)); // 黑色背景
return (HBRUSH)m_brBackground.GetSafeHandle();
}
return __super::OnCtlColor(pDC, pWnd, nCtlColor);
}
void CShellDlg::OnSize(UINT nType, int cx, int cy)
{
__super::OnSize(nType, cx, cy);
if (!m_Edit.GetSafeHwnd()) return; // 确保控件已创建
// 计算新位置和大小
CRect rc;
m_Edit.GetWindowRect(&rc);
ScreenToClient(&rc);
// 重新设置控件大小
m_Edit.MoveWindow(0, 0, cx, cy, TRUE);
}