Add GWin32InputStream and GWin32OutputStream classes
authorTor Lillqvist <tml@iki.fi>
Mon, 19 Apr 2010 08:32:05 +0000 (11:32 +0300)
committerTor Lillqvist <tml@iki.fi>
Mon, 19 Apr 2010 08:54:56 +0000 (11:54 +0300)
Correspond to GUnixInputStream and GUnixOutputStream. No true async
support though. But that is how the Win32 API is, for files not
explicitly opened for so-called overlapped IO.

The API to create these streams takes Win32 HANDLEs. Not file
descriptors, because file descriptors are specific to the C library
used. The user code and GLib might be using different C libraries.

Also add a test program for the new classes, and a gio-windows-2.0.pc
file.

15 files changed:
Makefile.am
build/win32/vs9/glib.vsprops
configure.in
gio-windows-2.0.pc.in [new file with mode: 0644]
gio/Makefile.am
gio/gio.symbols
gio/gioerror.c
gio/gioerror.h
gio/gwin32inputstream.c [new file with mode: 0644]
gio/gwin32inputstream.h [new file with mode: 0644]
gio/gwin32outputstream.c [new file with mode: 0644]
gio/gwin32outputstream.h [new file with mode: 0644]
gio/tests/Makefile.am
gio/tests/win32-streams.c [new file with mode: 0644]
glib-zip.in

index 708b16b..ace9c9a 100644 (file)
@@ -53,6 +53,7 @@ EXTRA_DIST +=                         \
        gthread-2.0.pc.in       \
        gio-2.0.pc.in           \
        gio-unix-2.0.pc.in      \
+       gio-windows-2.0.pc.in   \
        glib-2.0-uninstalled.pc.in      \
        gobject-2.0-uninstalled.pc.in   \
        gmodule-2.0-uninstalled.pc.in   \
@@ -108,6 +109,10 @@ if OS_UNIX
 pkgconfig_DATA += gio-unix-2.0.pc
 endif
 
+if OS_WIN32
+pkgconfig_DATA += gio-windows-2.0.pc
+endif
+
 $(pkgconfig_DATA): config.status
 
 # install mkinstalldirs for glib-gettextize's benefit
index af95dc8..d562434 100644 (file)
@@ -204,6 +204,10 @@ copy ..\..\..\gio\gvolumemonitor.h $(OutDir)\include\glib-2.0\gio&#x0D;&#x0A;
 copy ..\..\..\gio\gzlibcompressor.h $(OutDir)\include\glib-2.0\gio&#x0D;&#x0A;\r
 copy ..\..\..\gio\gzlibdecompressor.h $(OutDir)\include\glib-2.0\gio&#x0D;&#x0A;\r
 \r
+mkdir $(OutDir)\include\gio-win32-2.0\gio&#x0D;&#x0A;\r
+copy ..\..\..\gio\gwin32inputstream.h $(OutDir)\include\gio-win32-2.0\gio&#x0D;&#x0A;\r
+copy ..\..\..\gio\gwin32outputstream.h $(OutDir)\include\gio-win32-2.0\gio&#x0D;&#x0A;\r
+\r
 mkdir $(OutDir)\lib\glib-2.0\include&#x0D;&#x0A;\r
 copy ..\..\..\glibconfig.h $(OutDir)\lib\glib-2.0\include&#x0D;&#x0A;\r
 \r
index 759adf6..5001af6 100644 (file)
@@ -3510,6 +3510,7 @@ gobject-2.0.pc
 gobject-2.0-uninstalled.pc
 gio-2.0.pc
 gio-unix-2.0.pc
+gio-windows-2.0.pc
 gio-2.0-uninstalled.pc
 gio-unix-2.0-uninstalled.pc
 glib-zip
diff --git a/gio-windows-2.0.pc.in b/gio-windows-2.0.pc.in
new file mode 100644 (file)
index 0000000..77eecdf
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: GIO Windows specific APIs
+Description: Windows specific headers for glib I/O library
+Version: @VERSION@
+Requires: gobject-2.0,gmodule-no-export-2.0,gio-2.0
+Libs: -L${libdir} -lgio-2.0
+Cflags: -I${includedir}/gio-win32-2.0/
index 9749b3e..8e3c185 100644 (file)
@@ -202,6 +202,15 @@ win32_sources = \
        gwin32resolver.h \
        gwin32volumemonitor.c \
        gwin32volumemonitor.h \
+       gwin32inputstream.c \
+       gwin32outputstream.c \
+       gwin32outputstream.h \
+       $(NULL)
+
+giowin32includedir=$(includedir)/gio-win32-2.0/gio
+giowin32include_HEADERS = \
+       gwin32inputstream.h \
+       gwin32outputstream.h \
        $(NULL)
 
 endif
@@ -509,7 +518,7 @@ dist-hook: $(BUILT_EXTRA_DIST) ../build/win32/vs9/gio.vcproj
          cp $$d/$$f $(distdir) || exit 1; done
 
 ../build/win32/vs9/gio.vcproj: $(top_srcdir)/build/win32/vs9/gio.vcprojin
-       for F in $(libgio_2_0_la_SOURCES); do \
+       for F in $(libgio_2_0_la_SOURCES) $(win32_sources); do \
                case $$F in \
                *.c) echo '   <File RelativePath="..\..\..\gio\'$$F'" />' \
                     ;; \
index 42a569d..cedfd39 100644 (file)
@@ -611,6 +611,9 @@ g_io_stream_clear_pending
 #if IN_FILE(__G_IO_ERROR_C__)
 g_io_error_quark
 g_io_error_from_errno
+#ifdef G_OS_WIN32
+g_io_error_from_win32_error
+#endif
 #endif
 #endif
 
@@ -827,6 +830,30 @@ g_unix_output_stream_get_fd
 #endif
 #endif
 
+#if IN_HEADER(__G_WIN32_INPUT_STREAM_H__)
+#if IN_FILE(__G_WIN32_INPUT_STREAM_C__)
+#ifdef G_OS_WIN32
+g_win32_input_stream_get_type  G_GNUC_CONST
+g_win32_input_stream_new
+g_win32_input_stream_set_close_handle
+g_win32_input_stream_get_close_handle
+g_win32_input_stream_get_handle
+#endif /* G_OS_WIN32 */
+#endif
+#endif
+
+#if IN_HEADER(__G_WIN32_OUTPUT_STREAM_H__)
+#if IN_FILE(__G_WIN32_OUTPUT_STREAM_C__)
+#ifdef G_OS_WIN32
+g_win32_output_stream_get_type  G_GNUC_CONST
+g_win32_output_stream_new
+g_win32_output_stream_set_close_handle
+g_win32_output_stream_get_close_handle
+g_win32_output_stream_get_handle
+#endif /* G_OS_WIN32 */
+#endif
+#endif
+
 #if IN_HEADER(__G_MOUNT_H__)
 #if IN_FILE(__G_MOUNT_C__)
 g_mount_get_type  G_GNUC_CONST
index 287cb2b..8a1ee4f 100644 (file)
@@ -206,5 +206,32 @@ g_io_error_from_errno (gint err_no)
     }
 }
 
+#ifdef G_OS_WIN32
+
+/**
+ * g_io_error_from_win32_error:
+ * @error_code: Windows error number.
+ *
+ * Converts some common error codes into GIO error codes. The
+ * fallback value G_IO_ERROR_FAILED is returned for error codes not
+ * handled.
+ *
+ * Returns: #GIOErrorEnum value for the given error number.
+ *
+ * Since: 2.26
+ **/
+GIOErrorEnum
+g_io_error_from_win32_error (gint error_code)
+{
+  switch (error_code)
+    {
+    default:
+      return G_IO_ERROR_FAILED;
+      break;
+    }
+}
+
+#endif
+
 #define __G_IO_ERROR_C__
 #include "gioaliasdef.c"
index c1dc377..12c2b06 100644 (file)
@@ -43,6 +43,10 @@ G_BEGIN_DECLS
 GQuark       g_io_error_quark      (void);
 GIOErrorEnum g_io_error_from_errno (gint err_no);
 
+#ifdef G_OS_WIN32
+GIOErrorEnum g_io_error_from_win32_error (gint error_code);
+#endif
+
 G_END_DECLS
 
 #endif /* __G_IO_ERROR_H__ */
diff --git a/gio/gwin32inputstream.c b/gio/gwin32inputstream.c
new file mode 100644 (file)
index 0000000..3cfe5cc
--- /dev/null
@@ -0,0 +1,359 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ * Author: Tor Lillqvist <tml@iki.fi>
+ */
+
+#include "config.h"
+
+#include <windows.h>
+
+#include <io.h>
+
+#include <glib.h>
+#include "gioerror.h"
+#include "gsimpleasyncresult.h"
+#include "gwin32inputstream.h"
+#include "gcancellable.h"
+#include "gasynchelper.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gwin32inputstream
+ * @short_description: Streaming input operations for Windows file handles
+ * @include: gio/gwin32inputstream.h
+ * @see_also: #GInputStream
+ *
+ * #GWin32InputStream implements #GInputStream for reading from a
+ * Windows file handle.
+ *
+ * Note that <filename>&lt;gio/gwin32inputstream.h&gt;</filename> belongs
+ * to the Windows-specific GIO interfaces, thus you have to use the
+ * <filename>gio-windows-2.0.pc</filename> pkg-config file when using it.
+ */
+
+enum {
+  PROP_0,
+  PROP_HANDLE,
+  PROP_CLOSE_HANDLE
+};
+
+G_DEFINE_TYPE (GWin32InputStream, g_win32_input_stream, G_TYPE_INPUT_STREAM);
+
+struct _GWin32InputStreamPrivate {
+  HANDLE handle;
+  gboolean close_handle;
+};
+
+static void     g_win32_input_stream_set_property (GObject              *object,
+                                                  guint                 prop_id,
+                                                  const GValue         *value,
+                                                  GParamSpec           *pspec);
+static void     g_win32_input_stream_get_property (GObject              *object,
+                                                  guint                 prop_id,
+                                                  GValue               *value,
+                                                  GParamSpec           *pspec);
+static gssize   g_win32_input_stream_read         (GInputStream         *stream,
+                                                  void                 *buffer,
+                                                  gsize                 count,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+static gboolean g_win32_input_stream_close        (GInputStream         *stream,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+
+static void
+g_win32_input_stream_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (g_win32_input_stream_parent_class)->finalize (object);
+}
+
+static void
+g_win32_input_stream_class_init (GWin32InputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GWin32InputStreamPrivate));
+
+  gobject_class->get_property = g_win32_input_stream_get_property;
+  gobject_class->set_property = g_win32_input_stream_set_property;
+  gobject_class->finalize = g_win32_input_stream_finalize;
+
+  stream_class->read_fn = g_win32_input_stream_read;
+  stream_class->close_fn = g_win32_input_stream_close;
+
+  /**
+   * GWin32InputStream:handle:
+   *
+   * The handle that the stream reads from.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_HANDLE,
+                                  g_param_spec_pointer ("handle",
+                                                        P_("File handle"),
+                                                        P_("The file handle to read from"),
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  /**
+   * GWin32InputStream:close-handle:
+   *
+   * Whether to close the file handle when the stream is closed.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_CLOSE_HANDLE,
+                                  g_param_spec_boolean ("close-handle",
+                                                        P_("Close file handle"),
+                                                        P_("Whether to close the file handle when the stream is closed"),
+                                                        TRUE,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+}
+
+static void
+g_win32_input_stream_set_property (GObject         *object,
+                                  guint            prop_id,
+                                  const GValue    *value,
+                                  GParamSpec      *pspec)
+{
+  GWin32InputStream *win32_stream;
+
+  win32_stream = G_WIN32_INPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      win32_stream->priv->handle = g_value_get_pointer (value);
+      break;
+    case PROP_CLOSE_HANDLE:
+      win32_stream->priv->close_handle = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_win32_input_stream_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GWin32InputStream *win32_stream;
+
+  win32_stream = G_WIN32_INPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      g_value_set_pointer (value, win32_stream->priv->handle);
+      break;
+    case PROP_CLOSE_HANDLE:
+      g_value_set_boolean (value, win32_stream->priv->close_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_win32_input_stream_init (GWin32InputStream *win32_stream)
+{
+  win32_stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (win32_stream,
+                                                   G_TYPE_WIN32_INPUT_STREAM,
+                                                   GWin32InputStreamPrivate);
+
+  win32_stream->priv->handle = NULL;
+  win32_stream->priv->close_handle = TRUE;
+}
+
+/**
+ * g_win32_input_stream_new:
+ * @handle: a Win32 file handle
+ * @close_fd: %TRUE to close the handle when done
+ *
+ * Creates a new #GWin32InputStream for the given @fd.
+ *
+ * If @close_handle is %TRUE, the handle will be closed
+ * when the stream is closed.
+ *
+ * Note that "handle" here means a Win32 HANDLE, not a "file descriptor"
+ * as used in the Windows C libraries.
+ *
+ * Returns: a new #GWin32InputStream
+ **/
+GInputStream *
+g_win32_input_stream_new (void     *handle,
+                         gboolean close_handle)
+{
+  GWin32InputStream *stream;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  stream = g_object_new (G_TYPE_WIN32_INPUT_STREAM,
+                        "handle", handle,
+                        "close-handle", close_handle,
+                        NULL);
+
+  return G_INPUT_STREAM (stream);
+}
+
+/**
+ * g_win32_input_stream_set_close_handle:
+ * @stream: a #GWin32InputStream
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Sets whether the handle of @stream shall be closed
+ * when the stream is closed.
+ *
+ * Since: 2.26
+ */
+void
+g_win32_input_stream_set_close_handle (GWin32InputStream *stream,
+                                      gboolean          close_handle)
+{
+  g_return_if_fail (G_IS_WIN32_INPUT_STREAM (stream));
+
+  close_handle = close_handle != FALSE;
+  if (stream->priv->close_handle != close_handle)
+    {
+      stream->priv->close_handle = close_handle;
+      g_object_notify (G_OBJECT (stream), "close-handle");
+    }
+}
+
+/**
+ * g_win32_input_stream_get_close_handle:
+ * @stream: a #GWin32InputStream
+ *
+ * Returns whether the handle of @stream will be
+ * closed when the stream is closed.
+ *
+ * Return value: %TRUE if the handle is closed when done
+ *
+ * Since: 2.26
+ */
+gboolean
+g_win32_input_stream_get_close_handle (GWin32InputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), FALSE);
+
+  return stream->priv->close_handle;
+}
+
+/**
+ * g_win32_input_stream_get_handle:
+ * @stream: a #GWin32InputStream
+ *
+ * Return the Windows file handle that the stream reads from.
+ *
+ * Return value: The file handle of @stream
+ *
+ * Since: 2.26
+ */
+void *
+g_win32_input_stream_get_handle (GWin32InputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_INPUT_STREAM (stream), NULL);
+
+  return stream->priv->handle;
+}
+
+static gssize
+g_win32_input_stream_read (GInputStream  *stream,
+                          void          *buffer,
+                          gsize          count,
+                          GCancellable  *cancellable,
+                          GError       **error)
+{
+  GWin32InputStream *win32_stream;
+  BOOL res;
+  DWORD nbytes, nread;
+
+  win32_stream = G_WIN32_INPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  res = ReadFile (win32_stream->priv->handle, buffer, nbytes, &nread, NULL);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg;
+
+      if (errsv == ERROR_HANDLE_EOF ||
+         errsv == ERROR_BROKEN_PIPE)
+       return 0;
+
+      emsg = g_win32_error_message (errsv);
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_win32_error (errsv),
+                  _("Error reading from handle: %s"),
+                  emsg);
+      g_free (emsg);
+      return -1;
+    }
+
+  return nread;
+}
+
+static gboolean
+g_win32_input_stream_close (GInputStream  *stream,
+                          GCancellable  *cancellable,
+                          GError       **error)
+{
+  GWin32InputStream *win32_stream;
+  BOOL res;
+
+  win32_stream = G_WIN32_INPUT_STREAM (stream);
+
+  if (!win32_stream->priv->close_handle)
+    return TRUE;
+
+  res = CloseHandle (win32_stream->priv->handle);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_win32_error (errsv),
+                  _("Error closing handle: %s"),
+                  emsg);
+      g_free (emsg);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+#define __G_WIN32_INPUT_STREAM_C__
+#include "gioaliasdef.c"
diff --git a/gio/gwin32inputstream.h b/gio/gwin32inputstream.h
new file mode 100644 (file)
index 0000000..937685f
--- /dev/null
@@ -0,0 +1,79 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ * Author: Tor Lillqvist <tml@iki.fi>
+ */
+
+#ifndef __G_WIN32_INPUT_STREAM_H__
+#define __G_WIN32_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_INPUT_STREAM         (g_win32_input_stream_get_type ())
+#define G_WIN32_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_INPUT_STREAM, GWin32InputStream))
+#define G_WIN32_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_INPUT_STREAM, GWin32InputStreamClass))
+#define G_IS_WIN32_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_INPUT_STREAM))
+#define G_IS_WIN32_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_INPUT_STREAM))
+#define G_WIN32_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_INPUT_STREAM, GWin32InputStreamClass))
+
+/**
+ * GWin32InputStream:
+ *
+ * Implements #GInputStream for reading from selectable Windows file handles
+ **/
+typedef struct _GWin32InputStream         GWin32InputStream;
+typedef struct _GWin32InputStreamClass    GWin32InputStreamClass;
+typedef struct _GWin32InputStreamPrivate  GWin32InputStreamPrivate;
+
+struct _GWin32InputStream
+{
+  GInputStream parent_instance;
+
+  /*< private >*/
+  GWin32InputStreamPrivate *priv;
+};
+
+struct _GWin32InputStreamClass
+{
+  GInputStreamClass parent_class;
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType          g_win32_input_stream_get_type         (void) G_GNUC_CONST;
+
+GInputStream * g_win32_input_stream_new              (void              *handle,
+                                                     gboolean           close_handle);
+void           g_win32_input_stream_set_close_handle (GWin32InputStream *stream,
+                                                     gboolean           close_handle);
+gboolean       g_win32_input_stream_get_close_handle (GWin32InputStream *stream);
+void          *g_win32_input_stream_get_handle       (GWin32InputStream *stream);
+
+G_END_DECLS
+
+#endif /* __G_WIN32_INPUT_STREAM_H__ */
diff --git a/gio/gwin32outputstream.c b/gio/gwin32outputstream.c
new file mode 100644 (file)
index 0000000..7b9f03b
--- /dev/null
@@ -0,0 +1,359 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ * Author: Tor Lillqvist <tml@iki.fi>
+ */
+
+#include "config.h"
+
+#include <windows.h>
+
+#include <io.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "gioerror.h"
+#include "gwin32outputstream.h"
+#include "gcancellable.h"
+#include "gsimpleasyncresult.h"
+#include "gasynchelper.h"
+#include "glibintl.h"
+
+#include "gioalias.h"
+
+/**
+ * SECTION:gwin32outputstream
+ * @short_description: Streaming output operations for Windows file handles
+ * @include: gio/gwin32outputstream.h
+ * @see_also: #GOutputStream
+ *
+ * #GWin32OutputStream implements #GOutputStream for writing to a
+ * Windows file handle.
+ *
+ * Note that <filename>&lt;gio/gwin32outputstream.h&gt;</filename> belongs
+ * to the Windows-specific GIO interfaces, thus you have to use the
+ * <filename>gio-windows-2.0.pc</filename> pkg-config file when using it.
+ */
+
+enum {
+  PROP_0,
+  PROP_HANDLE,
+  PROP_CLOSE_HANDLE
+};
+
+G_DEFINE_TYPE (GWin32OutputStream, g_win32_output_stream, G_TYPE_OUTPUT_STREAM);
+
+
+struct _GWin32OutputStreamPrivate {
+  HANDLE handle;
+  gboolean close_handle;
+};
+
+static void     g_win32_output_stream_set_property (GObject              *object,
+                                                   guint                 prop_id,
+                                                   const GValue         *value,
+                                                   GParamSpec           *pspec);
+static void     g_win32_output_stream_get_property (GObject              *object,
+                                                   guint                 prop_id,
+                                                   GValue               *value,
+                                                   GParamSpec           *pspec);
+static gssize   g_win32_output_stream_write        (GOutputStream        *stream,
+                                                   const void           *buffer,
+                                                   gsize                 count,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static gboolean g_win32_output_stream_close        (GOutputStream        *stream,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+
+
+static void
+g_win32_output_stream_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (g_win32_output_stream_parent_class)->finalize (object);
+}
+
+static void
+g_win32_output_stream_class_init (GWin32OutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GWin32OutputStreamPrivate));
+
+  gobject_class->get_property = g_win32_output_stream_get_property;
+  gobject_class->set_property = g_win32_output_stream_set_property;
+  gobject_class->finalize = g_win32_output_stream_finalize;
+
+  stream_class->write_fn = g_win32_output_stream_write;
+  stream_class->close_fn = g_win32_output_stream_close;
+
+   /**
+   * GWin32OutputStream:handle:
+   *
+   * The file handle that the stream writes to.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_HANDLE,
+                                  g_param_spec_pointer ("handle",
+                                                        P_("File handle"),
+                                                        P_("The file handle to write to"),
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+
+  /**
+   * GWin32OutputStream:close-handle:
+   *
+   * Whether to close the file handle when the stream is closed.
+   *
+   * Since: 2.26
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_CLOSE_HANDLE,
+                                  g_param_spec_boolean ("close-handle",
+                                                        P_("Close file handle"),
+                                                        P_("Whether to close the file handle when the stream is closed"),
+                                                        TRUE,
+                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
+}
+
+static void
+g_win32_output_stream_set_property (GObject         *object,
+                                   guint            prop_id,
+                                   const GValue    *value,
+                                   GParamSpec      *pspec)
+{
+  GWin32OutputStream *win32_stream;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      win32_stream->priv->handle = g_value_get_pointer (value);
+      break;
+    case PROP_CLOSE_HANDLE:
+      win32_stream->priv->close_handle = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_win32_output_stream_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  GWin32OutputStream *win32_stream;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_HANDLE:
+      g_value_set_pointer (value, win32_stream->priv->handle);
+      break;
+    case PROP_CLOSE_HANDLE:
+      g_value_set_boolean (value, win32_stream->priv->close_handle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_win32_output_stream_init (GWin32OutputStream *win32_stream)
+{
+  win32_stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (win32_stream,
+                                                   G_TYPE_WIN32_OUTPUT_STREAM,
+                                                   GWin32OutputStreamPrivate);
+
+  win32_stream->priv->handle = NULL;
+  win32_stream->priv->close_handle = TRUE;
+}
+
+/**
+ * g_win32_output_stream_new:
+ * @handle: a Win32 file handle
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Creates a new #GWin32OutputStream for the given @handle.
+ *
+ * If @close_handle, is %TRUE, the handle will be closed when the
+ * output stream is destroyed.
+ *
+ * Returns: a new #GOutputStream
+ *
+ * Since: 2.26
+**/
+GOutputStream *
+g_win32_output_stream_new (void    *handle,
+                          gboolean close_handle)
+{
+  GWin32OutputStream *stream;
+
+  g_return_val_if_fail (handle != NULL, NULL);
+
+  stream = g_object_new (G_TYPE_WIN32_OUTPUT_STREAM,
+                        "handle", handle,
+                        "close-handle", close_handle,
+                        NULL);
+
+  return G_OUTPUT_STREAM (stream);
+}
+
+/**
+ * g_win32_output_stream_set_close_handle:
+ * @stream: a #GWin32OutputStream
+ * @close_handle: %TRUE to close the handle when done
+ *
+ * Sets whether the handle of @stream shall be closed when the stream
+ * is closed.
+ *
+ * Since: 2.26
+ */
+void
+g_win32_output_stream_set_close_handle (GWin32OutputStream *stream,
+                                       gboolean           close_handle)
+{
+  g_return_if_fail (G_IS_WIN32_OUTPUT_STREAM (stream));
+
+  close_handle = close_handle != FALSE;
+  if (stream->priv->close_handle != close_handle)
+    {
+      stream->priv->close_handle = close_handle;
+      g_object_notify (G_OBJECT (stream), "close-handle");
+    }
+}
+
+/**
+ * g_win32_output_stream_get_close_handle:
+ * @stream: a #GWin32OutputStream
+ *
+ * Returns whether the handle of @stream will be closed when the
+ * stream is closed.
+ *
+ * Return value: %TRUE if the handle is closed when done
+ *
+ * Since: 2.26
+ */
+gboolean
+g_win32_output_stream_get_close_handle (GWin32OutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_OUTPUT_STREAM (stream), FALSE);
+
+  return stream->priv->close_handle;
+}
+
+/**
+ * g_win32_output_stream_get_handle:
+ * @stream: a #GWin32OutputStream
+ *
+ * Return the Windows handle that the stream writes to.
+ *
+ * Return value: The handle descriptor of @stream
+ *
+ * Since: 2.26
+ */
+void *
+g_win32_output_stream_get_handle (GWin32OutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_WIN32_OUTPUT_STREAM (stream), NULL);
+
+  return stream->priv->handle;
+}
+
+static gssize
+g_win32_output_stream_write (GOutputStream  *stream,
+                           const void     *buffer,
+                           gsize           count,
+                           GCancellable   *cancellable,
+                           GError        **error)
+{
+  GWin32OutputStream *win32_stream;
+  BOOL res;
+  DWORD nbytes, nwritten;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+
+  if (count > G_MAXINT)
+    nbytes = G_MAXINT;
+  else
+    nbytes = count;
+
+  res = WriteFile (win32_stream->priv->handle, buffer, nbytes, &nwritten, NULL);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      if (errsv == ERROR_HANDLE_EOF)
+       return 0;
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_win32_error (errsv),
+                  _("Error writing to handle: %s"),
+                  emsg);
+      g_free (emsg);
+      return -1;
+    }
+
+  return nwritten;
+}
+
+static gboolean
+g_win32_output_stream_close (GOutputStream  *stream,
+                            GCancellable   *cancellable,
+                            GError        **error)
+{
+  GWin32OutputStream *win32_stream;
+  BOOL res;
+
+  win32_stream = G_WIN32_OUTPUT_STREAM (stream);
+
+  if (!win32_stream->priv->close_handle)
+    return TRUE;
+
+  res = CloseHandle (win32_stream->priv->handle);
+  if (!res)
+    {
+      int errsv = GetLastError ();
+      gchar *emsg = g_win32_error_message (errsv);
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_win32_error (errsv),
+                  _("Error closing handle: %s"),
+                  emsg);
+      g_free (emsg);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+#define __G_WIN32_OUTPUT_STREAM_C__
+#include "gioaliasdef.c"
diff --git a/gio/gwin32outputstream.h b/gio/gwin32outputstream.h
new file mode 100644 (file)
index 0000000..399a53a
--- /dev/null
@@ -0,0 +1,78 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2006-2010 Red Hat, Inc.
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ * Author: Tor Lillqvist <tml@iki.fi>
+ */
+
+#ifndef __G_WIN32_OUTPUT_STREAM_H__
+#define __G_WIN32_OUTPUT_STREAM_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_OUTPUT_STREAM         (g_win32_output_stream_get_type ())
+#define G_WIN32_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_OUTPUT_STREAM, GWin32OutputStream))
+#define G_WIN32_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_OUTPUT_STREAM, GWin32OutputStreamClass))
+#define G_IS_WIN32_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_OUTPUT_STREAM))
+#define G_IS_WIN32_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_OUTPUT_STREAM))
+#define G_WIN32_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_OUTPUT_STREAM, GWin32OutputStreamClass))
+
+/**
+ * GWin32OutputStream:
+ *
+ * Implements #GOutputStream for outputting to Windows file handles
+ **/
+typedef struct _GWin32OutputStream         GWin32OutputStream;
+typedef struct _GWin32OutputStreamClass    GWin32OutputStreamClass;
+typedef struct _GWin32OutputStreamPrivate  GWin32OutputStreamPrivate;
+
+struct _GWin32OutputStream
+{
+  GOutputStream parent_instance;
+
+  /*< private >*/
+  GWin32OutputStreamPrivate *priv;
+};
+
+struct _GWin32OutputStreamClass
+{
+  GOutputStreamClass parent_class;
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType           g_win32_output_stream_get_type         (void) G_GNUC_CONST;
+
+GOutputStream * g_win32_output_stream_new              (void               *handle,
+                                                       gboolean            close_handle);
+void            g_win32_output_stream_set_close_handle (GWin32OutputStream *stream,
+                                                       gboolean           close_handle);
+gboolean        g_win32_output_stream_get_close_handle (GWin32OutputStream *stream);
+void           *g_win32_output_stream_get_handle       (GWin32OutputStream *stream);
+G_END_DECLS
+
+#endif /* __G_WIN32_OUTPUT_STREAM_H__ */
index 523465e..ba3410d 100644 (file)
@@ -47,6 +47,10 @@ if OS_UNIX
 TEST_PROGS += live-g-file desktop-app-info unix-fd #unix-streams
 endif
 
+if OS_WIN32
+TEST_PROGS += win32-streams
+endif
+
 memory_input_stream_SOURCES      = memory-input-stream.c
 memory_input_stream_LDADD        = $(progs_ldadd)
 
@@ -90,6 +94,10 @@ unix_streams_SOURCES   = unix-streams.c
 unix_streams_LDADD       = $(progs_ldadd) \
        $(top_builddir)/gthread/libgthread-2.0.la
 
+win32_streams_SOURCES    = win32-streams.c
+win32_streams_LDADD      = $(progs_ldadd) \
+       $(top_builddir)/gthread/libgthread-2.0.la
+
 unix_fd_SOURCES          = unix-fd.c
 unix_fd_LDADD    = $(progs_ldadd)
 
diff --git a/gio/tests/win32-streams.c b/gio/tests/win32-streams.c
new file mode 100644 (file)
index 0000000..4ac04d4
--- /dev/null
@@ -0,0 +1,292 @@
+/* GLib testing framework examples and tests
+ * Copyright (C) 2008 Red Hat, Inc
+ *
+ * This work is provided "as is"; redistribution and modification
+ * in whole or in part, in any medium, physical or electronic is
+ * permitted without restriction.
+ *
+ * This work 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.
+ *
+ * In no event shall the authors or contributors be liable for any
+ * direct, indirect, incidental, special, exemplary, or consequential
+ * damages (including, but not limited to, procurement of substitute
+ * goods or services; loss of use, data, or profits; or business
+ * interruption) however caused and on any theory of liability, whether
+ * in contract, strict liability, or tort (including negligence or
+ * otherwise) arising in any way out of the use of this software, even
+ * if advised of the possibility of such damage.
+ */
+
+#include <glib/glib.h>
+#include <gio/gio.h>
+#include <gio/gwin32inputstream.h>
+#include <gio/gwin32outputstream.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <io.h>
+
+#include <windows.h>
+
+#define DATA "abcdefghijklmnopqrstuvwxyz"
+
+int writer_pipe[2], reader_pipe[2];
+GCancellable *writer_cancel, *reader_cancel, *main_cancel;
+GMainLoop *loop;
+
+static gpointer
+writer_thread (gpointer user_data)
+{
+  GOutputStream *out;
+  gssize nwrote, offset;
+  GError *err = NULL;
+  HANDLE out_handle;
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+                            (HANDLE) (gintptr) _get_osfhandle (writer_pipe[1]),
+                            GetCurrentProcess (),
+                            &out_handle,
+                            0, FALSE,
+                            DUPLICATE_SAME_ACCESS));
+  close (writer_pipe[1]);
+
+  out = g_win32_output_stream_new (out_handle, TRUE);
+  do
+    {
+      g_usleep (10);
+
+      offset = 0;
+      while (offset < (gssize) sizeof (DATA))
+       {
+         nwrote = g_output_stream_write (out, DATA + offset,
+                                         sizeof (DATA) - offset,
+                                         writer_cancel, &err);
+         if (nwrote <= 0 || err != NULL)
+           break;
+         offset += nwrote;
+       }
+
+      g_assert (nwrote > 0 || err != NULL);
+    }
+  while (err == NULL);
+
+  if (g_cancellable_is_cancelled (writer_cancel))
+    {
+      g_cancellable_cancel (main_cancel);
+      g_object_unref (out);
+      return NULL;
+    }
+
+  g_warning ("writer: %s", err->message);
+  g_assert_not_reached ();
+}
+
+static gpointer
+reader_thread (gpointer user_data)
+{
+  GInputStream *in;
+  gssize nread = 0, total;
+  GError *err = NULL;
+  char buf[sizeof (DATA)];
+  HANDLE in_handle;
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+                            (HANDLE) (gintptr) _get_osfhandle (reader_pipe[0]),
+                            GetCurrentProcess (),
+                            &in_handle,
+                            0, FALSE,
+                            DUPLICATE_SAME_ACCESS));
+  close (reader_pipe[0]);
+
+  in = g_win32_input_stream_new (in_handle, TRUE);
+
+  do
+    {
+      total = 0;
+      while (total < (gssize) sizeof (DATA))
+       {
+         nread = g_input_stream_read (in, buf + total, sizeof (buf) - total,
+                                      reader_cancel, &err);
+         if (nread <= 0 || err != NULL)
+           break;
+         total += nread;
+       }
+
+      if (err)
+       break;
+
+      if (nread == 0)
+       {
+         g_assert (err == NULL);
+         /* pipe closed */
+         g_object_unref (in);
+         return NULL;
+       }
+
+      g_assert_cmpstr (buf, ==, DATA);
+      g_assert (!g_cancellable_is_cancelled (reader_cancel));
+    }
+  while (err == NULL);
+
+  g_warning ("reader: %s", err->message);
+  g_assert_not_reached ();
+}
+
+char main_buf[sizeof (DATA)];
+gssize main_len, main_offset;
+
+static void readable (GObject *source, GAsyncResult *res, gpointer user_data);
+static void writable (GObject *source, GAsyncResult *res, gpointer user_data);
+
+static void
+do_main_cancel (GOutputStream *out)
+{
+  g_output_stream_close (out, NULL, NULL);
+  g_main_loop_quit (loop);
+}
+
+static void
+readable (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+  GInputStream *in = G_INPUT_STREAM (source);
+  GOutputStream *out = user_data;
+  GError *err = NULL;
+
+  main_len = g_input_stream_read_finish (in, res, &err);
+
+  if (g_cancellable_is_cancelled (main_cancel))
+    {
+      do_main_cancel (out);
+      return;
+    }
+
+  g_assert (err == NULL);
+
+  main_offset = 0;
+  g_output_stream_write_async (out, main_buf, main_len,
+                              G_PRIORITY_DEFAULT, main_cancel,
+                              writable, in);
+}
+
+static void
+writable (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+  GOutputStream *out = G_OUTPUT_STREAM (source);
+  GInputStream *in = user_data;
+  GError *err = NULL;
+  gssize nwrote;
+
+  nwrote = g_output_stream_write_finish (out, res, &err);
+
+  if (g_cancellable_is_cancelled (main_cancel))
+    {
+      do_main_cancel (out);
+      return;
+    }
+
+  g_assert (err == NULL);
+  g_assert_cmpint (nwrote, <=, main_len - main_offset);
+
+  main_offset += nwrote;
+  if (main_offset == main_len)
+    {
+      g_input_stream_read_async (in, main_buf, sizeof (main_buf),
+                                G_PRIORITY_DEFAULT, main_cancel,
+                                readable, out);
+    }
+  else
+    {
+      g_output_stream_write_async (out, main_buf + main_offset,
+                                  main_len - main_offset,
+                                  G_PRIORITY_DEFAULT, main_cancel,
+                                  writable, in);
+    }
+}
+
+static gboolean
+timeout (gpointer cancellable)
+{
+  g_cancellable_cancel (cancellable);
+  return FALSE;
+}
+
+static void
+test_pipe_io (void)
+{
+  GThread *writer, *reader;
+  GInputStream *in;
+  GOutputStream *out;
+  HANDLE in_handle, out_handle;
+
+  /* Split off two (additional) threads, a reader and a writer. From
+   * the writer thread, write data synchronously in small chunks,
+   * which gets read asynchronously by the main thread and then
+   * written asynchronously to the reader thread, which reads it
+   * synchronously. Eventually a timeout in the main thread will cause
+   * it to cancel the writer thread, which will in turn cancel the
+   * read op in the main thread, which will then close the pipe to
+   * the reader thread, causing the read op to fail.
+   */
+
+  g_assert (_pipe (writer_pipe, 10, _O_BINARY) == 0 && _pipe (reader_pipe, 10, _O_BINARY) == 0);
+
+  writer_cancel = g_cancellable_new ();
+  reader_cancel = g_cancellable_new ();
+  main_cancel = g_cancellable_new ();
+
+  writer = g_thread_create (writer_thread, NULL, TRUE, NULL);
+  reader = g_thread_create (reader_thread, NULL, TRUE, NULL);
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+                            (HANDLE) (gintptr) _get_osfhandle (writer_pipe[0]),
+                            GetCurrentProcess (),
+                            &in_handle,
+                            0, FALSE,
+                            DUPLICATE_SAME_ACCESS));
+  close (writer_pipe[0]);
+
+  g_assert (DuplicateHandle (GetCurrentProcess (),
+                            (HANDLE) (gintptr) _get_osfhandle (reader_pipe[1]),
+                            GetCurrentProcess (),
+                            &out_handle,
+                            0, FALSE,
+                            DUPLICATE_SAME_ACCESS));
+  close (reader_pipe[1]);
+
+  in = g_win32_input_stream_new (in_handle, TRUE);
+  out = g_win32_output_stream_new (out_handle, TRUE);
+
+  g_input_stream_read_async (in, main_buf, sizeof (main_buf),
+                            G_PRIORITY_DEFAULT, main_cancel,
+                            readable, out);
+
+  g_timeout_add (500, timeout, writer_cancel);
+
+  loop = g_main_loop_new (NULL, TRUE);
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+
+  g_thread_join (reader);
+  g_thread_join (writer);
+
+  g_object_unref (main_cancel);
+  g_object_unref (reader_cancel);
+  g_object_unref (writer_cancel);
+  g_object_unref (in);
+  g_object_unref (out);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_thread_init (NULL);
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/win32-streams/pipe-io-test", test_pipe_io);
+
+  return g_test_run();
+}
index 859a06a..b8ceb09 100755 (executable)
@@ -64,6 +64,7 @@ lib/pkgconfig/gmodule-no-export-2.0.pc
 lib/pkgconfig/gobject-2.0.pc
 lib/pkgconfig/gthread-2.0.pc
 lib/pkgconfig/gio-2.0.pc
+lib/pkgconfig/gio-windows-2.0.pc
 share/aclocal/glib-2.0.m4
 share/aclocal/glib-gettext.m4
 share/glib-2.0