├── .gitignore ├── CE.png ├── CEInfo.h ├── LICENSE ├── Main.cpp ├── README.md ├── ReadDirectoryChanges.cpp ├── ReadDirectoryChanges.h ├── ReadDirectoryChanges.sln ├── ReadDirectoryChanges.vcxproj ├── ReadDirectoryChanges.vcxproj.filters ├── ReadDirectoryChangesPrivate.cpp ├── ReadDirectoryChangesPrivate.h ├── ThreadSafeQueue.h ├── stdafx.cpp ├── stdafx.h └── targetver.h /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | 154 | # Windows Azure Build Output 155 | csx/ 156 | *.build.csdef 157 | 158 | # Windows Azure Emulator 159 | efc/ 160 | rfc/ 161 | 162 | # Visual Studio cache files 163 | # files ending in .cache can be ignored 164 | *.[Cc]ache 165 | # but keep track of directories ending in .cache 166 | !*.[Cc]ache/ 167 | 168 | # Others 169 | ClientBin/ 170 | [Ss]tyle[Cc]op.* 171 | ~$* 172 | *~ 173 | *.dbmdl 174 | *.dbproj.schemaview 175 | *.pfx 176 | *.publishsettings 177 | node_modules/ 178 | orleans.codegen.cs 179 | -------------------------------------------------------------------------------- /CE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmh5225/Detection-CheatEngine/8390b3870b79bea28bc6a67bf1368e5554458d6e/CE.png -------------------------------------------------------------------------------- /CEInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CE_DETECTION_1 L"ADDRESSES.FIRST" 4 | #define CE_DETECTION_2 L"MEMORY.FIRST" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010 James E. Beveridge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2010 James E Beveridge 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW" 26 | // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 27 | // See ReadMe.txt for overview information. 28 | 29 | 30 | #include "stdafx.h" 31 | #include "ReadDirectoryChanges.h" 32 | #include "CEInfo.h" 33 | 34 | LPCWSTR ExplainAction( DWORD dwAction ); 35 | bool TryGetKeyboardInput( HANDLE hStdIn, bool &bTerminate, char* buf ); 36 | 37 | 38 | // 39 | // When the application starts, it immediately starts monitoring your home 40 | // directory, including children, as well as C:\, not including children. 41 | // The application exits when you hit Esc. 42 | // You can add a directory to the monitoring list by typing the directory 43 | // name and hitting Enter. Notifications will pause while you type. 44 | // 45 | 46 | int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) 47 | { 48 | const DWORD dwNotificationFlags = 49 | FILE_NOTIFY_CHANGE_LAST_WRITE 50 | | FILE_NOTIFY_CHANGE_CREATION 51 | | FILE_NOTIFY_CHANGE_FILE_NAME; 52 | 53 | // Create the monitor and add two directories. 54 | CReadDirectoryChanges changes; 55 | changes.AddDirectory(_tgetenv(_T("USERPROFILE")), true, dwNotificationFlags); 56 | changes.AddDirectory(_T("C:\\"), false, dwNotificationFlags); 57 | 58 | HANDLE hStdIn = ::GetStdHandle(STD_INPUT_HANDLE); 59 | const HANDLE handles[] = { hStdIn, changes.GetWaitHandle() }; 60 | 61 | char buf[MAX_PATH]; 62 | bool bTerminate = false; 63 | 64 | while (!bTerminate) 65 | { 66 | DWORD rc = ::WaitForMultipleObjectsEx(_countof(handles), handles, false, INFINITE, true); 67 | switch (rc) 68 | { 69 | case WAIT_OBJECT_0 + 0: 70 | // hStdIn was signaled. This can happen due to mouse input, focus change, 71 | // Shift keys, and more. Delegate to TryGetKeyboardInput(). 72 | // TryGetKeyboardInput sets bTerminate to true if the user hits Esc. 73 | if (TryGetKeyboardInput(hStdIn, bTerminate, buf)) 74 | { 75 | std::string strbuf(buf); 76 | std::wstring wstrbuf(strbuf.begin(), strbuf.end()); 77 | changes.AddDirectory(wstrbuf.c_str(), false, dwNotificationFlags); 78 | } 79 | 80 | break; 81 | case WAIT_OBJECT_0 + 1: 82 | // We've received a notification in the queue. 83 | { 84 | DWORD dwAction; 85 | std::wstring wstrFilename; 86 | if (changes.CheckOverflow()) 87 | wprintf(L"Queue overflowed.\n"); 88 | else 89 | { 90 | changes.Pop(dwAction, wstrFilename); 91 | if (wstrFilename.find(CE_DETECTION_1) != std::wstring::npos || 92 | wstrFilename.find(CE_DETECTION_2) != std::wstring::npos 93 | ) 94 | { 95 | wprintf(L"Find CE:%s %s\n", ExplainAction(dwAction), wstrFilename.c_str()); 96 | } 97 | 98 | } 99 | } 100 | break; 101 | case WAIT_IO_COMPLETION: 102 | // Nothing to do. 103 | break; 104 | } 105 | } 106 | 107 | // Just for sample purposes. The destructor will 108 | // call Terminate() automatically. 109 | changes.Terminate(); 110 | 111 | return EXIT_SUCCESS; 112 | } 113 | 114 | LPCWSTR ExplainAction( DWORD dwAction ) 115 | { 116 | switch (dwAction) 117 | { 118 | case FILE_ACTION_ADDED : 119 | return L"Added"; 120 | case FILE_ACTION_REMOVED : 121 | return L"Deleted"; 122 | case FILE_ACTION_MODIFIED : 123 | return L"Modified"; 124 | case FILE_ACTION_RENAMED_OLD_NAME : 125 | return L"Renamed From"; 126 | case FILE_ACTION_RENAMED_NEW_NAME : 127 | return L"Renamed To"; 128 | default: 129 | return L"BAD DATA"; 130 | } 131 | } 132 | 133 | bool TryGetKeyboardInput( HANDLE hStdIn, bool &bTerminate, char* buf ) 134 | { 135 | DWORD dwNumberOfEventsRead=0; 136 | INPUT_RECORD rec = {0}; 137 | 138 | if (!::PeekConsoleInput(hStdIn, &rec, 1, &dwNumberOfEventsRead)) 139 | return false; 140 | 141 | if (rec.EventType == KEY_EVENT) 142 | { 143 | if (rec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) 144 | bTerminate = true; 145 | else if (rec.Event.KeyEvent.wVirtualKeyCode > VK_HELP) 146 | { 147 | if (!gets_s(buf, MAX_PATH)) // End of file, usually Ctrl-Z 148 | bTerminate = true; 149 | else 150 | return true; 151 | } 152 | } 153 | 154 | ::FlushConsoleInputBuffer(hStdIn); 155 | 156 | return false; 157 | } 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Detection-CheatEngine 2 | 3 | ![image](CE.png) 4 | 5 | ## Principle 6 | [Understanding ReadDirectoryChangesW]( 7 | http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html) 8 | 9 | ## Expansion 10 | You can add your detection vector easily in [CEInfo.h](https://github.com/gmh5225/Detection-CheatEngine/blob/master/CEInfo.h) 11 | ```C++ 12 | #pragma once 13 | 14 | #define CE_DETECTION_1 L"ADDRESSES.FIRST" 15 | #define CE_DETECTION_2 L"MEMORY.FIRST" 16 | ``` 17 | 18 | ## Compile 19 | - Visual Studio 2022 20 | - llvm-msvc [[link]](https://github.com/NewWorldComingSoon/llvm-msvc-build) 21 | 22 | ## Some discussions on UnknownCheats 23 | https://www.unknowncheats.me/forum/general-programming-and-reversing/502279-using-readdirectorychangesw-detect-cheatengine.html 24 | -------------------------------------------------------------------------------- /ReadDirectoryChanges.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2010 James E Beveridge 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW" 26 | // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 27 | // See ReadMe.txt for overview information. 28 | 29 | #include "stdafx.h" 30 | #include "ReadDirectoryChanges.h" 31 | #include "ReadDirectoryChangesPrivate.h" 32 | 33 | using namespace ReadDirectoryChangesPrivate; 34 | 35 | /////////////////////////////////////////////////////////////////////////// 36 | // CReadDirectoryChanges 37 | 38 | CReadDirectoryChanges::CReadDirectoryChanges(int nMaxCount) 39 | : m_Notifications(nMaxCount) 40 | { 41 | m_hThread = NULL; 42 | m_dwThreadId= 0; 43 | m_pServer = new CReadChangesServer(this); 44 | } 45 | 46 | CReadDirectoryChanges::~CReadDirectoryChanges() 47 | { 48 | Terminate(); 49 | delete m_pServer; 50 | } 51 | 52 | void CReadDirectoryChanges::Init() 53 | { 54 | // 55 | // Kick off the worker thread, which will be 56 | // managed by CReadChangesServer. 57 | // 58 | m_hThread = (HANDLE)_beginthreadex(NULL, 59 | 0, 60 | CReadChangesServer::ThreadStartProc, 61 | m_pServer, 62 | 0, 63 | &m_dwThreadId 64 | ); 65 | } 66 | 67 | void CReadDirectoryChanges::Terminate() 68 | { 69 | if (m_hThread) 70 | { 71 | ::QueueUserAPC(CReadChangesServer::TerminateProc, m_hThread, (ULONG_PTR)m_pServer); 72 | ::WaitForSingleObjectEx(m_hThread, 10000, true); 73 | ::CloseHandle(m_hThread); 74 | 75 | m_hThread = NULL; 76 | m_dwThreadId = 0; 77 | } 78 | } 79 | 80 | void CReadDirectoryChanges::AddDirectory( LPCTSTR szDirectory, BOOL bWatchSubtree, DWORD dwNotifyFilter, DWORD dwBufferSize ) 81 | { 82 | if (!m_hThread) 83 | Init(); 84 | 85 | CReadChangesRequest* pRequest = new CReadChangesRequest(m_pServer, szDirectory, bWatchSubtree, dwNotifyFilter, dwBufferSize); 86 | QueueUserAPC(CReadChangesServer::AddDirectoryProc, m_hThread, (ULONG_PTR)pRequest); 87 | } 88 | 89 | void CReadDirectoryChanges::Push(DWORD dwAction, std::wstring& wstrFilename) 90 | { 91 | auto item = TDirectoryChangeNotification(dwAction, wstrFilename); 92 | m_Notifications.push(item); 93 | } 94 | 95 | bool CReadDirectoryChanges::Pop(DWORD& dwAction, std::wstring& wstrFilename) 96 | { 97 | TDirectoryChangeNotification pair; 98 | if (!m_Notifications.pop(pair)) 99 | return false; 100 | 101 | dwAction = pair.first; 102 | wstrFilename = pair.second; 103 | 104 | return true; 105 | } 106 | 107 | bool CReadDirectoryChanges::CheckOverflow() 108 | { 109 | bool b = m_Notifications.overflow(); 110 | if (b) 111 | m_Notifications.clear(); 112 | return b; 113 | } 114 | -------------------------------------------------------------------------------- /ReadDirectoryChanges.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2010 James E Beveridge 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW" 26 | // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 27 | // See ReadMe.txt for overview information. 28 | 29 | 30 | #pragma once 31 | 32 | #include "ThreadSafeQueue.h" 33 | 34 | typedef pair TDirectoryChangeNotification; 35 | 36 | namespace ReadDirectoryChangesPrivate 37 | { 38 | class CReadChangesServer; 39 | } 40 | 41 | /////////////////////////////////////////////////////////////////////////// 42 | 43 | 44 | /// 45 | /// Track changes to filesystem directories and report them 46 | /// to the caller via a thread-safe queue. 47 | /// 48 | /// 49 | /// 50 | /// This sample code is based on my blog entry titled, "Understanding ReadDirectoryChangesW" 51 | /// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 52 | /// 53 | /// All functions in CReadDirectoryChangesServer run in 54 | /// the context of the calling thread. 55 | /// 56 | /// 57 | /// CReadDirectoryChanges changes; 58 | /// changes.AddDirectory(_T("C:\\"), false, dwNotificationFlags); 59 | /// 60 | /// const HANDLE handles[] = { hStopEvent, changes.GetWaitHandle() }; 61 | /// 62 | /// while (!bTerminate) 63 | /// { 64 | /// ::MsgWaitForMultipleObjectsEx( 65 | /// _countof(handles), 66 | /// handles, 67 | /// INFINITE, 68 | /// QS_ALLINPUT, 69 | /// MWMO_INPUTAVAILABLE | MWMO_ALERTABLE); 70 | /// switch (rc) 71 | /// { 72 | /// case WAIT_OBJECT_0 + 0: 73 | /// bTerminate = true; 74 | /// break; 75 | /// case WAIT_OBJECT_0 + 1: 76 | /// // We've received a notification in the queue. 77 | /// { 78 | /// DWORD dwAction; 79 | /// CStringW wstrFilename; 80 | /// changes.Pop(dwAction, wstrFilename); 81 | /// wprintf(L"%s %s\n", ExplainAction(dwAction), wstrFilename); 82 | /// } 83 | /// break; 84 | /// case WAIT_OBJECT_0 + _countof(handles): 85 | /// // Get and dispatch message 86 | /// break; 87 | /// case WAIT_IO_COMPLETION: 88 | /// // APC complete.No action needed. 89 | /// break; 90 | /// } 91 | /// } 92 | /// 93 | /// 94 | class CReadDirectoryChanges 95 | { 96 | public: 97 | CReadDirectoryChanges(int nMaxChanges=1000); 98 | ~CReadDirectoryChanges(); 99 | 100 | void Init(); 101 | void Terminate(); 102 | 103 | /// 104 | /// Add a new directory to be monitored. 105 | /// 106 | /// Directory to monitor. 107 | /// True to also monitor subdirectories. 108 | /// The types of file system events to monitor, such as FILE_NOTIFY_CHANGE_ATTRIBUTES. 109 | /// The size of the buffer used for overlapped I/O. 110 | /// 111 | /// 112 | /// This function will make an APC call to the worker thread to issue a new 113 | /// ReadDirectoryChangesW call for the given directory with the given flags. 114 | /// 115 | /// 116 | void AddDirectory( LPCTSTR wszDirectory, BOOL bWatchSubtree, DWORD dwNotifyFilter, DWORD dwBufferSize=16384 ); 117 | 118 | /// 119 | /// Return a handle for the Win32 Wait... functions that will be 120 | /// signaled when there is a queue entry. 121 | /// 122 | HANDLE GetWaitHandle() { return m_Notifications.GetWaitHandle(); } 123 | 124 | bool Pop(DWORD& dwAction, std::wstring& wstrFilename); 125 | 126 | // "Push" is for usage by ReadChangesRequest. Not intended for external usage. 127 | void Push(DWORD dwAction, std::wstring& wstrFilename); 128 | 129 | // Check if the queue overflowed. If so, clear it and return true. 130 | bool CheckOverflow(); 131 | 132 | unsigned int GetThreadId() { return m_dwThreadId; } 133 | 134 | protected: 135 | ReadDirectoryChangesPrivate::CReadChangesServer* m_pServer; 136 | 137 | HANDLE m_hThread; 138 | 139 | unsigned int m_dwThreadId; 140 | 141 | CThreadSafeQueue m_Notifications; 142 | }; 143 | -------------------------------------------------------------------------------- /ReadDirectoryChanges.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32519.379 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReadDirectoryChanges", "ReadDirectoryChanges.vcxproj", "{72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Debug|Win32.Build.0 = Debug|Win32 16 | {72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Release|Win32.ActiveCfg = Release|Win32 17 | {72A7916C-36BC-49D6-A9A9-B9FF8347BCE6}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /ReadDirectoryChanges.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {72A7916C-36BC-49D6-A9A9-B9FF8347BCE6} 15 | Win32Proj 16 | ReadDirectoryChanges 17 | 10.0 18 | 19 | 20 | 21 | Application 22 | true 23 | Unicode 24 | Dynamic 25 | Static 26 | LLVM-MSVC_v143 27 | 28 | 29 | Application 30 | false 31 | true 32 | Unicode 33 | Dynamic 34 | Static 35 | LLVM-MSVC_v143 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | true 49 | 50 | 51 | false 52 | 53 | 54 | 55 | Use 56 | Level3 57 | Disabled 58 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 59 | 60 | 61 | Console 62 | true 63 | 64 | 65 | 66 | 67 | Level3 68 | Use 69 | MaxSpeed 70 | true 71 | true 72 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 73 | false 74 | 75 | 76 | Console 77 | true 78 | true 79 | true 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | Create 99 | Create 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /ReadDirectoryChanges.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Header Files 19 | 20 | 21 | Header Files 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | 37 | 38 | Source Files 39 | 40 | 41 | Source Files 42 | 43 | 44 | Source Files 45 | 46 | 47 | Source Files 48 | 49 | 50 | -------------------------------------------------------------------------------- /ReadDirectoryChangesPrivate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2010 James E Beveridge 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW" 26 | // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 27 | // See ReadMe.txt for overview information. 28 | 29 | 30 | #include "stdafx.h" 31 | #include "ReadDirectoryChanges.h" 32 | #include "ReadDirectoryChangesPrivate.h" 33 | 34 | 35 | // The namespace is a convenience to emphasize that these are internals 36 | // interfaces. The namespace can be safely removed if you need to. 37 | namespace ReadDirectoryChangesPrivate 38 | { 39 | 40 | /////////////////////////////////////////////////////////////////////////// 41 | // CReadChangesRequest 42 | 43 | CReadChangesRequest::CReadChangesRequest(CReadChangesServer* pServer, LPCTSTR sz, BOOL b, DWORD dw, DWORD size) 44 | { 45 | m_pServer = pServer; 46 | m_dwFilterFlags = dw; 47 | m_bIncludeChildren = b; 48 | m_wstrDirectory = sz; 49 | m_hDirectory = 0; 50 | 51 | ::ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED)); 52 | 53 | // The hEvent member is not used when there is a completion 54 | // function, so it's ok to use it to point to the object. 55 | m_Overlapped.hEvent = this; 56 | 57 | m_Buffer.resize(size); 58 | m_BackupBuffer.resize(size); 59 | } 60 | 61 | 62 | CReadChangesRequest::~CReadChangesRequest() 63 | { 64 | // RequestTermination() must have been called successfully. 65 | _ASSERTE(m_hDirectory == NULL); 66 | } 67 | 68 | 69 | bool CReadChangesRequest::OpenDirectory() 70 | { 71 | // Allow this routine to be called redundantly. 72 | if (m_hDirectory) 73 | return true; 74 | 75 | m_hDirectory = ::CreateFileW( 76 | m_wstrDirectory.c_str(), // pointer to the file name 77 | FILE_LIST_DIRECTORY, // access (read/write) mode 78 | FILE_SHARE_READ // share mode 79 | | FILE_SHARE_WRITE 80 | | FILE_SHARE_DELETE, 81 | NULL, // security descriptor 82 | OPEN_EXISTING, // how to create 83 | FILE_FLAG_BACKUP_SEMANTICS // file attributes 84 | | FILE_FLAG_OVERLAPPED, 85 | NULL); // file with attributes to copy 86 | 87 | if (m_hDirectory == INVALID_HANDLE_VALUE) 88 | { 89 | return false; 90 | } 91 | 92 | return true; 93 | } 94 | 95 | void CReadChangesRequest::BeginRead() 96 | { 97 | DWORD dwBytes=0; 98 | 99 | // This call needs to be reissued after every APC. 100 | BOOL success = ::ReadDirectoryChangesW( 101 | m_hDirectory, // handle to directory 102 | &m_Buffer[0], // read results buffer 103 | m_Buffer.size(), // length of buffer 104 | m_bIncludeChildren, // monitoring option 105 | m_dwFilterFlags, // filter conditions 106 | &dwBytes, // bytes returned 107 | &m_Overlapped, // overlapped buffer 108 | &NotificationCompletion); // completion routine 109 | } 110 | 111 | //static 112 | VOID CALLBACK CReadChangesRequest::NotificationCompletion( 113 | DWORD dwErrorCode, // completion code 114 | DWORD dwNumberOfBytesTransfered, // number of bytes transferred 115 | LPOVERLAPPED lpOverlapped) // I/O information buffer 116 | { 117 | CReadChangesRequest* pBlock = (CReadChangesRequest*)lpOverlapped->hEvent; 118 | 119 | if (dwErrorCode == ERROR_OPERATION_ABORTED) 120 | { 121 | ::InterlockedDecrement(&pBlock->m_pServer->m_nOutstandingRequests); 122 | delete pBlock; 123 | return; 124 | } 125 | 126 | // Can't use sizeof(FILE_NOTIFY_INFORMATION) because 127 | // the structure is padded to 16 bytes. 128 | _ASSERTE(dwNumberOfBytesTransfered >= offsetof(FILE_NOTIFY_INFORMATION, FileName) + sizeof(WCHAR)); 129 | 130 | // This might mean overflow? Not sure. 131 | if(!dwNumberOfBytesTransfered) 132 | return; 133 | 134 | pBlock->BackupBuffer(dwNumberOfBytesTransfered); 135 | 136 | // Get the new read issued as fast as possible. The documentation 137 | // says that the original OVERLAPPED structure will not be used 138 | // again once the completion routine is called. 139 | pBlock->BeginRead(); 140 | 141 | pBlock->ProcessNotification(); 142 | } 143 | 144 | void CReadChangesRequest::ProcessNotification() 145 | { 146 | BYTE* pBase = m_BackupBuffer.data(); 147 | 148 | for (;;) 149 | { 150 | FILE_NOTIFY_INFORMATION& fni = (FILE_NOTIFY_INFORMATION&)*pBase; 151 | 152 | std::wstring wstrFilename(fni.FileName, fni.FileNameLength/sizeof(wchar_t)); 153 | // Handle a trailing backslash, such as for a root directory. 154 | if (m_wstrDirectory.find_last_of(L"\\") != std::wstring::npos) 155 | wstrFilename = m_wstrDirectory + L"\\" + wstrFilename; 156 | else 157 | wstrFilename = m_wstrDirectory + wstrFilename; 158 | 159 | // If it could be a short filename, expand it. 160 | LPCWSTR wszFilename = PathFindFileNameW(wstrFilename.c_str()); 161 | int len = lstrlenW(wszFilename); 162 | // The maximum length of an 8.3 filename is twelve, including the dot. 163 | if (len <= 12 && wcschr(wszFilename, L'~')) 164 | { 165 | // Convert to the long filename form. Unfortunately, this 166 | // does not work for deletions, so it's an imperfect fix. 167 | wchar_t wbuf[MAX_PATH]; 168 | if (::GetLongPathNameW(wstrFilename.c_str(), wbuf, _countof(wbuf)) > 0) 169 | wstrFilename = wbuf; 170 | } 171 | 172 | m_pServer->m_pBase->Push(fni.Action, wstrFilename); 173 | 174 | if (!fni.NextEntryOffset) 175 | break; 176 | pBase += fni.NextEntryOffset; 177 | }; 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /ReadDirectoryChangesPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2010 James E Beveridge 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW" 26 | // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 27 | // See ReadMe.txt for overview information. 28 | 29 | class CReadDirectoryChanges; 30 | 31 | namespace ReadDirectoryChangesPrivate 32 | { 33 | 34 | class CReadChangesServer; 35 | 36 | /////////////////////////////////////////////////////////////////////////// 37 | 38 | // All functions in CReadChangesRequest run in the context of the worker thread. 39 | // One instance of this object is created for each call to AddDirectory(). 40 | class CReadChangesRequest 41 | { 42 | public: 43 | CReadChangesRequest(CReadChangesServer* pServer, LPCTSTR sz, BOOL b, DWORD dw, DWORD size); 44 | 45 | ~CReadChangesRequest(); 46 | 47 | bool OpenDirectory(); 48 | 49 | void BeginRead(); 50 | 51 | // The dwSize is the actual number of bytes sent to the APC. 52 | void BackupBuffer(DWORD dwSize) 53 | { 54 | // We could just swap back and forth between the two 55 | // buffers, but this code is easier to understand and debug. 56 | memcpy(&m_BackupBuffer[0], &m_Buffer[0], dwSize); 57 | } 58 | 59 | void ProcessNotification(); 60 | 61 | void RequestTermination() 62 | { 63 | ::CancelIo(m_hDirectory); 64 | ::CloseHandle(m_hDirectory); 65 | m_hDirectory = nullptr; 66 | } 67 | 68 | CReadChangesServer* m_pServer; 69 | 70 | protected: 71 | 72 | static VOID CALLBACK NotificationCompletion( 73 | DWORD dwErrorCode, // completion code 74 | DWORD dwNumberOfBytesTransfered, // number of bytes transferred 75 | LPOVERLAPPED lpOverlapped); // I/O information buffer 76 | 77 | // Parameters from the caller for ReadDirectoryChangesW(). 78 | DWORD m_dwFilterFlags; 79 | BOOL m_bIncludeChildren; 80 | std::wstring m_wstrDirectory; 81 | 82 | // Result of calling CreateFile(). 83 | HANDLE m_hDirectory; 84 | 85 | // Required parameter for ReadDirectoryChangesW(). 86 | OVERLAPPED m_Overlapped; 87 | 88 | // Data buffer for the request. 89 | // Since the memory is allocated by malloc, it will always 90 | // be aligned as required by ReadDirectoryChangesW(). 91 | vector m_Buffer; 92 | 93 | // Double buffer strategy so that we can issue a new read 94 | // request before we process the current buffer. 95 | vector m_BackupBuffer; 96 | }; 97 | 98 | /////////////////////////////////////////////////////////////////////////// 99 | 100 | // All functions in CReadChangesServer run in the context of the worker thread. 101 | // One instance of this object is allocated for each instance of CReadDirectoryChanges. 102 | // This class is responsible for thread startup, orderly thread shutdown, and shimming 103 | // the various C++ member functions with C-style Win32 functions. 104 | class CReadChangesServer 105 | { 106 | public: 107 | CReadChangesServer(CReadDirectoryChanges* pParent) 108 | { 109 | m_bTerminate=false; m_nOutstandingRequests=0;m_pBase=pParent; 110 | } 111 | 112 | static unsigned int WINAPI ThreadStartProc(LPVOID arg) 113 | { 114 | CReadChangesServer* pServer = (CReadChangesServer*)arg; 115 | pServer->Run(); 116 | return 0; 117 | } 118 | 119 | // Called by QueueUserAPC to start orderly shutdown. 120 | static void CALLBACK TerminateProc(__in ULONG_PTR arg) 121 | { 122 | CReadChangesServer* pServer = (CReadChangesServer*)arg; 123 | pServer->RequestTermination(); 124 | } 125 | 126 | // Called by QueueUserAPC to add another directory. 127 | static void CALLBACK AddDirectoryProc(__in ULONG_PTR arg) 128 | { 129 | CReadChangesRequest* pRequest = (CReadChangesRequest*)arg; 130 | pRequest->m_pServer->AddDirectory(pRequest); 131 | } 132 | 133 | CReadDirectoryChanges* m_pBase; 134 | 135 | volatile DWORD m_nOutstandingRequests; 136 | 137 | protected: 138 | 139 | void Run() 140 | { 141 | while (m_nOutstandingRequests || !m_bTerminate) 142 | { 143 | DWORD rc = ::SleepEx(INFINITE, true); 144 | } 145 | } 146 | 147 | void AddDirectory( CReadChangesRequest* pBlock ) 148 | { 149 | if (pBlock->OpenDirectory()) 150 | { 151 | ::InterlockedIncrement(&pBlock->m_pServer->m_nOutstandingRequests); 152 | m_pBlocks.push_back(pBlock); 153 | pBlock->BeginRead(); 154 | } 155 | else 156 | delete pBlock; 157 | } 158 | 159 | void RequestTermination() 160 | { 161 | m_bTerminate = true; 162 | 163 | for (DWORD i=0; iRequestTermination(); 167 | } 168 | 169 | m_pBlocks.clear(); 170 | } 171 | 172 | vector m_pBlocks; 173 | 174 | bool m_bTerminate; 175 | }; 176 | 177 | } 178 | -------------------------------------------------------------------------------- /ThreadSafeQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // The MIT License 3 | // 4 | // Copyright (c) 2010 James E Beveridge 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | 25 | // This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW" 26 | // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html 27 | // See ReadMe.txt for overview information. 28 | 29 | #include 30 | 31 | template 32 | class CThreadSafeQueue : protected std::list 33 | { 34 | public: 35 | CThreadSafeQueue(int nMaxCount) 36 | { 37 | m_bOverflow = false; 38 | 39 | m_hSemaphore = ::CreateSemaphore( 40 | NULL, // no security attributes 41 | 0, // initial count 42 | nMaxCount, // max count 43 | NULL); // anonymous 44 | } 45 | 46 | ~CThreadSafeQueue() 47 | { 48 | ::CloseHandle(m_hSemaphore); 49 | m_hSemaphore = NULL; 50 | } 51 | 52 | void push(C& c) 53 | { 54 | CComCritSecLock lock( m_Crit, true ); 55 | push_back( c ); 56 | lock.Unlock(); 57 | 58 | if (!::ReleaseSemaphore(m_hSemaphore, 1, NULL)) 59 | { 60 | // If the semaphore is full, then take back the entry. 61 | lock.Lock(); 62 | pop_back(); 63 | if (GetLastError() == ERROR_TOO_MANY_POSTS) 64 | { 65 | m_bOverflow = true; 66 | } 67 | } 68 | } 69 | 70 | bool pop(C& c) 71 | { 72 | CComCritSecLock lock( m_Crit, true ); 73 | 74 | // If the user calls pop() more than once after the 75 | // semaphore is signaled, then the semaphore count will 76 | // get out of sync. We fix that when the queue empties. 77 | if (empty()) 78 | { 79 | while (::WaitForSingleObject(m_hSemaphore, 0) != WAIT_TIMEOUT) 80 | 1; 81 | return false; 82 | } 83 | 84 | c = front(); 85 | pop_front(); 86 | 87 | return true; 88 | } 89 | 90 | // If overflow, use this to clear the queue. 91 | void clear() 92 | { 93 | CComCritSecLock lock( m_Crit, true ); 94 | 95 | for (DWORD i=0; i 13 | 14 | #ifndef VC_EXTRALEAN 15 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers 16 | #endif 17 | 18 | #include 19 | 20 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | using namespace std; 33 | -------------------------------------------------------------------------------- /targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | --------------------------------------------------------------------------------