VC++文件监控之ReadDirectoryChangesW

时间:2022-01-30 13:37:34

我这里只介绍采用ReadDirectoryChangesW对文件目录实施监控

关键代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
CfgdsgDlg * dlg = (CfgdsgDlg*)lparam;
 
  HANDLE hDir;
  char notify[1024];
  DWORD cbBytes,i;
  char AnsiChar[3];
  wchar_t UnicodeChar[2];
  CString path;
 
  FILE_NOTIFY_INFORMATION *pnotify=(FILE_NOTIFY_INFORMATION *)notify;
  FILE_NOTIFY_INFORMATION *tmp;
 
  GetCurrentDirectory(MAX_PATH,path.GetBuffer(MAX_PATH+1));
  hDir = CreateFile( path, FILE_LIST_DIRECTORY,
    FILE_SHARE_READ |
    FILE_SHARE_WRITE |
    FILE_SHARE_DELETE, NULL,
    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
    FILE_FLAG_OVERLAPPED, NULL);
  if (hDir == INVALID_HANDLE_VALUE)
  {
 
    dlg->m_edit.ReplaceSel("hDir:INVALID_HANDLE_VALUE\r\n");
    return 0;
  }
 
  while (TRUE)
  {
    if(ReadDirectoryChangesW(hDir, &notify, sizeof(notify),
      FALSE, FILE_NOTIFY_CHANGE_FILE_NAME| FILE_NOTIFY_CHANGE_LAST_WRITE,
      &cbBytes, NULL, NULL))
    {
 
      tmp = pnotify;
 
      switch(tmp->Action)
      {
      case FILE_ACTION_ADDED:
 
        dlg->m_edit.ReplaceSel("Directory/File added (添加文件)- \r\n");
        break;
      case FILE_ACTION_REMOVED:
        dlg->m_edit.ReplaceSel("Directory/File removed (删除文件)- \r\n");
        break;
      case FILE_ACTION_MODIFIED:
        dlg->m_edit.ReplaceSel("Directory/File modified (修改文件内容)- \r\n");
        break;
      case FILE_ACTION_RENAMED_OLD_NAME:
        dlg->m_edit.ReplaceSel("Directory/File old name (修改文件名字)- \r\n");
        break;
      case FILE_ACTION_RENAMED_NEW_NAME:
        dlg->m_edit.ReplaceSel("Directory/File new name - \r\n");
        break;
      default:
        break;
      }
    }
  }

FILE_NOTIFY_INFORMATION //可以确定是那个文件进行的修改

typedef struct _FILE_NOTIFY_INFORMATION {
DWORD NextEntryOffset;
DWORD Action;//动作
DWORD FileNameLength;//文件名字的长度
WCHAR FileName[1];//文件名字
} FILE_NOTIFY_INFORMATION,
*PFILE_NOTIFY_INFORMATION;

ReadDirectoryChangesW 返回类型(见MSDN)

 

Value Meaning

FILE_ACTION_ADDED
0x00000001

The file was added to the directory.

FILE_ACTION_REMOVED
0x00000002

The file was removed from the directory.

FILE_ACTION_MODIFIED
0x00000003

The file was modified. This can be a change in the time stamp or attributes.

FILE_ACTION_RENAMED_OLD_NAME
0x00000004

The file was renamed and this is the old name.

FILE_ACTION_RENAMED_NEW_NAME
0x00000005

The file was renamed and this is the new name.

 

效果如下:

VC++文件监控之ReadDirectoryChangesW

不足的地方:

只能检测到指定目录和下一级目录,超过目录级数,该函数检测不到。

ReadDirectoryChangesW 监控文件夹 (一个简单的监控示例程序)

.h文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// .h文件
#pragma once
 
typedef void (*PFN_NotifyAction)(DWORD dwAction, LPWSTR szFile, DWORD dwLength);
 
class CDirectoryWatch
{
public:
    CDirectoryWatch(void);
    virtual ~CDirectoryWatch(void);
 
public:
    BOOL StartDirectoryWatch(LPCTSTR lpszDirectory, PFN_NotifyAction pFn_NotifyAction);
    BOOL StopDirectoryWatch(void);
 
private:
    static UINT __cdecl ThreadProc(LPVOID lParam);
    static UINT __cdecl DirectoryWatch(LPVOID lParam);
 
private:
    HANDLE m_hFile;
    CWinThread* m_pThread;
    TCHAR m_szDirectory[MAX_PATH];
};

.cpp文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// .cpp文件
#include "StdAfx.h"
#include "DirectoryWatch.h"
#include <strsafe.h>
 
typedef enum
{
    MSG_STARTWATCH = (WM_USER + 0x11),
    MSG_STOPWATCH,
    MSG_EXITTHREAD
};
 
#define MAX_BUFFER_SIZE (1024)
 
typedef struct _tagWATCHPARAMETERS
{
    _tagWATCHPARAMETERS()
    {
        hFile = INVALID_HANDLE_VALUE;
        hEvent = NULL;
        memset(&ol, 0, sizeof(OVERLAPPED));
        pBuffer = NULL;
        dwBufferSize = 0;
        bExit = FALSE;
        pFn_NotifyAction = NULL;
    }
    HANDLE hFile;
    HANDLE hEvent;
    OVERLAPPED ol;
    BYTE* pBuffer;
    DWORD dwBufferSize;
    BOOL bExit;
    PFN_NotifyAction pFn_NotifyAction;
}WATCH_PARAMETERS, *PWATCH_PARAMETERS;
 
CDirectoryWatch::CDirectoryWatch() : m_hFile(INVALID_HANDLE_VALUE), m_pThread(NULL)
{
    memset(m_szDirectory, 0, sizeof(m_szDirectory));
 
    m_pThread = AfxBeginThread(ThreadProc, NULL, 0, CREATE_SUSPENDED, 0, NULL);
    if(NULL == m_pThread)
    {
        TRACE("Error Code : %d\n", GetLastError());
        return ;
    }
    m_pThread->m_bAutoDelete = FALSE;
    m_pThread->ResumeThread();
}
 
 
CDirectoryWatch::~CDirectoryWatch()
{
    if(INVALID_HANDLE_VALUE != m_hFile)
    {
        CloseHandle(m_hFile);
        m_hFile = INVALID_HANDLE_VALUE;
    }
 
    if((NULL != m_pThread) && (NULL != m_pThread->m_hThread))
    {
 
        m_pThread->PostThreadMessage(MSG_EXITTHREAD, 0, 0);
        WaitForSingleObject(m_pThread->m_hThread, INFINITE);
        delete m_pThread;
        m_pThread = NULL;
    }
}
 
BOOL CDirectoryWatch::StartDirectoryWatch(LPCTSTR lpszDirectory, PFN_NotifyAction pFn_NotifyAction)
{
    if(NULL == m_pThread)
    {
        return FALSE;
    }
 
    if(NULL == lpszDirectory)
    {
        return FALSE;
    }
 
    if(NULL == pFn_NotifyAction)
    {
        return FALSE;
    }
 
    if(!PathFileExists(lpszDirectory))
    {
        TRACE("Error Code : %d\n", GetLastError());
        return FALSE;
    }
 
    if(!PathIsDirectory(lpszDirectory))
    {
        TRACE("Error Code : %d\n", GetLastError());
        return FALSE;
    }
 
    if(0 == _tcslen(m_szDirectory))
    {
        StringCchPrintf(m_szDirectory, _countof(m_szDirectory), _T("%s"), lpszDirectory);
    }
    else if(CSTR_EQUAL != CompareStringOrdinal(m_szDirectory, -1, lpszDirectory, -1, TRUE))
    {
        TRACE("Not Change Directory.\n");
        return FALSE;
    }
 
    if(INVALID_HANDLE_VALUE == m_hFile)
    {
        m_hFile = CreateFile(lpszDirectory, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
        if(INVALID_HANDLE_VALUE == m_hFile)
        {
            TRACE("Error Code : %d\n", GetLastError());
            return FALSE;
        }
    }
 
    return m_pThread->PostThreadMessage(MSG_STARTWATCH, (WPARAM)m_hFile, (LPARAM)pFn_NotifyAction);
}
 
BOOL CDirectoryWatch::StopDirectoryWatch()
{
    if(NULL != m_pThread)
    {
        return m_pThread->PostThreadMessage(MSG_STOPWATCH, 0, 0);
    }
 
    return FALSE;
}
 
UINT __cdecl CDirectoryWatch::DirectoryWatch(LPVOID lParam)
{
    WATCH_PARAMETERS* pParam = (WATCH_PARAMETERS*)lParam;
    if(NULL == pParam)
    {
        return 0;
    }
    HANDLE& hFile = pParam->hFile;
    BYTE* pBuffer = pParam->pBuffer;
    DWORD dwBufferSize = pParam->dwBufferSize;
    OVERLAPPED& ol = pParam->ol;
    HANDLE& hEvent = pParam->hEvent;
    BOOL& bExit = pParam->bExit;
    PFN_NotifyAction pFn_NotifyAction = pParam->pFn_NotifyAction;
    DWORD dwBytesReturn = 0;
    DWORD dwRet = WAIT_FAILED;
    DWORD dwOffSet = 0;
    TCHAR szFile[MAX_PATH] = {0};
    while(TRUE)
    {
        if(WAIT_OBJECT_0 != WaitForSingleObject(hEvent, INFINITE))
        {
            TRACE("Error Code : %d\n", GetLastError());
            break;
        }
 
        if(bExit)
        {
            break;
        }
    
        if(!ReadDirectoryChangesW(hFile, pBuffer, dwBufferSize, TRUE,
            FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
            | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
            | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, &dwBytesReturn, &ol, NULL))
        {
            TRACE("Error Code : %d\n", GetLastError());
            break;
        }
        if(!GetOverlappedResult(hFile, &ol, &dwBytesReturn, TRUE))
        {
            TRACE("Error Code : %d\n", GetLastError());
            break;
        }
        FILE_NOTIFY_INFORMATION* pFileNotify = (FILE_NOTIFY_INFORMATION*)pBuffer;
        
        do
        {
            if(pFn_NotifyAction && (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 0)))
            {
                pFn_NotifyAction(pFileNotify->Action, pFileNotify->FileName, (pFileNotify->FileNameLength) / sizeof(WCHAR));
            }
 
            dwOffSet = pFileNotify->NextEntryOffset;
            pFileNotify = (FILE_NOTIFY_INFORMATION*)((BYTE*)pFileNotify + dwOffSet);
        } while (dwOffSet);
    }
    TRACE0("DirectoryWatch Thread Exit ... \n");
    return 0;
}
 
UINT __cdecl CDirectoryWatch::ThreadProc(LPVOID lParam)
{
    WATCH_PARAMETERS* pParam = new WATCH_PARAMETERS;
 
    if(NULL == pParam)
    {
        goto __CLEANUP__;
    }
 
    BYTE* pBuffer = new BYTE[MAX_BUFFER_SIZE];
    if(NULL == pBuffer)
    {
        goto __CLEANUP__;
    }
    memset(pBuffer, 0, MAX_BUFFER_SIZE);
    pParam->pBuffer = pBuffer;
    pParam->dwBufferSize = MAX_BUFFER_SIZE;
    HANDLE hWatchEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if(NULL == hWatchEvent)
    {
        goto __CLEANUP__;
    }
    pParam->ol.hEvent = hWatchEvent;
    CWinThread* pThread = NULL;
    HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if(NULL == hEvent)
    {
        goto __CLEANUP__;
    }
    pParam->hEvent = hEvent;
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        switch(msg.message)
        {
        case MSG_STARTWATCH:
            {
                HANDLE hFile = (HANDLE)(msg.wParam);
                PFN_NotifyAction pFn_NotifyAction = (PFN_NotifyAction)(msg.lParam);
                if((INVALID_HANDLE_VALUE == hFile) && (NULL == pFn_NotifyAction))
                {
                    break;
                }
                if(NULL == pThread)
                {
                    pParam->hFile = hFile;
                    pParam->pFn_NotifyAction = pFn_NotifyAction;
                    pThread = AfxBeginThread(DirectoryWatch, (LPVOID)pParam, 0, CREATE_SUSPENDED, NULL);
                    if(NULL == pThread)
                    {
                        goto __CLEANUP__;
                    }
                    pThread->m_bAutoDelete = FALSE;
                    pThread->ResumeThread();
                }              
                SetEvent(hEvent);
            }
            break;
 
        case MSG_STOPWATCH:
            {
                ResetEvent(hEvent);
            }
            break;
 
        case MSG_EXITTHREAD:
            {
                SetEvent(hEvent);
                pParam->bExit = FALSE;
                
                if((NULL != pThread) && (NULL != pThread->m_hThread))
                {
                    WaitForSingleObject(pThread->m_hThread, INFINITE);
                    delete pThread;
                    pThread = NULL;
                }
                goto __CLEANUP__;
            }
            
        default:
            break;
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
__CLEANUP__:
    if(NULL != hWatchEvent)
    {
        CloseHandle(hWatchEvent);
        hWatchEvent = NULL;
    }
    if(NULL != pBuffer)
    {
        delete[] pBuffer;
        pBuffer = NULL;
    }
    if(NULL != pParam)
    {
        delete pParam;
        pParam = NULL;
    }
    TRACE0("ThreadProc Thread Exit ...\n");
    return 0;
}

测试代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 测试代码
 
#include "stdafx.h"
 
#include "DirectoryWatch.h"
 
void NotifyAction(DWORD dwAction, LPWSTR szFile, DWORD dwLength)
{
    switch(dwAction)
    {
    case FILE_ACTION_ADDED:
        wprintf(L"FILE_ACTION_ADDED: \n\t");
        break;
 
    case FILE_ACTION_REMOVED:
        wprintf(L"FILE_ACTION_REMOVED: \n\t");
        break;
 
    case FILE_ACTION_MODIFIED:
        wprintf(L"FILE_ACTION_MODIFIED: \n\t");
        break;
 
    case FILE_ACTION_RENAMED_OLD_NAME:
        wprintf(L"FILE_ACTION_RENAMED_OLD_NAME: \n\t");
        break;
 
    case FILE_ACTION_RENAMED_NEW_NAME:
        wprintf(L"FILE_ACTION_RENAMED_NEW_NAME: \n\t");
        break;
 
    default:
        break;
    }
    WCHAR szPath[MAX_PATH] = {0};
    wmemcpy(szPath, szFile, min(dwLength, MAX_PATH));
    wprintf(L"%s\n", szPath);
}
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    CDirectoryWatch watch;
    wprintf(L"Start Directory Watch ...\n");
    watch.StartDirectoryWatch(_T("F:\\11"), NotifyAction);
    Sleep(30 * 1000);  
    watch.StopDirectoryWatch();
    wprintf(L"Stop Directory Watch ...\n");
 
    Sleep(10 * 1000);
 
    wprintf(L"Start Directory Watch ...\n");
    watch.StartDirectoryWatch(_T("F:\\11"), NotifyAction);
    Sleep(30 * 1000);  
    watch.StopDirectoryWatch();
    wprintf(L"Stop Directory Watch ...\n");
    Sleep(30 * 1000);
    wprintf(L"Process Exit ...\n");
    return 0;
}

效果如下图所示:

VC++文件监控之ReadDirectoryChangesW

使用ReadDirectoryChangesW API监控文件系统的改变

在C++中若想要监控档案系统改变有很多方法,可以用FindFirstChangeNotification取得档案变更、或是Hook底层的API等方法来实现,这边使用ReadDirectoryChangesW API来实现,该API使用前必须先加入Kernel32.lib。

VC++文件监控之ReadDirectoryChangesW

并加入Windows.h的标头档

#include "Windows.h"

这些步骤做完后在程式中就可以看到ReadDirectoryChangesW API了,其函式原型如下:

?
1
2
3
4
5
6
7
8
9
10
BOOL WINAPI ReadDirectoryChangesW(
 __in     HANDLE hDirectory,
 __out    LPVOID lpBuffer,
 __in     DWORD nBufferLength,
 __in     BOOL bWatchSubtree,
 __in     DWORD dwNotifyFilter,
 __out_opt  LPDWORD lpBytesReturned,
 __inout_opt LPOVERLAPPED lpOverlapped,
 __in_opt   LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);

该API必须带入八个参数,hDirectory带入的是要监控的目录Handle、lpBuffer带入的是用来回传变动资料的空间、nBufferLength是lpBuffer空间的大小、bWatchSubtree是指定是否侦测子目录、dwNotifyFilter是指定监控的目录有哪些动作时需要通知、lpBytesReturned是用来回传变动资料内含的长度、lpOverlapped可用来在非同步环境下使用重叠IO用、lpCompletionRoutine则是当监控完成或取消时所呼叫的回调函式。

 

其中dwNotifyFilter的值可设定的有FILE_NOTIFY_CHANGE_FILE_NAME、FILE_NOTIFY_CHANGE_DIR_NAME、FILE_NOTIFY_CHANGE_ATTRIBUTES、FILE_NOTIFY_CHANGE_SIZE、FILE_NOTIFY_CHANGE_LAST_WRITE、FILE_NOTIFY_CHANGE_LAST_ACCESS、FILE_NOTIFY_CHANGE_CREATION、与FILE_NOTIFY_CHANGE_SECURITY,详细所代表的意义可参阅ReadDirectoryChangesW function

了解了函式原型后,就可以开始进入实际的使用。刚有提到说在ReadDirectoryChangesW API函式必须要带入的第一个参数是要监控的目录Handle,所以我们必须透过CreateFile API取得要监控的目录Handle,像是下面这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HANDLE hDirectoryHandle  = NULL;
hDirectoryHandle = ::CreateFileA(
  file,         
  FILE_LIST_DIRECTORY,       
  FILE_SHARE_READ           
  | FILE_SHARE_WRITE
  | FILE_SHARE_DELETE,
  NULL,               
  OPEN_EXISTING,          
  FILE_FLAG_BACKUP_SEMANTICS     
  | FILE_FLAG_OVERLAPPED,
  NULL);
 
if(hDirectoryHandle == INVALID_HANDLE_VALUE)
  return;

取得监控的目录Handle后,将其带入ReadDirectoryChangesw API,顺带带入像是回传变动资料的Buffer空间、与要监控的变动类型等必要参数。像是下面这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int    nBufferSize      = 1024;
char*  buffer        = new char[nBufferSize];
DWORD dwBytes = 0;
 
memset(buffer, 0, nBufferSize);
 
if(!::ReadDirectoryChangesW(
  hDirectoryHandle,           
  buffer,               
  nBufferSize,   
  bIncludeSubdirectories,      
  FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME,  
  &dwBytes,            
  NULL,         
  NULL) || GetLastError() == ERROR_INVALID_HANDLE)
{
  break;
}
 
if(!dwBytes)
{
  printf("Buffer overflow~~\r\n");
}

这边需注意到的是,若是变动的资料太多,提供的存储空间不足以存放时,回传的变动资料长度会是0,此时所有变动资料都会丢失。这样的情况多半只会出在一瞬间大量的变动,可以增大存储空间或是减少监控的变动类型,以减少回传的资料量,避免溢位的发生。

若是运行没发生问题,变动的资料会存放在当初塞进去的存储空间,该空间的资料其实是FILE_NOTIFY_INFORMATION structure的型态存在,因此我们可将存储空间的资料转换成PFILE_NOTIFY_INFORMATION。裡面的Action是我们所关注的变动类型,FileName是变动的档案名称,档案名称的部分是没有结尾符号的,必须要搭配FileNameLength去截取。另外变动的资料有时候不止一笔,因此我们必须在这边用迴圈搭配NextEntryOffset去重覆运行处理流程,处理所有变动的资料。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
PFILE_NOTIFY_INFORMATION record = (PFILE_NOTIFY_INFORMATION)buffer;
DWORD cbOffset = 0;
 
do
  switch (record->Action)
  {
  case FILE_ACTION_ADDED: 
    printf("FILE_ACTION_ADDED:");
    break;
  case FILE_ACTION_REMOVED:
    printf("FILE_ACTION_REMOVED:");
    break;
  case FILE_ACTION_MODIFIED:
    printf("FILE_ACTION_MODIFIED:");
    break;
  case FILE_ACTION_RENAMED_OLD_NAME:
    printf("FILE_ACTION_RENAMED_OLD_NAME:");       
    break;
 
  case FILE_ACTION_RENAMED_NEW_NAME:
    printf("FILE_ACTION_RENAMED_NEW_NAME:");
    break;
 
  default:
    break;
  }   
 
  char fileBuffer[512];
 
  WideCharToMultiByte(CP_ACP, 0, record->FileName, record->FileNameLength, fileBuffer, record->FileNameLength, NULL, NULL);
  printf(fileBuffer);
  printf("\r\n");
 
  cbOffset = record->NextEntryOffset;
  record = (PFILE_NOTIFY_INFORMATION)((LPBYTE) record + cbOffset);
}while(cbOffset);

这边示范一个简易的使用范例,实际使用时最好还是搭配执行绪处理:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// ConsoleApplication10.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
#include "Windows.h"
 
void MonitorDir(char* file, bool bIncludeSubdirectories = false)
{
  int    nBufferSize      = 1024;
  char*  buffer        = new char[nBufferSize]; 
  HANDLE hDirectoryHandle  = NULL;
 
 
  hDirectoryHandle = ::CreateFileA(
    file,         
    FILE_LIST_DIRECTORY,       
    FILE_SHARE_READ           
    | FILE_SHARE_WRITE
    | FILE_SHARE_DELETE,
    NULL,               
    OPEN_EXISTING,          
    FILE_FLAG_BACKUP_SEMANTICS     
    | FILE_FLAG_OVERLAPPED,
    NULL);
 
  if(hDirectoryHandle == INVALID_HANDLE_VALUE)
    return;
 
  while(1)
  {
    DWORD dwBytes = 0;
 
    memset(buffer, 0, nBufferSize);
 
    if(!::ReadDirectoryChangesW(
      hDirectoryHandle,           
      buffer,               
      nBufferSize,   
      bIncludeSubdirectories,      
      FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME,  
      &dwBytes,            
      NULL,         
      NULL) || GetLastError() == ERROR_INVALID_HANDLE)
    {
      break;
    }
 
    if(!dwBytes)
    {
      printf("Buffer overflow~~\r\n");
    }
    
    PFILE_NOTIFY_INFORMATION record = (PFILE_NOTIFY_INFORMATION)buffer;
    DWORD cbOffset = 0;
 
    do
    
      switch (record->Action)
      {
      case FILE_ACTION_ADDED: 
        printf("FILE_ACTION_ADDED:");
        break;
      case FILE_ACTION_REMOVED:
        printf("FILE_ACTION_REMOVED:");
        break;
      case FILE_ACTION_MODIFIED:
        printf("FILE_ACTION_MODIFIED:");
        break;
      case FILE_ACTION_RENAMED_OLD_NAME:
        printf("FILE_ACTION_RENAMED_OLD_NAME:");       
        break;
 
      case FILE_ACTION_RENAMED_NEW_NAME:
        printf("FILE_ACTION_RENAMED_NEW_NAME:");
        break;
 
      default:
        break;
      }   
 
      char fileBuffer[512];
 
      WideCharToMultiByte(CP_ACP, 0, record->FileName, record->FileNameLength, fileBuffer, record->FileNameLength, NULL, NULL);
      printf(fileBuffer);
      printf("\r\n");
 
      cbOffset = record->NextEntryOffset;
      record = (PFILE_NOTIFY_INFORMATION)((LPBYTE) record + cbOffset);
    }while(cbOffset);   
  }
 
  delete buffer;
 
  if(hDirectoryHandle)
    CloseHandle(hDirectoryHandle);
}
 
int _tmain(int argc, _TCHAR* argv[])
{
  MonitorDir("C:\\Users\\larry\\Desktop\\新增資料夾");

运行后去对监控的目录操作~可得到类似如下的结果:

VC++文件监控之ReadDirectoryChangesW