程序的主体框架如下列代码所示:

//by btwsmile#include "stdafx.h"
#include "juice.h"// entry
int _tmain(int argc, _TCHAR* argv[])
{CJuice juicer(argc, argv);juicer.Process();return 0;
}

不难看出,真正进行处理的是CJuice类对象juicer。CJuice类定义在juice.h头文件中,它包含的成员变量有:

PTSTR m_pszWrongSyntax;
PTSTR m_pszInvalidPath;
PTSTR m_pszHelp;
int m_argc;
PTSTR* m_argv;
// complete delete
BOOL m_bComplete;
// report
DWORD m_dwFileCount;
DWORD m_dwFolderCount;
__int64 m_nFileSize;
DWORD m_dwMilliseconds;
// enum type
enum {VSJT_WRONGSYNTAX = 1,VSJT_INVALIDPATH,VSJT_QUERYHELP,VSJT_JUICE
};

前3个变量是字符串指针对象,它们是输出到屏幕上的提示信息。比如,当用户键入的命令有错误,则输出m_pszWrongSyntax提示语法错误。

m_argc和m_argv由main函数指定,它来自用户的输入。比如,用户键入命令vsj /help,此时m_argc = 2,而m_argv含两个字符串,即"vsj"和"/help"。
bool变量m_bComplete表示是否彻底删除文件和目录,缺省为FALSE,只有用户键入命令时使用了/C开关才将它置为TRUE。彻底删除的文件和目录不会进入回收站。

随后的4个整数变量是一些运行统计数据,依次表示:删除的文件计数、删除的目录计数、节省的磁盘空间以及操作所用的时间。

最后是一个匿名枚举,表示用户键入命令的意图。

CJuice类首先要实现自己的构造方法,在构造方法中初始化成员变量。

// constructor
CJuice(int argc, PTSTR* argv) : m_argc(argc), m_argv(argv), m_bComplete(FALSE),m_dwFileCount(0), m_dwFolderCount(0), m_nFileSize(0), m_dwMilliseconds(0)
{m_pszWrongSyntax = _T("Command syntax is incorrect.");m_pszInvalidPath = _T("Invalid directory path.");m_pszHelp =  _T("\nVisual Studio Juicer (c)2012 by btwsmile")_T("\nDelete insignificant files and directories of visual studio solutions.")_T("\n\nVSJ path [/C]")_T("\n\npath\tDirctory path containing visual studio solutions.")_T("\n/C\tCompletely delete files and directories.")_T("\n\nThis command only delete")_T("\n(1) files with extension .sdf, .suo and .aps")_T("\n(2) directories named ipch, debug and release.");
}

为了代码美观一点,3个字符串指针对象的初始化并未放在初始化列表中,这样做也并不会损害程序的效率。

main函数构造了CJuice类对象后,立马调用了Process方法,因此,CJuice需向外提供Process方法。定义如下所示:

// process
void Process()
{int uRet = check_arguments();switch(uRet) {case VSJT_WRONGSYNTAX:display(m_pszWrongSyntax); break;case VSJT_INVALIDPATH:display(m_pszInvalidPath); break;case VSJT_QUERYHELP:display(m_pszHelp); break;case VSJT_JUICE:m_dwMilliseconds = ::GetTickCount();juice(m_argv[1]); m_dwMilliseconds = ::GetTickCount() - m_dwMilliseconds;report();};
}

首先调用了私有方法check_arguments,判断用户键入命令的意图,然后对不同情况进行响应。display方法的作用是打印传入的字符串参数,实现很简单:

// display message
void display(PTSTR psz)
{_tprintf(_T("%s\n"), psz);
}

juice是程序的核心方法,它将对目录进行提取处理,删除那些多余的中间文件。而report方法的作用是打印vsjuicer运行相关的统计数据。

下面依次来看check_arguments,juice以及report方法的实现。首先是check_arguments,其定义如下列代码所示:

// check arguments
UINT check_arguments()
{if(m_argc < 2 || m_argc > 3)return VSJT_WRONGSYNTAX;if(m_argc == 2) {if(   ::lstrcmpi(m_argv[1], _T("/?")) == 0 ||::lstrcmpi(m_argv[1], _T("/help")) == 0)return VSJT_QUERYHELP;return is_path_valid() ? VSJT_JUICE : VSJT_INVALIDPATH;}if(::lstrcmpi(m_argv[2], _T("/c")) != 0)return VSJT_WRONGSYNTAX;else m_bComplete = TRUE;return is_path_valid() ? VSJT_JUICE : VSJT_INVALIDPATH;
}

对用户键入的命令进行检查,也就是检查命令参数是否正确。vsjuicer仅仅支持3条命令:

  1. vsj /?或vsj /help
  2. vsj path
  3. vsj path /c

因此m_argc只能是2或3,接着再分别对参数个数为2和3两种情况分别进行判断。check_arguments调用了私有方法is_path_valid,其作用是检查path是否有效,其定义为:

// is path valid
BOOL is_path_valid()
{if(!::PathFileExists(m_argv[1]))return FALSE;if(::PathIsDirectory(m_argv[1]) != FILE_ATTRIBUTE_DIRECTORY)return FALSE;return TRUE;
}

is_path_valid方法调用两个API函数来实现。

如果用户键入命令的意图是对path目录下的文件进行清理,check_arguments方法的返回值就是VSJT_JUICE。接着,juice方法就会被调用。前面已经说过,juice方法是程序最重要的部分,其实现相对复杂一些,我们先理一理思路:

  • path目录下既包括普通文件,也包括子目录。
  • 对普通文件来说,我们只需判断其后缀名是否为.sdf,.suo或.aps。如果是则删除之,否则就保留它。
  • 对于子目录来说,首先要判断它的名字是否为ipch,debug或release。如果是则直接删除整个文件,否则就进入该目录,递归进行处理。

基于这样的思路,juice方法将是一个递归方法,传入参数pszPath是目录的全路径。juice方法的代码如下,稍微有一点复杂,随后我会对它进行说明。

// juice
void juice(PTSTR pszPath)
{       // delete folder if matchedPTSTR pszFolderName = folder_name(pszPath);if(pszFolderName) {if( ::lstrcmpi(pszFolderName, _T("ipch\\")) == 0 ||::lstrcmpi(pszFolderName, _T("debug\\")) == 0 ||::lstrcmpi(pszFolderName, _T("release\\")) == 0 ) {__int64 nSize = folder_size(pszPath);if(delete_item(pszPath)) {m_dwFolderCount++;m_nFileSize += nSize;}return;}}// delete files if matchedTCHAR szSubPath[MAX_PATH];TCHAR szFileName[MAX_PATH];        ::lstrcpy(szFileName, pszPath);::PathAddBackslash(szFileName);::lstrcat(szFileName, _T("*.*"));WIN32_FIND_DATA fd;BOOL bRet = TRUE;HANDLE hSearch = ::FindFirstFile(szFileName, &fd);while(hSearch != INVALID_HANDLE_VALUE && bRet) {// skip . and ..if(::lstrcmpi(fd.cFileName, _T(".")) == 0 ||::lstrcmpi(fd.cFileName, _T("..")) == 0) {bRet = ::FindNextFile(hSearch, &fd);continue;}// match and delete::memset(szSubPath, 0, sizeof(TCHAR)*MAX_PATH);::lstrcpy(szSubPath, pszPath);::PathAddBackslash(szSubPath);::lstrcat(szSubPath, fd.cFileName);if((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)juice(szSubPath);else {PTSTR pszExtension = ::PathFindExtension(szSubPath);if( ::lstrcmpi(pszExtension, _T(".sdf")) == 0 ||::lstrcmpi(pszExtension, _T(".suo")) == 0 ||::lstrcmpi(pszExtension, _T(".aps")) == 0 )if(delete_item(szSubPath)) {m_dwFileCount++;m_nFileSize += (fd.nFileSizeHigh * ((__int64)MAXDWORD+1)) + fd.nFileSizeLow;}}bRet = ::FindNextFile(hSearch, &fd);}// end while::FindClose(hSearch);
}

首先判断目录名称是否为ipch,debug或release,如果是则直接删除它,否则遍历该目录,分别处理各个文件和子目录。对于子目录的处理是递归调用juice方法来实现的。

juice方法调用了folder_name方法,它的作用是从目录全路径字符串中分解出目录的名称,本质是字符查找。其实现如下所示:

// folder name
PTSTR folder_name(PTSTR pszPath)
{::PathAddBackslash(pszPath);int nLen = ::lstrlen(pszPath);for(int i = nLen - 2; i > -1; --i)if(pszPath[i] == _T('\\') || pszPath[i] == _T('/'))return pszPath + i + 1;return NULL;
}

juice方法还调用了folder_size方法,它的作用是获取某个目录中所有文件的大小之和。因为没有直接的API方法获得目录的大小,所以需递归的遍历目录中的所有文件,将它们的大小累加起来。folder_size的方法如下所示:

// folder size
__int64 folder_size(PTSTR pszPath)
{__int64 nSize = 0;TCHAR szFileName[MAX_PATH];TCHAR szSubPath[MAX_PATH];BOOL bRet = TRUE;::lstrcpy(szFileName, pszPath);::PathAddBackslash(szFileName);::lstrcat(szFileName, _T("*.*"));WIN32_FIND_DATA fd;HANDLE hSearch = ::FindFirstFile(szFileName, &fd);while(hSearch != INVALID_HANDLE_VALUE && bRet) {// skip . and ..if(::lstrcmpi(fd.cFileName, _T(".")) == 0 ||::lstrcmpi(fd.cFileName, _T("..")) == 0) {bRet = ::FindNextFile(hSearch, &fd);continue;}// calculate sizeif((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!= 0) {::memset(szSubPath, 0, MAX_PATH*sizeof(TCHAR));::lstrcpy(szSubPath, pszPath);::PathAddBackslash(szSubPath);::lstrcat(szSubPath, fd.cFileName);nSize += folder_size(szSubPath);}else nSize += fd.nFileSizeHigh*((__int64)MAXDWORD+1) + fd.nFileSizeLow;bRet = ::FindNextFile(hSearch, &fd);}::FindClose(hSearch);return nSize;
}

folder_size内部采用了与juice内部一样的遍历方法,即调用FindFirstFile,FindNextFile以及FindClose这3个API函数来实现。

对目录和文件的删除使用了统一的方式,都是调用的delete_item来实现的。delete_item方法的定义为:

// delete item
BOOL delete_item(PTSTR pszPath)
{TCHAR szTempPath[MAX_PATH] = { 0 };::lstrcpy(szTempPath, pszPath);FILEOP_FLAGS fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO;if(m_bComplete) fFlags &= (~FOF_ALLOWUNDO);SHFILEOPSTRUCT fops = { NULL,FO_DELETE,szTempPath,NULL,fFlags,FALSE,NULL,NULL };int nRet = ::SHFileOperation(&fops);return nRet == 0 ? TRUE : FALSE;
}

其内部调用了API函数SHFileOperation。在填充SHFILEOPSTRUCT变量fops时,根据m_bComplete的值取舍FOF_ALLOWUNDO标志。

好了,就快大功告成了,还剩下最后一个方法report,它的定义再简单不过了:

// report
void report()
{_tprintf(_T("Juicing finished.")_T("\n\nTarget directory: %s")_T("\n\nDeleted\t %10d files.")_T("\nDeleted\t %10d directories.")_T("\n  Saved\t %10.2lf kilobytes.")_T("\n  Spent\t %10d milliseconds.\n"),m_argv[1], m_dwFileCount, m_dwFolderCount,(double)m_nFileSize/1024, m_dwMilliseconds);
}

4个整数变量的计算穿插在上述各个方法之中,比较简单,所以不在此处赘述。

这篇文件贴出了vsjuicer所用到的所有源码,读者完全可以将它们组织起来,顺利通过编译链接。如果你需要vsjuicer的solution files,请留言或发站内信索取。

VS源文件提取工具vsjuicer 实现细节相关推荐

  1. javadoc提取工具_使JavaDoc保持最新状态的工具

    javadoc提取工具 在许多项目中,文档不是最新的. 更改代码后,很容易忘记更改文档. 原因是可以理解的. 在代码中进行更改,然后进行调试,然后希望在测试中进行更改(或者,如果您使用的是更多TDD, ...

  2. NLP标签/关键词-提取工具-java开发

    一.简介 旨在帮助用户自动挖掘文本标签,是特征关键词提取工具,工具中集成了TextRank.TF-IDF算法.词跨度(SPAN)算法和LDA主题模型算法. 使用方法: 二.使用方法 2.1.TextR ...

  3. 小程序源码提取工具 完美解包,最新脚本,一键提取小程序源代码工具

    小程序源码提取工具 完美解包,最新脚本,一键提取小程序源代码工具 小程序解包工具,具体功能请百度一下,解码后直接换掉人家的地址就OK 那么如何才能在手机里找到小程序的源文件包呢? 具体目录位置直接给出 ...

  4. CAB归档文件提取工具cabextract

    CAB归档文件提取工具cabextract 在对Windows系统进行数字取证中,经常会遇到.cab的文件.该文件是Windows的压缩格式,一般是作为安装包文件.Kali Linux预置了专用的提取 ...

  5. Outlook数据提取工具readpst

    Outlook数据提取工具readpst Outlook是Windows常用的邮件客户端.它将用户的信息保存到.pst文件中,如邮件.约会.日历.联系人等信息.为了便于查看这些信息,Kali Linu ...

  6. 信息批量提取工具bulk-extractor

    信息批量提取工具bulk-extractor 在数字取证中,通常需要面对海量的数据,如几百GB甚至TB级别的数据.从这些海量数据中,提取有价值的数据是一个漫长.枯燥.繁琐的过程.Kali Linux提 ...

  7. 注册表数据提取工具RegRipper

    注册表数据提取工具RegRipper 注册表是Windows操作系统一个数据库,用来存储系统和应用程序设置信息.注册表信息分别保存在操作系统中的6个Hive文件中.获取这几个文件,就可以从中提取注册表 ...

  8. 恶意软件 自动化规则提取工具 yargen 原理分析

    yara是用来检测恶意软件的利器,yara规则由特征字符串.特征字节码等元素组成,只要恶意软件包含这些特征元素,就说明该文件是恶意的.但一个一个文件提特征是很耗人力的,所以业界就慢慢出现了一些出色的y ...

  9. Digital Color Meter 颜色值提取工具

    本文已停止更新,点击此链接查看本文最新内容 !!! 1.Digital Color Meter 简介 Digital Color Meter 是一款 Mac 自带的颜色值提取工具. 其它下载地址 Di ...

最新文章

  1. IIS7.5 错误代码0x8007007e HTTP 错误 500.19
  2. Unexpected end of JSON input while parsing near
  3. 阿里中间件再获高度肯定,“三位一体”推动技术普惠
  4. c语言顺序查找算法,c语言实现排序和查找所有算法
  5. vue-cli2、vue-cli3脚手架详细讲解
  6. 为什么很多人C语言学不下去
  7. webpack4.0学习笔记
  8. 几台服务器怎么虚拟成一台,多台服务器虚拟成一台
  9. 最基本的25道深度学习面试问题和答案
  10. Java 全栈工程师进阶路线图
  11. 调和平均数,几何平均数,算数平均数,平方平均数
  12. 腾亚幕墙 HTML 教程
  13. H5开发html文件转换pdf,将HTML页面转换为PDF文件并导出
  14. 涨停前常见的K线形态
  15. html水晶按钮图片,css 如何实现一个水晶按钮的效果呢?
  16. 过完备深度子空间聚类网络:《Overcomplete Deep Subspace Clustering Networks》
  17. 无线破解攻击工具使用详解
  18. Qt+QtWebApp开发笔记(一):QtWebApp介绍、下载和搭建基础封装http轻量级服务器Demo
  19. java执行sql列名无效_JAVA 里查找SQL数据 列名无效
  20. LINUX FTP用户的创建

热门文章

  1. c语言中格式符号错误,C语言中符号格式说明
  2. Centos yum安装 jdk11
  3. 一个微服务业务系统的中台构建之路
  4. 我爱刷题001-2018.02.01
  5. 继域名解析后------------我的网页进不去了!!!(修复方法)
  6. Win7 64位IIS集成php(独创)
  7. C语言逗号表达式赋值、野指针成因、用户标识符、字符串赋值的几个概念
  8. php药膳 源码,-道家药膳
  9. html中绘制渐变伞,CorelDRAW X8快速制作雨伞教程
  10. 数据仓库—stg层_数据产品-数据仓库分层建模