wdm 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: