Click here to Skip to main content
15,891,184 members
Articles / Desktop Programming / MFC

MsAccess MdbTools with MFC and .NET

Rate me:
Please Sign up or sign in to vote.
4.82/5 (9 votes)
13 Jan 2012LGPL310 min read 69.2K   9.9K   49  
Viewer of MsAccess databases directly from MFC and .NET - Repair corrupt databases
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * giowin32.c: IO Channels for Win32.
 * Copyright 1998 Owen Taylor and Tor Lillqvist
 * Copyright 1999-2000 Tor Lillqvist and Craig Setera
 * Copyright 2001-2003 Andrew Lanoix
 *
 * 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.
 */

/*
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/.
 */

/*
 * Bugs that are related to the code in this file:
 *
 * Bug 137968 - Sometimes a GIOFunc on Win32 is called with zero condition
 * http://bugzilla.gnome.org/show_bug.cgi?id=137968
 *
 * Bug 324234 - Using g_io_add_watch_full() to wait for connect() to return on a non-blocking socket returns prematurely
 * http://bugzilla.gnome.org/show_bug.cgi?id=324234
 *
 * Bug 331214 - g_io_channel async socket io stalls
 * http://bugzilla.gnome.org/show_bug.cgi?id=331214
 *
 * Bug 338943 - Multiple watches on the same socket
 * http://bugzilla.gnome.org/show_bug.cgi?id=338943
 *
 * Bug 357674 - 2 serious bugs in giowin32.c making glib iochannels useless
 * http://bugzilla.gnome.org/show_bug.cgi?id=357674
 *
 * Bug 425156 - GIOChannel deadlocks on a win32 socket
 * http://bugzilla.gnome.org/show_bug.cgi?id=425156
 *
 * Bug 468910 - giofunc condition=0
 * http://bugzilla.gnome.org/show_bug.cgi?id=468910
 *
 * Bug 500246 - Bug fixes for giowin32
 * http://bugzilla.gnome.org/show_bug.cgi?id=500246
 *
 * Bug 548278 - Async GETs connections are always terminated unexpectedly on windows
 * http://bugzilla.gnome.org/show_bug.cgi?id=548278
 *
 * Bug 548536 - giowin32 problem when adding and removing watches
 * http://bugzilla.gnome.org/show_bug.cgi?id=548536
 *
 * When fixing bugs related to the code in this file, either the above
 * bugs or others, make sure that the test programs attached to the
 * above bugs continue to work.
 */

#include "config.h"

#include "glib.h"

#include <stdlib.h>
#include <winsock2.h>
#include <windows.h>
#include <conio.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#include <errno.h>
#include <sys/stat.h>

#include "gstdio.h"
#include "glibintl.h"


typedef struct _GIOWin32Channel GIOWin32Channel;
typedef struct _GIOWin32Watch GIOWin32Watch;

#define BUFFER_SIZE 4096

typedef enum {
  G_IO_WIN32_WINDOWS_MESSAGES,	/* Windows messages */

  G_IO_WIN32_FILE_DESC,		/* Unix-like file descriptors from
				 * _open() or _pipe(), except for
				 * console IO. Separate thread to read
				 * or write.
				 */

  G_IO_WIN32_CONSOLE,		/* Console IO (usually stdin, stdout, stderr) */

  G_IO_WIN32_SOCKET		/* Sockets. No separate thread. */
} GIOWin32ChannelType;

struct _GIOWin32Channel {
  GIOChannel channel;
  gint fd;			/* Either a Unix-like file handle as provided
				 * by the Microsoft C runtime, or a SOCKET
				 * as provided by WinSock.
				 */
  GIOWin32ChannelType type;
  
  gboolean debug;

  /* Field used by G_IO_WIN32_WINDOWS_MESSAGES channels */
  HWND hwnd;			/* Handle of window, or NULL */
  
  /* Fields used by G_IO_WIN32_FILE_DESC channels. */
  CRITICAL_SECTION mutex;

  int direction;		/* 0 means we read from it,
				 * 1 means we write to it.
				 */

  gboolean running;		/* Is reader or writer thread
				 * running. FALSE if EOF has been
				 * reached by the reader thread.
				 */

  gboolean needs_close;		/* If the channel has been closed while
				 * the reader thread was still running.
				 */

  guint thread_id;		/* If non-NULL the channel has or has
				 * had a reader or writer thread.
				 */
  HANDLE data_avail_event;

  gushort revents;

  /* Data is kept in a circular buffer. To be able to distinguish between
   * empty and full buffers, we cannot fill it completely, but have to
   * leave a one character gap.
   *
   * Data available is between indexes rdp and wrp-1 (modulo BUFFER_SIZE).
   *
   * Empty:    wrp == rdp
   * Full:     (wrp + 1) % BUFFER_SIZE == rdp
   * Partial:  otherwise
   */
  guchar *buffer;		/* (Circular) buffer */
  gint wrp, rdp;		/* Buffer indices for writing and reading */
  HANDLE space_avail_event;

  /* Fields used by G_IO_WIN32_SOCKET channels */
  int event_mask;
  int last_events;
  HANDLE event;
  gboolean write_would_have_blocked;
  gboolean ever_writable;
};

struct _GIOWin32Watch {
  GSource       source;
  GPollFD       pollfd;
  GIOChannel   *channel;
  GIOCondition  condition;
};

static void
g_win32_print_access_mode (int flags)
{
  g_print ("%s%s%s%s%s%s%s%s%s%s",
	   ((flags & 0x3) == _O_RDWR ? "O_RDWR" :
	    ((flags & 0x3) == _O_RDONLY ? "O_RDONLY" :
	     ((flags & 0x3) == _O_WRONLY ? "O_WRONLY" : "0"))),
	   (flags & _O_APPEND ? "|O_APPEND" : ""),
	   (flags & _O_RANDOM ? "|O_RANDOM" : ""),
	   (flags & _O_SEQUENTIAL ? "|O_SEQUENTIAL" : ""),
	   (flags & _O_TEMPORARY ? "|O_TEMPORARY" : ""),
	   (flags & _O_CREAT ? "|O_CREAT" : ""),
	   (flags & _O_TRUNC ? "|O_TRUNC" : ""),
	   (flags & _O_EXCL ? "|O_EXCL" : ""),
	   (flags & _O_TEXT ? "|O_TEXT" : ""),
	   (flags & _O_BINARY ? "|O_BINARY" : ""));
}

static void
g_win32_print_gioflags (GIOFlags flags)
{
  char *bar = "";

  if (flags & G_IO_FLAG_APPEND)
    bar = "|", g_print ("APPEND");
  if (flags & G_IO_FLAG_NONBLOCK)
    g_print ("%sNONBLOCK", bar), bar = "|";
  if (flags & G_IO_FLAG_IS_READABLE)
    g_print ("%sREADABLE", bar), bar = "|";
  if (flags & G_IO_FLAG_IS_WRITEABLE)
    g_print ("%sWRITEABLE", bar), bar = "|";
  if (flags & G_IO_FLAG_IS_SEEKABLE)
    g_print ("%sSEEKABLE", bar), bar = "|";
}

static const char *
event_mask_to_string (int mask)
{
  char buf[100];
  int checked_bits = 0;
  char *bufp = buf;

  if (mask == 0)
    return "";

#define BIT(n) checked_bits |= FD_##n; if (mask & FD_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))

  BIT (READ);
  BIT (WRITE);
  BIT (OOB);
  BIT (ACCEPT);
  BIT (CONNECT);
  BIT (CLOSE);
  BIT (QOS);
  BIT (GROUP_QOS);
  BIT (ROUTING_INTERFACE_CHANGE);
  BIT (ADDRESS_LIST_CHANGE);
  
#undef BIT

  if ((mask & ~checked_bits) != 0)
	  bufp += sprintf (bufp, "|%#x", mask & ~checked_bits);
  
  return g_quark_to_string (g_quark_from_string (buf));
}

static const char *
condition_to_string (GIOCondition condition)
{
  char buf[100];
  int checked_bits = 0;
  char *bufp = buf;

  if (condition == 0)
    return "";

#define BIT(n) checked_bits |= G_IO_##n; if (condition & G_IO_##n) bufp += sprintf (bufp, "%s" #n, (bufp>buf ? "|" : ""))

  BIT (IN);
  BIT (OUT);
  BIT (PRI);
  BIT (ERR);
  BIT (HUP);
  BIT (NVAL);
  
#undef BIT

  if ((condition & ~checked_bits) != 0)
	  bufp += sprintf (bufp, "|%#x", condition & ~checked_bits);
  
  return g_quark_to_string (g_quark_from_string (buf));
}

static gboolean
g_io_win32_get_debug_flag (void)
{
  return (getenv ("G_IO_WIN32_DEBUG") != NULL);
}

static void
g_io_channel_win32_init (GIOWin32Channel *channel)
{
  channel->debug = g_io_win32_get_debug_flag ();

  InitializeCriticalSection (&channel->mutex);
  channel->running = FALSE;
  channel->needs_close = FALSE;
  channel->thread_id = 0;
  channel->data_avail_event = NULL;
  channel->revents = 0;
  channel->buffer = NULL;
  channel->space_avail_event = NULL;

  channel->event_mask = 0;
  channel->last_events = 0;
  channel->event = NULL;
  channel->write_would_have_blocked = FALSE;
  channel->ever_writable = FALSE;
}

static void
create_events (GIOWin32Channel *channel)
{
  SECURITY_ATTRIBUTES sec_attrs;
  
  sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
  sec_attrs.lpSecurityDescriptor = NULL;
  sec_attrs.bInheritHandle = FALSE;

  /* The data available event is manual reset, the space available event
   * is automatic reset.
   */
  if (!(channel->data_avail_event = CreateEvent (&sec_attrs, TRUE, FALSE, NULL))
      || !(channel->space_avail_event = CreateEvent (&sec_attrs, FALSE, FALSE, NULL)))
    {
      gchar *emsg = g_win32_error_message (GetLastError ());

      g_error ("Error creating event: %s", emsg);
      g_free (emsg);
    }
}

static unsigned __stdcall
read_thread (void *parameter)
{
  GIOWin32Channel *channel = parameter;
  guchar *buffer;
  gint nbytes;

  g_io_channel_ref ((GIOChannel *)channel);

  if (channel->debug)
    g_print ("read_thread %#x: start fd=%d, data_avail=%p space_avail=%p\n",
	     channel->thread_id,
	     channel->fd,
	     channel->data_avail_event,
	     channel->space_avail_event);

  channel->direction = 0;
  channel->buffer = g_malloc (BUFFER_SIZE);
  channel->rdp = channel->wrp = 0;
  channel->running = TRUE;

  SetEvent (channel->space_avail_event);
  
  EnterCriticalSection (&channel->mutex);
  while (channel->running)
    {
      if (channel->debug)
	g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
		 channel->thread_id, channel->rdp, channel->wrp);
      if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
	{
	  /* Buffer is full */
	  if (channel->debug)
	    g_print ("read_thread %#x: resetting space_avail\n",
		     channel->thread_id);
	  ResetEvent (channel->space_avail_event);
	  if (channel->debug)
	    g_print ("read_thread %#x: waiting for space\n",
		     channel->thread_id);
	  LeaveCriticalSection (&channel->mutex);
	  WaitForSingleObject (channel->space_avail_event, INFINITE);
	  EnterCriticalSection (&channel->mutex);
	  if (channel->debug)
	    g_print ("read_thread %#x: rdp=%d, wrp=%d\n",
		     channel->thread_id, channel->rdp, channel->wrp);
	}
      
      buffer = channel->buffer + channel->wrp;
      
      /* Always leave at least one byte unused gap to be able to
       * distinguish between the full and empty condition...
       */
      nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
		    BUFFER_SIZE - channel->wrp);

      if (channel->debug)
	g_print ("read_thread %#x: calling read() for %d bytes\n",
		 channel->thread_id, nbytes);

      LeaveCriticalSection (&channel->mutex);

      nbytes = read (channel->fd, buffer, nbytes);
      
      EnterCriticalSection (&channel->mutex);

      channel->revents = G_IO_IN;
      if (nbytes == 0)
	channel->revents |= G_IO_HUP;
      else if (nbytes < 0)
	channel->revents |= G_IO_ERR;

      if (channel->debug)
	g_print ("read_thread %#x: read() returned %d, rdp=%d, wrp=%d\n",
		 channel->thread_id, nbytes, channel->rdp, channel->wrp);

      if (nbytes <= 0)
	break;

      channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
      if (channel->debug)
	g_print ("read_thread %#x: rdp=%d, wrp=%d, setting data_avail\n",
		 channel->thread_id, channel->rdp, channel->wrp);
      SetEvent (channel->data_avail_event);
    }
  
  channel->running = FALSE;
  if (channel->needs_close)
    {
      if (channel->debug)
	g_print ("read_thread %#x: channel fd %d needs closing\n",
		 channel->thread_id, channel->fd);
      close (channel->fd);
      channel->fd = -1;
    }

  if (channel->debug)
    g_print ("read_thread %#x: EOF, rdp=%d, wrp=%d, setting data_avail\n",
	     channel->thread_id, channel->rdp, channel->wrp);
  SetEvent (channel->data_avail_event);
  LeaveCriticalSection (&channel->mutex);
  
  g_io_channel_unref ((GIOChannel *)channel);
  
  /* No need to call _endthreadex(), the actual thread starter routine
   * in MSVCRT (see crt/src/threadex.c:_threadstartex) calls
   * _endthreadex() for us.
   */

  return 0;
}

static unsigned __stdcall
write_thread (void *parameter)
{
  GIOWin32Channel *channel = parameter;
  guchar *buffer;
  gint nbytes;

  g_io_channel_ref ((GIOChannel *)channel);

  if (channel->debug)
    g_print ("write_thread %#x: start fd=%d, data_avail=%p space_avail=%p\n",
	     channel->thread_id,
	     channel->fd,
	     channel->data_avail_event,
	     channel->space_avail_event);
  
  channel->direction = 1;
  channel->buffer = g_malloc (BUFFER_SIZE);
  channel->rdp = channel->wrp = 0;
  channel->running = TRUE;

  SetEvent (channel->space_avail_event);

  /* We use the same event objects as for a reader thread, but with
   * reversed meaning. So, space_avail is used if data is available
   * for writing, and data_avail is used if space is available in the
   * write buffer.
   */

  EnterCriticalSection (&channel->mutex);
  while (channel->running || channel->rdp != channel->wrp)
    {
      if (channel->debug)
	g_print ("write_thread %#x: rdp=%d, wrp=%d\n",
		 channel->thread_id, channel->rdp, channel->wrp);
      if (channel->wrp == channel->rdp)
	{
	  /* Buffer is empty. */
	  if (channel->debug)
	    g_print ("write_thread %#x: resetting space_avail\n",
		     channel->thread_id);
	  ResetEvent (channel->space_avail_event);
	  if (channel->debug)
	    g_print ("write_thread %#x: waiting for data\n",
		     channel->thread_id);
	  channel->revents = G_IO_OUT;
	  SetEvent (channel->data_avail_event);
	  LeaveCriticalSection (&channel->mutex);
	  WaitForSingleObject (channel->space_avail_event, INFINITE);

	  EnterCriticalSection (&channel->mutex);
	  if (channel->rdp == channel->wrp)
	    break;

	  if (channel->debug)
	    g_print ("write_thread %#x: rdp=%d, wrp=%d\n",
		     channel->thread_id, channel->rdp, channel->wrp);
	}
      
      buffer = channel->buffer + channel->rdp;
      if (channel->rdp < channel->wrp)
	nbytes = channel->wrp - channel->rdp;
      else
	nbytes = BUFFER_SIZE - channel->rdp;

      if (channel->debug)
	g_print ("write_thread %#x: calling write() for %d bytes\n",
		 channel->thread_id, nbytes);

      LeaveCriticalSection (&channel->mutex);
      nbytes = write (channel->fd, buffer, nbytes);
      EnterCriticalSection (&channel->mutex);

      if (channel->debug)
	g_print ("write_thread %#x: write(%i) returned %d, rdp=%d, wrp=%d\n",
		 channel->thread_id, channel->fd, nbytes, channel->rdp, channel->wrp);

      channel->revents = 0;
      if (nbytes > 0)
	channel->revents |= G_IO_OUT;
      else if (nbytes <= 0)
	channel->revents |= G_IO_ERR;

      channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;

      if (nbytes <= 0)
	break;

      if (channel->debug)
	g_print ("write_thread: setting data_avail for thread %#x\n",
		 channel->thread_id);
      SetEvent (channel->data_avail_event);
    }
  
  channel->running = FALSE;
  if (channel->needs_close)
    {
      if (channel->debug)
	g_print ("write_thread %#x: channel fd %d needs closing\n",
		 channel->thread_id, channel->fd);
      close (channel->fd);
      channel->fd = -1;
    }

  LeaveCriticalSection (&channel->mutex);
  
  g_io_channel_unref ((GIOChannel *)channel);
  
  return 0;
}

static void
create_thread (GIOWin32Channel     *channel,
	       GIOCondition         condition,
	       unsigned (__stdcall *thread) (void *parameter))
{
  HANDLE thread_handle;

  thread_handle = (HANDLE) _beginthreadex (NULL, 0, thread, channel, 0,
					   &channel->thread_id);
  if (thread_handle == 0)
    g_warning ("Error creating thread: %s.",
	       g_strerror (errno));
  else if (!CloseHandle (thread_handle))
    {
      gchar *emsg = g_win32_error_message (GetLastError ());

      g_warning ("Error closing thread handle: %s.", emsg);
      g_free (emsg);
    }

  WaitForSingleObject (channel->space_avail_event, INFINITE);
}

static GIOStatus
buffer_read (GIOWin32Channel *channel,
	     gchar           *dest,
	     gsize            count,
	     gsize           *bytes_read,
	     GError         **err)
{
  guint nbytes;
  guint left = count;
  
  EnterCriticalSection (&channel->mutex);
  if (channel->debug)
    g_print ("reading from thread %#x %" G_GSIZE_FORMAT " bytes, rdp=%d, wrp=%d\n",
	     channel->thread_id, count, channel->rdp, channel->wrp);
  
  if (channel->wrp == channel->rdp)
    {
      LeaveCriticalSection (&channel->mutex);
      if (channel->debug)
	g_print ("waiting for data from thread %#x\n", channel->thread_id);
      WaitForSingleObject (channel->data_avail_event, INFINITE);
      if (channel->debug)
	g_print ("done waiting for data from thread %#x\n", channel->thread_id);
      EnterCriticalSection (&channel->mutex);
      if (channel->wrp == channel->rdp && !channel->running)
	{
	  if (channel->debug)
	    g_print ("wrp==rdp, !running\n");
	  LeaveCriticalSection (&channel->mutex);
          *bytes_read = 0;
	  return G_IO_STATUS_EOF;
	}
    }
  
  if (channel->rdp < channel->wrp)
    nbytes = channel->wrp - channel->rdp;
  else
    nbytes = BUFFER_SIZE - channel->rdp;
  LeaveCriticalSection (&channel->mutex);
  nbytes = MIN (left, nbytes);
  if (channel->debug)
    g_print ("moving %d bytes from thread %#x\n",
	     nbytes, channel->thread_id);
  memcpy (dest, channel->buffer + channel->rdp, nbytes);
  dest += nbytes;
  left -= nbytes;
  EnterCriticalSection (&channel->mutex);
  channel->rdp = (channel->rdp + nbytes) % BUFFER_SIZE;
  if (channel->debug)
    g_print ("setting space_avail for thread %#x\n", channel->thread_id);
  SetEvent (channel->space_avail_event);
  if (channel->debug)
    g_print ("for thread %#x: rdp=%d, wrp=%d\n",
	     channel->thread_id, channel->rdp, channel->wrp);
  if (channel->running && channel->wrp == channel->rdp)
    {
      if (channel->debug)
	g_print ("resetting data_avail of thread %#x\n",
		 channel->thread_id);
      ResetEvent (channel->data_avail_event);
    };
  LeaveCriticalSection (&channel->mutex);
  
  /* We have no way to indicate any errors form the actual
   * read() or recv() call in the reader thread. Should we have?
   */
  *bytes_read = count - left;
  return (*bytes_read > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
}


static GIOStatus
buffer_write (GIOWin32Channel *channel,
	      const gchar     *dest,
	      gsize            count,
	      gsize           *bytes_written,
	      GError         **err)
{
  guint nbytes;
  guint left = count;
  
  EnterCriticalSection (&channel->mutex);
  if (channel->debug)
    g_print ("buffer_write: writing to thread %#x %" G_GSIZE_FORMAT " bytes, rdp=%d, wrp=%d\n",
	     channel->thread_id, count, channel->rdp, channel->wrp);
  
  if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
    {
      /* Buffer is full */
      if (channel->debug)
	g_print ("buffer_write: tid %#x: resetting data_avail\n",
		 channel->thread_id);
      ResetEvent (channel->data_avail_event);
      if (channel->debug)
	g_print ("buffer_write: tid %#x: waiting for space\n",
		 channel->thread_id);
      LeaveCriticalSection (&channel->mutex);
      WaitForSingleObject (channel->data_avail_event, INFINITE);
      EnterCriticalSection (&channel->mutex);
      if (channel->debug)
	g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d\n",
		 channel->thread_id, channel->rdp, channel->wrp);
    }
   
  nbytes = MIN ((channel->rdp + BUFFER_SIZE - channel->wrp - 1) % BUFFER_SIZE,
		BUFFER_SIZE - channel->wrp);

  LeaveCriticalSection (&channel->mutex);
  nbytes = MIN (left, nbytes);
  if (channel->debug)
    g_print ("buffer_write: tid %#x: writing %d bytes\n",
	     channel->thread_id, nbytes);
  memcpy (channel->buffer + channel->wrp, dest, nbytes);
  dest += nbytes;
  left -= nbytes;
  EnterCriticalSection (&channel->mutex);

  channel->wrp = (channel->wrp + nbytes) % BUFFER_SIZE;
  if (channel->debug)
    g_print ("buffer_write: tid %#x: rdp=%d, wrp=%d, setting space_avail\n",
	     channel->thread_id, channel->rdp, channel->wrp);
  SetEvent (channel->space_avail_event);

  if ((channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
    {
      /* Buffer is full */
      if (channel->debug)
	g_print ("buffer_write: tid %#x: resetting data_avail\n",
		 channel->thread_id);
      ResetEvent (channel->data_avail_event);
    }

  LeaveCriticalSection (&channel->mutex);
  
  /* We have no way to indicate any errors form the actual
   * write() call in the writer thread. Should we have?
   */
  *bytes_written = count - left;
  return (*bytes_written > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
}


static gboolean
g_io_win32_prepare (GSource *source,
		    gint    *timeout)
{
  GIOWin32Watch *watch = (GIOWin32Watch *)source;
  GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
  GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
  int event_mask;
  
  *timeout = -1;
  
  if (channel->debug)
    g_print ("g_io_win32_prepare: source=%p channel=%p", source, channel);

  switch (channel->type)
    {
    case G_IO_WIN32_WINDOWS_MESSAGES:
      if (channel->debug)
	g_print (" MSG");
      break;

    case G_IO_WIN32_CONSOLE:
      if (channel->debug)
	g_print (" CON");
      break;

    case G_IO_WIN32_FILE_DESC:
      if (channel->debug)
	g_print (" FD thread=%#x buffer_condition:{%s}"
		 "\n  watch->pollfd.events:{%s} watch->pollfd.revents:{%s} channel->revents:{%s}",
		 channel->thread_id, condition_to_string (buffer_condition),
		 condition_to_string (watch->pollfd.events),
		 condition_to_string (watch->pollfd.revents),
		 condition_to_string (channel->revents));
      
      EnterCriticalSection (&channel->mutex);
      if (channel->running)
	{
	  if (channel->direction == 0 && channel->wrp == channel->rdp)
	    {
	      if (channel->debug)
		g_print ("\n  setting revents=0");
	      channel->revents = 0;
	    }
	}
      else
	{
	  if (channel->direction == 1
	      && (channel->wrp + 1) % BUFFER_SIZE == channel->rdp)
	    {
	      if (channel->debug)
		g_print ("\n setting revents=0");
	      channel->revents = 0;
	    }
	}	  
      LeaveCriticalSection (&channel->mutex);
      break;

    case G_IO_WIN32_SOCKET:
      if (channel->debug)
	g_print (" SOCK");
      event_mask = 0;
      if (watch->condition & G_IO_IN)
	event_mask |= (FD_READ | FD_ACCEPT);
      if (watch->condition & G_IO_OUT)
	event_mask |= (FD_WRITE | FD_CONNECT);
      event_mask |= FD_CLOSE;

      if (channel->event_mask != event_mask)
	{
	  if (channel->debug)
	    g_print ("\n  WSAEventSelect(%d,%p,{%s})",
		     channel->fd, (HANDLE) watch->pollfd.fd,
		     event_mask_to_string (event_mask));
	  if (WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd,
			      event_mask) == SOCKET_ERROR)
	    if (channel->debug)
	      {
		gchar *emsg = g_win32_error_message (WSAGetLastError ());

		g_print (" failed: %s", emsg);
		g_free (emsg);
	      }
	  channel->event_mask = event_mask;

	  if (channel->debug)
	    g_print ("\n  setting last_events=0");
	  channel->last_events = 0;

	  if ((event_mask & FD_WRITE) &&
	      channel->ever_writable &&
	      !channel->write_would_have_blocked)
	    {
	      if (channel->debug)
		g_print (" WSASetEvent(%p)", (WSAEVENT) watch->pollfd.fd);
	      WSASetEvent ((WSAEVENT) watch->pollfd.fd);
	    }
	}
      break;

    default:
      g_assert_not_reached ();
      abort ();
    }
  if (channel->debug)
    g_print ("\n");

  return ((watch->condition & buffer_condition) == watch->condition);
}

static gboolean
g_io_win32_check (GSource *source)
{
  MSG msg;
  GIOWin32Watch *watch = (GIOWin32Watch *)source;
  GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
  GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
  WSANETWORKEVENTS events;

  if (channel->debug)
    g_print ("g_io_win32_check: source=%p channel=%p", source, channel);

  switch (channel->type)
    {
    case G_IO_WIN32_WINDOWS_MESSAGES:
      if (channel->debug)
	g_print (" MSG\n");
      return (PeekMessage (&msg, channel->hwnd, 0, 0, PM_NOREMOVE));

    case G_IO_WIN32_FILE_DESC:
      if (channel->debug)
	g_print (" FD thread=%#x buffer_condition=%s\n"
		 "  watch->pollfd.events={%s} watch->pollfd.revents={%s} channel->revents={%s}\n",
		 channel->thread_id, condition_to_string (buffer_condition),
		 condition_to_string (watch->pollfd.events),
		 condition_to_string (watch->pollfd.revents),
		 condition_to_string (channel->revents));
      
      watch->pollfd.revents = (watch->pollfd.events & channel->revents);

      return ((watch->pollfd.revents | buffer_condition) & watch->condition);

    case G_IO_WIN32_CONSOLE:
      if (channel->debug)
	g_print (" CON\n");
      if (watch->channel->is_writeable)
	return TRUE;
      else if (watch->channel->is_readable)
        {
	  INPUT_RECORD buffer;
	  DWORD n;
	  if (PeekConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n) &&
	      n == 1)
	    {
	      /* _kbhit() does quite complex processing to find out
	       * whether at least one of the key events pending corresponds
	       * to a "real" character that can be read.
	       */
	      if (_kbhit ())
		return TRUE;
	      
	      /* Discard all other kinds of events */
	      ReadConsoleInput ((HANDLE) watch->pollfd.fd, &buffer, 1, &n);
	    }
        }
      return FALSE;

    case G_IO_WIN32_SOCKET:
      if (channel->debug)
	g_print (" SOCK");
      if (channel->last_events & FD_WRITE)
	{
	  if (channel->debug)
	    g_print (" sock=%d event=%p last_events has FD_WRITE",
		     channel->fd, (HANDLE) watch->pollfd.fd);
	}
      else
	{
	  WSAEnumNetworkEvents (channel->fd, 0, &events);

	  if (channel->debug)
	    g_print ("\n  revents={%s} condition={%s}"
		     "\n  WSAEnumNetworkEvents(%d,0) sets events={%s}",
		     condition_to_string (watch->pollfd.revents),
		     condition_to_string (watch->condition),
		     channel->fd, 
		     event_mask_to_string (events.lNetworkEvents));
	  
	  if (watch->pollfd.revents != 0 &&
	      events.lNetworkEvents == 0 &&
	      !(channel->event_mask & FD_WRITE))
	    {
	      channel->event_mask = 0;
	      if (channel->debug)
		g_print ("\n  WSAEventSelect(%d,%p,{})",
			 channel->fd, (HANDLE) watch->pollfd.fd);
	      WSAEventSelect (channel->fd, (HANDLE) watch->pollfd.fd, 0);
	      if (channel->debug)
		g_print ("  ResetEvent(%p)",
			 (HANDLE) watch->pollfd.fd);
	      ResetEvent ((HANDLE) watch->pollfd.fd);
	    }
	  else if (events.lNetworkEvents & FD_WRITE)
	    channel->ever_writable = TRUE;
	  channel->last_events = events.lNetworkEvents;
	}

      watch->pollfd.revents = 0;
      if (channel->last_events & (FD_READ | FD_ACCEPT))
	watch->pollfd.revents |= G_IO_IN;

      if (channel->last_events & FD_WRITE)
	watch->pollfd.revents |= G_IO_OUT;
      else
	{
	  /* We have called WSAEnumNetworkEvents() above but it didn't
	   * set FD_WRITE.
	   */
	  if (events.lNetworkEvents & FD_CONNECT)
	    {
	      if (events.iErrorCode[FD_CONNECT_BIT] == 0)
		watch->pollfd.revents |= G_IO_OUT;
	      else
		watch->pollfd.revents |= (G_IO_HUP | G_IO_ERR);
	    }
	  if (watch->pollfd.revents == 0 && (channel->last_events & (FD_CLOSE)))
	    watch->pollfd.revents |= G_IO_HUP;
	}

      /* Regardless of WSAEnumNetworkEvents() result, if watching for
       * writability, and if we have ever got a FD_WRITE event, and
       * unless last write would have blocked, set G_IO_OUT. But never
       * set both G_IO_OUT and G_IO_HUP.
       */
      if (!(watch->pollfd.revents & G_IO_HUP) &&
	  channel->ever_writable &&
	  !channel->write_would_have_blocked &&
	  (channel->event_mask & FD_WRITE))
	watch->pollfd.revents |= G_IO_OUT;

      if (channel->debug)
	g_print ("\n  revents={%s} retval={%s}\n",
		 condition_to_string (watch->pollfd.revents),
		 condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition));

      return ((watch->pollfd.revents | buffer_condition) & watch->condition);

    default:
      g_assert_not_reached ();
      abort ();
    }
}

static gboolean
g_io_win32_dispatch (GSource     *source,
		     GSourceFunc  callback,
		     gpointer     user_data)
{
  GIOFunc func = (GIOFunc)callback;
  GIOWin32Watch *watch = (GIOWin32Watch *)source;
  GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
  GIOCondition buffer_condition = g_io_channel_get_buffer_condition (watch->channel);
  
  if (!func)
    {
      g_warning ("IO Watch dispatched without callback\n"
		 "You must call g_source_connect().");
      return FALSE;
    }
  
  if (channel->debug)
    g_print ("g_io_win32_dispatch: pollfd.revents=%s condition=%s result=%s\n",
	     condition_to_string (watch->pollfd.revents),
	     condition_to_string (watch->condition),
	     condition_to_string ((watch->pollfd.revents | buffer_condition) & watch->condition));

  return (*func) (watch->channel,
		  (watch->pollfd.revents | buffer_condition) & watch->condition,
		  user_data);
}

static void
g_io_win32_finalize (GSource *source)
{
  GIOWin32Watch *watch = (GIOWin32Watch *)source;
  GIOWin32Channel *channel = (GIOWin32Channel *)watch->channel;
  
  if (channel->debug)
    g_print ("g_io_win32_finalize: source=%p channel=%p", source, channel);

  switch (channel->type)
    {
    case G_IO_WIN32_WINDOWS_MESSAGES:
      if (channel->debug)
	g_print (" MSG");
      break;

    case G_IO_WIN32_CONSOLE:
      if (channel->debug)
	g_print (" CON");
      break;

    case G_IO_WIN32_FILE_DESC:
      if (channel->debug)
	g_print (" FD thread=%#x", channel->thread_id);
      break;

    case G_IO_WIN32_SOCKET:
      if (channel->debug)
	g_print (" SOCK sock=%d", channel->fd);
      break;

    default:
      g_assert_not_reached ();
      abort ();
    }
  if (channel->debug)
    g_print ("\n");
  g_io_channel_unref (watch->channel);
}

GSourceFuncs g_io_watch_funcs = {
  g_io_win32_prepare,
  g_io_win32_check,
  g_io_win32_dispatch,
  g_io_win32_finalize
};

static GIOStatus
g_io_win32_msg_read (GIOChannel *channel,
		     gchar      *buf,
		     gsize       count,
		     gsize      *bytes_read,
		     GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  MSG msg;               /* In case of alignment problems */
  
  if (count < sizeof (MSG))
    {
      g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
                           "Incorrect message size"); /* Informative enough error message? */
      return G_IO_STATUS_ERROR;
    }
  
  if (win32_channel->debug)
    g_print ("g_io_win32_msg_read: channel=%p hwnd=%p\n",
	     channel, win32_channel->hwnd);
  if (!PeekMessage (&msg, win32_channel->hwnd, 0, 0, PM_REMOVE))
    return G_IO_STATUS_AGAIN;

  memmove (buf, &msg, sizeof (MSG));
  *bytes_read = sizeof (MSG);

  return G_IO_STATUS_NORMAL;
}

static GIOStatus
g_io_win32_msg_write (GIOChannel  *channel,
		      const gchar *buf,
		      gsize        count,
		      gsize       *bytes_written,
		      GError     **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  MSG msg;
  
  if (count != sizeof (MSG))
    {
      g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_INVAL,
                           "Incorrect message size"); /* Informative enough error message? */
      return G_IO_STATUS_ERROR;
    }
  
  /* In case of alignment problems */
  memmove (&msg, buf, sizeof (MSG));
  if (!PostMessage (win32_channel->hwnd, msg.message, msg.wParam, msg.lParam))
    {
      gchar *emsg = g_win32_error_message (GetLastError ());

      g_set_error_literal (err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, emsg);
      g_free (emsg);

      return G_IO_STATUS_ERROR;
    }

  *bytes_written = sizeof (MSG);

  return G_IO_STATUS_NORMAL;
}

static GIOStatus
g_io_win32_msg_close (GIOChannel *channel,
		      GError    **err)
{
  /* Nothing to be done. Or should we set hwnd to some invalid value? */

  return G_IO_STATUS_NORMAL;
}

static void
g_io_win32_free (GIOChannel *channel)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  
  if (win32_channel->debug)
    g_print ("g_io_win32_free channel=%p fd=%d\n", channel, win32_channel->fd);

  DeleteCriticalSection (&win32_channel->mutex);

  if (win32_channel->data_avail_event)
    if (!CloseHandle (win32_channel->data_avail_event))
      if (win32_channel->debug)
	{
	  gchar *emsg = g_win32_error_message (GetLastError ());

	  g_print ("  CloseHandle(%p) failed: %s\n",
		   win32_channel->data_avail_event, emsg);
	  g_free (emsg);
	}

  g_free (win32_channel->buffer);

  if (win32_channel->space_avail_event)
    if (!CloseHandle (win32_channel->space_avail_event))
      if (win32_channel->debug)
	{
	  gchar *emsg = g_win32_error_message (GetLastError ());

	  g_print ("  CloseHandle(%p) failed: %s\n",
		   win32_channel->space_avail_event, emsg);
	  g_free (emsg);
	}

  if (win32_channel->type == G_IO_WIN32_SOCKET &&
      win32_channel->fd != -1)
    if (WSAEventSelect (win32_channel->fd, NULL, 0) == SOCKET_ERROR)
      if (win32_channel->debug)
	{
	  gchar *emsg = g_win32_error_message (WSAGetLastError ());

	  g_print ("  WSAEventSelect(%d,NULL,{}) failed: %s\n",
		   win32_channel->fd, emsg);
	  g_free (emsg);
	}

  if (win32_channel->event)
    if (!WSACloseEvent (win32_channel->event))
      if (win32_channel->debug)
	{
	  gchar *emsg = g_win32_error_message (WSAGetLastError ());

	  g_print ("  WSACloseEvent(%p) failed: %s\n",
		   win32_channel->event, emsg);
	  g_free (emsg);
	}

  g_free (win32_channel);
}

static GSource *
g_io_win32_msg_create_watch (GIOChannel   *channel,
			     GIOCondition  condition)
{
  GIOWin32Watch *watch;
  GSource *source;

  source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
  g_source_set_name (source, "GIOChannel (Win32)");
  watch = (GIOWin32Watch *)source;
  
  watch->channel = channel;
  g_io_channel_ref (channel);
  
  watch->condition = condition;
  
  watch->pollfd.fd = (gintptr) G_WIN32_MSG_HANDLE;
  watch->pollfd.events = condition;
  
  g_source_add_poll (source, &watch->pollfd);
  
  return source;
}

static GIOStatus
g_io_win32_fd_and_console_read (GIOChannel *channel,
				gchar      *buf,
				gsize       count,
				gsize      *bytes_read,
				GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  gint result;
  
  if (win32_channel->debug)
    g_print ("g_io_win32_fd_read: fd=%d count=%" G_GSIZE_FORMAT "\n",
	     win32_channel->fd, count);
  
  if (win32_channel->thread_id)
    {
      return buffer_read (win32_channel, buf, count, bytes_read, err);
    }

  result = read (win32_channel->fd, buf, count);

  if (win32_channel->debug)
    g_print ("g_io_win32_fd_read: read() => %d\n", result);

  if (result < 0)
    {
      *bytes_read = 0;

      switch (errno)
        {
#ifdef EAGAIN
	case EAGAIN:
	  return G_IO_STATUS_AGAIN;
#endif
	default:
	  g_set_error_literal (err, G_IO_CHANNEL_ERROR,
                               g_io_channel_error_from_errno (errno),
                               g_strerror (errno));
	  return G_IO_STATUS_ERROR;
        }
    }

  *bytes_read = result;

  return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
}

static GIOStatus
g_io_win32_fd_and_console_write (GIOChannel  *channel,
				 const gchar *buf,
				 gsize        count,
				 gsize       *bytes_written,
				 GError     **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  gint result;

  if (win32_channel->thread_id)
    {
      return buffer_write (win32_channel, buf, count, bytes_written, err);
    }
  
  result = write (win32_channel->fd, buf, count);
  if (win32_channel->debug)
    g_print ("g_io_win32_fd_write: fd=%d count=%" G_GSIZE_FORMAT " => %d\n",
	     win32_channel->fd, count, result);

  if (result < 0)
    {
      *bytes_written = 0;

      switch (errno)
        {
#ifdef EAGAIN
	case EAGAIN:
	  return G_IO_STATUS_AGAIN;
#endif
	default:
	  g_set_error_literal (err, G_IO_CHANNEL_ERROR,
                               g_io_channel_error_from_errno (errno),
                               g_strerror (errno));
	  return G_IO_STATUS_ERROR;
        }
    }

  *bytes_written = result;

  return G_IO_STATUS_NORMAL;
}

static GIOStatus
g_io_win32_fd_seek (GIOChannel *channel,
		    gint64      offset,
		    GSeekType   type,
		    GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  int whence;
  off_t tmp_offset;
  off_t result;
  
  switch (type)
    {
    case G_SEEK_SET:
      whence = SEEK_SET;
      break;
    case G_SEEK_CUR:
      whence = SEEK_CUR;
      break;
    case G_SEEK_END:
      whence = SEEK_END;
      break;
    default:
      whence = -1; /* Keep the compiler quiet */
      g_assert_not_reached ();
      abort ();
    }

  tmp_offset = offset;
  if (tmp_offset != offset)
    {
      g_set_error_literal (err, G_IO_CHANNEL_ERROR,
                           g_io_channel_error_from_errno (EINVAL),
                           g_strerror (EINVAL));
      return G_IO_STATUS_ERROR;
    }
  
  result = lseek (win32_channel->fd, tmp_offset, whence);
  
  if (result < 0)
    {
      g_set_error_literal (err, G_IO_CHANNEL_ERROR,
                           g_io_channel_error_from_errno (errno),
                           g_strerror (errno));
      return G_IO_STATUS_ERROR;
    }

  return G_IO_STATUS_NORMAL;
}

static GIOStatus
g_io_win32_fd_close (GIOChannel *channel,
	             GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  
  if (win32_channel->debug)
    g_print ("g_io_win32_fd_close: thread=%#x: fd=%d\n",
	     win32_channel->thread_id,
	     win32_channel->fd);
  EnterCriticalSection (&win32_channel->mutex);
  if (win32_channel->running)
    {
      if (win32_channel->debug)
	g_print ("thread %#x: running, marking fd %d for later close\n",
		 win32_channel->thread_id, win32_channel->fd);
      win32_channel->running = FALSE;
      win32_channel->needs_close = TRUE;
      if (win32_channel->direction == 0)
	SetEvent (win32_channel->data_avail_event);
      else
	SetEvent (win32_channel->space_avail_event);
    }
  else
    {
      if (win32_channel->debug)
	g_print ("closing fd %d\n", win32_channel->fd);
      close (win32_channel->fd);
      if (win32_channel->debug)
	g_print ("closed fd %d, setting to -1\n",
		 win32_channel->fd);
      win32_channel->fd = -1;
    }
  LeaveCriticalSection (&win32_channel->mutex);

  /* FIXME error detection? */

  return G_IO_STATUS_NORMAL;
}

static GSource *
g_io_win32_fd_create_watch (GIOChannel    *channel,
			    GIOCondition   condition)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
  GIOWin32Watch *watch = (GIOWin32Watch *)source;

  watch->channel = channel;
  g_io_channel_ref (channel);
  
  watch->condition = condition;
  
  if (win32_channel->data_avail_event == NULL)
    create_events (win32_channel);

  watch->pollfd.fd = (gintptr) win32_channel->data_avail_event;
  watch->pollfd.events = condition;
  
  if (win32_channel->debug)
    g_print ("g_io_win32_fd_create_watch: channel=%p fd=%d condition={%s} event=%p\n",
	     channel, win32_channel->fd,
	     condition_to_string (condition), (HANDLE) watch->pollfd.fd);

  EnterCriticalSection (&win32_channel->mutex);
  if (win32_channel->thread_id == 0)
    {
      if (condition & G_IO_IN)
	create_thread (win32_channel, condition, read_thread);
      else if (condition & G_IO_OUT)
	create_thread (win32_channel, condition, write_thread);
    }

  g_source_add_poll (source, &watch->pollfd);
  LeaveCriticalSection (&win32_channel->mutex);

  return source;
}

static GIOStatus
g_io_win32_console_close (GIOChannel *channel,
		          GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  
  if (close (win32_channel->fd) < 0)
    {
      g_set_error_literal (err, G_IO_CHANNEL_ERROR,
                           g_io_channel_error_from_errno (errno),
                           g_strerror (errno));
      return G_IO_STATUS_ERROR;
    }

  return G_IO_STATUS_NORMAL;
}

static GSource *
g_io_win32_console_create_watch (GIOChannel    *channel,
				 GIOCondition   condition)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
  GIOWin32Watch *watch = (GIOWin32Watch *)source;

  watch->channel = channel;
  g_io_channel_ref (channel);
  
  watch->condition = condition;
  
  watch->pollfd.fd = _get_osfhandle (win32_channel->fd);
  watch->pollfd.events = condition;
  
  g_source_add_poll (source, &watch->pollfd);

  return source;
}

static GIOStatus
g_io_win32_sock_read (GIOChannel *channel,
		      gchar      *buf,
		      gsize       count,
		      gsize      *bytes_read,
		      GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  gint result;
  GIOChannelError error;
  int winsock_error;

  if (win32_channel->debug)
    g_print ("g_io_win32_sock_read: channel=%p sock=%d count=%" G_GSIZE_FORMAT,
	     channel, win32_channel->fd, count);

  result = recv (win32_channel->fd, buf, count, 0);
  if (result == SOCKET_ERROR)
    winsock_error = WSAGetLastError ();

  if (win32_channel->debug)
    g_print (" recv=%d", result);
  
  if (result == SOCKET_ERROR)
    {
      gchar *emsg = g_win32_error_message (winsock_error);

      if (win32_channel->debug)
	g_print (" %s\n", emsg);

      *bytes_read = 0;

      switch (winsock_error)
	{
	case WSAEINVAL:
          error = G_IO_CHANNEL_ERROR_INVAL;
          break;
	case WSAEWOULDBLOCK:
	  g_free (emsg);
          return G_IO_STATUS_AGAIN;
	default:
	  error = G_IO_CHANNEL_ERROR_FAILED;
          break;
	}
      g_set_error_literal (err, G_IO_CHANNEL_ERROR, error, emsg);
      g_free (emsg);

      return G_IO_STATUS_ERROR;
    }
  else
    {
      if (win32_channel->debug)
	g_print ("\n");
      *bytes_read = result;
      if (result == 0)
	return G_IO_STATUS_EOF;
      else
	return G_IO_STATUS_NORMAL;
    }
}

static GIOStatus
g_io_win32_sock_write (GIOChannel  *channel,
		       const gchar *buf,
		       gsize        count,
		       gsize       *bytes_written,
		       GError     **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  gint result;
  GIOChannelError error;
  int winsock_error;
  
  if (win32_channel->debug)
    g_print ("g_io_win32_sock_write: channel=%p sock=%d count=%" G_GSIZE_FORMAT,
	     channel, win32_channel->fd, count);

  result = send (win32_channel->fd, buf, count, 0);
  if (result == SOCKET_ERROR)
    winsock_error = WSAGetLastError ();

  if (win32_channel->debug)
    g_print (" send=%d", result);
  
  if (result == SOCKET_ERROR)
    {
      gchar *emsg = g_win32_error_message (winsock_error);

      if (win32_channel->debug)
	g_print (" %s\n", emsg);

      *bytes_written = 0;

      switch (winsock_error)
	{
	case WSAEINVAL:
	  error = G_IO_CHANNEL_ERROR_INVAL;
          break;
	case WSAEWOULDBLOCK:
	  win32_channel->write_would_have_blocked = TRUE;
	  win32_channel->last_events = 0;
	  g_free (emsg);
          return G_IO_STATUS_AGAIN;
	default:
	  error = G_IO_CHANNEL_ERROR_FAILED;
          break;
	}
      g_set_error_literal (err, G_IO_CHANNEL_ERROR, error, emsg);
      g_free (emsg);

      return G_IO_STATUS_ERROR;
    }
  else
    {
      if (win32_channel->debug)
	g_print ("\n");
      *bytes_written = result;
      win32_channel->write_would_have_blocked = FALSE;

      return G_IO_STATUS_NORMAL;
    }
}

static GIOStatus
g_io_win32_sock_close (GIOChannel *channel,
		       GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  if (win32_channel->fd != -1)
    {
      if (win32_channel->debug)
	g_print ("g_io_win32_sock_close: channel=%p sock=%d\n",
		 channel, win32_channel->fd);
      
      closesocket (win32_channel->fd);
      win32_channel->fd = -1;
    }

  /* FIXME error detection? */

  return G_IO_STATUS_NORMAL;
}

static GSource *
g_io_win32_sock_create_watch (GIOChannel    *channel,
			      GIOCondition   condition)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  GSource *source = g_source_new (&g_io_watch_funcs, sizeof (GIOWin32Watch));
  GIOWin32Watch *watch = (GIOWin32Watch *)source;
  
  watch->channel = channel;
  g_io_channel_ref (channel);
  
  watch->condition = condition;

  if (win32_channel->event == 0)
    win32_channel->event = WSACreateEvent ();

  watch->pollfd.fd = (gintptr) win32_channel->event;
  watch->pollfd.events = condition;
  
  if (win32_channel->debug)
    g_print ("g_io_win32_sock_create_watch: channel=%p sock=%d event=%p condition={%s}\n",
	     channel, win32_channel->fd, (HANDLE) watch->pollfd.fd,
	     condition_to_string (watch->condition));

  g_source_add_poll (source, &watch->pollfd);

  return source;
}

GIOChannel *
g_io_channel_new_file (const gchar  *filename,
                       const gchar  *mode,
                       GError      **error)
{
  int fid, flags, pmode;
  GIOChannel *channel;

  enum { /* Cheesy hack */
    MODE_R = 1 << 0,
    MODE_W = 1 << 1,
    MODE_A = 1 << 2,
    MODE_PLUS = 1 << 3,
  } mode_num;

  g_return_val_if_fail (filename != NULL, NULL);
  g_return_val_if_fail (mode != NULL, NULL);
  g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL);

  switch (mode[0])
    {
      case 'r':
        mode_num = MODE_R;
        break;
      case 'w':
        mode_num = MODE_W;
        break;
      case 'a':
        mode_num = MODE_A;
        break;
      default:
        g_warning ("Invalid GIOFileMode %s.", mode);
        return NULL;
    }

  switch (mode[1])
    {
      case '\0':
        break;
      case '+':
        if (mode[2] == '\0')
          {
            mode_num |= MODE_PLUS;
            break;
          }
        /* Fall through */
      default:
        g_warning ("Invalid GIOFileMode %s.", mode);
        return NULL;
    }

  switch (mode_num)
    {
      case MODE_R:
        flags = O_RDONLY;
        pmode = _S_IREAD;
        break;
      case MODE_W:
        flags = O_WRONLY | O_TRUNC | O_CREAT;
        pmode = _S_IWRITE;
        break;
      case MODE_A:
        flags = O_WRONLY | O_APPEND | O_CREAT;
        pmode = _S_IWRITE;
        break;
      case MODE_R | MODE_PLUS:
        flags = O_RDWR;
        pmode = _S_IREAD | _S_IWRITE;
        break;
      case MODE_W | MODE_PLUS:
        flags = O_RDWR | O_TRUNC | O_CREAT;
        pmode = _S_IREAD | _S_IWRITE;
        break;
      case MODE_A | MODE_PLUS:
        flags = O_RDWR | O_APPEND | O_CREAT;
        pmode = _S_IREAD | _S_IWRITE;
        break;
      default:
        g_assert_not_reached ();
	abort ();
    }

  /* always open 'untranslated' */
  fid = g_open (filename, flags | _O_BINARY, pmode);

  if (g_io_win32_get_debug_flag ())
    {
      g_print ("g_io_channel_win32_new_file: open(\"%s\",", filename);
      g_win32_print_access_mode (flags|_O_BINARY);
      g_print (",%#o)=%d\n", pmode, fid);
    }

  if (fid < 0)
    {
      g_set_error_literal (error, G_FILE_ERROR,
                           g_file_error_from_errno (errno),
                           g_strerror (errno));
      return (GIOChannel *)NULL;
    }

  channel = g_io_channel_win32_new_fd (fid);

  /* XXX: move this to g_io_channel_win32_new_fd () */
  channel->close_on_unref = TRUE;
  channel->is_seekable = TRUE;

  /* g_io_channel_win32_new_fd sets is_readable and is_writeable to
   * correspond to actual readability/writeability. Set to FALSE those
   * that mode doesn't allow
   */
  switch (mode_num)
    {
      case MODE_R:
        channel->is_writeable = FALSE;
        break;
      case MODE_W:
      case MODE_A:
        channel->is_readable = FALSE;
        break;
      case MODE_R | MODE_PLUS:
      case MODE_W | MODE_PLUS:
      case MODE_A | MODE_PLUS:
        break;
      default:
        g_assert_not_reached ();
	abort ();
    }

  return channel;
}

#if !defined (_WIN64)

#undef g_io_channel_new_file

/* Binary compatibility version. Not for newly compiled code. */

GIOChannel *
g_io_channel_new_file (const gchar  *filename,
                       const gchar  *mode,
                       GError      **error)
{
  gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, error);
  GIOChannel *retval;

  if (utf8_filename == NULL)
    return NULL;

  retval = g_io_channel_new_file_utf8 (utf8_filename, mode, error);

  g_free (utf8_filename);

  return retval;
}

#endif

static GIOStatus
g_io_win32_unimpl_set_flags (GIOChannel *channel,
			     GIOFlags    flags,
			     GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  if (win32_channel->debug)
    {
      g_print ("g_io_win32_unimpl_set_flags: ");
      g_win32_print_gioflags (flags);
      g_print ("\n");
    }

  g_set_error_literal (err, G_IO_CHANNEL_ERROR,
                       G_IO_CHANNEL_ERROR_FAILED,
                       "Not implemented on Win32");

  return G_IO_STATUS_ERROR;
}

static GIOFlags
g_io_win32_fd_get_flags_internal (GIOChannel      *channel,
				  struct _stati64 *st)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
  gchar c;
  DWORD count;

  if (st->st_mode & _S_IFIFO)
    {
      channel->is_readable =
	(PeekNamedPipe ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL, NULL) != 0) || GetLastError () == ERROR_BROKEN_PIPE;
      channel->is_writeable =
	(WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
      channel->is_seekable  = FALSE;
    }
  else
    {
      channel->is_readable =
	(ReadFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
      channel->is_writeable =
	(WriteFile ((HANDLE) _get_osfhandle (win32_channel->fd), &c, 0, &count, NULL) != 0);
      channel->is_seekable = TRUE;
    }

  /* XXX: G_IO_FLAG_APPEND */
  /* XXX: G_IO_FLAG_NONBLOCK */

  return 0;
}

static GIOFlags
g_io_win32_fd_get_flags (GIOChannel *channel)
{
  struct _stati64 st;
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  g_return_val_if_fail (win32_channel != NULL, 0);
  g_return_val_if_fail (win32_channel->type == G_IO_WIN32_FILE_DESC, 0);

  if (0 == _fstati64 (win32_channel->fd, &st))
    return g_io_win32_fd_get_flags_internal (channel, &st);
  else
    return 0;
}

static GIOFlags
g_io_win32_console_get_flags_internal (GIOChannel  *channel)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *) channel;
  HANDLE handle = (HANDLE) _get_osfhandle (win32_channel->fd);
  gchar c;
  DWORD count;
  INPUT_RECORD record;

  channel->is_readable = PeekConsoleInput (handle, &record, 1, &count);
  channel->is_writeable = WriteFile (handle, &c, 0, &count, NULL);
  channel->is_seekable = FALSE;

  return 0;
}

static GIOFlags
g_io_win32_console_get_flags (GIOChannel *channel)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  g_return_val_if_fail (win32_channel != NULL, 0);
  g_return_val_if_fail (win32_channel->type == G_IO_WIN32_CONSOLE, 0);

  return g_io_win32_console_get_flags_internal (channel);
}

static GIOFlags
g_io_win32_msg_get_flags (GIOChannel *channel)
{
  return 0;
}

static GIOStatus
g_io_win32_sock_set_flags (GIOChannel *channel,
			   GIOFlags    flags,
			   GError    **err)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
  u_long arg;

  if (win32_channel->debug)
    {
      g_print ("g_io_win32_sock_set_flags: ");
      g_win32_print_gioflags (flags);
      g_print ("\n");
    }

  if (flags & G_IO_FLAG_NONBLOCK)
    {
      arg = 1;
      if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
	{
	  gchar *emsg = g_win32_error_message (WSAGetLastError ());

	  g_set_error_literal (err, G_IO_CHANNEL_ERROR,
			       G_IO_CHANNEL_ERROR_FAILED,
			       emsg);
	  g_free (emsg);

	  return G_IO_STATUS_ERROR;
	}
    }
  else
    {
      arg = 0;
      if (ioctlsocket (win32_channel->fd, FIONBIO, &arg) == SOCKET_ERROR)
	{
	  gchar *emsg = g_win32_error_message (WSAGetLastError ());

	  g_set_error_literal (err, G_IO_CHANNEL_ERROR,
			       G_IO_CHANNEL_ERROR_FAILED,
			       emsg);
	  g_free (emsg);

	  return G_IO_STATUS_ERROR;
	}
    }

  return G_IO_STATUS_NORMAL;
}

static GIOFlags
g_io_win32_sock_get_flags (GIOChannel *channel)
{
  /* Could we do something here? */
  return 0;
}

static GIOFuncs win32_channel_msg_funcs = {
  g_io_win32_msg_read,
  g_io_win32_msg_write,
  NULL,
  g_io_win32_msg_close,
  g_io_win32_msg_create_watch,
  g_io_win32_free,
  g_io_win32_unimpl_set_flags,
  g_io_win32_msg_get_flags,
};

static GIOFuncs win32_channel_fd_funcs = {
  g_io_win32_fd_and_console_read,
  g_io_win32_fd_and_console_write,
  g_io_win32_fd_seek,
  g_io_win32_fd_close,
  g_io_win32_fd_create_watch,
  g_io_win32_free,
  g_io_win32_unimpl_set_flags,
  g_io_win32_fd_get_flags,
};

static GIOFuncs win32_channel_console_funcs = {
  g_io_win32_fd_and_console_read,
  g_io_win32_fd_and_console_write,
  NULL,
  g_io_win32_console_close,
  g_io_win32_console_create_watch,
  g_io_win32_free,
  g_io_win32_unimpl_set_flags,
  g_io_win32_console_get_flags,
};

static GIOFuncs win32_channel_sock_funcs = {
  g_io_win32_sock_read,
  g_io_win32_sock_write,
  NULL,
  g_io_win32_sock_close,
  g_io_win32_sock_create_watch,
  g_io_win32_free,
  g_io_win32_sock_set_flags,
  g_io_win32_sock_get_flags,
};

/**
 * g_io_channel_win32_new_messages:
 * @hwnd: a window handle.
 * @Returns: a new #GIOChannel.
 *
 * Creates a new #GIOChannel given a window handle on Windows.
 *
 * This function creates a #GIOChannel that can be used to poll for
 * Windows messages for the window in question.
 **/
GIOChannel *
#if GLIB_SIZEOF_VOID_P == 8
g_io_channel_win32_new_messages (gsize hwnd)
#else
g_io_channel_win32_new_messages (guint hwnd)
#endif
{
  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
  GIOChannel *channel = (GIOChannel *)win32_channel;

  g_io_channel_init (channel);
  g_io_channel_win32_init (win32_channel);
  if (win32_channel->debug)
    g_print ("g_io_channel_win32_new_messages: channel=%p hwnd=%p\n",
	     channel, (HWND) hwnd);
  channel->funcs = &win32_channel_msg_funcs;
  win32_channel->type = G_IO_WIN32_WINDOWS_MESSAGES;
  win32_channel->hwnd = (HWND) hwnd;

  /* XXX: check this. */
  channel->is_readable = IsWindow (win32_channel->hwnd);
  channel->is_writeable = IsWindow (win32_channel->hwnd);

  channel->is_seekable = FALSE;

  return channel;
}

static GIOChannel *
g_io_channel_win32_new_fd_internal (gint             fd,
				    struct _stati64 *st)
{
  GIOWin32Channel *win32_channel;
  GIOChannel *channel;

  win32_channel = g_new (GIOWin32Channel, 1);
  channel = (GIOChannel *)win32_channel;

  g_io_channel_init (channel);
  g_io_channel_win32_init (win32_channel);

  win32_channel->fd = fd;

  if (win32_channel->debug)
    g_print ("g_io_channel_win32_new_fd: channel=%p fd=%u\n",
	     channel, fd);

  if (st->st_mode & _S_IFCHR) /* console */
    {
      channel->funcs = &win32_channel_console_funcs;
      win32_channel->type = G_IO_WIN32_CONSOLE;
      g_io_win32_console_get_flags_internal (channel);
    }
  else
    {
      channel->funcs = &win32_channel_fd_funcs;
      win32_channel->type = G_IO_WIN32_FILE_DESC;
      g_io_win32_fd_get_flags_internal (channel, st);
    }
  
  return channel;
}

/**
 * g_io_channel_win32_new_fd:
 * @fd: a C library file descriptor.
 * @Returns: a new #GIOChannel.
 *
 * Creates a new #GIOChannel given a file descriptor on Windows. This
 * works for file descriptors from the C runtime.
 *
 * This function works for file descriptors as returned by the open(),
 * creat(), pipe() and fileno() calls in the Microsoft C runtime. In
 * order to meaningfully use this function your code should use the
 * same C runtime as GLib uses, which is msvcrt.dll. Note that in
 * current Microsoft compilers it is near impossible to convince it to
 * build code that would use msvcrt.dll. The last Microsoft compiler
 * version that supported using msvcrt.dll as the C runtime was version
 * 6. The GNU compiler and toolchain for Windows, also known as Mingw,
 * fully supports msvcrt.dll.
 *
 * If you have created a #GIOChannel for a file descriptor and started
 * watching (polling) it, you shouldn't call read() on the file
 * descriptor. This is because adding polling for a file descriptor is
 * implemented in GLib on Windows by starting a thread that sits
 * blocked in a read() from the file descriptor most of the time. All
 * reads from the file descriptor should be done by this internal GLib
 * thread. Your code should call only g_io_channel_read().
 *
 * This function is available only in GLib on Windows.
 **/
GIOChannel *
g_io_channel_win32_new_fd (gint fd)
{
  struct _stati64 st;

  if (_fstati64 (fd, &st) == -1)
    {
      g_warning ("g_io_channel_win32_new_fd: %d isn't an open file descriptor in the C library GLib uses.", fd);
      return NULL;
    }

  return g_io_channel_win32_new_fd_internal (fd, &st);
}

gint
g_io_channel_win32_get_fd (GIOChannel *channel)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  return win32_channel->fd;
}

/**
 * g_io_channel_win32_new_socket:
 * @socket: a Winsock socket
 * @Returns: a new #GIOChannel
 *
 * Creates a new #GIOChannel given a socket on Windows.
 *
 * This function works for sockets created by Winsock. It's available
 * only in GLib on Windows.
 *
 * Polling a #GSource created to watch a channel for a socket puts the
 * socket in non-blocking mode. This is a side-effect of the
 * implementation and unavoidable.
 **/
GIOChannel *
g_io_channel_win32_new_socket (int socket)
{
  GIOWin32Channel *win32_channel = g_new (GIOWin32Channel, 1);
  GIOChannel *channel = (GIOChannel *)win32_channel;

  g_io_channel_init (channel);
  g_io_channel_win32_init (win32_channel);
  if (win32_channel->debug)
    g_print ("g_io_channel_win32_new_socket: channel=%p sock=%d\n",
	     channel, socket);
  channel->funcs = &win32_channel_sock_funcs;
  win32_channel->type = G_IO_WIN32_SOCKET;
  win32_channel->fd = socket;

  channel->is_readable = TRUE;
  channel->is_writeable = TRUE;
  channel->is_seekable = FALSE;

  return channel;
}

GIOChannel *
g_io_channel_unix_new (gint fd)
{
  gboolean is_fd, is_socket;
  struct _stati64 st;
  int optval, optlen;

  is_fd = (_fstati64 (fd, &st) == 0);

  optlen = sizeof (optval);
  is_socket = (getsockopt (fd, SOL_SOCKET, SO_TYPE, (char *) &optval, &optlen) != SOCKET_ERROR);

  if (is_fd && is_socket)
    g_warning ("g_io_channel_unix_new: %d is both a file descriptor and a socket. File descriptor interpretation assumed. To avoid ambiguity, call either g_io_channel_win32_new_fd() or g_io_channel_win32_new_socket() instead.", fd);

  if (is_fd)
    return g_io_channel_win32_new_fd_internal (fd, &st);

  if (is_socket)
    return g_io_channel_win32_new_socket(fd);

  g_warning ("g_io_channel_unix_new: %d is neither a file descriptor or a socket.", fd);

  return NULL;
}

gint
g_io_channel_unix_get_fd (GIOChannel *channel)
{
  return g_io_channel_win32_get_fd (channel);
}

void
g_io_channel_win32_set_debug (GIOChannel *channel,
			      gboolean    flag)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  win32_channel->debug = flag;
}

gint
g_io_channel_win32_poll (GPollFD *fds,
			 gint     n_fds,
			 gint     timeout)
{
  g_return_val_if_fail (n_fds >= 0, 0);

  return g_poll (fds, n_fds, timeout);
}

void
g_io_channel_win32_make_pollfd (GIOChannel   *channel,
				GIOCondition  condition,
				GPollFD      *fd)
{
  GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;

  switch (win32_channel->type)
    {
    case G_IO_WIN32_FILE_DESC:
      if (win32_channel->data_avail_event == NULL)
	create_events (win32_channel);

      fd->fd = (gintptr) win32_channel->data_avail_event;

      if (win32_channel->thread_id == 0)
	{
	  /* Is it meaningful for a file descriptor to be polled for
	   * both IN and OUT? For what kind of file descriptor would
	   * that be? Doesn't seem to make sense, in practise the file
	   * descriptors handled here are always read or write ends of
	   * pipes surely, and thus unidirectional.
	   */
	  if (condition & G_IO_IN)
	    create_thread (win32_channel, condition, read_thread);
	  else if (condition & G_IO_OUT)
	    create_thread (win32_channel, condition, write_thread);
	}
      break;

    case G_IO_WIN32_CONSOLE:
      fd->fd = _get_osfhandle (win32_channel->fd);
      break;

    case G_IO_WIN32_SOCKET:
      fd->fd = (gintptr) WSACreateEvent ();
      break;
      
    case G_IO_WIN32_WINDOWS_MESSAGES:
      fd->fd = G_WIN32_MSG_HANDLE;
      break;

    default:
      g_assert_not_reached ();
      abort ();
    }
  
  fd->events = condition;
}

#ifndef _WIN64

/* Binary compatibility */
GIOChannel *
g_io_channel_win32_new_stream_socket (int socket)
{
  return g_io_channel_win32_new_socket (socket);
}

#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Argentina Argentina
System developer from Argentina.

Programmed in VB 5,6,.NET, C#, Java, PL-SQL, Transac-SQL, C, C++ and even some "calculator" language.

Love to build small, useful applications.
Usually building big and complicated apps based on solid, reliable components.

Hobbies: reading, photography, chess, paddle, running.

Comments and Discussions