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>
25 #include "gwin32directorymonitor.h"
28 G_DEFINE_TYPE_WITH_CODE (GWin32DirectoryMonitor
,
29 g_win32_directory_monitor
,
30 G_TYPE_LOCAL_DIRECTORY_MONITOR
,
31 g_io_extension_point_implement (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME
,
33 "readdirectorychanges",
36 struct _GWin32DirectoryMonitorPrivate
{
37 OVERLAPPED overlapped
;
38 DWORD buffer_allocated_bytes
;
39 gchar
*file_notify_buffer
;
40 DWORD buffer_filled_bytes
;
42 /* Needed in the APC where we only have this private struct */
46 static void g_win32_directory_monitor_finalize (GObject
*base
);
47 static gboolean
g_win32_directory_monitor_cancel (GFileMonitor
*base
);
49 static GObject
*g_win32_directory_monitor_constructor (GType type
,
50 guint n_construct_properties
,
51 GObjectConstructParam
*construct_properties
);
54 g_win32_directory_monitor_is_supported (void)
60 g_win32_directory_monitor_finalize (GObject
*base
)
62 GWin32DirectoryMonitor
*self
;
63 self
= G_WIN32_DIRECTORY_MONITOR (base
);
65 if (self
->priv
->hDirectory
== INVALID_HANDLE_VALUE
)
67 /* If we don't have a directory handle we can free
68 * self->priv->file_notify_buffer and self->priv here. The
69 * callback won't be called obviously any more (and presumably
70 * never has been called).
72 g_free (self
->priv
->file_notify_buffer
);
73 self
->priv
->file_notify_buffer
= NULL
;
78 /* If we have a directory handle, the OVERLAPPED struct is
79 * passed once more to the callback as a result of the
80 * CloseHandle() done in the cancel method, so self->priv has to
81 * be kept around. The GWin32DirectoryMonitor object is
82 * disappearing, so can't leave a pointer to it in
85 self
->priv
->self
= NULL
;
88 if (G_OBJECT_CLASS (g_win32_directory_monitor_parent_class
)->finalize
)
89 (*G_OBJECT_CLASS (g_win32_directory_monitor_parent_class
)->finalize
) (base
);
93 g_win32_directory_monitor_cancel (GFileMonitor
*base
)
95 GWin32DirectoryMonitor
*self
;
96 self
= G_WIN32_DIRECTORY_MONITOR (base
);
98 /* This triggers a last callback() with nBytes==0. */
100 /* Actually I am not so sure about that, it seems to trigger a last
101 * callback allright, but the way to recognize that it is the final
102 * one is not to check for nBytes==0, I think that was a
105 if (self
->priv
->hDirectory
!= INVALID_HANDLE_VALUE
)
106 CloseHandle (self
->priv
->hDirectory
);
108 if (G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class
)->cancel
)
109 (*G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class
)->cancel
) (base
);
114 g_win32_directory_monitor_callback (DWORD error
,
116 LPOVERLAPPED lpOverlapped
)
119 PFILE_NOTIFY_INFORMATION pfile_notify_walker
;
124 GWin32DirectoryMonitorPrivate
*priv
= (GWin32DirectoryMonitorPrivate
*) lpOverlapped
;
126 static GFileMonitorEvent events
[] =
129 G_FILE_MONITOR_EVENT_CREATED
, /* FILE_ACTION_ADDED */
130 G_FILE_MONITOR_EVENT_DELETED
, /* FILE_ACTION_REMOVED */
131 G_FILE_MONITOR_EVENT_CHANGED
, /* FILE_ACTION_MODIFIED */
132 G_FILE_MONITOR_EVENT_DELETED
, /* FILE_ACTION_RENAMED_OLD_NAME */
133 G_FILE_MONITOR_EVENT_CREATED
, /* FILE_ACTION_RENAMED_NEW_NAME */
136 /* If priv->self is NULL the GWin32DirectoryMonitor object has been destroyed. */
137 if (priv
->self
== NULL
||
138 g_file_monitor_is_cancelled (priv
->self
) ||
139 priv
->file_notify_buffer
== NULL
)
141 g_free (priv
->file_notify_buffer
);
148 pfile_notify_walker
= (PFILE_NOTIFY_INFORMATION
)(priv
->file_notify_buffer
+ offset
);
149 if (pfile_notify_walker
->Action
> 0)
151 file_name
= g_utf16_to_utf8 (pfile_notify_walker
->FileName
, pfile_notify_walker
->FileNameLength
/ sizeof(WCHAR
), NULL
, &file_name_len
, NULL
);
152 path
= g_build_filename(G_LOCAL_DIRECTORY_MONITOR (priv
->self
)->dirname
, file_name
, NULL
);
153 file
= g_file_new_for_path (path
);
154 g_file_monitor_emit_event (priv
->self
, file
, NULL
, events
[pfile_notify_walker
->Action
]);
155 g_object_unref (file
);
159 offset
+= pfile_notify_walker
->NextEntryOffset
;
160 } while (pfile_notify_walker
->NextEntryOffset
);
162 ReadDirectoryChangesW (priv
->hDirectory
,
163 (gpointer
)priv
->file_notify_buffer
,
164 priv
->buffer_allocated_bytes
,
166 FILE_NOTIFY_CHANGE_FILE_NAME
|
167 FILE_NOTIFY_CHANGE_DIR_NAME
|
168 FILE_NOTIFY_CHANGE_ATTRIBUTES
|
169 FILE_NOTIFY_CHANGE_SIZE
,
170 &priv
->buffer_filled_bytes
,
172 g_win32_directory_monitor_callback
);
176 g_win32_directory_monitor_constructor (GType type
,
177 guint n_construct_properties
,
178 GObjectConstructParam
*construct_properties
) {
180 GWin32DirectoryMonitorClass
*klass
;
181 GObjectClass
*parent_class
;
182 GWin32DirectoryMonitor
*self
;
185 klass
= G_WIN32_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_WIN32_DIRECTORY_MONITOR
));
186 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
187 obj
= parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
188 self
= G_WIN32_DIRECTORY_MONITOR (obj
);
189 wdirname
= g_utf8_to_utf16 (G_LOCAL_DIRECTORY_MONITOR (obj
)->dirname
, -1, NULL
, NULL
, NULL
);
191 self
->priv
->hDirectory
= CreateFileW (wdirname
,
193 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
197 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
,
200 if (self
->priv
->hDirectory
== INVALID_HANDLE_VALUE
)
206 ReadDirectoryChangesW (self
->priv
->hDirectory
,
207 (gpointer
)self
->priv
->file_notify_buffer
,
208 self
->priv
->buffer_allocated_bytes
,
210 FILE_NOTIFY_CHANGE_FILE_NAME
|
211 FILE_NOTIFY_CHANGE_DIR_NAME
|
212 FILE_NOTIFY_CHANGE_ATTRIBUTES
|
213 FILE_NOTIFY_CHANGE_SIZE
,
214 &self
->priv
->buffer_filled_bytes
,
215 &self
->priv
->overlapped
,
216 g_win32_directory_monitor_callback
);
223 g_win32_directory_monitor_class_init (GWin32DirectoryMonitorClass
*klass
)
225 g_win32_directory_monitor_parent_class
= g_type_class_peek_parent (klass
);
227 G_OBJECT_CLASS (klass
)->constructor
= g_win32_directory_monitor_constructor
;
228 G_OBJECT_CLASS (klass
)->finalize
= g_win32_directory_monitor_finalize
;
229 G_FILE_MONITOR_CLASS (klass
)->cancel
= g_win32_directory_monitor_cancel
;
231 G_LOCAL_DIRECTORY_MONITOR_CLASS (klass
)->mount_notify
= FALSE
;
232 G_LOCAL_DIRECTORY_MONITOR_CLASS (klass
)->is_supported
= g_win32_directory_monitor_is_supported
;
236 g_win32_directory_monitor_init (GWin32DirectoryMonitor
*self
)
238 self
->priv
= (GWin32DirectoryMonitorPrivate
*)g_new0 (GWin32DirectoryMonitorPrivate
, 1);
239 g_assert (self
->priv
!= 0);
241 self
->priv
->buffer_allocated_bytes
= 32768;
242 self
->priv
->file_notify_buffer
= g_new0 (gchar
, self
->priv
->buffer_allocated_bytes
);
243 g_assert (self
->priv
->file_notify_buffer
);
245 self
->priv
->self
= G_FILE_MONITOR (self
);