master
gmh5225 2 years ago
parent ab57bdf32a
commit d2fcbe9625
No known key found for this signature in database
GPG Key ID: 3BBC731F40B2CEC1

178
.gitignore vendored

@ -0,0 +1,178 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Azure Emulator
efc/
rfc/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

BIN
CE.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

@ -0,0 +1,4 @@
#pragma once
#define CE_DETECTION_1 L"ADDRESSES.FIRST"
#define CE_DETECTION_2 L"MEMORY.FIRST"

@ -1,9 +0,0 @@
MIT License
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,157 @@
//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
#include "stdafx.h"
#include "ReadDirectoryChanges.h"
#include "CEInfo.h"
LPCWSTR ExplainAction( DWORD dwAction );
bool TryGetKeyboardInput( HANDLE hStdIn, bool &bTerminate, char* buf );
//
// When the application starts, it immediately starts monitoring your home
// directory, including children, as well as C:\, not including children.
// The application exits when you hit Esc.
// You can add a directory to the monitoring list by typing the directory
// name and hitting Enter. Notifications will pause while you type.
//
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
const DWORD dwNotificationFlags =
FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_CREATION
| FILE_NOTIFY_CHANGE_FILE_NAME;
// Create the monitor and add two directories.
CReadDirectoryChanges changes;
changes.AddDirectory(_tgetenv(_T("USERPROFILE")), true, dwNotificationFlags);
changes.AddDirectory(_T("C:\\"), false, dwNotificationFlags);
HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE);
const HANDLE handles[] = { hStdIn, changes.GetWaitHandle() };
char buf[MAX_PATH];
bool bTerminate = false;
while (!bTerminate)
{
DWORD rc = ::WaitForMultipleObjectsEx(_countof(handles), handles, false, INFINITE, true);
switch (rc)
{
case WAIT_OBJECT_0 + 0:
// hStdIn was signaled. This can happen due to mouse input, focus change,
// Shift keys, and more. Delegate to TryGetKeyboardInput().
// TryGetKeyboardInput sets bTerminate to true if the user hits Esc.
if (TryGetKeyboardInput(hStdIn, bTerminate, buf))
{
std::string strbuf(buf);
std::wstring wstrbuf(strbuf.begin(), strbuf.end());
changes.AddDirectory(wstrbuf.c_str(), false, dwNotificationFlags);
}
break;
case WAIT_OBJECT_0 + 1:
// We've received a notification in the queue.
{
DWORD dwAction;
std::wstring wstrFilename;
if (changes.CheckOverflow())
wprintf(L"Queue overflowed.\n");
else
{
changes.Pop(dwAction, wstrFilename);
if (wstrFilename.find(CE_DETECTION_1) != std::wstring::npos ||
wstrFilename.find(CE_DETECTION_2) != std::wstring::npos
)
{
wprintf(L"Find CE:%s %s\n", ExplainAction(dwAction), wstrFilename.c_str());
}
}
}
break;
case WAIT_IO_COMPLETION:
// Nothing to do.
break;
}
}
// Just for sample purposes. The destructor will
// call Terminate() automatically.
changes.Terminate();
return EXIT_SUCCESS;
}
LPCWSTR ExplainAction( DWORD dwAction )
{
switch (dwAction)
{
case FILE_ACTION_ADDED :
return L"Added";
case FILE_ACTION_REMOVED :
return L"Deleted";
case FILE_ACTION_MODIFIED :
return L"Modified";
case FILE_ACTION_RENAMED_OLD_NAME :
return L"Renamed From";
case FILE_ACTION_RENAMED_NEW_NAME :
return L"Renamed To";
default:
return L"BAD DATA";
}
}
bool TryGetKeyboardInput( HANDLE hStdIn, bool &bTerminate, char* buf )
{
DWORD dwNumberOfEventsRead=0;
INPUT_RECORD rec = {0};
if (!::PeekConsoleInput(hStdIn, &rec, 1, &dwNumberOfEventsRead))
return false;
if (rec.EventType == KEY_EVENT)
{
if (rec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
bTerminate = true;
else if (rec.Event.KeyEvent.wVirtualKeyCode > VK_HELP)
{
if (!gets_s(buf, MAX_PATH)) // End of file, usually Ctrl-Z
bTerminate = true;
else
return true;
}
}
::FlushConsoleInputBuffer(hStdIn);
return false;
}

@ -1,3 +1,23 @@
# Detection-CheatEngine
## Detection-CheatEngine
Using ReadDirectoryChangesW to detect CheatEngine
![image](CE.png)
## Principle
[Understanding ReadDirectoryChangesW](
http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html)
## Expansion
You can add your detection vector easily in [CEInfo.h](https://github.com/gmh5225/Detection-CheatEngine/blob/master/CEInfo.h)
```C++
#pragma once
#define CE_DETECTION_1 L"ADDRESSES.FIRST"
#define CE_DETECTION_2 L"MEMORY.FIRST"
```
## Compile
- Visual Studio 2022
- llvm-msvc [[link]](https://github.com/NewWorldComingSoon/llvm-msvc-build)
## Some discussions on UnknownCheats
https://www.unknowncheats.me/forum/general-programming-and-reversing/502279-using-readdirectorychangesw-detect-cheatengine.html

@ -0,0 +1,113 @@
//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
#include "stdafx.h"
#include "ReadDirectoryChanges.h"
#include "ReadDirectoryChangesPrivate.h"
using namespace ReadDirectoryChangesPrivate;
///////////////////////////////////////////////////////////////////////////
// CReadDirectoryChanges
CReadDirectoryChanges::CReadDirectoryChanges(int nMaxCount)
: m_Notifications(nMaxCount)
{
m_hThread = NULL;
m_dwThreadId= 0;
m_pServer = new CReadChangesServer(this);
}
CReadDirectoryChanges::~CReadDirectoryChanges()
{
Terminate();
delete m_pServer;
}
void CReadDirectoryChanges::Init()
{
//
// Kick off the worker thread, which will be
// managed by CReadChangesServer.
//
m_hThread = (HANDLE)_beginthreadex(NULL,
0,
CReadChangesServer::ThreadStartProc,
m_pServer,
0,
&m_dwThreadId
);
}
void CReadDirectoryChanges::Terminate()
{
if (m_hThread)
{
::QueueUserAPC(CReadChangesServer::TerminateProc, m_hThread, (ULONG_PTR)m_pServer);
::WaitForSingleObjectEx(m_hThread, 10000, true);
::CloseHandle(m_hThread);
m_hThread = NULL;
m_dwThreadId = 0;
}
}
void CReadDirectoryChanges::AddDirectory( LPCTSTR szDirectory, BOOL bWatchSubtree, DWORD dwNotifyFilter, DWORD dwBufferSize )
{
if (!m_hThread)
Init();
CReadChangesRequest* pRequest = new CReadChangesRequest(m_pServer, szDirectory, bWatchSubtree, dwNotifyFilter, dwBufferSize);
QueueUserAPC(CReadChangesServer::AddDirectoryProc, m_hThread, (ULONG_PTR)pRequest);
}
void CReadDirectoryChanges::Push(DWORD dwAction, std::wstring& wstrFilename)
{
auto item = TDirectoryChangeNotification(dwAction, wstrFilename);
m_Notifications.push(item);
}
bool CReadDirectoryChanges::Pop(DWORD& dwAction, std::wstring& wstrFilename)
{
TDirectoryChangeNotification pair;
if (!m_Notifications.pop(pair))
return false;
dwAction = pair.first;
wstrFilename = pair.second;
return true;
}
bool CReadDirectoryChanges::CheckOverflow()
{
bool b = m_Notifications.overflow();
if (b)
m_Notifications.clear();
return b;
}

@ -0,0 +1,142 @@
//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
#pragma once
#include "ThreadSafeQueue.h"
typedef pair<DWORD, std::wstring> TDirectoryChangeNotification;
namespace ReadDirectoryChangesPrivate
{
class CReadChangesServer;
}
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Track changes to filesystem directories and report them
/// to the caller via a thread-safe queue.
/// </summary>
/// <remarks>
/// <para>
/// This sample code is based on my blog entry titled, "Understanding ReadDirectoryChangesW"
/// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
/// </para><para>
/// All functions in CReadDirectoryChangesServer run in
/// the context of the calling thread.
/// </para>
/// <example><code>
/// CReadDirectoryChanges changes;
/// changes.AddDirectory(_T("C:\\"), false, dwNotificationFlags);
///
/// const HANDLE handles[] = { hStopEvent, changes.GetWaitHandle() };
///
/// while (!bTerminate)
/// {
/// ::MsgWaitForMultipleObjectsEx(
/// _countof(handles),
/// handles,
/// INFINITE,
/// QS_ALLINPUT,
/// MWMO_INPUTAVAILABLE | MWMO_ALERTABLE);
/// switch (rc)
/// {
/// case WAIT_OBJECT_0 + 0:
/// bTerminate = true;
/// break;
/// case WAIT_OBJECT_0 + 1:
/// // We've received a notification in the queue.
/// {
/// DWORD dwAction;
/// CStringW wstrFilename;
/// changes.Pop(dwAction, wstrFilename);
/// wprintf(L"%s %s\n", ExplainAction(dwAction), wstrFilename);
/// }
/// break;
/// case WAIT_OBJECT_0 + _countof(handles):
/// // Get and dispatch message
/// break;
/// case WAIT_IO_COMPLETION:
/// // APC complete.No action needed.
/// break;
/// }
/// }
/// </code></example>
/// </remarks>
class CReadDirectoryChanges
{
public:
CReadDirectoryChanges(int nMaxChanges=1000);
~CReadDirectoryChanges();
void Init();
void Terminate();
/// <summary>
/// Add a new directory to be monitored.
/// </summary>
/// <param name="wszDirectory">Directory to monitor.</param>
/// <param name="bWatchSubtree">True to also monitor subdirectories.</param>
/// <param name="dwNotifyFilter">The types of file system events to monitor, such as FILE_NOTIFY_CHANGE_ATTRIBUTES.</param>
/// <param name="dwBufferSize">The size of the buffer used for overlapped I/O.</param>
/// <remarks>
/// <para>
/// This function will make an APC call to the worker thread to issue a new
/// ReadDirectoryChangesW call for the given directory with the given flags.
/// </para>
/// </remarks>
void AddDirectory( LPCTSTR wszDirectory, BOOL bWatchSubtree, DWORD dwNotifyFilter, DWORD dwBufferSize=16384 );
/// <summary>
/// Return a handle for the Win32 Wait... functions that will be
/// signaled when there is a queue entry.
/// </summary>
HANDLE GetWaitHandle() { return m_Notifications.GetWaitHandle(); }
bool Pop(DWORD& dwAction, std::wstring& wstrFilename);
// "Push" is for usage by ReadChangesRequest. Not intended for external usage.
void Push(DWORD dwAction, std::wstring& wstrFilename);
// Check if the queue overflowed. If so, clear it and return true.
bool CheckOverflow();
unsigned int GetThreadId() { return m_dwThreadId; }
protected:
ReadDirectoryChangesPrivate::CReadChangesServer* m_pServer;
HANDLE m_hThread;
unsigned int m_dwThreadId;
CThreadSafeQueue<TDirectoryChangeNotification> m_Notifications;
};

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32519.379
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReadDirectoryChanges", "ReadDirectoryChanges.vcxproj", "{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Debug|Win32.ActiveCfg = Debug|Win32
{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Debug|Win32.Build.0 = Debug|Win32
{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Release|Win32.ActiveCfg = Release|Win32
{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ReadDirectoryChanges</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>Dynamic</UseOfMfc>
<UseOfAtl>Static</UseOfAtl>
<PlatformToolset>LLVM-MSVC_v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>Dynamic</UseOfMfc>
<UseOfAtl>Static</UseOfAtl>
<PlatformToolset>LLVM-MSVC_v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CEInfo.h" />
<ClInclude Include="ReadDirectoryChangesPrivate.h" />
<ClInclude Include="ReadDirectoryChanges.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="ThreadSafeQueue.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Main.cpp" />
<ClCompile Include="ReadDirectoryChangesPrivate.cpp" />
<ClCompile Include="ReadDirectoryChanges.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ReadDirectoryChanges.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ThreadSafeQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ReadDirectoryChangesPrivate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CEInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ReadDirectoryChanges.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ReadDirectoryChangesPrivate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -0,0 +1,180 @@
//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
#include "stdafx.h"
#include "ReadDirectoryChanges.h"
#include "ReadDirectoryChangesPrivate.h"
// The namespace is a convenience to emphasize that these are internals
// interfaces. The namespace can be safely removed if you need to.
namespace ReadDirectoryChangesPrivate
{
///////////////////////////////////////////////////////////////////////////
// CReadChangesRequest
CReadChangesRequest::CReadChangesRequest(CReadChangesServer* pServer, LPCTSTR sz, BOOL b, DWORD dw, DWORD size)
{
m_pServer = pServer;
m_dwFilterFlags = dw;
m_bIncludeChildren = b;
m_wstrDirectory = sz;
m_hDirectory = 0;
::ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
// The hEvent member is not used when there is a completion
// function, so it's ok to use it to point to the object.
m_Overlapped.hEvent = this;
m_Buffer.resize(size);
m_BackupBuffer.resize(size);
}
CReadChangesRequest::~CReadChangesRequest()
{
// RequestTermination() must have been called successfully.
_ASSERTE(m_hDirectory == NULL);
}
bool CReadChangesRequest::OpenDirectory()
{
// Allow this routine to be called redundantly.
if (m_hDirectory)
return true;
m_hDirectory = ::CreateFileW(
m_wstrDirectory.c_str(), // pointer to the file name
FILE_LIST_DIRECTORY, // access (read/write) mode
FILE_SHARE_READ // share mode
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE,
NULL, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS // file attributes
| FILE_FLAG_OVERLAPPED,
NULL); // file with attributes to copy
if (m_hDirectory == INVALID_HANDLE_VALUE)
{
return false;
}
return true;
}
void CReadChangesRequest::BeginRead()
{
DWORD dwBytes=0;
// This call needs to be reissued after every APC.
BOOL success = ::ReadDirectoryChangesW(
m_hDirectory, // handle to directory
&m_Buffer[0], // read results buffer
m_Buffer.size(), // length of buffer
m_bIncludeChildren, // monitoring option
m_dwFilterFlags, // filter conditions
&dwBytes, // bytes returned
&m_Overlapped, // overlapped buffer
&NotificationCompletion); // completion routine
}
//static
VOID CALLBACK CReadChangesRequest::NotificationCompletion(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped) // I/O information buffer
{
CReadChangesRequest* pBlock = (CReadChangesRequest*)lpOverlapped->hEvent;
if (dwErrorCode == ERROR_OPERATION_ABORTED)
{
::InterlockedDecrement(&pBlock->m_pServer->m_nOutstandingRequests);
delete pBlock;
return;
}
// Can't use sizeof(FILE_NOTIFY_INFORMATION) because
// the structure is padded to 16 bytes.
_ASSERTE(dwNumberOfBytesTransfered >= offsetof(FILE_NOTIFY_INFORMATION, FileName) + sizeof(WCHAR));
// This might mean overflow? Not sure.
if(!dwNumberOfBytesTransfered)
return;
pBlock->BackupBuffer(dwNumberOfBytesTransfered);
// Get the new read issued as fast as possible. The documentation
// says that the original OVERLAPPED structure will not be used
// again once the completion routine is called.
pBlock->BeginRead();
pBlock->ProcessNotification();
}
void CReadChangesRequest::ProcessNotification()
{
BYTE* pBase = m_BackupBuffer.data();
for (;;)
{
FILE_NOTIFY_INFORMATION& fni = (FILE_NOTIFY_INFORMATION&)*pBase;
std::wstring wstrFilename(fni.FileName, fni.FileNameLength/sizeof(wchar_t));
// Handle a trailing backslash, such as for a root directory.
if (m_wstrDirectory.find_last_of(L"\\") != std::wstring::npos)
wstrFilename = m_wstrDirectory + L"\\" + wstrFilename;
else
wstrFilename = m_wstrDirectory + wstrFilename;
// If it could be a short filename, expand it.
LPCWSTR wszFilename = PathFindFileNameW(wstrFilename.c_str());
int len = lstrlenW(wszFilename);
// The maximum length of an 8.3 filename is twelve, including the dot.
if (len <= 12 && wcschr(wszFilename, L'~'))
{
// Convert to the long filename form. Unfortunately, this
// does not work for deletions, so it's an imperfect fix.
wchar_t wbuf[MAX_PATH];
if (::GetLongPathNameW(wstrFilename.c_str(), wbuf, _countof(wbuf)) > 0)
wstrFilename = wbuf;
}
m_pServer->m_pBase->Push(fni.Action, wstrFilename);
if (!fni.NextEntryOffset)
break;
pBase += fni.NextEntryOffset;
};
}
}

@ -0,0 +1,177 @@
//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
class CReadDirectoryChanges;
namespace ReadDirectoryChangesPrivate
{
class CReadChangesServer;
///////////////////////////////////////////////////////////////////////////
// All functions in CReadChangesRequest run in the context of the worker thread.
// One instance of this object is created for each call to AddDirectory().
class CReadChangesRequest
{
public:
CReadChangesRequest(CReadChangesServer* pServer, LPCTSTR sz, BOOL b, DWORD dw, DWORD size);
~CReadChangesRequest();
bool OpenDirectory();
void BeginRead();
// The dwSize is the actual number of bytes sent to the APC.
void BackupBuffer(DWORD dwSize)
{
// We could just swap back and forth between the two
// buffers, but this code is easier to understand and debug.
memcpy(&m_BackupBuffer[0], &m_Buffer[0], dwSize);
}
void ProcessNotification();
void RequestTermination()
{
::CancelIo(m_hDirectory);
::CloseHandle(m_hDirectory);
m_hDirectory = nullptr;
}
CReadChangesServer* m_pServer;
protected:
static VOID CALLBACK NotificationCompletion(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped); // I/O information buffer
// Parameters from the caller for ReadDirectoryChangesW().
DWORD m_dwFilterFlags;
BOOL m_bIncludeChildren;
std::wstring m_wstrDirectory;
// Result of calling CreateFile().
HANDLE m_hDirectory;
// Required parameter for ReadDirectoryChangesW().
OVERLAPPED m_Overlapped;
// Data buffer for the request.
// Since the memory is allocated by malloc, it will always
// be aligned as required by ReadDirectoryChangesW().
vector<BYTE> m_Buffer;
// Double buffer strategy so that we can issue a new read
// request before we process the current buffer.
vector<BYTE> m_BackupBuffer;
};
///////////////////////////////////////////////////////////////////////////
// All functions in CReadChangesServer run in the context of the worker thread.
// One instance of this object is allocated for each instance of CReadDirectoryChanges.
// This class is responsible for thread startup, orderly thread shutdown, and shimming
// the various C++ member functions with C-style Win32 functions.
class CReadChangesServer
{
public:
CReadChangesServer(CReadDirectoryChanges* pParent)
{
m_bTerminate=false; m_nOutstandingRequests=0;m_pBase=pParent;
}
static unsigned int WINAPI ThreadStartProc(LPVOID arg)
{
CReadChangesServer* pServer = (CReadChangesServer*)arg;
pServer->Run();
return 0;
}
// Called by QueueUserAPC to start orderly shutdown.
static void CALLBACK TerminateProc(__in ULONG_PTR arg)
{
CReadChangesServer* pServer = (CReadChangesServer*)arg;
pServer->RequestTermination();
}
// Called by QueueUserAPC to add another directory.
static void CALLBACK AddDirectoryProc(__in ULONG_PTR arg)
{
CReadChangesRequest* pRequest = (CReadChangesRequest*)arg;
pRequest->m_pServer->AddDirectory(pRequest);
}
CReadDirectoryChanges* m_pBase;
volatile DWORD m_nOutstandingRequests;
protected:
void Run()
{
while (m_nOutstandingRequests || !m_bTerminate)
{
DWORD rc = ::SleepEx(INFINITE, true);
}
}
void AddDirectory( CReadChangesRequest* pBlock )
{
if (pBlock->OpenDirectory())
{
::InterlockedIncrement(&pBlock->m_pServer->m_nOutstandingRequests);
m_pBlocks.push_back(pBlock);
pBlock->BeginRead();
}
else
delete pBlock;
}
void RequestTermination()
{
m_bTerminate = true;
for (DWORD i=0; i<m_pBlocks.size(); ++i)
{
// Each Request object will delete itself.
m_pBlocks[i]->RequestTermination();
}
m_pBlocks.clear();
}
vector<CReadChangesRequest*> m_pBlocks;
bool m_bTerminate;
};
}

@ -0,0 +1,116 @@
//
// The MIT License
//
// Copyright (c) 2010 James E Beveridge
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
// See ReadMe.txt for overview information.
#include <list>
template <typename C>
class CThreadSafeQueue : protected std::list<C>
{
public:
CThreadSafeQueue(int nMaxCount)
{
m_bOverflow = false;
m_hSemaphore = ::CreateSemaphore(
NULL, // no security attributes
0, // initial count
nMaxCount, // max count
NULL); // anonymous
}
~CThreadSafeQueue()
{
::CloseHandle(m_hSemaphore);
m_hSemaphore = NULL;
}
void push(C& c)
{
CComCritSecLock<CComAutoCriticalSection> lock( m_Crit, true );
push_back( c );
lock.Unlock();
if (!::ReleaseSemaphore(m_hSemaphore, 1, NULL))
{
// If the semaphore is full, then take back the entry.
lock.Lock();
pop_back();
if (GetLastError() == ERROR_TOO_MANY_POSTS)
{
m_bOverflow = true;
}
}
}
bool pop(C& c)
{
CComCritSecLock<CComAutoCriticalSection> lock( m_Crit, true );
// If the user calls pop() more than once after the
// semaphore is signaled, then the semaphore count will
// get out of sync. We fix that when the queue empties.
if (empty())
{
while (::WaitForSingleObject(m_hSemaphore, 0) != WAIT_TIMEOUT)
1;
return false;
}
c = front();
pop_front();
return true;
}
// If overflow, use this to clear the queue.
void clear()
{
CComCritSecLock<CComAutoCriticalSection> lock( m_Crit, true );
for (DWORD i=0; i<size(); i++)
WaitForSingleObject(m_hSemaphore, 0);
__super::clear();
m_bOverflow = false;
}
bool overflow()
{
return m_bOverflow;
}
HANDLE GetWaitHandle() { return m_hSemaphore; }
protected:
HANDLE m_hSemaphore;
CComAutoCriticalSection m_Crit;
bool m_bOverflow;
};

@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ReadDirectoryChanges.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

@ -0,0 +1,32 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#define _CRT_SECURE_NO_DEPRECATE
#include "targetver.h"
#include <stdio.h>
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#endif
#include <windows.h>
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit
#include <atlbase.h>
#include <atlstr.h>
#include <vector>
#include <list>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sstream>
using namespace std;

@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>
Loading…
Cancel
Save