1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Vlad Grecescu <b100dian@gmail.com>
24 #define _WIN32_WINNT 0x0400
27 #include "gwin32directorymonitor.h"
30 G_DEFINE_TYPE_WITH_CODE (GWin32DirectoryMonitor
,
31 g_win32_directory_monitor
,
32 G_TYPE_LOCAL_DIRECTORY_MONITOR
,
33 g_io_extension_point_implement (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME
,
35 "readdirectorychanges",
38 struct _GWin32DirectoryMonitorPrivate
{
39 OVERLAPPED overlapped
;
40 DWORD buffer_allocated_bytes
;
41 gchar
*file_notify_buffer
;
42 DWORD buffer_filled_bytes
;
44 /* Needed in the APC where we only have this private struct */
48 static void g_win32_directory_monitor_finalize (GObject
*base
);
49 static gboolean
g_win32_directory_monitor_cancel (GFileMonitor
*base
);
51 static GObject
*g_win32_directory_monitor_constructor (GType type
,
52 guint n_construct_properties
,
53 GObjectConstructParam
*construct_properties
);
56 g_win32_directory_monitor_is_supported (void)
62 g_win32_directory_monitor_finalize (GObject
*base
)
64 GWin32DirectoryMonitor
*self
;
65 self
= G_WIN32_DIRECTORY_MONITOR (base
);
67 if (self
->priv
->hDirectory
== INVALID_HANDLE_VALUE
)
69 /* If we don't have a directory handle we can free
70 * self->priv->file_notify_buffer and self->priv here. The
71 * callback won't be called obviously any more (and presumably
72 * never has been called).
74 g_free (self
->priv
->file_notify_buffer
);
75 self
->priv
->file_notify_buffer
= NULL
;
80 /* If we have a directory handle, the OVERLAPPED struct is
81 * passed once more to the callback as a result of the
82 * CloseHandle() done in the cancel method, so self->priv has to
83 * be kept around. The GWin32DirectoryMonitor object is
84 * disappearing, so can't leave a pointer to it in
87 self
->priv
->self
= NULL
;
90 if (G_OBJECT_CLASS (g_win32_directory_monitor_parent_class
)->finalize
)
91 (*G_OBJECT_CLASS (g_win32_directory_monitor_parent_class
)->finalize
) (base
);
95 g_win32_directory_monitor_cancel (GFileMonitor
*base
)
97 GWin32DirectoryMonitor
*self
;
98 self
= G_WIN32_DIRECTORY_MONITOR (base
);
100 /* This triggers a last callback() with nBytes==0. */
102 /* Actually I am not so sure about that, it seems to trigger a last
103 * callback allright, but the way to recognize that it is the final
104 * one is not to check for nBytes==0, I think that was a
107 if (self
->priv
->hDirectory
!= INVALID_HANDLE_VALUE
)
108 CloseHandle (self
->priv
->hDirectory
);
110 if (G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class
)->cancel
)
111 (*G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class
)->cancel
) (base
);
116 g_win32_directory_monitor_callback (DWORD error
,
118 LPOVERLAPPED lpOverlapped
)
121 PFILE_NOTIFY_INFORMATION pfile_notify_walker
;
126 GWin32DirectoryMonitorPrivate
*priv
= (GWin32DirectoryMonitorPrivate
*) lpOverlapped
;
128 static GFileMonitorEvent events
[] =
131 G_FILE_MONITOR_EVENT_CREATED
, /* FILE_ACTION_ADDED */
132 G_FILE_MONITOR_EVENT_DELETED
, /* FILE_ACTION_REMOVED */
133 G_FILE_MONITOR_EVENT_CHANGED
, /* FILE_ACTION_MODIFIED */
134 G_FILE_MONITOR_EVENT_DELETED
, /* FILE_ACTION_RENAMED_OLD_NAME */
135 G_FILE_MONITOR_EVENT_CREATED
, /* FILE_ACTION_RENAMED_NEW_NAME */
138 /* If priv->self is NULL the GWin32DirectoryMonitor object has been destroyed. */
139 if (priv
->self
== NULL
||
140 g_file_monitor_is_cancelled (priv
->self
) ||
141 priv
->file_notify_buffer
== NULL
)
143 g_free (priv
->file_notify_buffer
);
150 pfile_notify_walker
= (PFILE_NOTIFY_INFORMATION
)(priv
->file_notify_buffer
+ offset
);
151 if (pfile_notify_walker
->Action
> 0)
153 file_name
= g_utf16_to_utf8 (pfile_notify_walker
->FileName
, pfile_notify_walker
->FileNameLength
/ sizeof(WCHAR
), NULL
, &file_name_len
, NULL
);
154 path
= g_build_filename(G_LOCAL_DIRECTORY_MONITOR (priv
->self
)->dirname
, file_name
, NULL
);
155 file
= g_file_new_for_path (path
);
156 g_file_monitor_emit_event (priv
->self
, file
, NULL
, events
[pfile_notify_walker
->Action
]);
157 g_object_unref (file
);
161 offset
+= pfile_notify_walker
->NextEntryOffset
;
162 } while (pfile_notify_walker
->NextEntryOffset
);
164 ReadDirectoryChangesW (priv
->hDirectory
,
165 (gpointer
)priv
->file_notify_buffer
,
166 priv
->buffer_allocated_bytes
,
168 FILE_NOTIFY_CHANGE_FILE_NAME
|
169 FILE_NOTIFY_CHANGE_DIR_NAME
|
170 FILE_NOTIFY_CHANGE_ATTRIBUTES
|
171 FILE_NOTIFY_CHANGE_SIZE
,
172 &priv
->buffer_filled_bytes
,
174 g_win32_directory_monitor_callback
);
178 g_win32_directory_monitor_constructor (GType type
,
179 guint n_construct_properties
,
180 GObjectConstructParam
*construct_properties
) {
182 GWin32DirectoryMonitorClass
*klass
;
183 GObjectClass
*parent_class
;
184 GWin32DirectoryMonitor
*self
;
188 klass
= G_WIN32_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_WIN32_DIRECTORY_MONITOR
));
189 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
190 obj
= parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
191 self
= G_WIN32_DIRECTORY_MONITOR (obj
);
192 wdirname
= g_utf8_to_utf16 (G_LOCAL_DIRECTORY_MONITOR (obj
)->dirname
, -1, NULL
, NULL
, NULL
);
194 self
->priv
->hDirectory
= CreateFileW (wdirname
,
196 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
200 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
,
203 if (self
->priv
->hDirectory
== INVALID_HANDLE_VALUE
)
209 result
= ReadDirectoryChangesW (self
->priv
->hDirectory
,
210 (gpointer
)self
->priv
->file_notify_buffer
,
211 self
->priv
->buffer_allocated_bytes
,
213 FILE_NOTIFY_CHANGE_FILE_NAME
|
214 FILE_NOTIFY_CHANGE_DIR_NAME
|
215 FILE_NOTIFY_CHANGE_ATTRIBUTES
|
216 FILE_NOTIFY_CHANGE_SIZE
,
217 &self
->priv
->buffer_filled_bytes
,
218 &self
->priv
->overlapped
,
219 g_win32_directory_monitor_callback
);
226 g_win32_directory_monitor_class_init (GWin32DirectoryMonitorClass
*klass
)
228 g_win32_directory_monitor_parent_class
= g_type_class_peek_parent (klass
);
230 G_OBJECT_CLASS (klass
)->constructor
= g_win32_directory_monitor_constructor
;
231 G_OBJECT_CLASS (klass
)->finalize
= g_win32_directory_monitor_finalize
;
232 G_FILE_MONITOR_CLASS (klass
)->cancel
= g_win32_directory_monitor_cancel
;
234 G_LOCAL_DIRECTORY_MONITOR_CLASS (klass
)->mount_notify
= FALSE
;
235 G_LOCAL_DIRECTORY_MONITOR_CLASS (klass
)->is_supported
= g_win32_directory_monitor_is_supported
;
239 g_win32_directory_monitor_init (GWin32DirectoryMonitor
*self
)
241 self
->priv
= (GWin32DirectoryMonitorPrivate
*)g_new0 (GWin32DirectoryMonitorPrivate
, 1);
242 g_assert (self
->priv
!= 0);
244 self
->priv
->buffer_allocated_bytes
= 32768;
245 self
->priv
->file_notify_buffer
= g_new0 (gchar
, self
->priv
->buffer_allocated_bytes
);
246 g_assert (self
->priv
->file_notify_buffer
);
248 self
->priv
->self
= G_FILE_MONITOR (self
);