wdm 0.1.0 → 0.1.1

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,73 +1,73 @@
1
- #include <Windows.h>
2
-
3
- #include "entry.h"
4
-
5
- #ifndef WDM_QUEUE_H
6
- #define WDM_QUEUE_H
7
-
8
- #ifdef __cplusplus
9
- extern "C" {
10
- #endif // __cplusplus
11
-
12
- // ---------------------------------------------------------
13
- // Types
14
- // ---------------------------------------------------------
15
-
16
- typedef enum {
17
- WDM_QUEUE_ITEM_TYPE_ERROR,
18
- WDM_QUEUE_ITEM_TYPE_DATA
19
- } WDM_QueueItemType;
20
-
21
- typedef struct {
22
- WDM_PEntryUserData user_data;
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
- };
37
- struct WDM_QueueItem* next;
38
- } WDM_QueueItem, *WDM_PQueueItem;
39
-
40
- typedef struct {
41
- CRITICAL_SECTION lock;
42
- WDM_PQueueItem front;
43
- WDM_PQueueItem rear;
44
- } WDM_Queue, *WDM_PQueue;
45
-
46
- // ---------------------------------------------------------
47
- // Prototypes
48
- // ---------------------------------------------------------
49
-
50
- WDM_PQueueItemError wdm_queue_item_error_new(VALUE, LPCSTR, ...);
51
- void wdm_queue_item_error_free(WDM_PQueueItemError);
52
-
53
- WDM_PQueueItemData wdm_queue_item_data_new();
54
- void wdm_queue_item_data_free(WDM_PQueueItemData);
55
-
56
- WDM_PQueueItem wdm_queue_item_new(WDM_QueueItemType);
57
- void wdm_queue_item_free(WDM_PQueueItem);
58
-
59
- WDM_PQueue wdm_queue_new();
60
- void wdm_queue_free(WDM_PQueue);
61
-
62
- void wdm_queue_enqueue(WDM_PQueue, WDM_PQueueItem);
63
- WDM_PQueueItem wdm_queue_dequeue(WDM_PQueue);
64
- void wdm_queue_empty(WDM_PQueue);
65
- BOOL wdm_queue_is_empty(WDM_PQueue);
66
-
67
- // ---------------------------------------------------------
68
-
69
- #ifdef __cplusplus
70
- }
71
- #endif // __cplusplus
72
-
1
+ #include <Windows.h>
2
+
3
+ #include "entry.h"
4
+
5
+ #ifndef WDM_QUEUE_H
6
+ #define WDM_QUEUE_H
7
+
8
+ #ifdef __cplusplus
9
+ extern "C" {
10
+ #endif // __cplusplus
11
+
12
+ // ---------------------------------------------------------
13
+ // Types
14
+ // ---------------------------------------------------------
15
+
16
+ typedef enum {
17
+ WDM_QUEUE_ITEM_TYPE_ERROR,
18
+ WDM_QUEUE_ITEM_TYPE_DATA
19
+ } WDM_QueueItemType;
20
+
21
+ typedef struct {
22
+ WDM_PEntryUserData user_data;
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
+ };
37
+ struct WDM_QueueItem* next;
38
+ } WDM_QueueItem, *WDM_PQueueItem;
39
+
40
+ typedef struct {
41
+ CRITICAL_SECTION lock;
42
+ WDM_PQueueItem front;
43
+ WDM_PQueueItem rear;
44
+ } WDM_Queue, *WDM_PQueue;
45
+
46
+ // ---------------------------------------------------------
47
+ // Prototypes
48
+ // ---------------------------------------------------------
49
+
50
+ WDM_PQueueItemError wdm_queue_item_error_new(VALUE, LPCSTR, ...);
51
+ void wdm_queue_item_error_free(WDM_PQueueItemError);
52
+
53
+ WDM_PQueueItemData wdm_queue_item_data_new();
54
+ void wdm_queue_item_data_free(WDM_PQueueItemData);
55
+
56
+ WDM_PQueueItem wdm_queue_item_new(WDM_QueueItemType);
57
+ void wdm_queue_item_free(WDM_PQueueItem);
58
+
59
+ WDM_PQueue wdm_queue_new();
60
+ void wdm_queue_free(WDM_PQueue);
61
+
62
+ void wdm_queue_enqueue(WDM_PQueue, WDM_PQueueItem);
63
+ WDM_PQueueItem wdm_queue_dequeue(WDM_PQueue);
64
+ void wdm_queue_empty(WDM_PQueue);
65
+ BOOL wdm_queue_is_empty(WDM_PQueue);
66
+
67
+ // ---------------------------------------------------------
68
+
69
+ #ifdef __cplusplus
70
+ }
71
+ #endif // __cplusplus
72
+
73
73
  #endif // WDM_QUEUE_H
@@ -1,199 +1,199 @@
1
- #include <stdlib.h>
2
- #include <wchar.h>
3
-
4
- #include "wdm.h"
5
-
6
- #include "utils.h"
7
-
8
- #include "rb_change.h"
9
-
10
- // ---------------------------------------------------------
11
- // Internal constants
12
- // ---------------------------------------------------------
13
-
14
- // The _wsplitpat constants account for two NULL chars, so subtract 1 because we only need one!
15
- #define WDM_MAX_FILENAME (_MAX_FNAME + _MAX_EXT - 1)
16
-
17
- // ----------------------------------------------------------
18
- // Global variables
19
- // ----------------------------------------------------------
20
-
21
- VALUE cWDM_Change;
22
-
23
- static ID wdm_rb_sym_at_path;
24
- static ID wdm_rb_sym_at_type;
25
- static ID wdm_rb_sym_added;
26
- static ID wdm_rb_sym_modified;
27
- static ID wdm_rb_sym_removed;
28
- static ID wdm_rb_sym_renamed_old_file;
29
- static ID wdm_rb_sym_renamed_new_file;
30
-
31
- // ----------------------------------------------------------
32
- // Prototypes of static functions
33
- // ----------------------------------------------------------
34
-
35
- static VALUE extract_absolute_path_from_notification(const LPWSTR, const PFILE_NOTIFY_INFORMATION);
36
- static VALUE extract_change_type_from_notification(const PFILE_NOTIFY_INFORMATION);
37
-
38
- // ----------------------------------------------------------
39
-
40
- // TODO:
41
- // 1. this function uses a lot of 'alloca' calls, which AFAIK is not recommended! Can this be avoided?
42
- // 2. all wcscat calls can be done faster with memcpy, but is it worth sacrificing the readability?
43
- static VALUE
44
- extract_absolute_path_from_notification(const LPWSTR base_dir, const PFILE_NOTIFY_INFORMATION info)
45
- {
46
- LPWSTR buffer, absolute_filepath;
47
- WCHAR file[_MAX_FNAME], ext[_MAX_EXT], filename[WDM_MAX_FILENAME];
48
- DWORD filename_len, absolute_filepath_len;
49
- LPSTR multibyte_filepath;
50
- int multibyte_filepath_buffer_size;
51
- VALUE path;
52
-
53
- filename_len = info->FileNameLength/sizeof(WCHAR);
54
-
55
- // The file in the 'info' struct is NOT null-terminated, so add 1 extra char to the allocation
56
- buffer = ALLOCA_N(WCHAR, filename_len + 1);
57
-
58
- memcpy(buffer, info->FileName, info->FileNameLength);
59
-
60
- // Null-terminate the string
61
- buffer[filename_len] = L'\0';
62
-
63
- WDM_WDEBUG("change in: '%s'", buffer);
64
-
65
- absolute_filepath_len = wcslen(base_dir) + filename_len;
66
- absolute_filepath = ALLOCA_N(WCHAR, absolute_filepath_len + 1); // 1 for NULL
67
- absolute_filepath[0] = L'\0';
68
-
69
- wcscat(absolute_filepath, base_dir);
70
- wcscat(absolute_filepath, buffer);
71
-
72
- WDM_WDEBUG("absolute path is: '%s'", absolute_filepath);
73
-
74
- _wsplitpath(buffer, NULL, NULL, file, ext);
75
-
76
- // TODO: Extracting the file name from 'buffer' is only needed when watching sub-dirs
77
- filename[0] = L'\0';
78
- if ( file[0] != L'\0' ) wcscat(filename, file);
79
- if ( ext[0] != L'\0' ) wcscat(filename, ext);
80
-
81
- WDM_WDEBUG("filename: '%s'", filename);
82
-
83
- filename_len = wcslen(filename);
84
-
85
- // The maximum length of an 8.3 filename is twelve, including the dot.
86
- if (filename_len <= 12 && wcschr(filename, L'~'))
87
- {
88
- LPWSTR unicode_absolute_filepath;
89
- WCHAR absolute_long_filepath[WDM_MAX_WCHAR_LONG_PATH];
90
- BOOL is_unc_path;
91
-
92
- is_unc_path = wdm_utils_is_unc_path(absolute_filepath);
93
-
94
- 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
95
-
96
- unicode_absolute_filepath[0] = L'\0';
97
- wcscat(unicode_absolute_filepath, L"\\\\?\\");
98
-
99
- if ( is_unc_path ) {
100
- wcscat(unicode_absolute_filepath, L"UNC\\");
101
- wcscat(unicode_absolute_filepath, absolute_filepath + 2); // +2 to skip the begin of a UNC path
102
- }
103
- else {
104
- wcscat(unicode_absolute_filepath, absolute_filepath);
105
- }
106
-
107
- // Convert to the long filename form. Unfortunately, this
108
- // does not work for deletions, so it's an imperfect fix.
109
- if (GetLongPathNameW(unicode_absolute_filepath, absolute_long_filepath, WDM_MAX_WCHAR_LONG_PATH) != 0) {
110
- absolute_filepath = absolute_long_filepath + 4; // Skip first 4 pointers of "\\?\"
111
- absolute_filepath_len = wcslen(absolute_filepath);
112
- WDM_WDEBUG("Short path converted to long: '%s'", absolute_filepath);
113
- }
114
- else {
115
- WDM_DEBUG("Can't convert short path to long: '%s'", rb_w32_strerror(GetLastError()));
116
- }
117
- }
118
-
119
- // The convention in Ruby is to use forward-slashes to separate dirs on all platforms.
120
- wdm_utils_convert_back_to_forward_slashes(absolute_filepath, absolute_filepath_len + 1);
121
-
122
- // Convert the path from WCHAR to multibyte CHAR to use it in a ruby string
123
- multibyte_filepath_buffer_size =
124
- WideCharToMultiByte(CP_UTF8, 0, absolute_filepath, absolute_filepath_len + 1, NULL, 0, NULL, NULL);
125
-
126
- multibyte_filepath = ALLOCA_N(CHAR, multibyte_filepath_buffer_size);
127
-
128
- if ( 0 == WideCharToMultiByte(CP_UTF8, 0, absolute_filepath, absolute_filepath_len + 1,
129
- multibyte_filepath, multibyte_filepath_buffer_size, NULL, NULL) ) {
130
- rb_raise(eWDM_Error, "Failed to add the change file path to the event!");
131
- }
132
-
133
- WDM_DEBUG("will report change in: '%s'", multibyte_filepath);
134
-
135
- path = rb_enc_str_new(multibyte_filepath,
136
- multibyte_filepath_buffer_size - 1, // -1 because this func takes the chars count, not bytes count
137
- wdm_rb_enc_utf8);
138
-
139
- OBJ_TAINT(path);
140
-
141
- return path;
142
- }
143
-
144
- static VALUE
145
- extract_change_type_from_notification(const PFILE_NOTIFY_INFORMATION info)
146
- {
147
- ID type;
148
-
149
- switch(info->Action) {
150
- case FILE_ACTION_ADDED: type = wdm_rb_sym_added; break;
151
- case FILE_ACTION_REMOVED: type = wdm_rb_sym_removed; break;
152
- case FILE_ACTION_MODIFIED: type = wdm_rb_sym_modified; break;
153
- case FILE_ACTION_RENAMED_OLD_NAME: type = wdm_rb_sym_renamed_old_file; break;
154
- case FILE_ACTION_RENAMED_NEW_NAME: type = wdm_rb_sym_renamed_new_file; break;
155
- default:
156
- rb_raise(eWDM_Error, "Unknown change happened to a file in a watched directory!");
157
- }
158
-
159
- #if WDM_DEBUG_ENABLED // Used to avoid the func call when in release mode
160
- WDM_DEBUG("change type: '%s'", rb_id2name(type));
161
- #endif
162
-
163
- return ID2SYM(type);
164
- }
165
-
166
- VALUE
167
- wdm_rb_change_new_from_notification(const LPWSTR base_dir, const PFILE_NOTIFY_INFORMATION info)
168
- {
169
- VALUE change;
170
-
171
- change = rb_class_new_instance(0, NULL, cWDM_Change);
172
-
173
- // Set '@type' to the change type
174
- rb_ivar_set(change, wdm_rb_sym_at_type, extract_change_type_from_notification(info));
175
-
176
- // Set '@path' to the absolute path of the changed file/directory
177
- rb_ivar_set(change, wdm_rb_sym_at_path, extract_absolute_path_from_notification(base_dir, info));
178
-
179
- return change;
180
- }
181
-
182
- void
183
- wdm_rb_change_init()
184
- {
185
- WDM_DEBUG("Registering WDM::Event with Ruby!");
186
-
187
- wdm_rb_sym_at_path = rb_intern("@path");
188
- wdm_rb_sym_at_type = rb_intern("@type");
189
- wdm_rb_sym_added = rb_intern("added");
190
- wdm_rb_sym_modified = rb_intern("modified");
191
- wdm_rb_sym_removed = rb_intern("removed");
192
- wdm_rb_sym_renamed_old_file = rb_intern("renamed_old_file");
193
- wdm_rb_sym_renamed_new_file = rb_intern("renamed_new_file");
194
-
195
- cWDM_Change = rb_define_class_under(mWDM, "Change", rb_cObject);
196
-
197
- rb_define_attr(cWDM_Change, "path", 1, 0);
198
- rb_define_attr(cWDM_Change, "type", 1, 0);
1
+ #include <stdlib.h>
2
+ #include <wchar.h>
3
+
4
+ #include "wdm.h"
5
+
6
+ #include "utils.h"
7
+
8
+ #include "rb_change.h"
9
+
10
+ // ---------------------------------------------------------
11
+ // Internal constants
12
+ // ---------------------------------------------------------
13
+
14
+ // The _wsplitpat constants account for two NULL chars, so subtract 1 because we only need one!
15
+ #define WDM_MAX_FILENAME (_MAX_FNAME + _MAX_EXT - 1)
16
+
17
+ // ----------------------------------------------------------
18
+ // Global variables
19
+ // ----------------------------------------------------------
20
+
21
+ VALUE cWDM_Change;
22
+
23
+ static ID wdm_rb_sym_at_path;
24
+ static ID wdm_rb_sym_at_type;
25
+ static ID wdm_rb_sym_added;
26
+ static ID wdm_rb_sym_modified;
27
+ static ID wdm_rb_sym_removed;
28
+ static ID wdm_rb_sym_renamed_old_file;
29
+ static ID wdm_rb_sym_renamed_new_file;
30
+
31
+ // ----------------------------------------------------------
32
+ // Prototypes of static functions
33
+ // ----------------------------------------------------------
34
+
35
+ static VALUE extract_absolute_path_from_notification(const LPWSTR, const PFILE_NOTIFY_INFORMATION);
36
+ static VALUE extract_change_type_from_notification(const PFILE_NOTIFY_INFORMATION);
37
+
38
+ // ----------------------------------------------------------
39
+
40
+ // TODO:
41
+ // 1. this function uses a lot of 'alloca' calls, which AFAIK is not recommended! Can this be avoided?
42
+ // 2. all wcscat calls can be done faster with memcpy, but is it worth sacrificing the readability?
43
+ static VALUE
44
+ extract_absolute_path_from_notification(const LPWSTR base_dir, const PFILE_NOTIFY_INFORMATION info)
45
+ {
46
+ LPWSTR buffer, absolute_filepath;
47
+ WCHAR file[_MAX_FNAME], ext[_MAX_EXT], filename[WDM_MAX_FILENAME];
48
+ DWORD filename_len, absolute_filepath_len;
49
+ LPSTR multibyte_filepath;
50
+ int multibyte_filepath_buffer_size;
51
+ VALUE path;
52
+
53
+ filename_len = info->FileNameLength/sizeof(WCHAR);
54
+
55
+ // The file in the 'info' struct is NOT null-terminated, so add 1 extra char to the allocation
56
+ buffer = ALLOCA_N(WCHAR, filename_len + 1);
57
+
58
+ memcpy(buffer, info->FileName, info->FileNameLength);
59
+
60
+ // Null-terminate the string
61
+ buffer[filename_len] = L'\0';
62
+
63
+ WDM_WDEBUG("change in: '%s'", buffer);
64
+
65
+ absolute_filepath_len = wcslen(base_dir) + filename_len;
66
+ absolute_filepath = ALLOCA_N(WCHAR, absolute_filepath_len + 1); // 1 for NULL
67
+ absolute_filepath[0] = L'\0';
68
+
69
+ wcscat(absolute_filepath, base_dir);
70
+ wcscat(absolute_filepath, buffer);
71
+
72
+ WDM_WDEBUG("absolute path is: '%s'", absolute_filepath);
73
+
74
+ _wsplitpath(buffer, NULL, NULL, file, ext);
75
+
76
+ // TODO: Extracting the file name from 'buffer' is only needed when watching sub-dirs
77
+ filename[0] = L'\0';
78
+ if ( file[0] != L'\0' ) wcscat(filename, file);
79
+ if ( ext[0] != L'\0' ) wcscat(filename, ext);
80
+
81
+ WDM_WDEBUG("filename: '%s'", filename);
82
+
83
+ filename_len = wcslen(filename);
84
+
85
+ // The maximum length of an 8.3 filename is twelve, including the dot.
86
+ if (filename_len <= 12 && wcschr(filename, L'~'))
87
+ {
88
+ LPWSTR unicode_absolute_filepath;
89
+ WCHAR absolute_long_filepath[WDM_MAX_WCHAR_LONG_PATH];
90
+ BOOL is_unc_path;
91
+
92
+ is_unc_path = wdm_utils_is_unc_path(absolute_filepath);
93
+
94
+ 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
95
+
96
+ unicode_absolute_filepath[0] = L'\0';
97
+ wcscat(unicode_absolute_filepath, L"\\\\?\\");
98
+
99
+ if ( is_unc_path ) {
100
+ wcscat(unicode_absolute_filepath, L"UNC\\");
101
+ wcscat(unicode_absolute_filepath, absolute_filepath + 2); // +2 to skip the begin of a UNC path
102
+ }
103
+ else {
104
+ wcscat(unicode_absolute_filepath, absolute_filepath);
105
+ }
106
+
107
+ // Convert to the long filename form. Unfortunately, this
108
+ // does not work for deletions, so it's an imperfect fix.
109
+ if (GetLongPathNameW(unicode_absolute_filepath, absolute_long_filepath, WDM_MAX_WCHAR_LONG_PATH) != 0) {
110
+ absolute_filepath = absolute_long_filepath + 4; // Skip first 4 pointers of "\\?\"
111
+ absolute_filepath_len = wcslen(absolute_filepath);
112
+ WDM_WDEBUG("Short path converted to long: '%s'", absolute_filepath);
113
+ }
114
+ else {
115
+ WDM_DEBUG("Can't convert short path to long: '%s'", rb_w32_strerror(GetLastError()));
116
+ }
117
+ }
118
+
119
+ // The convention in Ruby is to use forward-slashes to separate dirs on all platforms.
120
+ wdm_utils_convert_back_to_forward_slashes(absolute_filepath, absolute_filepath_len + 1);
121
+
122
+ // Convert the path from WCHAR to multibyte CHAR to use it in a ruby string
123
+ multibyte_filepath_buffer_size =
124
+ WideCharToMultiByte(CP_UTF8, 0, absolute_filepath, absolute_filepath_len + 1, NULL, 0, NULL, NULL);
125
+
126
+ multibyte_filepath = ALLOCA_N(CHAR, multibyte_filepath_buffer_size);
127
+
128
+ if ( 0 == WideCharToMultiByte(CP_UTF8, 0, absolute_filepath, absolute_filepath_len + 1,
129
+ multibyte_filepath, multibyte_filepath_buffer_size, NULL, NULL) ) {
130
+ rb_raise(eWDM_Error, "Failed to add the change file path to the event!");
131
+ }
132
+
133
+ WDM_DEBUG("will report change in: '%s'", multibyte_filepath);
134
+
135
+ path = rb_enc_str_new(multibyte_filepath,
136
+ multibyte_filepath_buffer_size - 1, // -1 because this func takes the chars count, not bytes count
137
+ wdm_rb_enc_utf8);
138
+
139
+ OBJ_TAINT(path);
140
+
141
+ return path;
142
+ }
143
+
144
+ static VALUE
145
+ extract_change_type_from_notification(const PFILE_NOTIFY_INFORMATION info)
146
+ {
147
+ ID type;
148
+
149
+ switch(info->Action) {
150
+ case FILE_ACTION_ADDED: type = wdm_rb_sym_added; break;
151
+ case FILE_ACTION_REMOVED: type = wdm_rb_sym_removed; break;
152
+ case FILE_ACTION_MODIFIED: type = wdm_rb_sym_modified; break;
153
+ case FILE_ACTION_RENAMED_OLD_NAME: type = wdm_rb_sym_renamed_old_file; break;
154
+ case FILE_ACTION_RENAMED_NEW_NAME: type = wdm_rb_sym_renamed_new_file; break;
155
+ default:
156
+ rb_raise(eWDM_Error, "Unknown change happened to a file in a watched directory!");
157
+ }
158
+
159
+ #if WDM_DEBUG_ENABLED // Used to avoid the func call when in release mode
160
+ WDM_DEBUG("change type: '%s'", rb_id2name(type));
161
+ #endif
162
+
163
+ return ID2SYM(type);
164
+ }
165
+
166
+ VALUE
167
+ wdm_rb_change_new_from_notification(const LPWSTR base_dir, const PFILE_NOTIFY_INFORMATION info)
168
+ {
169
+ VALUE change;
170
+
171
+ change = rb_class_new_instance(0, NULL, cWDM_Change);
172
+
173
+ // Set '@type' to the change type
174
+ rb_ivar_set(change, wdm_rb_sym_at_type, extract_change_type_from_notification(info));
175
+
176
+ // Set '@path' to the absolute path of the changed file/directory
177
+ rb_ivar_set(change, wdm_rb_sym_at_path, extract_absolute_path_from_notification(base_dir, info));
178
+
179
+ return change;
180
+ }
181
+
182
+ void
183
+ wdm_rb_change_init()
184
+ {
185
+ WDM_DEBUG("Registering WDM::Event with Ruby!");
186
+
187
+ wdm_rb_sym_at_path = rb_intern("@path");
188
+ wdm_rb_sym_at_type = rb_intern("@type");
189
+ wdm_rb_sym_added = rb_intern("added");
190
+ wdm_rb_sym_modified = rb_intern("modified");
191
+ wdm_rb_sym_removed = rb_intern("removed");
192
+ wdm_rb_sym_renamed_old_file = rb_intern("renamed_old_file");
193
+ wdm_rb_sym_renamed_new_file = rb_intern("renamed_new_file");
194
+
195
+ cWDM_Change = rb_define_class_under(mWDM, "Change", rb_cObject);
196
+
197
+ rb_define_attr(cWDM_Change, "path", 1, 0);
198
+ rb_define_attr(cWDM_Change, "type", 1, 0);
199
199
  }