类的封装
虽然前面几节实现了一个文件扫描器,但是总的来说都比较不好,因为我们无法对这些代码重用,所以我们要对文件扫描器进行封装。
但是一般的对于查找器的封装有两种查询方式,一种是阻塞的方式,另一种是非阻塞的方式,相信大家对这两种工作方式都已经很熟悉了。但是我还是要简单的介绍一下吧!
阻塞方式就是程序在执行这个函数的时候是被卡主的,直到所有的文件找完之后才退出;
非阻塞方式执行这个函数时候,会立即返回,然后调用next函数一个一个的去遍历,这两种方式各有优缺,视应用场景而定。
代码实现
首先来简单的看一下工程结构,如下图所示:
在Windows下进行开发的时候,一般会有一个command的一系列函数,比如获取系统中的内核数,这些方法我们没有办法把它们封装成一个类,但是可以使用一个类把它们装起来。
#ifndef _SYSTEM_H_
#define _SYSTEM_H_
//System.h
#include <Windows.h>
namespace PoEdu
{
class CSystem
{
public:
CSystem();
~CSystem();
inline static unsigned GetSystemCPUNum()
{
if (!m_bInit)
{
GetSystemInfo(&m_systemInfo);
m_bInit = TRUE;
}
return m_systemInfo.dwNumberOfProcessors;
}
private:
static SYSTEM_INFO m_systemInfo; // 系统信息
static BOOL m_bInit;
};
SYSTEM_INFO CSystem::m_systemInfo = { 0 };
BOOL CSystem::m_bInit = FALSE;
}
#endif//!_SYSTEM_H_
#ifndef _POEDUFILEFIND_H_
#define _POEDUFILEFIND_H_
#include <string>
#include <vector>
#include <Windows.h>
//PoEduFileFind.h文件
// 头文件包含顺序
// C/C++
// 系统
// 自己的
namespace PoEdu
{
class CFileFinder
{
class CThread
{
public:
typedef unsigned(__stdcall *ThreadFunc)(void *);
CThread(ThreadFunc func, void *lParam) : m_func(func), m_lParam(lParam), m_hThread(INVALID_HANDLE_VALUE)
{
}
~CThread()
{
if (m_hThread != INVALID_HANDLE_VALUE)
CloseHandle(m_hThread);
}
void Run(unsigned initFlag);
private:
ThreadFunc m_func;
void *m_lParam;
HANDLE m_hThread;
};
public:
CFileFinder(const std::wstring &wstrBeginDirName, const std::wstring &wstrSearch, const unsigned unMaxThreadNumber = 0, const std::wstring wstrFilter = L"*.*");
~CFileFinder();
// 默认是不阻塞
// 阻塞时返回文件数量
// 非阻塞时返回0
unsigned Start(bool bBlock = false);
// 返回下一个文件名(这是针对于非阻塞情况下的)
BOOL GetNextSearchData(std::wstring &wstrNextData);
// 返回所有找到的文件名(这是针对于阻塞情况下的)
std::vector<std::wstring> GetAllGetSearchData();
std::wstring MakeStandardDir(std::wstring wstrDir);
public:
std::vector<std::wstring> m_vecDirNames; // 待扫描的文件夹名
private:
const std::wstring m_wstrSearch; // 搜索对比的内容
unsigned int m_uMaxThreadNum; // 最大线程数
const std::wstring m_wstrFilter; // 过滤条件
std::vector<std::wstring> m_vecSearchResult; // 扫描到的所有文件名
HANDLE m_hHasFile; // 文件是否扫描完的时间内核对象句柄
HANDLE m_hGetFile; // 文件是否拿走
bool m_bHasnextFile; // 是否还有下一个文件
long m_lWaitThreadNum;
HANDLE m_hExitEvent;
HANDLE m_hPushDirEvent;
std::vector<HANDLE> m_vecHandles; // 存放所有的线程句柄
CRITICAL_SECTION m_cs;
// 类函数无法成为线程函数,它是类所有的
// 全局函数才能成为线程函数
// 全局函数才能成为线程函数
static unsigned __stdcall ThreadBegin(void *lParam);
unsigned FuncBegin();
static unsigned __stdcall FindFileThreadFunc(void *lParam);
};
}
#endif//!_POEDUFILEFIND_H_
//PoEduFileFind.cpp文件
#include <process.h>
#include "PoEduFileFind.h"
#include "System.h"
namespace PoEdu
{
void CFileFinder::CThread::Run(unsigned initFlag)
{
m_hThread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, m_func, m_lParam, 0, nullptr));
}
CFileFinder::CFileFinder(const std::wstring& wstrBeginDirName, const std::wstring& wstrSearch, const unsigned uMaxThreadNum, const std::wstring wstrFilter)
: m_wstrSearch(wstrSearch),
m_uMaxThreadNum(uMaxThreadNum == 0 ? PoEdu::CSystem::GetSystemCPUNum() * 2 : uMaxThreadNum),
m_wstrFilter(wstrFilter),
m_hHasFile(nullptr),
m_hGetFile(nullptr),
m_bHasnextFile(true),
m_lWaitThreadNum(0),
m_hExitEvent(nullptr),
m_hPushDirEvent(nullptr)
{
m_vecDirNames.push_back(wstrBeginDirName);
InitializeCriticalSection(&m_cs);
}
CFileFinder::~CFileFinder()
{
DeleteCriticalSection(&m_cs);
for (auto handle : m_vecHandles)
{
CloseHandle(handle);
}
if (m_hHasFile)
{
CloseHandle(m_hHasFile);
}
if (m_hExitEvent)
{
CloseHandle(m_hExitEvent);
}
if (m_hPushDirEvent)
{
CloseHandle(m_hPushDirEvent);
}
}
unsigned CFileFinder::Start(bool bBlock)
{
unsigned ret = 0;
if (bBlock)
{
// 运行查找函数
ret = FuncBegin();
}
else
{
// 启动线程运行查找函数
// 和外部的交互方式开始变得不一样了
m_hHasFile = CreateEvent(nullptr, TRUE, FALSE, nullptr);
m_hGetFile = CreateEvent(nullptr, TRUE, FALSE, nullptr);
CThread thread(ThreadBegin, this);
thread.Run(0);
ret = 0;
}
return ret;
}
BOOL CFileFinder::GetNextSearchData(std::wstring &wstrNextData)
{
SetEvent(m_hGetFile);
BOOL ret = TRUE;
if (m_lWaitThreadNum < (long)m_uMaxThreadNum)
{
WaitForSingleObject(m_hHasFile, INFINITE);
wstrNextData = m_vecSearchResult.back();
ResetEvent(m_hHasFile);
if (!m_bHasnextFile)
{
ret = FALSE;
}
}
return ret;
}
std::vector<std::wstring> CFileFinder::GetAllGetSearchData()
{
std::vector<std::wstring> ret;
return m_vecSearchResult;
}
std::wstring CFileFinder::MakeStandardDir(std::wstring wstrDir)
{
if (wstrDir.back() != L'\\')
{
wstrDir += L'\\';
}
return wstrDir;
}
unsigned CFileFinder::ThreadBegin(void* lParam)
{
// 使用this指针需要考虑同步的问题
CFileFinder *pThis = (CFileFinder *)lParam;
pThis->FuncBegin();
return 0;
}
unsigned CFileFinder::FuncBegin()
{
m_hExitEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
m_hPushDirEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
for (unsigned i = 0; i < m_uMaxThreadNum; ++i)
{
HANDLE handle = (HANDLE)_beginthreadex(nullptr, 0, FindFileThreadFunc, this, 0, nullptr);
if (NULL == handle)
{
--i;
}
else
{
m_vecHandles.push_back(handle);
}
}
// 等待线程查找完成
WaitForSingleObject(m_hExitEvent, INFINITE);
return m_vecSearchResult.size();
}
unsigned CFileFinder::FindFileThreadFunc(void* lParam)
{
CFileFinder *pThis = (CFileFinder *)lParam;
std::wstring wstrSerach = pThis->m_wstrSearch;
std::wstring wstrDirName;
BOOL bRunnable = TRUE;
while (true)
{
EnterCriticalSection(&(pThis->m_cs));
if (pThis->m_vecDirNames.empty())
{
bRunnable = FALSE;
}
else
{
wstrDirName = pThis->m_vecDirNames.back();
pThis->m_vecDirNames.pop_back();
}
LeaveCriticalSection(&(pThis->m_cs));
if (!bRunnable)
{
ResetEvent(pThis->m_hPushDirEvent);
InterlockedAdd(&(pThis->m_lWaitThreadNum), 1);
if (pThis->m_lWaitThreadNum == pThis->m_uMaxThreadNum)
{
SetEvent(pThis->m_hExitEvent);
break;
}
WaitForSingleObject(pThis->m_hPushDirEvent, INFINITE);
InterlockedAdd(&(pThis->m_lWaitThreadNum), -1);
bRunnable = TRUE;
continue;
}
// 扫描
WIN32_FIND_DATA find_data = { 0 };
HANDLE hFile = FindFirstFileW((pThis->MakeStandardDir(wstrDirName) + L"*.*").c_str(), &find_data);
if (INVALID_HANDLE_VALUE != hFile)
{
do
{
if (wcscmp(find_data.cFileName, L".") == 0)
{
continue;
}
if (wcscmp(find_data.cFileName, L"..") == 0)
{
continue;
}
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
EnterCriticalSection(&(pThis->m_cs));
pThis->m_vecDirNames.push_back(pThis->MakeStandardDir(wstrDirName) + find_data.cFileName);
SetEvent(pThis->m_hPushDirEvent);
LeaveCriticalSection(&(pThis->m_cs));
}
else
{
if (wcsstr(find_data.cFileName, wstrSerach.c_str()) != nullptr)
{
EnterCriticalSection(&(pThis->m_cs));
std::wstring wstr = (pThis->MakeStandardDir(wstrDirName) + find_data.cFileName).c_str();
WaitForSingleObject(pThis->m_hGetFile, INFINITE);
pThis->m_vecSearchResult.push_back(wstr);
ResetEvent(pThis->m_hGetFile);
SetEvent(pThis->m_hHasFile);
LeaveCriticalSection(&(pThis->m_cs));
}
}
} while (FindNextFileW(hFile, &find_data));
if (!FindClose(hFile))
printf("error no:%d\r\n", errno);
}
}
pThis->m_bHasnextFile = false;
SetEvent(pThis->m_hHasFile);
return pThis->m_vecDirNames.size();
}
}
// main.cpp函数
#include "PoEduFileFind.h"
int main()
{
printf("阻塞方式:\r\n");
PoEdu::CFileFinder fileFinder(L"C:", L"ntdll");
unsigned uFindSerchNum = fileFinder.Start(false);
std::wstring wstrData;
while (fileFinder.GetNextSearchData(wstrData))
{
printf("%ls\r\n", wstrData.c_str());
}
printf("\r\n\r\n非阻塞方式:\r\n");
PoEdu::CFileFinder fileFinder2(L"C:", L"ntdll");
uFindSerchNum = fileFinder2.Start(true);
printf("共找到%d个与ntdll相关的文件\r\n", uFindSerchNum);
std::vector<std::wstring> ret = fileFinder2.GetAllGetSearchData();
for (auto wstr : ret)
{
printf("%ls\r\n", wstr.c_str());
}
system("pause");
return 0;
}
运行结果如下:
文件扫描器的总结
即使我们使用多线程扫描,也达不到everything那样的扫描速度,这是为什么呢?难道everything使用了更厉害的多线程什么的吗?答案是否定的!因为扫描的原理不一样,我们自己实现的是每一次的搜索都是真真实实的搜索,而everything只有在一开始安装的时候会真正的去搜索,而以后的搜索会在数据库中搜索,它有一个后台服务线程在更新数据。
所以如果我们也使用一个数据库,那么扫描速度也会达到everything那样的速度的。