wdm 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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->user_data = NULL;
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);
@@ -13,9 +13,27 @@ extern "C" {
13
13
  // Types
14
14
  // ---------------------------------------------------------
15
15
 
16
- typedef struct WDM_QueueItem {
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
- WDM_PQueueItem wdm_queue_item_new();
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();
@@ -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
- unicode_absolute_filepath = ALLOCA_N(WCHAR, absolute_filepath_len + 4 + 1); // 4 for "\\?\" and 1 for NULL
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
- wcscat(unicode_absolute_filepath, absolute_filepath);
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.
@@ -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 void register_monitoring_entry(WDM_PEntry);
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( param->monitor->process_event, 0) != WAIT_OBJECT_0 ) { // Check if already signaled
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 void
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
- // TODO: Handle error!
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
- for(;;) {
373
- info = (PFILE_NOTIFY_INFORMATION)current_info_entry_offset;
374
- event = wdm_rb_change_new_from_notification(item->user_data->dir, info);
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
- WDM_DEBUG("---------------------------");
377
- WDM_DEBUG("Running user callback");
378
- WDM_DEBUG("--------------------------");
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
- rb_funcall(item->user_data->callback, wdm_rb_sym_call, 1, event);
396
+ WDM_DEBUG("---------------------------");
397
+ WDM_DEBUG("Running user callback");
398
+ WDM_DEBUG("--------------------------");
381
399
 
382
- WDM_DEBUG("---------------------------");
400
+ rb_funcall(item->data->user_data->callback, wdm_rb_sym_call, 1, event);
383
401
 
384
- if ( ! info->NextEntryOffset ) break;
402
+ WDM_DEBUG("---------------------------");
385
403
 
386
- current_info_entry_offset += info->NextEntryOffset;
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
 
@@ -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
@@ -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
- wcscat(unicode_path, path);
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
  }
@@ -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
 
@@ -16,6 +16,7 @@ VALUE mWDM;
16
16
  VALUE eWDM_Error;
17
17
  VALUE eWDM_MonitorRunningError;
18
18
  VALUE eWDM_InvalidDirectoryError;
19
+ VALUE eWDM_UnwatchableDirectoryError;
19
20
 
20
21
  ID wdm_rb_sym_call;
21
22
  ID wdm_rb_sym_at_file;
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.1
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-07-23 00:00:00.000000000 Z
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.23
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: