UltraVNC:C++源码分析远程文件传输界面显示远程端文件系统流程
UltraVNC远程文件传输客户端主要在 FileTransfer.cpp 处理,服务端主要在 vncclient.cpp 处理。
另外这里顺便提一下windows下文件属性结构体 : WIN32_FIND_DATA
前面44个字节表示文件属性。
FILETIME结构持有的64位无符号的文件的日期和时间值。此值表示自1601年1月1日开始的100纳秒为单位的时间。
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件类型,4个字节
FILETIME ftCreationTime; // 文件创建时间,8个字节
FILETIME ftLastAccessTime; // 文件最后一次访问时间,8个字节
FILETIME ftLastWriteTime; // 文件最后一次修改时间,8个字节
DWORD nFileSizeHigh; // 文件长度高32位,指向内存地址的指针,一般是0,4个字节
DWORD nFileSizeLow; // 文件长度低32位,内存大小,4个字节
DWORD dwReserved0; // 系统保留,4个字节
DWORD dwReserved1; // 系统保留,4个字节
TCHAR cFileName[ MAX_PATH ]; // 长文件名
TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;
第一部分 客户端文件传输界面显示远程端磁盘信息
客户端请求远程端磁盘信息
//
// request the list of remote drives
//
void FileTransfer::RequestRemoteDrives()
{
// vnclog.Print(0, _T("RequestRemoteDrives\n"));
if (!m_fFTAllowed) return;
// TODO : hook error !
rfbFileTransferMsg ft;
ft.type = rfbFileTransfer;
ft.contentType = rfbDirContentRequest;
ft.contentParam = rfbRDrivesList; // List of Remote Drives please
ft.length = 0;
m_pCC->WriteExact((char *)&ft, sz_rfbFileTransferMsg, rfbFileTransfer);
return;
}
服务端接收请求,然后获取系统磁盘信息
获取磁盘信息的接口是 dwLen = GetLogicalDriveStrings(256, szDrivesList);
// The client requests the content of a directory or Drives List
case rfbDirContentRequest:
switch (msg.ft.contentParam)
{
// Client requests the List of Local Drives
case rfbRDrivesList:
{
...
}
break;
// Client requests the content of a directory
case rfbRDirContent:
{
...
}
break;
}
break;
服务端发送系统磁盘信息给客户端
rfbFileTransferMsg ft;
ft.type = rfbFileTransfer;
ft.contentType = rfbDirPacket;
ft.contentParam = rfbADrivesList;
ft.length = Swap32IfLE((int)dwLen);
//adzm 2010-09 - minimize packets. SendExact flushes the queue.
m_socket->SendExactQueue((char *)&ft, sz_rfbFileTransferMsg, rfbFileTransfer);
m_socket->SendExact((char *)szDrivesList, (int)dwLen);
客户端接收磁盘信息,并处理 ListRemoteDrives(hWnd, Swap32IfLE(ft.length));
case rfbADrivesList:
ListRemoteDrives(hWnd, Swap32IfLE(ft.length));
m_fFileCommandPending = false;
break;
第二部分 客户端文件传输界面显示远程端某个磁盘内具体信息
客户端请求远程端发送某个磁盘下具体文件夹信息
//
// Request the contents of a remote directory
//
void FileTransfer::RequestRemoteDirectoryContent(HWND hWnd, LPSTR szPath)
{
// vnclog.Print(0, _T("RequestRemoteDirectoryContent\n"));
if (!m_fFTAllowed)
{
m_fFileCommandPending = false;
return;
}
char ofDir[MAX_PATH];
char ofDirT[MAX_PATH];
int nSelected = -1;
int nCount = 0;
HWND hWndRemoteList = GetDlgItem(hWnd, IDC_REMOTE_FILELIST);
ofDir[0] = '\0';
ofDirT[0] = '\0';
if (lstrlen(szPath) == 0)
{
nCount = ListView_GetItemCount(hWndRemoteList);
for (nSelected = 0; nSelected < nCount; nSelected++)
{
if(ListView_GetItemState(hWndRemoteList, nSelected, LVIS_SELECTED) & LVIS_SELECTED)
{
LVITEM Item;
Item.mask = LVIF_TEXT;
Item.iItem = nSelected;
Item.iSubItem = 0;
Item.pszText = ofDirT;
Item.cchTextMax = MAX_PATH;
ListView_GetItem(hWndRemoteList, &Item);
break;
}
}
}
else
{
if (!IsShortcutFolder(szPath))
szPath[6] = '\0';
// szPath always contains a drive letter (X:) or (..)
strcpy(ofDirT, szPath);
// In the case of (..) we keep the current path intact
char szUpDirMask[16];
sprintf(szUpDirMask, "%s..%s", rfbDirPrefix, rfbDirSuffix);
if (strcmp(ofDirT, szUpDirMask))
SetDlgItemText(hWnd, IDC_CURR_REMOTE, "");
}
if (nSelected == nCount || lstrlen(ofDirT) == 0)
{
GetDlgItemText(hWnd, IDC_CURR_REMOTE, ofDirT, sizeof(ofDirT));
if (strlen(ofDirT) == 0)
{
m_fFileCommandPending = false;
return;
}
}
else
{
if (ofDirT[0] == rfbDirPrefix[0] && ofDirT[1] == rfbDirPrefix[1])
{
strncpy(ofDir, ofDirT + 2, strlen(ofDirT) - 3);
ofDir[strlen(ofDirT) - 4] = '\0';
}
else
{
m_fFileCommandPending = false;
return;
}
GetDlgItemText(hWnd, IDC_CURR_REMOTE, ofDirT, sizeof(ofDirT));
if (!_stricmp(ofDir, ".."))
{
char* p;
ofDirT[strlen(ofDirT) - 1] = '\0';
p = strrchr(ofDirT, '\\');
if (p == NULL)
{
m_fFileCommandPending = false;
return;
}
*p = '\0';
}
else
strcat(ofDirT, ofDir);
strcat(ofDirT, "\\");
SetDlgItemText(hWnd, IDC_CURR_REMOTE, ofDirT);
}
strcpy(ofDir, ofDirT);
// Todo: In case of shortcuts dir, do a translation here !
// Select the good drive in the drives combo box (the first time only)
int nIndex = SendDlgItemMessage(hWnd, IDC_REMOTE_DRIVECB, CB_GETCURSEL, 0, 0L);
if (nIndex == LB_ERR)
{
char szDrive[5];
strcpy(szDrive, rfbDirPrefix);
strncat(szDrive, ofDir, 2);
nIndex = SendDlgItemMessage(hWnd, IDC_REMOTE_DRIVECB, CB_FINDSTRING, -1, (LPARAM)(LPSTR)szDrive);
SendDlgItemMessage(hWnd, IDC_REMOTE_DRIVECB, CB_SETCURSEL, nIndex, 0L);
}
ListView_DeleteAllItems(hWndRemoteList);
rfbFileTransferMsg ft;
ft.type = rfbFileTransfer;
ft.contentType = rfbDirContentRequest;
ft.contentParam = rfbRDirContent; // Directory content please
ft.length = Swap32IfLE(strlen(ofDir));
//adzm 2010-09
m_pCC->WriteExactQueue((char *)&ft, sz_rfbFileTransferMsg, rfbFileTransfer);
m_pCC->WriteExact((char *)ofDir, strlen(ofDir));
return;
}
服务端接收请求,然后获取文件夹信息
// The client requests the content of a directory or Drives List
case rfbDirContentRequest:
switch (msg.ft.contentParam)
{
// Client requests the List of Local Drives
case rfbRDrivesList:
{
...
}
break;
// Client requests the content of a directory
case rfbRDirContent:
{
...
}
break;
}
break;
获取文件夹内容主要方法
WIN32_FIND_DATA fd;
HANDLE ff;
BOOL fRet = TRUE;
ff = FindFirstFile(szDir, &fd);
while ( fRet )
{
...
/*这里依次把文件夹下每个文件或者目录信息发送到客户端*/
....
fRet = FindNextFile(ff, &fd);
}
FindClose(ff);
最后服务端发送结束标志
// End of the transfer
ft.contentParam = 0;
ft.length = Swap32IfLE(0);
m_socket->SendExact((char *)&ft, sz_rfbFileTransferMsg, rfbFileTransfer);
客户端接收服务端文件夹信息,这里有两个函数
PopulateRemoteListBox(hWnd, Swap32IfLE(ft.length)); 更新文件传输界面远程端显示的当前目录
ReceiveDirectoryItem(hWnd, Swap32IfLE(ft.length));更新文件传输界面远程端显示具体目录信息
case rfbDirPacket:
switch (ft.contentParam)
{
// Response to a rfbRDrivesList request
case rfbADrivesList:
ListRemoteDrives(hWnd, Swap32IfLE(ft.length));
m_fFileCommandPending = false;
break;
// Response to a rfbRDirContent request
case rfbADirectory:
case rfbAFile:
if (!m_fDirectoryReceptionRunning)
PopulateRemoteListBox(hWnd, Swap32IfLE(ft.length));
else
ReceiveDirectoryItem(hWnd, Swap32IfLE(ft.length));
break;
default: // This is bad. Add rfbADirectoryEnd instead...
if (m_fDirectoryReceptionRunning)
{
FinishDirectoryReception();
m_fFileCommandPending = false;
}
break;
}
break;