create a remote management software of your own (3)

Posted by punzalan at 2020-03-11

In the last article of this series, I will introduce the realization of the control terminal. Compared with the controlled terminal, the control terminal mainly involves software interface, command sending and message display. Next, I will introduce these three aspects to you.

Architecture of control end

The main interface uses a tmpsocket * type container to store all host IDs. This structure uses socket and hard disk serial number to uniquely identify the remote host. Every time a remote host connects to the control side, the control side processes the problem of duplicate clients by checking whether the hard disk serial number already exists.

typedef struct tagTmpSocket {
SOCKET ClientSocket; //连接socket
char HDSerial[64]; //对应主机硬盘序列号

The initialization interface is as follows:

When the IP address and listening port are set, click the start button to start the myserverthread thread. The parameters are IP address and port. Its function is to initialize the socket related library and enter the loop to wait for the connection of the remote host.

DWORD WINAPI MyServerThread()
CTcpTran m_tcptran ; //初始化相关库
SOCKET ServerSocket = m_tcptran.InitSocket(SOCKETBIND,str_ip,u_port,0);
if (ServerSocket == SOCKET_ERROR)
return -1;


while(1) //循环等待监听
ClientSocket = m_tcptran.myaccept(ServerSocket,(struct sockaddr*)&Client_addr,&addrlen) ;
if(ClientSocket == SOCKET_ERROR) //返回新创建的套接字
strcpy( client_ip, inet_ntoa(Client_addr.sin_addr) ); //新创建的套接字的地址结构
clientLinkinfo.s = ClientSocket ; //全局变量clientLinkinfo
clientLinkinfo.BindPort = m_linkinfo.BindPort ;
hThread = CreateThread(0,0,ServerThread,(LPVOID)&clientLinkinfo,0,&Threadid); //创建ServerThread线程
return 0;


While (1) / / loop and wait for listening {ClientSocket = m ﹐ tcptran. Myaccept (ServerSocket, (struct SOCKADDR *) & client ﹐ addrlen); if (ClientSocket = = socket ﹐ error) / / return the newly created socket break; strcpy (client ﹐ IP, INET ﹐ NTA (client ﹐ addr. Sin ﹐); //The address structure of the newly created socket is clientlinkinfo. S = ClientSocket; / / the global variable clientlinkinfo. Clientlinkinfo. Bindport = m linkinfo. Bindport; hthread = createthread (0,0, serverthread, (LPVOID) & clientlinkinfo, 0, & thread); / / create serverthread thread} return 0;}

Once you accept a connection from a remote host, create a serverthread thread for that remote host. This thread solves the problem of duplicate clients. Whenever a remote host is linked, the serial number of the hard disk in the computer information sent by the host is always compared with the serial number of the connected computer hard disk stored in the container. If there is the same serial number, the existing one will be deleted from the container and the new one will be added in, so as to cope with the situation of reconnection after the host drops the line halfway.

LPLINKINFO plast_linkinfo =(LPLINKINFO)lp ;
CTcpTran m_tcptran ;
m_tcptran.m_Socket = plast_linkinfo->s ;
SYSTEMINIT m_sendmsg ;
char ReceiveBuf[sizeof(m_sendmsg)] = {0};
int DataLen = 0;
DataLen = m_tcptran.myrecv(m_tcptran.m_Socket,(char *)&m_sendmsg,sizeof(m_sendmsg),0,60,0,false) ; //接收被控端发来的计算机信息
if (DataLen == 0)
BOOL BDeleteid =FALSE;
UINT InseartItem =0;
for(int j = 0; j<tmp_vector.size();j++)
if (stricmp(m_sendmsg.HDSerial,tmp_vector[j]->HDSerial)==0) //如果已经存在该硬盘序列号
for(int i=0; i<m_clientdlg->m_list.GetItemCount(); i++)
if(stricmp(m_sendmsg.HDSerial,tmp_vector[j]->HDSerial)==0 ) //从客户端列表中查询
BDeleteid = TRUE;
InseartItem = i;
tmp_vector.erase(tmp_vector.begin()+j); //从容器中移除

CString tmp = _T("");
CString m_phyaddr = _T("");
int mm = m_clientdlg->m_list.GetItemCount(); if (BDeleteid)
mm = InseartItem;
m_clientdlg->m_list.DeleteItem(InseartItem); //从列表中移除
tmp.Format("%s",client_ip); //显示IP
m_clientdlg->m_list.InsertItem(mm,""); //列表控件,首先插入一个空的行

.... //在列表中显示远程主机信息

TMPSOCKET *tmp00 = new TMPSOCKET; //创建远程主机标识
tmp00->ClientSocket = m_tcptran.m_Socket; //写入对应的socket
lstrcpy(tmp00->HDSerial,m_sendmsg.HDSerial); //写入硬盘序列号
tmp_vector.push_back(tmp00); //将当前主机标识压入容器
return true;

CString TMP = _t (""); CString m_phyaddr = _t (""); int mm = m_clientdlg - > m_list. Getitemcount(); if (bdeleteid) {mm = insertitem; m_clientdlg - > m_list. Deleteitem (insertitem); / / remove} TMP. Format (""% s ", client_ip); / / display IP m_clientdlg - > m_list. Insertitem (mm," "); //For list control, first insert an empty row, M > clientdlg - > m > list.setitemtext (mm, 0, TMP);

... / / display remote host information in the list

TMP socket * tmp00 = new tmpsocket; / / create the remote host ID memset (tmp00,0, sizeof (tmpsocket)); tmp00 - > ClientSocket = m_tcptran. M_socket; / / write the corresponding socket lstrcpy (tmp00 - > hdserial, m_sendmsg. Hdserial); / / write the serial number tmp_vector. Push_back (tmp00); / / press the current host ID into the container return true;}


Process management interface

After the interface is initialized, the OnStart function is called to send the CMD > process > management command to the remote host, receive the process message sent by the remote host, and store each pointer to the processInfo type into the container.

typedef struct tagProcessInfo
char ProcName[64];
char ProcPath[128];

The InitList function is then invoked to display the pointer to the list.

void OnStart()
COMMAND m_command;
int len = 0; memset((char *)&m_command, 0,sizeof(m_command));
m_command.wCmd = CMD_PROCESS_MANAGE;
m_command.DataSize = 0;
CTcpTran m_tcptran ;
int buf = 0;
len = m_tcptran.mysend(m_procmanagedlg->ClientSocket,(char *)&m_command,sizeof(m_command),0,60);
if (len<0) {
len = m_tcptran.mysend(m_procmanagedlg->ClientSocket,(char *)&m_command,sizeof(m_command),0,60);
int processlen = m_tcptran.myrecv(m_procmanagedlg->ClientSocket,(char *)&buf,sizeof(int),0,60,NULL,false);
if (processlen>0)
std::vector<PROCESSINFO *> pProcInfo;
for(int i=0;i<buf;i++)
tmp = new PROCESSINFO;
memset(tmp, 0,sizeof(PROCESSINFO));
m_tcptran.myrecv(m_procmanagedlg->ClientSocket,(char *)tmp,sizeof(PROCESSINFO),0,60,0,false);

The same service management interface is also the design idea.

Document management interface

The initialization interface stage calls DriverInfoThread to display all the symbols in the left tree list. When clicking one of the nodes, call OnClickTree1 to expand the node, then call ListDirThread to display all files in the current folder under the right remote host file list.

Add the right-click event in the right list, pop up the file deletion option, and call the message processing function onfiledel to delete the file.

Remote shell interface

When clicking the "execute" button, obtain the input command line content, call cmdshellthread to start the remote command line thread, send the command to the remote host, and return the execution result after execution. Note here that when receiving the target data, the last parameter of myrecv function needs to be set to true, which means that the received data will return to display immediately. If it is set to false, because the data length is usually long, there will be obvious display delay, so it is set to return immediately once the data is received.

UINT CmdShellThread(LPVOID lparam)
CTcpTran m_tcptran;
COMMAND m_control;
m_control.wCmd = CMD_CMDSHELL;
CShellDlg *pDlg=(CShellDlg *)lparam;
{ int ret; char RecvBuf[1024]={0}; //接收缓冲 char command[120];
ret=m_tcptran.mysend(pDlg->ClientSocket,(char *)&m_control,sizeof(m_control),0,60);//开启CMDSHELL
ret=m_tcptran.myrecv(pDlg->ClientSocket,RecvBuf,sizeof (RecvBuf),0,10,0,true); //接收目标数据
CString current;
memset(RecvBuf,0,sizeof(RecvBuf)); //缓冲清零
pCmdShellThread = NULL;
return 0;

Remote desktop

After the remote desktop window is started, the DIB (device independent bitmap file, which is a file format, to ensure that the bitmap graphics created by an application can be loaded or displayed by other applications) image of the remote host is obtained, and then it is displayed in the Set BackGround Image function. Entering the Set_BackGround_Image function first sets the header format of the bitmap file and then calls the StretchDIBits function to display it.

DWORD WINAPI Set_BackGroud_Image()
if (m_remotedesktopdlg->m_lpImageData==NULL) return 1;
HWND hWnd=m_remotedesktopdlg->GetSafeHwnd();
CRect rc;
HWND Handle = GetDlgItem(hWnd,IDC_STATIC_PICTURE);//获取窗口主句柄 CWnd *hwnd = CWnd::FromHandle(Handle);
hwnd->GetWindowRect(&rc);//获取picture control的指针,得到区间范围rect
CDC *theDC = m_remotedesktopdlg->m_picStatArea.GetWindowDC ();
if(theDC!= NULL)
int left = m_remotedesktopdlg->m_nLeft; //-m_hScrollBar.GetScrollPos();
int top = m_remotedesktopdlg->m_nTop; //-m_vScrollBar.GetScrollPos();
BYTE *tmp = m_remotedesktopdlg->m_lpImageData;
// set up a DIB
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = m_remotedesktopdlg->m_nBmpWidth; //1024/2;//
bmiHeader.biHeight =m_remotedesktopdlg->m_nBmpHeight; // 768/2;
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24; //24
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = 0;
bmiHeader.biXPelsPerMeter = 0;
bmiHeader.biYPelsPerMeter = 0;
bmiHeader.biClrUsed = 0;
bmiHeader.biClrImportant = 0;
int lines = StretchDIBits(theDC->m_hDC,


return 0;

ReleaseDC(hWnd,*theDC); }

Return 0;}

Code gate

*Original author of this article: Mr Jimu Chu Tianshu, reprint please indicate that it is from