wdm 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/wdm/queue.c +57 -4
- data/ext/wdm/queue.h +26 -2
- data/ext/wdm/rb_change.c +12 -2
- data/ext/wdm/rb_monitor.c +49 -27
- data/ext/wdm/rb_monitor.h +1 -0
- data/ext/wdm/utils.c +13 -1
- data/ext/wdm/utils.h +1 -0
- data/ext/wdm/wdm.c +1 -0
- metadata +22 -4
data/ext/wdm/queue.c
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
#include <stdarg.h>
|
2
|
+
|
1
3
|
#include "wdm.h"
|
2
4
|
|
3
5
|
#include "memory.h"
|
@@ -7,22 +9,73 @@
|
|
7
9
|
// Queue item functions
|
8
10
|
// ---------------------------------------------------------
|
9
11
|
|
12
|
+
WDM_PQueueItemError wdm_queue_item_error_new(VALUE exception, LPCSTR format, ...) {
|
13
|
+
WDM_PQueueItemError error;
|
14
|
+
va_list ap;
|
15
|
+
int length;
|
16
|
+
|
17
|
+
error = WDM_ALLOC(WDM_QueueItemError);
|
18
|
+
|
19
|
+
va_start(ap, format);
|
20
|
+
length = _vscprintf(format, ap);
|
21
|
+
error->message = WDM_ALLOC_N(CHAR, length + 1);
|
22
|
+
vsprintf(error->message, format, ap);
|
23
|
+
va_end(ap);
|
24
|
+
|
25
|
+
error->exception_klass = exception;
|
26
|
+
|
27
|
+
return error;
|
28
|
+
}
|
29
|
+
|
30
|
+
void wdm_queue_item_error_free(WDM_PQueueItemError error) {
|
31
|
+
if ( error->message != NULL ) free(error->message);
|
32
|
+
free(error);
|
33
|
+
}
|
34
|
+
|
35
|
+
WDM_PQueueItemData wdm_queue_item_data_new() {
|
36
|
+
WDM_PQueueItemData data;
|
37
|
+
|
38
|
+
data = ALLOC(WDM_QueueItemData);
|
39
|
+
data->user_data = NULL;
|
40
|
+
|
41
|
+
ZeroMemory(&data->buffer, WDM_BUFFER_SIZE);
|
42
|
+
|
43
|
+
return data;
|
44
|
+
}
|
45
|
+
|
46
|
+
void wdm_queue_item_data_free(WDM_PQueueItemData data) {
|
47
|
+
free(data);
|
48
|
+
}
|
49
|
+
|
10
50
|
WDM_PQueueItem
|
11
|
-
wdm_queue_item_new() {
|
51
|
+
wdm_queue_item_new(WDM_QueueItemType type) {
|
12
52
|
WDM_PQueueItem item;
|
13
53
|
|
14
54
|
item = WDM_ALLOC(WDM_QueueItem);
|
15
|
-
item->
|
55
|
+
item->type = type;
|
56
|
+
|
57
|
+
if ( type == WDM_QUEUE_ITEM_TYPE_ERROR ) {
|
58
|
+
item->error = NULL;
|
59
|
+
}
|
60
|
+
else {
|
61
|
+
item->data = NULL;
|
62
|
+
}
|
63
|
+
|
16
64
|
item->previous = NULL;
|
17
65
|
item->next = NULL;
|
18
66
|
|
19
|
-
ZeroMemory(&item->buffer, WDM_BUFFER_SIZE);
|
20
|
-
|
21
67
|
return item;
|
22
68
|
}
|
23
69
|
|
24
70
|
void
|
25
71
|
wdm_queue_item_free(WDM_PQueueItem item) {
|
72
|
+
if ( item->type == WDM_QUEUE_ITEM_TYPE_ERROR ) {
|
73
|
+
if ( item->error != NULL ) wdm_queue_item_error_free(item->error);
|
74
|
+
}
|
75
|
+
else {
|
76
|
+
if ( item->data != NULL ) wdm_queue_item_data_free(item->data);
|
77
|
+
}
|
78
|
+
|
26
79
|
// We can't really do anything to the prev pointer nor the next pointer,
|
27
80
|
// because we might break any linking the user has established.
|
28
81
|
free(item);
|
data/ext/wdm/queue.h
CHANGED
@@ -13,9 +13,27 @@ extern "C" {
|
|
13
13
|
// Types
|
14
14
|
// ---------------------------------------------------------
|
15
15
|
|
16
|
-
typedef
|
16
|
+
typedef enum {
|
17
|
+
WDM_QUEUE_ITEM_TYPE_ERROR,
|
18
|
+
WDM_QUEUE_ITEM_TYPE_DATA
|
19
|
+
} WDM_QueueItemType;
|
20
|
+
|
21
|
+
typedef struct {
|
17
22
|
WDM_PEntryUserData user_data;
|
18
23
|
BYTE buffer[WDM_BUFFER_SIZE];
|
24
|
+
} WDM_QueueItemData, *WDM_PQueueItemData;
|
25
|
+
|
26
|
+
typedef struct {
|
27
|
+
VALUE exception_klass;
|
28
|
+
LPSTR message;
|
29
|
+
} WDM_QueueItemError, *WDM_PQueueItemError;
|
30
|
+
|
31
|
+
typedef struct WDM_QueueItem {
|
32
|
+
WDM_QueueItemType type;
|
33
|
+
union {
|
34
|
+
WDM_PQueueItemData data;
|
35
|
+
WDM_PQueueItemError error;
|
36
|
+
};
|
19
37
|
struct WDM_QueueItem* previous;
|
20
38
|
struct WDM_QueueItem* next;
|
21
39
|
} WDM_QueueItem, *WDM_PQueueItem;
|
@@ -30,7 +48,13 @@ typedef struct WDM_Queue {
|
|
30
48
|
// Prototypes
|
31
49
|
// ---------------------------------------------------------
|
32
50
|
|
33
|
-
|
51
|
+
WDM_PQueueItemError wdm_queue_item_error_new(VALUE, LPCSTR, ...);
|
52
|
+
void wdm_queue_item_error_free(WDM_PQueueItemError);
|
53
|
+
|
54
|
+
WDM_PQueueItemData wdm_queue_item_data_new();
|
55
|
+
void wdm_queue_item_data_free(WDM_PQueueItemData);
|
56
|
+
|
57
|
+
WDM_PQueueItem wdm_queue_item_new(WDM_QueueItemType);
|
34
58
|
void wdm_queue_item_free(WDM_PQueueItem);
|
35
59
|
|
36
60
|
WDM_PQueue wdm_queue_new();
|
data/ext/wdm/rb_change.c
CHANGED
@@ -86,12 +86,22 @@ extract_absolute_path_from_notification(const LPWSTR base_dir, const PFILE_NOTIF
|
|
86
86
|
{
|
87
87
|
LPWSTR unicode_absolute_filepath;
|
88
88
|
WCHAR absolute_long_filepath[WDM_MAX_WCHAR_LONG_PATH];
|
89
|
+
BOOL is_unc_path;
|
89
90
|
|
90
|
-
|
91
|
+
is_unc_path = wdm_utils_is_unc_path(absolute_filepath);
|
92
|
+
|
93
|
+
unicode_absolute_filepath = ALLOCA_N(WCHAR, absolute_filepath_len + (is_unc_path ? 8 : 4) + 1); // 8 for "\\?\UNC\" or 4 for "\\?\", and 1 for \0
|
91
94
|
|
92
95
|
unicode_absolute_filepath[0] = L'\0';
|
93
96
|
wcscat(unicode_absolute_filepath, L"\\\\?\\");
|
94
|
-
|
97
|
+
|
98
|
+
if ( is_unc_path ) {
|
99
|
+
wcscat(unicode_absolute_filepath, L"UNC\\");
|
100
|
+
wcscat(unicode_absolute_filepath, absolute_filepath + 2); // +2 to skip the begin of a UNC path
|
101
|
+
}
|
102
|
+
else {
|
103
|
+
wcscat(unicode_absolute_filepath, absolute_filepath);
|
104
|
+
}
|
95
105
|
|
96
106
|
// Convert to the long filename form. Unfortunately, this
|
97
107
|
// does not work for deletions, so it's an imperfect fix.
|
data/ext/wdm/rb_monitor.c
CHANGED
@@ -47,7 +47,7 @@ static VALUE rb_monitor_watch(int, VALUE*, VALUE);
|
|
47
47
|
static VALUE rb_monitor_watch_recursively(int, VALUE*, VALUE);
|
48
48
|
|
49
49
|
static void CALLBACK handle_entry_change(DWORD, DWORD, LPOVERLAPPED);
|
50
|
-
static
|
50
|
+
static BOOL register_monitoring_entry(WDM_PEntry);
|
51
51
|
static DWORD WINAPI start_monitoring(LPVOID);
|
52
52
|
|
53
53
|
static VALUE wait_for_changes(LPVOID);
|
@@ -169,9 +169,6 @@ combined_watch(BOOL recursively, int argc, VALUE *argv, VALUE self) {
|
|
169
169
|
entry->user_data->callback = rb_block_proc();
|
170
170
|
entry->user_data->flags = RARRAY_LEN(flags) == 0 ? WDM_MONITOR_FLAGS_DEFAULT : extract_flags_from_rb_array(flags);
|
171
171
|
|
172
|
-
// Prevent the GC from collecting the block until we are done.
|
173
|
-
rb_global_variable(&entry->user_data->callback);
|
174
|
-
|
175
172
|
// WTF Ruby source: The original code (file.c) uses the following macro to make sure that the encoding
|
176
173
|
// of the string is ASCII-compatible, but UTF-16LE (Windows default encoding) is not!!!
|
177
174
|
//
|
@@ -273,14 +270,15 @@ handle_entry_change(
|
|
273
270
|
}
|
274
271
|
|
275
272
|
param = (WDM_PMonitorCallbackParam)event_container->hEvent;
|
276
|
-
data_to_process = wdm_queue_item_new();
|
273
|
+
data_to_process = wdm_queue_item_new(WDM_QUEUE_ITEM_TYPE_DATA);
|
274
|
+
data_to_process->data = wdm_queue_item_data_new();
|
277
275
|
|
278
276
|
WDM_WDEBUG("Change detected in '%s'", param->entry->user_data->dir);
|
279
277
|
|
280
|
-
data_to_process->user_data = param->entry->user_data;
|
278
|
+
data_to_process->data->user_data = param->entry->user_data;
|
281
279
|
|
282
280
|
// Copy change data to the backup buffer
|
283
|
-
memcpy(data_to_process->buffer, param->entry->buffer, bytes_transfered);
|
281
|
+
memcpy(data_to_process->data->buffer, param->entry->buffer, bytes_transfered);
|
284
282
|
|
285
283
|
// Add the backup buffer to the change queue
|
286
284
|
wdm_queue_enqueue(param->monitor->changes, data_to_process);
|
@@ -289,12 +287,12 @@ handle_entry_change(
|
|
289
287
|
register_monitoring_entry(param->entry);
|
290
288
|
|
291
289
|
// Tell the processing thread to process the changes
|
292
|
-
if ( WaitForSingleObject(
|
290
|
+
if ( WaitForSingleObject(param->monitor->process_event, 0) != WAIT_OBJECT_0 ) { // Check if already signaled
|
293
291
|
SetEvent(param->monitor->process_event);
|
294
292
|
}
|
295
293
|
}
|
296
294
|
|
297
|
-
static
|
295
|
+
static BOOL
|
298
296
|
register_monitoring_entry(WDM_PEntry entry) {
|
299
297
|
BOOL success;
|
300
298
|
DWORD bytes;
|
@@ -312,11 +310,11 @@ register_monitoring_entry(WDM_PEntry entry) {
|
|
312
310
|
);
|
313
311
|
|
314
312
|
if ( ! success ) {
|
315
|
-
|
316
|
-
|
317
|
-
// --------------------------
|
318
|
-
WDM_DEBUG("Failed registering directory: '%s'!", entry->user_data->dir);
|
313
|
+
WDM_DEBUG("ReadDirectoryChangesW failed with error (%d): %s", GetLastError(), rb_w32_strerror(GetLastError()));
|
314
|
+
return FALSE;
|
319
315
|
}
|
316
|
+
|
317
|
+
return TRUE;
|
320
318
|
}
|
321
319
|
|
322
320
|
static DWORD WINAPI
|
@@ -330,7 +328,24 @@ start_monitoring(LPVOID param) {
|
|
330
328
|
WDM_DEBUG("Starting the monitoring thread!");
|
331
329
|
|
332
330
|
while(curr_entry != NULL) {
|
333
|
-
register_monitoring_entry(curr_entry)
|
331
|
+
if ( ! register_monitoring_entry(curr_entry) ) {
|
332
|
+
WDM_PQueueItem error_item;
|
333
|
+
int directory_bytes;
|
334
|
+
LPSTR multibyte_directory;
|
335
|
+
|
336
|
+
directory_bytes = WideCharToMultiByte(CP_UTF8, 0, curr_entry->user_data->dir, -1, NULL, 0, NULL, NULL);
|
337
|
+
multibyte_directory = ALLOCA_N(CHAR, directory_bytes);
|
338
|
+
WideCharToMultiByte(CP_UTF8, 0, curr_entry->user_data->dir, -1, multibyte_directory, directory_bytes, NULL, NULL);
|
339
|
+
|
340
|
+
error_item = wdm_queue_item_new(WDM_QUEUE_ITEM_TYPE_ERROR);
|
341
|
+
error_item->error = wdm_queue_item_error_new(
|
342
|
+
eWDM_UnwatchableDirectoryError, "Can't watch directory: '%s'!", multibyte_directory
|
343
|
+
);
|
344
|
+
|
345
|
+
wdm_queue_enqueue(monitor->changes, error_item);
|
346
|
+
SetEvent(monitor->process_event);
|
347
|
+
}
|
348
|
+
|
334
349
|
curr_entry = curr_entry->next;
|
335
350
|
}
|
336
351
|
|
@@ -367,23 +382,29 @@ process_changes(WDM_PQueue changes) {
|
|
367
382
|
|
368
383
|
while( ! wdm_queue_is_empty(changes) ) {
|
369
384
|
item = wdm_queue_dequeue(changes);
|
370
|
-
current_info_entry_offset = (LPBYTE)item->buffer;
|
371
385
|
|
372
|
-
|
373
|
-
|
374
|
-
|
386
|
+
if ( item->type == WDM_QUEUE_ITEM_TYPE_ERROR ) {
|
387
|
+
rb_raise(item->error->exception_klass, item->error->message);
|
388
|
+
}
|
389
|
+
else {
|
390
|
+
current_info_entry_offset = (LPBYTE)item->data->buffer;
|
375
391
|
|
376
|
-
|
377
|
-
|
378
|
-
|
392
|
+
for(;;) {
|
393
|
+
info = (PFILE_NOTIFY_INFORMATION)current_info_entry_offset;
|
394
|
+
event = wdm_rb_change_new_from_notification(item->data->user_data->dir, info);
|
379
395
|
|
380
|
-
|
396
|
+
WDM_DEBUG("---------------------------");
|
397
|
+
WDM_DEBUG("Running user callback");
|
398
|
+
WDM_DEBUG("--------------------------");
|
381
399
|
|
382
|
-
|
400
|
+
rb_funcall(item->data->user_data->callback, wdm_rb_sym_call, 1, event);
|
383
401
|
|
384
|
-
|
402
|
+
WDM_DEBUG("---------------------------");
|
385
403
|
|
386
|
-
|
404
|
+
if ( ! info->NextEntryOffset ) break;
|
405
|
+
|
406
|
+
current_info_entry_offset += info->NextEntryOffset;
|
407
|
+
}
|
387
408
|
}
|
388
409
|
|
389
410
|
wdm_queue_item_free(item);
|
@@ -450,7 +471,7 @@ rb_monitor_run_bang(VALUE self) {
|
|
450
471
|
|
451
472
|
if (already_running) {
|
452
473
|
WDM_DEBUG("Not doing anything because the monitor is already running!");
|
453
|
-
return;
|
474
|
+
return Qnil;
|
454
475
|
}
|
455
476
|
|
456
477
|
// Reset events
|
@@ -479,7 +500,7 @@ rb_monitor_run_bang(VALUE self) {
|
|
479
500
|
|
480
501
|
if ( ! monitor->running ) {
|
481
502
|
wdm_queue_empty(monitor->changes);
|
482
|
-
return;
|
503
|
+
return Qnil;
|
483
504
|
}
|
484
505
|
|
485
506
|
process_changes(monitor->changes);
|
@@ -523,6 +544,7 @@ wdm_rb_monitor_init() {
|
|
523
544
|
eWDM_MonitorRunningError = rb_define_class_under(mWDM, "MonitorRunningError", eWDM_Error);
|
524
545
|
eWDM_InvalidDirectoryError = rb_define_class_under(mWDM, "InvalidDirectoryError", eWDM_Error);
|
525
546
|
eWDM_UnknownFlagError = rb_define_class_under(mWDM, "UnknownFlagError", eWDM_Error);
|
547
|
+
eWDM_UnwatchableDirectoryError = rb_define_class_under(mWDM, "UnwatchableDirectoryError", eWDM_Error);
|
526
548
|
|
527
549
|
cWDM_Monitor = rb_define_class_under(mWDM, "Monitor", rb_cObject);
|
528
550
|
|
data/ext/wdm/rb_monitor.h
CHANGED
@@ -23,6 +23,7 @@ extern VALUE cWDM_Monitor;
|
|
23
23
|
extern VALUE eWDM_UnknownFlagError;
|
24
24
|
extern VALUE eWDM_MonitorRunningError;
|
25
25
|
extern VALUE eWDM_InvalidDirectoryError;
|
26
|
+
extern VALUE eWDM_UnwatchableDirectoryError;
|
26
27
|
|
27
28
|
// ---------------------------------------------------------
|
28
29
|
// Prototypes
|
data/ext/wdm/utils.c
CHANGED
@@ -46,7 +46,14 @@ wdm_utils_unicode_is_directory(const LPWSTR path) {
|
|
46
46
|
WCHAR unicode_path[WDM_MAX_WCHAR_LONG_PATH];
|
47
47
|
|
48
48
|
wcscpy(unicode_path, L"\\\\?\\");
|
49
|
-
|
49
|
+
|
50
|
+
if ( wdm_utils_is_unc_path(path) ) {
|
51
|
+
wcscat(unicode_path, L"UNC\\");
|
52
|
+
wcscat(unicode_path, path + 2); // +2 to skip the begin of a UNC path
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
wcscat(unicode_path, path);
|
56
|
+
}
|
50
57
|
|
51
58
|
return wdm_utils_is_directory(unicode_path);
|
52
59
|
}
|
@@ -57,4 +64,9 @@ wdm_utils_is_directory(const LPWSTR path) {
|
|
57
64
|
|
58
65
|
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
59
66
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
67
|
+
}
|
68
|
+
|
69
|
+
BOOL
|
70
|
+
wdm_utils_is_unc_path(const LPWSTR path) {
|
71
|
+
return path[0] == path[1] && path[0] == L'\\';
|
60
72
|
}
|
data/ext/wdm/utils.h
CHANGED
@@ -13,6 +13,7 @@ extern "C" {
|
|
13
13
|
|
14
14
|
LPWSTR wdm_utils_convert_back_to_forward_slashes(LPWSTR, DWORD);
|
15
15
|
LPWSTR wdm_utils_full_pathname(const LPWSTR path);
|
16
|
+
BOOL wdm_utils_is_unc_path(const LPWSTR);
|
16
17
|
BOOL wdm_utils_is_directory(const LPWSTR);
|
17
18
|
BOOL wdm_utils_unicode_is_directory(const LPWSTR);
|
18
19
|
|
data/ext/wdm/wdm.c
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wdm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
@@ -107,6 +107,22 @@ dependencies:
|
|
107
107
|
- - ! '>='
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: pimpmychangelog
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
110
126
|
description: Windows Directory Monitor (WDM) is a library which can be used to monitor
|
111
127
|
directories for changes. It's mostly implemented in C and uses the Win32 API for
|
112
128
|
a better performance.
|
@@ -157,11 +173,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
173
|
- - ! '>='
|
158
174
|
- !ruby/object:Gem::Version
|
159
175
|
version: '0'
|
176
|
+
segments:
|
177
|
+
- 0
|
178
|
+
hash: 341068911
|
160
179
|
requirements: []
|
161
180
|
rubyforge_project:
|
162
|
-
rubygems_version: 1.8.
|
181
|
+
rubygems_version: 1.8.24
|
163
182
|
signing_key:
|
164
183
|
specification_version: 3
|
165
184
|
summary: Windows Directory Monitor (WDM) is a threaded directories monitor for Windows.
|
166
185
|
test_files: []
|
167
|
-
has_rdoc:
|