[glib/wip/desrt/gio-tool] Continue to Improve File Monitoring on Windows



commit 55847d9258df41b6d937c75ea4e3631168649686
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Mon Mar 30 14:24:38 2015 +0800

    Continue to Improve File Monitoring on Windows
    
    This updates the file monitoring on Windows by:
    -Clean up the code, and ensure things are indeed being freed when we exit.
    -Have attributes changes in files (when monitoring directories) properly
     done.
    -Split up the code, to ease readability
    
    To Possibly Do:
    -Check on whether we want to monitor the case when we try to pull out a USB
     stick without ejecting in in Windows (the monitoring mechanism
     understandbly denies such ejection requests, since the monitored
     file/directory is obviously in use in this case)
    -Investigate on the interesting/boredom algorithm, whether we can do it
     here.
    
    Possible Limitations:
    -If moving a file out of the directory (or vice versa), the system reports
     that as a delete event (or create event in the vice versa case), so in the
     current form without using NTFS hournals (which is not optimal as that
     would trigger UAC), so we can't reliably emit MOVE OUT or MOVE IN events
     on Windows.
    -Hard links are supported transparently, but notifications are only sent
     by the system on changes when the monitored file/directory (or monitored
     hard link) is being changed, when the file is modified via the hard link
     or original file respectively in this case.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=730116

 gio/Makefile.am                     |    3 +-
 gio/win32/Makefile.am               |    8 +-
 gio/win32/gwin32filemonitor.c       |  117 ++++++++-
 gio/win32/gwin32filemonitor.h       |    6 +-
 gio/win32/gwin32filemonitorevents.c |  400 ++++++++++++++++++++++++++++++
 gio/win32/gwin32filemonitorevents.h |   32 +++
 gio/win32/gwin32filemonitorutils.c  |  454 +++++++++++++++++++++++++++++++++++
 gio/win32/gwin32filemonitorutils.h  |   89 +++++++
 gio/win32/gwin32fsmonitorutils.c    |  422 --------------------------------
 gio/win32/gwin32fsmonitorutils.h    |   76 ------
 10 files changed, 1089 insertions(+), 518 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index d0e53b3..d79c2c4 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -317,7 +317,8 @@ win32_more_sources_for_vcproj = \
        win32/gwinhttpfileinputstream.c \
        win32/gwinhttpfileoutputstream.c \
        win32/gwinhttpvfs.c \
-       win32/gwin32fsmonitorutils.c \
+       win32/gwin32filemonitorevents.c \
+       win32/gwin32filemonitorutils.c \
        win32/gwin32filemonitor.c
 
 if OS_WIN32
diff --git a/gio/win32/Makefile.am b/gio/win32/Makefile.am
index 2fc10f6..909bb53 100644
--- a/gio/win32/Makefile.am
+++ b/gio/win32/Makefile.am
@@ -2,9 +2,11 @@ include $(top_srcdir)/glib.mk
 
 noinst_LTLIBRARIES += libgiowin32.la
 
-libgiowin32_la_SOURCES =                       \
-       gwin32fsmonitorutils.c                  \
-       gwin32fsmonitorutils.h                  \
+libgiowin32_la_SOURCES =               \
+       gwin32filemonitorevents.c       \
+       gwin32filemonitorevents.h       \
+       gwin32filemonitorutils.c        \
+       gwin32filemonitorutils.h        \
        gwin32filemonitor.c                     \
        gwin32filemonitor.h                     \
        gwinhttpvfs.c                           \
diff --git a/gio/win32/gwin32filemonitor.c b/gio/win32/gwin32filemonitor.c
index 1b93b82..30acef4 100644
--- a/gio/win32/gwin32filemonitor.c
+++ b/gio/win32/gwin32filemonitor.c
@@ -1,7 +1,7 @@
 /* GIO - GLib Input, Output and Streaming Library
  *
- * Copyright (C) 2006-2007 Red Hat, Inc.
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -24,15 +24,56 @@
 #include "config.h"
 
 #include "gwin32filemonitor.h"
-#include "gwin32fsmonitorutils.h"
-
-#include <windows.h>
+#include "gwin32filemonitorutils.h"
 
 G_DEFINE_TYPE_WITH_CODE (GWin32FileMonitor, g_win32_file_monitor, G_TYPE_LOCAL_FILE_MONITOR,
                          g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
                                                          g_define_type_id, "win32filemonitor", 20))
 
 static void
+g_win32_file_monitor_close_handle (HANDLE hDirectory)
+{
+  /* This triggers a last callback() with nBytes==0. */
+
+  /* Actually I am not so sure about that, it seems to trigger a last
+   * callback allright, but the way to recognize that it is the final
+   * one is not to check for nBytes==0, I think that was a
+   * misunderstanding.
+   */
+  if (hDirectory != INVALID_HANDLE_VALUE)
+    {
+      CloseHandle (hDirectory);
+      hDirectory = INVALID_HANDLE_VALUE;
+    }
+}
+
+static void
+g_win32_file_monitor_free (GWin32FileMonitorInfo *info)
+{
+  if (info != NULL)
+    {
+      g_free (info->shortname);
+      g_free (info->longname);
+      g_free (info->name);
+      g_free (info->dirname_with_long_prefix);
+      g_free (info);
+    }
+}
+
+static GWin32FileMonitorPrivate*
+g_win32_file_monitor_create (void)
+{
+  GWin32FileMonitorPrivate* monitor = (GWin32FileMonitorPrivate*) g_new0 (GWin32FileMonitorPrivate, 1);
+  g_assert (monitor != 0);
+
+  monitor->buffer_allocated_bytes = 32784;
+  monitor->file_notify_buffer = g_new0 (FILE_NOTIFY_INFORMATION, monitor->buffer_allocated_bytes);
+  g_assert (monitor->file_notify_buffer);
+
+  return monitor;
+}
+
+static void
 g_win32_file_monitor_start (GLocalFileMonitor *monitor,
                             const gchar *dirname,
                             const gchar *basename,
@@ -43,14 +84,32 @@ g_win32_file_monitor_start (GLocalFileMonitor *monitor,
   gboolean isfile = (filename == NULL && basename == NULL) ? FALSE : TRUE;
 
   win32_monitor->priv->fms = source;
+  win32_monitor->priv->ht_watched_names =
+    g_hash_table_new_full (g_str_hash,
+                           g_str_equal,
+                           g_free,
+                           g_win32_file_monitor_free);
+
+  win32_monitor->priv->ht_watched_dirs =
+    g_hash_table_new_full (g_direct_hash,
+                           g_direct_equal,
+                           g_win32_file_monitor_close_handle,
+                           g_free);
+
+  if (!isfile)
+    win32_monitor->priv->ht_files_attribs =
+      g_hash_table_new_full (g_str_hash,
+                             g_str_equal,
+                             g_free,
+                             NULL);
 
   if (isfile)
     if (basename != NULL)
-      g_win32_fs_monitor_init (win32_monitor->priv, dirname, basename, TRUE);
+      g_win32_file_monitor_prepare (win32_monitor->priv, dirname, basename, TRUE);
     else
-      g_win32_fs_monitor_init (win32_monitor->priv, NULL, filename, TRUE);
+      g_win32_file_monitor_prepare (win32_monitor->priv, NULL, filename, TRUE);
   else
-    g_win32_fs_monitor_init (win32_monitor->priv, dirname, NULL, FALSE);
+    g_win32_file_monitor_prepare (win32_monitor->priv, dirname, NULL, FALSE);
 }
 
 static gboolean
@@ -62,18 +121,51 @@ g_win32_file_monitor_is_supported (void)
 static void
 g_win32_file_monitor_init (GWin32FileMonitor* monitor)
 {
-  monitor->priv = g_win32_fs_monitor_create (TRUE);
+  monitor->priv = g_win32_file_monitor_create ();
 
   monitor->priv->self = G_FILE_MONITOR (monitor);
 }
 
 static void
+g_win32_destroy_monitor_hashtables (GWin32FileMonitorPrivate *monitor)
+{
+  if (monitor->ht_files_attribs != NULL)
+    {
+      GList *l_attrib_keys = g_hash_table_get_keys (monitor->ht_files_attribs);
+      for (;l_attrib_keys != NULL; l_attrib_keys = l_attrib_keys->next)
+        {
+          GHashTable *ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs,
+                                                        l_attrib_keys->data);
+          if (ht_attribs != NULL)
+            g_hash_table_destroy (ht_attribs);
+        }
+    }
+
+  if (monitor->ht_watched_names != NULL)
+    {
+      g_hash_table_destroy (monitor->ht_watched_names);
+      monitor->ht_watched_names = NULL;
+    }
+
+  if (monitor->ht_watched_dirs != NULL)
+    {
+      g_hash_table_destroy (monitor->ht_watched_dirs);
+      monitor->ht_watched_dirs = NULL;
+    }
+}
+
+static void
 g_win32_file_monitor_finalize (GObject *base)
 {
   GWin32FileMonitor *monitor;
   monitor = G_WIN32_FILE_MONITOR (base);
 
-  g_win32_fs_monitor_finalize (monitor->priv);
+  g_win32_destroy_monitor_hashtables (monitor->priv);
+
+  g_free (monitor->priv->file_notify_buffer);
+  monitor->priv->file_notify_buffer = NULL;
+
+  g_free (monitor->priv);
 
   if (G_OBJECT_CLASS (g_win32_file_monitor_parent_class)->finalize)
     (*G_OBJECT_CLASS (g_win32_file_monitor_parent_class)->finalize) (base);
@@ -82,10 +174,9 @@ g_win32_file_monitor_finalize (GObject *base)
 static gboolean
 g_win32_file_monitor_cancel (GFileMonitor* monitor)
 {
-  GWin32FileMonitor *file_monitor;
-  file_monitor = G_WIN32_FILE_MONITOR (monitor);
+  GWin32FileMonitor *file_monitor = G_WIN32_FILE_MONITOR (monitor);
 
-  g_win32_fs_monitor_close_handle (file_monitor->priv);
+  g_win32_destroy_monitor_hashtables (file_monitor->priv);
 
   return TRUE;
 }
diff --git a/gio/win32/gwin32filemonitor.h b/gio/win32/gwin32filemonitor.h
index 5aef2ba..185dd77 100644
--- a/gio/win32/gwin32filemonitor.h
+++ b/gio/win32/gwin32filemonitor.h
@@ -1,6 +1,6 @@
 /* GIO - GLib Input, Output and Streaming Library
  *
- * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -31,7 +31,7 @@
 #include "gio/glocalfilemonitor.h"
 #include "gio/giomodule.h"
 
-#include "gwin32fsmonitorutils.h"
+#include "gwin32filemonitorutils.h"
 
 G_BEGIN_DECLS
 
@@ -48,7 +48,7 @@ typedef struct _GWin32FileMonitorPrivate GWin32FileMonitorPrivate;
 
 struct _GWin32FileMonitor {
   GLocalFileMonitor parent_instance;
-  GWin32FSMonitorPrivate * priv;
+  GWin32FileMonitorPrivate * priv;
 };
 struct _GWin32FileMonitorClass {
   GLocalFileMonitorClass parent_class;
diff --git a/gio/win32/gwin32filemonitorevents.c b/gio/win32/gwin32filemonitorevents.c
new file mode 100644
index 0000000..620f498
--- /dev/null
+++ b/gio/win32/gwin32filemonitorevents.c
@@ -0,0 +1,400 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Chun-wei Fan <fanc999 yahoo com tw>
+ *
+ */
+
+#include "config.h"
+
+#include "gwin32filemonitorevents.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+static gboolean
+g_win32_file_monitor_handle_event (GWin32FileMonitorPrivate *monitor,
+                                   gchar *filename,
+                                   PFILE_NOTIFY_INFORMATION pfni,
+                                   gboolean is_renamed_to)
+{
+  GFileMonitorEvent fme;
+  PFILE_NOTIFY_INFORMATION pfni_next;
+  gchar *from_to_file = NULL;
+  glong file_name_len = 0;
+  gboolean reacquire_names = FALSE;
+  gchar *fullpath = g_hash_table_lookup (monitor->ht_watched_dirs, monitor->hDirectory);
+  GWin32FileMonitorInfo *info = g_hash_table_lookup (monitor->ht_watched_names, fullpath);
+
+  gboolean is_attrib_changed = FALSE;
+
+  gboolean update_all_attribs = FALSE;
+  gchar *update_file = NULL;
+
+  switch (pfni->Action)
+    {
+      case FILE_ACTION_ADDED:
+        fme = G_FILE_MONITOR_EVENT_CREATED;
+        reacquire_names = TRUE;
+        break;
+
+      case FILE_ACTION_REMOVED:
+        fme = G_FILE_MONITOR_EVENT_DELETED;
+        reacquire_names = TRUE;
+        break;
+
+      case FILE_ACTION_MODIFIED:
+        if (info->isfile)
+          is_attrib_changed = g_win32_file_monitor_check_attrib_changed (info, filename, NULL);
+        else
+          {
+            GHashTable *ht_attribs =  g_hash_table_lookup (monitor->ht_files_attribs, fullpath);
+            is_attrib_changed = g_win32_file_monitor_check_attrib_changed (info, filename, ht_attribs);
+            if (is_attrib_changed)
+              {
+                g_hash_table_remove (monitor->ht_files_attribs, fullpath);
+                g_hash_table_insert (monitor->ht_files_attribs, g_strdup (fullpath), ht_attribs);
+              }
+          }
+        if (is_attrib_changed)
+          fme = G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
+        else
+          fme = G_FILE_MONITOR_EVENT_CHANGED;
+
+        break;
+
+      case FILE_ACTION_RENAMED_OLD_NAME:
+        if (pfni->NextEntryOffset != 0)
+          {
+            /* If the file was renamed in the same directory, we would get a
+             * FILE_ACTION_RENAMED_NEW_NAME action in the next FILE_NOTIFY_INFORMATION
+             * structure.
+             */
+
+            pfni_next = (PFILE_NOTIFY_INFORMATION) ((BYTE*)pfni + pfni->NextEntryOffset);
+            if (pfni_next->Action == FILE_ACTION_RENAMED_NEW_NAME)
+              {
+                from_to_file = g_utf16_to_utf8 (pfni_next->FileName,
+                                                pfni_next->FileNameLength / sizeof(WCHAR),
+                                                NULL, &file_name_len,
+                                                NULL);
+
+                fme = G_FILE_MONITOR_EVENT_RENAMED;
+              }
+            else
+               fme = G_FILE_MONITOR_EVENT_MOVED_OUT;
+          }
+        else
+          fme = G_FILE_MONITOR_EVENT_MOVED_OUT;
+
+        reacquire_names = TRUE;
+        break;
+
+      case FILE_ACTION_RENAMED_NEW_NAME:
+        if (monitor->pfni_prev != NULL &&
+            monitor->pfni_prev->Action == FILE_ACTION_RENAMED_OLD_NAME)
+          {
+            if (is_renamed_to)
+              {
+                from_to_file = g_utf16_to_utf8 (monitor->pfni_prev->FileName,
+                                                monitor->pfni_prev->FileNameLength / sizeof(WCHAR),
+                                                NULL,
+                                                &file_name_len,
+                                                NULL);
+                fme = G_FILE_MONITOR_EVENT_RENAMED;
+              }
+            else
+              /* don't bother sending events, was already sent (renamed from monitored file) */
+              fme = -1;
+          }
+        else
+          fme = G_FILE_MONITOR_EVENT_MOVED_IN;
+
+        reacquire_names = TRUE;
+
+        break;
+
+      default:
+        /* The possible Windows actions are all above, so shouldn't get here */
+        g_assert_not_reached ();
+        break;
+    }
+  if (fme != -1)
+    {
+      gboolean interesting;
+
+      if (info->isfile)
+        {
+          if (pfni->Action == FILE_ACTION_RENAMED_NEW_NAME &&
+              is_renamed_to)
+            {
+              /* we are renaming to the monitored file */
+              interesting = g_file_monitor_source_handle_event (monitor->fms,
+                                                                fme,
+                                                                from_to_file,
+                                                                filename,
+                                                                NULL,
+                                                                g_get_monotonic_time ());
+            }
+          else
+            {
+              interesting = g_file_monitor_source_handle_event (monitor->fms,
+                                                                fme,
+                                                                filename,
+                                                                from_to_file,
+                                                                NULL,
+                                                                g_get_monotonic_time ());
+            }
+        }
+      else
+        {
+          gchar *from_to_file_stripped = NULL;
+          gchar *filename_stripped = NULL;
+
+          if (filename != NULL)
+            {
+              if (strrchr (filename, G_DIR_SEPARATOR) != NULL)
+                filename_stripped = g_strdup (strrchr (filename, G_DIR_SEPARATOR) + 1);
+              else
+                filename_stripped = g_strdup (filename);
+            }
+          if (from_to_file != NULL)
+            {
+              if (strrchr (from_to_file, G_DIR_SEPARATOR) != NULL)
+                from_to_file_stripped = g_strdup (strrchr (from_to_file, G_DIR_SEPARATOR) + 1);
+              else
+                from_to_file_stripped = g_strdup (from_to_file);
+            }
+
+          if (pfni->Action == FILE_ACTION_RENAMED_NEW_NAME &&
+              is_renamed_to)
+            {
+              /* we are renaming to the monitored file */
+              interesting = g_file_monitor_source_handle_event (monitor->fms,
+                                                                fme,
+                                                                from_to_file_stripped,
+                                                                filename_stripped,
+                                                                NULL,
+                                                                g_get_monotonic_time ());
+            }
+          else
+            {
+              interesting = g_file_monitor_source_handle_event (monitor->fms,
+                                                                fme,
+                                                                filename_stripped,
+                                                                from_to_file_stripped,
+                                                                NULL,
+                                                                g_get_monotonic_time ());
+            }
+          if (filename_stripped != NULL)
+            g_free (filename_stripped);
+          if (from_to_file_stripped != NULL)
+            g_free (from_to_file_stripped);
+        }
+
+      if (from_to_file != NULL)
+        g_free (from_to_file);
+      if (reacquire_names)
+        {
+          g_win32_file_monitor_set_names (info);
+          if (!info->isfile)
+            g_win32_file_monitor_dir_refresh_attribs (monitor, info);
+        }
+
+      return interesting;
+    }
+  else
+    return FALSE;
+}
+
+void CALLBACK
+g_win32_file_monitor_callback (DWORD        error,
+                               DWORD        nBytes,
+                               LPOVERLAPPED lpOverlapped)
+{
+  gulong offset;
+  PFILE_NOTIFY_INFORMATION pfile_notify_walker;
+
+  GWin32FileMonitorPrivate *monitor = (GWin32FileMonitorPrivate *) lpOverlapped;
+  GWin32FileMonitorInfo *info;
+
+  DWORD notify_filter;
+
+  gchar *fullpath;
+  gchar *changed_file;
+  glong file_name_len;
+  GHashTable *ht_attribs;
+
+  fullpath = g_hash_table_lookup (monitor->ht_watched_dirs, monitor->hDirectory);
+
+  info = g_hash_table_lookup (monitor->ht_watched_names, fullpath);
+
+  notify_filter = info->isfile ?
+                  (FILE_NOTIFY_CHANGE_FILE_NAME |
+                   FILE_NOTIFY_CHANGE_DIR_NAME |
+                   FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                   FILE_NOTIFY_CHANGE_SIZE) :
+                  (FILE_NOTIFY_CHANGE_FILE_NAME |
+                   FILE_NOTIFY_CHANGE_DIR_NAME |
+                   FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                   FILE_NOTIFY_CHANGE_SIZE);
+  if (!info->isfile)
+    ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs,
+                                      fullpath);
+
+  /* If monitor->self is NULL the GWin32FileMonitor object has been destroyed. */
+  if (monitor->self == NULL ||
+      g_file_monitor_is_cancelled (monitor->self) ||
+      monitor->file_notify_buffer == NULL)
+    {
+      g_free (monitor->file_notify_buffer);
+
+      g_free (monitor);
+      return;
+    }
+
+  offset = 0;
+
+  do
+    {
+      pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)((BYTE*)monitor->file_notify_buffer + offset);
+      if (pfile_notify_walker->Action > 0)
+        {
+          gboolean is_rename = FALSE;
+          gboolean is_renamed_to = FALSE;
+          gboolean is_handle_event = FALSE;
+          gchar *shortname_casefold, *longname_casefold, *changed_file_casefold;
+
+          gint long_filename_length = g_utf8_strlen (info->longname, -1);
+          gint short_filename_length = g_utf8_strlen (info->shortname, -1);
+          PFILE_NOTIFY_INFORMATION pfni_next =
+            (PFILE_NOTIFY_INFORMATION)((BYTE*)pfile_notify_walker + pfile_notify_walker->NextEntryOffset);
+
+          gchar *longfilename_with_path, *shortfilename_with_path, *changed_file_with_path;
+
+          longname_casefold = g_utf8_casefold (info->longname, -1);
+          shortname_casefold = g_utf8_casefold (info->shortname, -1);
+
+          longfilename_with_path = g_strconcat (info->dirname_with_long_prefix,
+                                                G_DIR_SEPARATOR_S,
+                                                longname_casefold,
+                                                NULL);
+          shortfilename_with_path = g_strconcat (info->dirname_with_long_prefix,
+                                                 G_DIR_SEPARATOR_S,
+                                                 shortname_casefold,
+                                                 NULL);
+
+          changed_file = g_utf16_to_utf8 (pfile_notify_walker->FileName,
+                                          pfile_notify_walker->FileNameLength / sizeof(WCHAR),
+                                          NULL,
+                                          &file_name_len,
+                                          NULL);
+          changed_file_casefold = g_utf8_casefold (changed_file, -1);
+          changed_file_with_path = g_strconcat (info->dirname_with_long_prefix,
+                                                G_DIR_SEPARATOR_S,
+                                                changed_file_casefold,
+                                                NULL);
+
+          if (pfile_notify_walker->Action == FILE_ACTION_RENAMED_OLD_NAME &&
+              pfni_next->Action == FILE_ACTION_RENAMED_NEW_NAME)
+            {
+              is_rename = TRUE;
+            }
+
+          /* If monitoring a file, check that the changed file
+           * in the directory matches the file that is to be monitored
+           * We need to check both the long and short file names for the same file, as
+           * ReadDirectoryChangesW() could return either one of them.
+           *
+           * We need to send in the name of the monitored file, not its long (or short) variant,
+           * if such long and/or short filenames exist.
+           */
+          if (info->isfile)
+            is_handle_event = g_str_equal (longfilename_with_path, changed_file_with_path) ||
+                              g_str_equal (shortfilename_with_path, changed_file_with_path);
+
+          else
+            {
+              /* don't monitor next directory, at least for now */
+              gint changed_file_length = g_utf8_strlen (g_utf8_strrchr(changed_file_with_path, -1, 
G_DIR_SEPARATOR), -1);
+              gchar *changed_dir = g_utf8_substring (changed_file_with_path,
+                                                     0,
+                                                     g_utf8_strlen (changed_file_with_path, -1) - 
changed_file_length);
+
+              is_handle_event = g_str_equal (longfilename_with_path, changed_file_with_path) ||
+                                g_str_equal (shortfilename_with_path, changed_file_with_path) ||
+                                g_str_equal (longfilename_with_path, changed_dir) ||
+                                g_str_equal (shortfilename_with_path, changed_dir);
+
+              g_free (changed_dir);
+            }
+
+          if (is_rename = TRUE &&
+              monitor->pfni_prev != NULL &&
+              monitor->pfni_prev->Action == FILE_ACTION_RENAMED_OLD_NAME &&
+              pfile_notify_walker->Action == FILE_ACTION_RENAMED_NEW_NAME)
+            {
+              gchar *next_file = g_utf16_to_utf8 (pfile_notify_walker->FileName,
+                                                  pfile_notify_walker->FileNameLength / sizeof(WCHAR),
+                                                  NULL,
+                                                  &file_name_len,
+                                                  NULL);
+              gchar *next_file_with_path = g_strconcat (info->dirname_with_long_prefix,
+                                                        G_DIR_SEPARATOR_S,
+                                                        next_file,
+                                                        NULL);
+              if (g_str_equal (longfilename_with_path, next_file_with_path) ||
+                  g_str_equal (shortfilename_with_path, next_file_with_path))
+                {
+                  is_renamed_to = TRUE;
+                  is_handle_event = TRUE;
+                }
+              g_free (next_file);
+            }
+          g_free (longfilename_with_path);
+          g_free (shortfilename_with_path);
+          g_free (changed_file_with_path);
+
+          if (is_handle_event)
+            {
+              if (info->isfile)
+                g_win32_file_monitor_handle_event (monitor,
+                                                   info->name,
+                                                   pfile_notify_walker,
+                                                   is_renamed_to);
+              else
+                g_win32_file_monitor_handle_event (monitor,
+                                                   changed_file,
+                                                   pfile_notify_walker,
+                                                   is_renamed_to);
+            }
+          g_free (changed_file);
+        }
+      monitor->pfni_prev = pfile_notify_walker;
+      offset += pfile_notify_walker->NextEntryOffset;
+    }
+  while (pfile_notify_walker->NextEntryOffset);
+
+  ReadDirectoryChangesW (monitor->hDirectory,
+                         monitor->file_notify_buffer,
+                         monitor->buffer_allocated_bytes,
+                         !info->isfile,
+                         notify_filter,
+                         &monitor->buffer_filled_bytes,
+                         &monitor->overlapped,
+                         g_win32_file_monitor_callback);
+}
diff --git a/gio/win32/gwin32filemonitorevents.h b/gio/win32/gwin32filemonitorevents.h
new file mode 100644
index 0000000..9c125be
--- /dev/null
+++ b/gio/win32/gwin32filemonitorevents.h
@@ -0,0 +1,32 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Chun-wei Fan <fanc999 yahoo com tw>
+ *
+ */
+
+#include "gwin32filemonitorutils.h"
+#include "gio/gfile.h"
+
+#ifndef __G_WIN32_FILE_MONITOR_EVENTS_H__
+#define __G_WIN32_FILE_MONITOR_EVENTS_H__
+
+void CALLBACK g_win32_file_monitor_callback (DWORD        error,
+                                             DWORD        nBytes,
+                                             LPOVERLAPPED lpOverlapped);
+
+#endif /* __G_WIN32_FILE_MONITOR_EVENTS_H__ */
diff --git a/gio/win32/gwin32filemonitorutils.c b/gio/win32/gwin32filemonitorutils.c
new file mode 100644
index 0000000..e0a51e2
--- /dev/null
+++ b/gio/win32/gwin32filemonitorutils.c
@@ -0,0 +1,454 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vlad Grecescu <b100dian gmail com>
+ * Author: Chun-wei Fan <fanc999 yahoo com tw>
+ *
+ */
+
+#include "config.h"
+
+#include "gwin32filemonitorutils.h"
+#include "gwin32filemonitorevents.h"
+#include "gio/gfile.h"
+
+/*
+ * We are using the Wide/Unicode (W) variants of the Windows APIs as:
+ * -There isn't a ReadDirectoryChangesA()
+ * -We want to support long paths, over 256 characters, and using the W variants is required in this case
+ */
+
+/*
+ * Note: We don't support monitoring for "normal" unmounts in Windows as the Windows file/directory 
monitoring
+ * mechanism will disallow the unmounting of the monitored volume (unless of course we pull out the USB 
stick,
+ * for instance, without ejecting it
+ */
+
+/*
+ * On Windows, there is the notion of hard links, but they are treated more or less like standard files, but 
any
+ * changes to them will also cause a notification to be sent for the directory where the original file 
resides,
+ * so ReadDirectoryChangesW() also works with hard links, though transparently, so there isn't a special 
code path
+ * for hard links here.  The notification, however, may be sent only when the file (or hard link) in question
+ * is being accessed.
+ */
+
+gboolean
+g_win32_file_monitor_long_pfx_needed (const gchar* path)
+{
+  if (g_utf8_strlen (path, -1) > MAX_PATH)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+GHashTable*
+g_win32_file_monitor_dir_refresh_attribs (GWin32FileMonitorPrivate *monitor,
+                                          GWin32FileMonitorInfo *info)
+{
+  GHashTable *ht_attribs;
+  WIN32_FILE_ATTRIBUTE_DATA *attrib_data;
+  gchar *fullpath, *dirname, *dirname_casefold;
+  wchar_t *wfullpath;
+  HANDLE hFind = INVALID_HANDLE_VALUE;
+  WIN32_FIND_DATA file_data = {0,};
+
+  if (info->isfile)
+    return NULL;
+
+  fullpath = g_strconcat (info->dirname_with_long_prefix,
+                          G_DIR_SEPARATOR_S,
+                          info->name,
+                          G_DIR_SEPARATOR_S,
+                          "*",
+                          NULL);
+  wfullpath = g_utf8_to_utf16 (fullpath, -1, NULL, NULL, NULL);
+  dirname = g_strconcat (info->dirname_with_long_prefix + STRIP_PFX_LENGTH,
+                         G_DIR_SEPARATOR_S,
+                         info->name,
+                         NULL);
+  dirname_casefold = g_utf8_casefold (dirname, -1);
+
+  hFind = FindFirstFileExW (wfullpath,
+                            FindExInfoBasic,
+                            &file_data,
+                            0,
+                            NULL,
+                            0);
+
+  ht_attribs = g_hash_table_lookup (monitor->ht_files_attribs, dirname_casefold);
+  if (ht_attribs == NULL)
+    {
+      ht_attribs = g_hash_table_new_full (g_str_hash,
+                                          g_str_equal,
+                                          g_free,
+                                          g_free);
+    }
+  else
+    g_hash_table_remove_all (ht_attribs);
+
+  g_free (dirname_casefold);
+  g_free (dirname);
+
+  if (hFind == INVALID_HANDLE_VALUE)
+    /*
+     * Initially, we won't get here, if we started monitoring on a non-existing directory.
+     * If we do later on, the directory was moved or deleted, forget about updating the info
+     */
+     return NULL;
+  else
+    {
+      do
+        {
+          attrib_data = g_new0 (WIN32_FILE_ATTRIBUTE_DATA, 1);
+          attrib_data->dwFileAttributes = file_data.dwFileAttributes;
+          attrib_data->ftCreationTime = file_data.ftCreationTime;
+          attrib_data->ftLastAccessTime = file_data.ftLastAccessTime;
+          attrib_data->ftLastWriteTime = file_data.ftLastWriteTime;
+          attrib_data->nFileSizeHigh = file_data.nFileSizeHigh;
+          attrib_data->nFileSizeLow = file_data.nFileSizeLow;
+
+          g_hash_table_insert (ht_attribs,
+                               g_utf8_casefold (file_data.cFileName, -1),
+                               attrib_data);
+        }
+      while (FindNextFile (hFind, &file_data));
+      FindClose (hFind);
+    }
+  return ht_attribs;
+}
+
+/*
+ * ReadDirectoryChangesW() can return the normal filename or the
+ * "8.3" format filename, so we need to keep track of both these names
+ * so that we can check against them later when it returns
+ */
+void
+g_win32_file_monitor_set_names (GWin32FileMonitorInfo *info)
+{
+  wchar_t *wfullpath, *wbasename_long, *wbasename_short;
+  wchar_t wlongname[MAX_PATH_LONG];
+  wchar_t wshortname[MAX_PATH_LONG];
+  gboolean success_attribs;
+
+  gchar *fullpath_with_long_pfx = g_strconcat (info->dirname_with_long_prefix,
+                                               G_DIR_SEPARATOR_S,
+                                               info->name,
+                                               NULL);
+
+  wfullpath = g_utf8_to_utf16 (fullpath_with_long_pfx, -1, NULL, NULL, NULL);
+  if (info->longname != NULL)
+    g_free (info->longname);
+  if (info->shortname != NULL)
+    g_free (info->shortname);
+
+  /*
+   * Get long and short paths of the specified path, if it exists, otherwise
+   * just assume the filename part in the passed in filename and directory as
+   * the long and short filenames
+   */
+  if (GetLongPathNameW (wfullpath, wlongname, MAX_PATH_LONG) != 0)
+    {
+      wbasename_long = wcsrchr (wlongname, G_DIR_SEPARATOR_W) != NULL ?
+                       wcsrchr (wlongname, G_DIR_SEPARATOR_W) + 1 :
+                       wlongname + STRIP_PFX_LENGTH;
+    }
+  else
+    {
+      wbasename_long = wcsrchr (wfullpath, G_DIR_SEPARATOR_W) != NULL ?
+                       wcsrchr (wfullpath, G_DIR_SEPARATOR_W) + 1 :
+                       wfullpath + STRIP_PFX_LENGTH;
+    }
+  if (GetShortPathNameW (wfullpath, wshortname, MAX_PATH_LONG) != 0)
+    {
+      wbasename_short = wcsrchr (wshortname, G_DIR_SEPARATOR_W) != NULL ?
+                        wcsrchr (wshortname, G_DIR_SEPARATOR_W) + 1 :
+                        wshortname + STRIP_PFX_LENGTH;
+    }
+  else
+    {
+      wbasename_short = wcsrchr (wfullpath, G_DIR_SEPARATOR_W) != NULL ?
+                        wcsrchr (wfullpath, G_DIR_SEPARATOR_W) + 1 :
+                        wfullpath + STRIP_PFX_LENGTH;
+    }
+
+  info->longname = g_utf16_to_utf8 (wbasename_long, -1, NULL, NULL, NULL);
+  info->shortname = g_utf16_to_utf8 (wbasename_short, -1, NULL, NULL, NULL);
+
+  /* Store up current file attributes */
+  success_attribs = GetFileAttributesExW (wfullpath,
+                                          GetFileExInfoStandard,
+                                          &info->attribs);
+
+  g_free (wfullpath);
+  g_free (fullpath_with_long_pfx);
+
+  if (!success_attribs)
+    info->attribs.dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+}
+
+gboolean
+g_win32_file_monitor_check_attrib_changed (GWin32FileMonitorInfo *info,
+                                           gchar *filename,
+                                           GHashTable *ht_attribs)
+{
+  gchar *fullpath_with_long_prefix = g_strconcat (info->dirname_with_long_prefix,
+                                                  G_DIR_SEPARATOR_S,
+                                                  filename,
+                                                  NULL);
+  wchar_t *wfullpath_with_long_prefix = g_utf8_to_utf16 (fullpath_with_long_prefix, -1, NULL, NULL, NULL);
+  gboolean is_attribs_changed = FALSE;
+  WIN32_FILE_ATTRIBUTE_DATA attrib_data = {0, };
+  gboolean success_attribs = GetFileAttributesExW (wfullpath_with_long_prefix,
+                                                   GetFileExInfoStandard,
+                                                   &attrib_data);
+
+  if (!success_attribs)
+    attrib_data.dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+
+  if (info->isfile)
+    {
+      /*
+       * Monitoring a file: Simply check if attributes changed, or file changed, and
+       * store up the new attributes for the file if needed.
+       */
+      if (info->attribs.dwFileAttributes != INVALID_FILE_ATTRIBUTES &&
+          success_attribs &&
+          attrib_data.dwFileAttributes != info->attribs.dwFileAttributes)
+        {
+          is_attribs_changed = TRUE;
+          info->attribs = attrib_data;
+        }
+      return is_attribs_changed;
+    }
+  else
+    {
+      /*
+       * Monitoring directories: The situation is more complex, we need to first see whether
+       * a file changed, or a directory changed.  If it is just the directory that changed,
+       * than it's like the file case, that is to check only the attribute of the directory.
+       * If it is on a file in a directory, we need to check against the attributes of the file,
+       * against the attribues of the files in the directory, which we stored up when we were
+       * creating the monitor object, and update our stored records on that file if the attributes
+       * of that file changed.
+       */
+
+      WIN32_FILE_ATTRIBUTE_DATA *attrib_data_orig;
+      gchar *filename_casefold;
+
+      if (g_utf8_strrchr (filename, -1, G_DIR_SEPARATOR) != NULL)
+        {
+          /* A file in the monitored directory changed */
+          gboolean attribs_success;
+          gchar *longname;
+          wchar_t wfullpath_long[MAX_PATH_LONG];
+
+          WIN32_FILE_ATTRIBUTE_DATA *curr_attrib_data = g_new0 (WIN32_FILE_ATTRIBUTE_DATA, 1);
+          gchar *fullpath = g_strconcat (info->dirname_with_long_prefix,
+                                         G_DIR_SEPARATOR_S,
+                                         info->name,
+                                         G_DIR_SEPARATOR_S,
+                                         g_utf8_strrchr (filename, -1, G_DIR_SEPARATOR) + 1,
+                                         NULL);
+          wchar_t *wfullpath = g_utf8_to_utf16 (fullpath, -1, NULL, NULL, NULL);
+
+          attribs_success = GetFileAttributesExW (wfullpath,
+                                                  GetFileExInfoStandard,
+                                                  curr_attrib_data);
+
+          if (!attribs_success)
+            {
+              curr_attrib_data->dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+              longname = g_strdup (g_utf8_strrchr (longname, -1, G_DIR_SEPARATOR) + 1);
+            }
+          else
+            {
+              if (GetLongPathNameW (wfullpath, wfullpath_long, MAX_PATH_LONG) != 0)
+                longname = g_utf16_to_utf8 (wcsrchr (wfullpath_long, G_DIR_SEPARATOR_W) + 1,
+                                            -1,
+                                            NULL,
+                                            NULL,
+                                            NULL);
+              else
+                longname = g_strdup (g_utf8_strrchr (longname, -1, G_DIR_SEPARATOR) + 1);
+            }
+
+          filename_casefold = g_utf8_casefold (longname, -1);
+          g_free (longname);
+          attrib_data_orig = g_hash_table_lookup (ht_attribs, filename_casefold);
+
+          if (attrib_data_orig->dwFileAttributes != INVALID_FILE_ATTRIBUTES &&
+              success_attribs &&
+              curr_attrib_data->dwFileAttributes != attrib_data_orig->dwFileAttributes)
+            {
+              is_attribs_changed = TRUE;
+            }
+
+          if (is_attribs_changed)
+            g_hash_table_replace (ht_attribs, g_strdup (filename_casefold), curr_attrib_data);
+          else
+            g_free (curr_attrib_data);
+
+          g_free (filename_casefold);
+          return is_attribs_changed;
+        }
+      else
+        {
+          /* The monitored directory itself changed */
+          if (info->attribs.dwFileAttributes != INVALID_FILE_ATTRIBUTES &&
+              success_attribs &&
+              attrib_data.dwFileAttributes != info->attribs.dwFileAttributes)
+            {
+              is_attribs_changed = TRUE;
+              info->attribs = attrib_data;
+            }
+          return is_attribs_changed;
+        }
+    }
+}
+
+static GWin32FileMonitorInfo *
+g_win32_file_monitor_set_paths (GWin32FileMonitorPrivate *monitor,
+                              const gchar *dirname,
+                              const gchar *filename)
+{
+  GWin32FileMonitorInfo *info = g_new0 (GWin32FileMonitorInfo, 1);
+  gboolean updated;
+
+  if (filename != NULL)
+    {
+      /* monitor a file */
+      gchar *fullpath;
+      info->isfile = TRUE;
+      info->dirname_with_long_prefix = g_strconcat (LONGPFX, dirname, NULL);
+      info->name = g_strdup (filename);
+      fullpath = g_strconcat (dirname,
+                              G_DIR_SEPARATOR_S,
+                              info->name,
+                              NULL);
+      updated = g_hash_table_replace (monitor->ht_watched_names,
+                                      g_utf8_casefold (fullpath, -1),
+                                      info);
+      g_free (fullpath);
+    }
+  else
+    {
+      /* monitor a directory */
+      gchar *parentdir = g_malloc0 (g_utf8_strlen (dirname, -1) + 1);
+      GHashTable *ht_attribs;
+
+      g_utf8_strncpy (parentdir,
+                      dirname,
+                      g_utf8_strlen (dirname, -1) -
+                      g_utf8_strlen (g_utf8_strrchr (dirname, -1, G_DIR_SEPARATOR), -1));
+
+      info->isfile = FALSE;
+      info->dirname_with_long_prefix = g_strconcat (LONGPFX, parentdir, NULL);
+      info->name = g_strdup (g_utf8_strrchr (dirname, -1, G_DIR_SEPARATOR) + 1);
+
+      g_free (parentdir);
+
+      ht_attribs = g_win32_file_monitor_dir_refresh_attribs (monitor, info);
+
+      if (ht_attribs != NULL)
+        g_hash_table_replace (monitor->ht_files_attribs,
+                              g_utf8_casefold (dirname, -1),
+                              ht_attribs);
+      g_hash_table_replace (monitor->ht_watched_names,
+                            g_utf8_casefold (dirname, -1),
+                            info);
+    }
+
+  g_win32_file_monitor_set_names (info);
+  return info;
+}
+
+/* Set up call to ReadDirectoryChangesW() */
+static void
+g_win32_file_monitor_begin_monitor (GWin32FileMonitorPrivate *monitor,
+                                  GWin32FileMonitorInfo *info)
+{
+  gchar *fullpath;
+  wchar_t *wdirname;
+
+  DWORD notify_filter = info->isfile ?
+                        (FILE_NOTIFY_CHANGE_FILE_NAME |
+                         FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                         FILE_NOTIFY_CHANGE_SIZE) :
+                        (FILE_NOTIFY_CHANGE_FILE_NAME |
+                         FILE_NOTIFY_CHANGE_DIR_NAME |
+                         FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                         FILE_NOTIFY_CHANGE_SIZE);
+
+  fullpath = g_strconcat (info->dirname_with_long_prefix + STRIP_PFX_LENGTH,
+                          G_DIR_SEPARATOR_S,
+                          info->name,
+                          NULL);
+
+  wdirname = g_utf8_to_utf16 (info->dirname_with_long_prefix, -1, NULL, NULL, NULL);
+
+  monitor->hDirectory = CreateFileW (wdirname,
+                                     FILE_GENERIC_READ | FILE_GENERIC_WRITE,
+                                     FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                     NULL,
+                                     OPEN_EXISTING,
+                                     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+                                     NULL);
+
+  g_hash_table_insert (monitor->ht_watched_dirs,
+                       monitor->hDirectory,
+                       g_utf8_casefold (fullpath, -1));
+
+  if (monitor->hDirectory != INVALID_HANDLE_VALUE)
+    {
+      ReadDirectoryChangesW (monitor->hDirectory,
+                             monitor->file_notify_buffer,
+                             monitor->buffer_allocated_bytes,
+                             !info->isfile,
+                             notify_filter,
+                             &monitor->buffer_filled_bytes,
+                             &monitor->overlapped,
+                             g_win32_file_monitor_callback);
+    }
+}
+
+void
+g_win32_file_monitor_prepare (GWin32FileMonitorPrivate *monitor,
+                         gchar *dirname,
+                         gchar *filename,
+                         gboolean isfile)
+{
+  GWin32FileMonitorInfo *info;
+  gchar *fullpath_with_long_prefix, *dirname_with_long_prefix;
+  DWORD notify_filter = isfile ?
+                        (FILE_NOTIFY_CHANGE_FILE_NAME |
+                         FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                         FILE_NOTIFY_CHANGE_SIZE) :
+                        (FILE_NOTIFY_CHANGE_FILE_NAME |
+                         FILE_NOTIFY_CHANGE_DIR_NAME |
+                         FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                         FILE_NOTIFY_CHANGE_SIZE);
+
+  monitor->pfni_prev = NULL;
+
+  if (isfile)
+    info = g_win32_file_monitor_set_paths (monitor, dirname, filename);
+  else
+    info = g_win32_file_monitor_set_paths (monitor, dirname, NULL);
+
+  g_win32_file_monitor_begin_monitor (monitor, info);
+}
diff --git a/gio/win32/gwin32filemonitorutils.h b/gio/win32/gwin32filemonitorutils.h
new file mode 100644
index 0000000..0909808
--- /dev/null
+++ b/gio/win32/gwin32filemonitorutils.h
@@ -0,0 +1,89 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2015 Chun-wei Fan
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Vlad Grecescu <b100dian gmail com>
+ * Author: Chun-wei Fan <fanc999 yahoo com tw>
+ *
+ */
+
+#ifndef __G_WIN32_FILE_MONITOR_UTILS_H__
+#define __G_WIN32_FILE_MONITOR_UTILS_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "gio/glocalfilemonitor.h"
+
+#include "gio/gfilemonitor.h"
+
+#define MAX_PATH_LONG 32767 /* Support Paths longer than MAX_PATH (260) characters */
+#define LONGPFX "\\\\?\\" /* MSDN: Append Paths with \\?\ to support over paths over 256 characters */
+#define G_DIR_SEPARATOR_W L'\\'
+#define STRIP_PFX_LENGTH g_utf8_strlen (LONGPFX, -1)
+
+G_BEGIN_DECLS
+
+typedef struct _GWin32FileMonitorPrivate GWin32FileMonitorPrivate;
+typedef struct _GWin32FileMonitorInfo GWin32FileMonitorInfo;
+typedef struct _GWin32FileMonitorDirInfo GWin32FileMonitorDirInfo;
+
+struct _GWin32FileMonitorPrivate
+{
+  OVERLAPPED overlapped;
+  DWORD buffer_allocated_bytes;
+  PFILE_NOTIFY_INFORMATION file_notify_buffer;
+  DWORD buffer_filled_bytes;
+  HANDLE hDirectory;
+  gboolean isfile;
+  GHashTable *ht_watched_dirs;
+  GHashTable *ht_watched_names;
+  GHashTable *ht_files_attribs;
+  DWORD file_attribs;
+  PFILE_NOTIFY_INFORMATION pfni_prev;
+  /* Needed in the APC where we only have this private struct */
+  GFileMonitor *self;
+  GFileMonitorSource *fms;
+};
+
+struct _GWin32FileMonitorInfo
+{
+  gchar *name;
+  gchar *dirname_with_long_prefix;
+  gchar *longname;
+  gchar *shortname;
+  gboolean isfile;
+  WIN32_FILE_ATTRIBUTE_DATA attribs;
+};
+
+void g_win32_file_monitor_prepare (GWin32FileMonitorPrivate *monitor,
+                                   gchar *dirname,
+                                   gchar *filename,
+                                   gboolean isfile);
+
+void g_win32_file_monitor_set_names (GWin32FileMonitorInfo *info);
+
+GHashTable* g_win32_file_monitor_dir_refresh_attribs (GWin32FileMonitorPrivate *monitor,
+                                                      GWin32FileMonitorInfo *info);
+
+gboolean g_win32_file_monitor_check_attrib_changed (GWin32FileMonitorInfo *info,
+                                                    gchar *filename,
+                                                    GHashTable *ht_attribs);
+
+G_END_DECLS
+
+#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]