在上一篇中,我们以经介绍了程序的流程和框架,在本篇将详细讨论各个功能的实现主要包括

1.获取磁盘信息
2.获取目录信息
3.获取文件信息
4.运行指定文件
5.删除指定文件
6.删除指定目录
7.创建指定目录
8.上传下载文件
9.获取远程文件图标

获取磁盘信息

磁盘信息可以用API GetDriveType来实现,它以路径名作为参数(如C:/)返回磁盘类型,其实例代码如下

DWORD GetDriverProc(COMMAND command,SOCKET client)
{
for(char i='A';i<='Z';i++)
{
char x[20]={i,':'};
UINT Type=GetDriveType(x);
if(Type==DRIVE_FIXED||Type==DRIVE_REMOVABLE||Type==DRIVE_CDROM)
{
/*返回处理结果...*/
}
}
return 0;
}

GetDriveType可能返回的结果如下

#define DRIVE_UNKNOWN    0 // 无效路径名
#define DRIVE_NO_ROOT_DIR   1 // 无效路经,如无法找到的卷标
#define DRIVE_REMOVABLE 2 // 可移动驱动器
#define DRIVE_FIXED        3 // 固定的驱动器
#define DRIVE_REMOTE    4 // 网络驱动器
#define DRIVE_CDROM        5 // CD-ROM
#define DRIVE_RAMDISK    6 // 随机存取(RAM)磁盘

在上面的实例代码中我们只取,硬盘,光驱和移动磁盘

获取目录信息

这里只要枚举用户指定的目录就可以了,其实例代码如下:

DWORD GetDirInfoProc(COMMAND command,SOCKET client)
{
/*command为要枚举的路径如(C:/)client为返回结果的SOCKET句柄*/

FILEINFO fi;
memset((char*)&fi,0,sizeof(fi));

strcat((char*)command.lparam,"*.*");//枚举所有文件

CFileFind file;
BOOL bContinue = file.FindFile((char*)command.lparam);

while(bContinue)
{
memset((char*)&fi,0,sizeof(fi));

bContinue = file.FindNextFile();
if(file.IsDirectory()) //为目录
{
fi.IsDir=true;
}
strcpy(fi.FileName,file.GetFileName().LockBuffer()); //保存文件名称

if(send(client,(char*)&fi,sizeof(cmd),0)==SOCKET_ERROR)
{
cout << "Send Dir is Error/n";
}
}
return 0;
}

获取文件信息

以下实例代码用来获取 文件的名称,路径,时间,属性等信息

DWORD FileInfoProc (COMMAND command,SOCKET client)
{
/*command为要查看的文件如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/
FILEINFO fi;
HANDLE hFile;
WIN32_FIND_DATA WFD;
memset((char*)&WFD,0,sizeof(WFD));
if((hFile=FindFirstFile((char*)command.lparam,&WFD))==INVALID_HANDLE_VALUE) //查看文件属性
{
fi.Error=true;
return 0;
}
//得到文件的相关信息
SHGetFileInfo(WFD.cFileName, 
FILE_ATTRIBUTE_NORMAL,
&shfi, sizeof(shfi),
SHGFI_ICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME );
strcpy(fi.FileName,(char*)command.lparam); //文件路径
FileLen=(WFD.nFileSizeHigh*MAXDWORD+WFD.nFileSizeLow)/1024; //文件长度
fi.FileLen=FileLen;
//转化格林时间到本地时间
FileTimeToLocalFileTime(&WFD.ftLastWriteTime,&localtime);
FileTimeToSystemTime(&localtime,&systime);
//文件修改时间
sprintf(stime,"%4d-%02d-%02d %02d:%02d:%02d",
systime.wYear,systime.wMonth,systime.wDay,systime.wHour,
systime.wMinute,systime.wSecond);
if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_HIDDEN)
{
/*隐藏文件...*/
}else
if(GetFileAttributes((char*)command.lparam)&FILE_ATTRIBUTE_READONLY)

/*只读文件...*/
}
send(client,(char*)&fi,sizeof(fi),0);
FindClose(hFile);
return 0;
}

运行指定文件

运行文件 有以下几种方法 1.WinExec 2.ShellExecute 3.CreateProcess

这里使用的是ShellExecute其实例代码如下

DWORD ExecFileProc (COMMAND command,SOCKET client)
{
/*command为要运行的文件路径如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/

COMMAND     cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=ExecFile;

if(ShellExecute(NULL,"open",(char*)command.lparam,NULL,NULL,SW_HIDE)<(HINSTANCE)32)
{
strcpy((char*)cmd.lparam,"文件执行失败!");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"文件执行成功!");
send(client,(char*)&cmd,sizeof(cmd),0);
}

return 0;
}

API函数ShellExecute原形为:

HINSTANCE ShellExecute(    
HWND hwnd,             //窗口句柄 
LPCTSTR lpOperation, //操作类型 
LPCTSTR lpFile,          //文件指针 
LPCTSTR lpParameters,    //文件参数 
LPCTSTR lpDirectory, //缺省目录 
INT nShowCmd          //显示方式 
);

这是一个相当有意思的函数,在调用此函数时只须指定要执行的文件名,而不必管用什么程序去打开
或执行文件,WINDOWS会自动根据要打开或执行的文件去判断该如何执行文件或用什么程序去打开文件,如果
要求不高的话比CreateProcess要好用的多,如果想做出像NCPH和灰鸽子那样带参数执行的话,其实也不难
只要指定lpParameters为执行参数就可了

删除指定文件

DWORD DelFileProc (COMMAND command,SOCKET client)
{
/*command为要删除的文件路径如(C:/TEST.EXE)client为返回结果的SOCKET句柄*/
COMMAND     cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=DelFile;
SetFileAttributes((char*)command.lparam,FILE_ATTRIBUTE_NORMAL); //去掉文件的系统和隐藏属性
if(DeleteFile((char*)command.lparam)==0)
{
strcpy((char*)cmd.lparam,"文件删除失败!");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"文件删除成功!");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return 0;
}

需要注意的是在 DeleteFile前应该去文件的系统和隐藏属性,否则会删除失败

删除目录

可以用RemoveDirectory函数删除目录,但是RemoveDirectory有个缺点就是只能删除为空的的目录,对于不为空
的目录就无能为力了,想要删除不无空的目录可以使用下面的实例代码

BOOL DeleteDirectory(char *DirName)
{
CFileFind tempFind;
char tempFileFind[200];
sprintf(tempFileFind,"%s*.*",DirName);
BOOL IsFinded=(BOOL)tempFind.FindFile(tempFileFind);
while(IsFinded)
{
   IsFinded=(BOOL)tempFind.FindNextFile();
   if(!tempFind.IsDots())
   {
      char foundFileName[200];
      strcpy(foundFileName,tempFind.GetFileName().GetBuffer(200));
      if(tempFind.IsDirectory())
      {
         char tempDir[200];
         sprintf(tempDir,"%s//%s",DirName,foundFileName);
         DeleteDirectory(tempDir);
      }
      else
      {
         char tempFileName[200];
         sprintf(tempFileName,"%s//%s",DirName,foundFileName);
SetFileAttributes(tempFileName,FILE_ATTRIBUTE_NORMAL); //去掉文件的系统和隐藏属性
         DeleteFile(tempFileName);
cout <<"now delete "<<tempFileName<<"/n";
      }
   }
}
tempFind.Close();
if(!RemoveDirectory(DirName))
{
   return FALSE;
}
return TRUE;
}

这个函数的代码可以参照上面 枚举目录的代码来看,它的原理就是枚举目录下的所有文件并删除,最后删除
指定目录,成功返回TRUE失败则返回FALSE,这段代码可以直使用,但要小心使用,因为我在传参数时的失误
结果把整个D盘差点清空了..........

创建目录

实例代码如下:

DWORD CreateDirProc (COMMAND command,SOCKET client)
{
/*command为要创建目录的路径如(C:/)client为返回结果的SOCKET句柄*/
COMMAND     cmd;
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=CreateDir;
if(::CreateDirectory((char*)command.lparam,NULL))
{
strcpy((char*)cmd.lparam,"创建目录成功!");
send(client,(char*)&cmd,sizeof(cmd),0);
}
else
{
strcpy((char*)cmd.lparam,"创建目录失败!可能有重名文件或文件夹");
send(client,(char*)&cmd,sizeof(cmd),0);
}
return 0;
}

在创建目录时应该注意几点,首先创始目录的上层目录必须是存在的,比如想创建C:/DIR1/DIR2目录,要求
DIR1是必须存在,用CreateDirectory并不能创建多级目录.再者不可以存在和要创建目录同名的目录和文件
因为在磁盘上目录和文件的存放格式是相同的,惟一不同的是 目录的属性与文件属性不同
(FILE_ATTRIBUTE_DIRECTORY属性),所在即使有同名文件也会创始失败.

上传下载文件

上传下载是是文件管理的重点所在,在这里按文件的大小,分两种情况讨论文件的传输方法

小文件的传输相对比较简单可按以下方法进行

1.首先发送文件长度和名称
2.跟据文件长度建立缓冲区
3.读取整个文件到缓冲区
4.发送缓冲区里的内容

其实现代码如下:

CFile file;
FILEINFO fileinfo;
if(file.Open(path,CFile::modeRead|CFile::typeBinary))
{
fileinfo.FileLen=file.GetLength(); //文件长度
strcpy(fileinfo.FileName,file.GetFileName()); //文件名称
send(client,(char*)&fileinfo,sizeof(fileinfo),0); //发送长度和名称

char *date=new char[fileinfo.FileLen]; //分配和文件长度相同的缓冲区
int nLeft=fileinfo.FileLen;
int idx=0;
file.Read(date,fileinfo.FileLen); //读整个文件到缓冲区
while(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0); //发送文件
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
}
file.Close();
delete[] date;
}
跟据上面的实例相信大家可以领悟到文件传输的基本原理和方法,虽然很简单但用它传输小文件还是非常实用的

大文件传输方法

用上面的方法传输小文件还可以,但是大文件呢?比如一个500M的电影.上面的方法就会力不从心了因为
按思路要创建一个跟文件大小相同的缓冲区,显然这是不太现实的,我们就得采用另种方法了,在这里我们使用
分块文件传输,所谓分块是指把大文件分成若干小文件,然后传输,比如设定每块大小为64KB其思路如下

1.取得文件长度和名称
2.跟据长度/64KB计算文件块数
3.分配64KB缓冲区
4.读文件到缓冲区
5.发送缓冲的数据
6.重复4,5两步直到发完所有数据

其实现代码如下:

#define CHUNK_SIZE (64*1024) //分为64K块传输

DWORD   GetFileProc (COMMAND command,SOCKET client)
{
/*command为要下载文件的路径如(C:/TEST.EXE)client为发送文件的SOCKET句柄*/

COMMAND     cmd;
FILEINFO fi;
memset((char*)&fi,0,sizeof(fi));
memset((char*)&cmd,0,sizeof(cmd));
cmd.ID=GetFile;

CFile file;
int nChunkCount=0; //文件块数

if(file.Open((char*)command.lparam,CFile::modeRead|CFile::typeBinary))//打开文件
{
int FileLen=file.GetLength(); //取文件长度
fi.FileLen=file.GetLength();
strcpy((char*)fi.FileName,file.GetFileName()); //取文件名称
memcpy((char*)&cmd.lparam,(char*)&fi,sizeof(fi));
send(client,(char*)&cmd,sizeof(cmd),0); //发送文件名称和长度

nChunkCount=FileLen/CHUNK_SIZE; //文件块数
if(FileLen%nChunkCount!=0)
nChunkCount++;
char *date=new char[CHUNK_SIZE]; //创建数据缓冲区
for(int i=0;i<nChunkCount;i++) //分为nChunkCount块发送
{
int nLeft;
if(i+1==nChunkCount) //最后一块
nLeft=FileLen-CHUNK_SIZE*(nChunkCount-1);
else
nLeft=CHUNK_SIZE;
int idx=0;
file.Read(date,CHUNK_SIZE); //读取文件
while(nLeft>0)
{
int ret=send(client,&date[idx],nLeft,0);//发送文件
if(ret==SOCKET_ERROR)
{
break;
}
nLeft-=ret;
idx+=ret;
}
}
file.Close();
delete[] date;
}
return 0;
}
这样文件传输部分就完成了,止于客户端的实现于上面代码其本相同,只是由读文件变为写文件,详细请参考源代码

获取远程ICO文件图标

我们在文件列表框中需要显示文件的图标,但远程文件的ICO图标是无法直接得到的
猛若RADMIN 黑洞者也没有到(对于EXE文件只显示可执行程序图示),当然了也不见的决对没有......

我们可以通过如下变通方法得到:就是跟据文件的扩展名,从本地注册表中查找对应的程序图标

不过这也有它的缺点对于EXE文件它只能显示一个可执行文件的图示,而且只能显示注册过的图示比如,如果
本机装有WINRAR那么就可以识别.RAR的文件图示,否则就无法识别...

实现方法

CImageList m_ImageList;

m_ImageList.Create(32,32,ILC_COLOR32,10,30); //创建图示
m_list.SetImageList(&m_ImageList,LVSIL_NORMAL); //与列表控件相关连

SHFILEINFO info;
memset((char*)&info,0,sizeof(info));

SHGetFileInfo(fi->FileName,0,&info,sizeof(&info),   SHGFI_ICON|SHGFI_USEFILEATTRIBUTES);//关键所在

int i = m_ImageList.Add(info.hIcon);
m_list.InsertItem(i,fi->FileName,i);

原来我试图在Server端通过上面的代码把info.hIcon句柄保存下来,然后放到Client,在单台电脑上很好使,但
Server在另一台电脑上时就玩完了,因为info.hIcon里保存的句柄是个索引而每台机器上的索引是不相同的所以
直接导致的结果就是:什么也显示不出来.....

到这里程序的主要功能实现就介绍完了,下一篇将详细介绍断点续传和多线程传输的实现

VC实用小知识总结 (一),转http://blog.csdn.net/myiszjf/article/details/10007431的更多相关文章

  1. Socket的用法——NIO包下SocketChannel的用法 ———————————————— 版权声明:本文为CSDN博主「茶_小哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ycgslh/article/details/79604074

    服务端代码实现如下,其中包括一个静态内部类Handler来作为处理器,处理不同的操作.注意在遍历选择键集合时,没处理完一个操作,要将该请求在集合中移除./*模拟服务端-nio-Socket实现*/pu ...

  2. 微信小程序真机预览跟本地不同的问题。原文地址:https://blog.csdn.net/qq_27187991/article/details/69664247/

    微信小程序中出现最多的一个问题,就是真机跟本地不同:我简单列举一些我发现的原因,给大家参考,大家也可以把自己发现的东西回复给我,给我参考: 本地看不到数据,就先让本地能看到数据,再看本帖....特别提 ...

  3. 关于泛型擦除的知识(来源于csdn地址:https://blog.csdn.net/briblue/article/details/76736356)

    泛型,一个孤独的守门者. 大家可能会有疑问,我为什么叫做泛型是一个守门者.这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇.泛型是 Java 中一个很小巧的概念,但 ...

  4. ubuntu下设置jupyter notebook 2017年07月29日 19:28:34 小旋锋 阅读数:8329 标签: ubuntu 更多 个人分类: python 二三事 来源:http://blog.csdn.net/suzyu12345/article/details/51037905 Ipython Notebook现在已经改名为Ipython jupyter,是最知名最好用的

    ubuntu下设置jupyter notebook     来源:http://blog.csdn.net/suzyu12345/article/details/51037905 Ipython No ...

  5. VC常用小知识

    (1) 如何通过代码获得应用程序主窗口的 指针?主窗口的 指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现.AfxGetMainWnd() ->Sho ...

  6. SpringBoot实用小知识之Maven中dependencys和dependencymanagement区别

    利用pom管理引用包时,如果是单项目的话就直接在dependencies引用了,若有一个大工程项目里面包含多个子模块,则为了所有项目模块包的版本统一和好管理,则需要用到dependencyManage ...

  7. Maven在Eclipse中的实用小技巧

    前言     我们在开发的工程中很多都是Maven项目,这样更加便于我们jar包的管理.而我们一般使用的IDE都是Eclipse,由于我们在日常的开发过程中会经常要用到一些Maven的操作,所以我今天 ...

  8. Android简易实战教程--第三十四话《 自定义SeekBar以及里面的一些小知识》

    转载本专栏文章,请注明出处尊重原创:博客地址http://blog.csdn.net/qq_32059827/article/details/52849676:小杨的博客 许多应用可能需要加入进度,例 ...

  9. 实用小技巧(一):UIScrollView中上下左右滚动方向的判断

    https://www.jianshu.com/p/93e8459b6dae 2017.06.01 01:13* 字数 674 阅读 1201评论 0喜欢 1 2017.06.01 01:13* 字数 ...

随机推荐

  1. Web之CSS开发技巧: CSS 居中大全

    <center> text-align:center 在父容器里水平居中 inline 文字,或 inline 元素 vertical-align:middle 垂直居中 inline 文 ...

  2. 《python基础教程》笔记之 其它语句1

    print 相关 print可以打印多个表达式,只要将它们用逗号隔开就好,结果中每个参数之间都会插入一个空格,使用+可以避免空格,如 >>> print 'age:',42age: ...

  3. 【学习笔记】【Foundation】集合Set

    不可变集合 NSSet :集合元素无顺序,没有索引号,元素不可重复. NSSet在功能上可看做是NSArray的父集,它是一个更通用的类. NSSet包含如下常用方法: setByAddingObje ...

  4. .NET MVC插件化开发(支持Script和css压缩)

    上一篇博文里面,没有支持Script和css的压缩功能以及script和css的路径问题也没有解决,所以重新发布一个版本,解决了这几个问题,并且优化了插件路由注册,现在可以很方便的实现热插拔web插件 ...

  5. web安全:xss && csrf

    首先在user.php文件中去除黑名单的第一行标签,在白名单中添加<script>E1:csrf攻击zoobarcsrf:cross-site request forgery    跨站伪 ...

  6. 无线wifi-PJ-之在开启WPS下使用reaver

    PJ简单解释: PIN码分前4和后4,先破前4只有最多一万个组合,破后4中的前3只有一千个组 合,一共就是一万一千个密码组合. 10的4次方+10的3次方=11000个密码组合. 当reaver确定前 ...

  7. 设定PCB电路板形状和物理边界

    1 设定PCB电路板形状和物理边界 在Protel DXP的PCB板文件向导中,我们已经初步确定了电路板的形状和物理边界.但我们在绘制PCB板之前,也许还会对电路板的边界的细节加以调整.如果我们要对电 ...

  8. MCS-51系列和80C51系列单片机是否相同

    MCS是Intel公司单片机的系列符号.Intel推出有MCS-48.MCS-51.MCS-96系列单片机. MCS-51系列单既包括三个基本型80C31.8051.8751,以及对应的低功耗型号80 ...

  9. op编译信赖的库

    Table of known prerequisites and their corresponding packages Here's a table with the package name f ...

  10. 九度oj 1482:玛雅人的密码

    题意:输入一个长度为n(2<=n<=13)的字符串(所有字符为'0','1'或'2'),通过交换相邻的两个字符,至少要交换多少次才能处出现字串"2012",输出这个值, ...