The "rfb_win32" library merged with VNC 4.1.1 code.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/branches/merge-with-vnc-4.1.1@523 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/rfb_win32/AboutDialog.cxx b/rfb_win32/AboutDialog.cxx
index efb15c0..030be1b 100644
--- a/rfb_win32/AboutDialog.cxx
+++ b/rfb_win32/AboutDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -43,7 +43,7 @@
   // Get our executable's version info
   FileVersionInfo verInfo;
 
-  SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("FileVersion")));
+  SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("ProductVersion")));
   SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright")));
-  SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("FileDescription")));
+  SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("ProductName")));
 }
diff --git a/rfb_win32/AboutDialog.h b/rfb_win32/AboutDialog.h
index cb1713d..0dd9d49 100644
--- a/rfb_win32/AboutDialog.h
+++ b/rfb_win32/AboutDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/BitmapInfo.h b/rfb_win32/BitmapInfo.h
new file mode 100644
index 0000000..6a6f0d2
--- /dev/null
+++ b/rfb_win32/BitmapInfo.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_BITMAP_INFO_H__
+#define __RFB_WIN32_BITMAP_INFO_H__
+
+#include <windows.h>
+#include <rdr/types.h>
+
+namespace rfb {
+  namespace win32 {
+
+    struct BitmapInfo {
+      BITMAPINFOHEADER bmiHeader;
+      union {
+        struct {
+          DWORD red;
+          DWORD green;
+          DWORD blue;
+        } mask;
+        RGBQUAD color[256];
+      };
+    };
+
+    inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+      for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+        (*max) = (rdr::U16)mask;
+    }
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/CKeyboard.cxx b/rfb_win32/CKeyboard.cxx
index ad852a0..28aceab 100644
--- a/rfb_win32/CKeyboard.cxx
+++ b/rfb_win32/CKeyboard.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,7 +24,6 @@
 #include <rfb/keysymdef.h>
 
 #include <rfb_win32/CKeyboard.h>
-#include <rfb/CMsgWriter.h>
 #include <rfb/LogWriter.h>
 #include <rfb_win32/OSVersion.h>
 #include "keymap.h"
@@ -67,7 +66,7 @@
 
 class ModifierKeyReleaser {
 public:
-  ModifierKeyReleaser(CMsgWriter* writer_, int vkCode, bool extended)
+  ModifierKeyReleaser(InputHandler* writer_, int vkCode, bool extended)
     : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)),
       keysym(0)
   {}
@@ -76,17 +75,17 @@
       keysym = (*downKeysym)[extendedVkey];
       vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x",
                  extendedVkey, keysym);
-      writer->writeKeyEvent(keysym, false);
+      writer->keyEvent(keysym, false);
     }
   }
   ~ModifierKeyReleaser() {
     if (keysym) {
       vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x",
                  extendedVkey, keysym);
-      writer->writeKeyEvent(keysym, true);
+      writer->keyEvent(keysym, true);
     }
   }
-  CMsgWriter* writer;
+  InputHandler* writer;
   int extendedVkey;
   rdr::U32 keysym;
 };
@@ -96,7 +95,7 @@
 #define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \
                                 ((c) >= 160 && (c) <= 255))
 
-void win32::CKeyboard::keyEvent(CMsgWriter* writer, rdr::U8 vkey,
+void win32::CKeyboard::keyEvent(InputHandler* writer, rdr::U8 vkey,
                                 rdr::U32 flags, bool down)
 {
   bool extended = (flags & 0x1000000);
@@ -212,11 +211,11 @@
 
 // releaseAllKeys() - write key release events to the server for all keys
 // that are currently regarded as being down.
-void win32::CKeyboard::releaseAllKeys(CMsgWriter* writer) {
+void win32::CKeyboard::releaseAllKeys(InputHandler* writer) {
   std::map<int,rdr::U32>::iterator i, next_i;
   for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) {
     next_i = i; next_i++;
-    writer->writeKeyEvent((*i).second, false);
+    writer->keyEvent((*i).second, false);
     downKeysym.erase(i);
   }
 }
@@ -225,12 +224,12 @@
 // actually sent a key down event for the given key.  The key up event always
 // contains the same keysym we used in the key down event, regardless of what
 // it would look up as using the current keyboard state.
-void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey)
+void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey)
 {
   if (downKeysym.find(extendedVkey) != downKeysym.end()) {
     vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
                extendedVkey, downKeysym[extendedVkey]);
-    writer->writeKeyEvent(downKeysym[extendedVkey], false);
+    writer->keyEvent(downKeysym[extendedVkey], false);
     downKeysym.erase(extendedVkey);
   }
 }
@@ -242,18 +241,18 @@
 // first.  This can happen in two cases: (a) when a single key press results in
 // more than one character, and (b) when shift is released while another key is
 // autorepeating.
-void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey,
                                 rdr::U32 keysym)
 {
   if (downKeysym.find(extendedVkey) != downKeysym.end()) {
     if (downKeysym[extendedVkey] != keysym) {
       vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
                  extendedVkey, downKeysym[extendedVkey]);
-      writer->writeKeyEvent(downKeysym[extendedVkey], false);
+      writer->keyEvent(downKeysym[extendedVkey], false);
     }
   }
   vlog.debug("press   extendedVkey 0x%x, keysym 0x%x",
              extendedVkey, keysym);
-  writer->writeKeyEvent(keysym, true);
+  writer->keyEvent(keysym, true);
   downKeysym[extendedVkey] = keysym;
 }
diff --git a/rfb_win32/CKeyboard.h b/rfb_win32/CKeyboard.h
index 10346ff..666ebce 100644
--- a/rfb_win32/CKeyboard.h
+++ b/rfb_win32/CKeyboard.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -23,25 +23,23 @@
 #ifndef __RFB_WIN32_CKEYBOARD_H__
 #define __RFB_WIN32_CKEYBOARD_H__
 
-#include <rdr/types.h>
+#include <rfb/InputHandler.h>
 #include <map>
 
 namespace rfb {
 
-  class CMsgWriter;
-
   namespace win32 {
 
     class CKeyboard {
     public:
-      void keyEvent(CMsgWriter* writer, rdr::U8 vkey, rdr::U32 flags,
+      void keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags,
                     bool down);
-      void releaseAllKeys(CMsgWriter* writer);
+      void releaseAllKeys(InputHandler* writer);
       const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;};
       bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();}
     private:
-      void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey);
-      void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+      void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey);
+      void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey,
                                       rdr::U32 keysym);
       std::map<int,rdr::U32> downKeysym;
     };
diff --git a/rfb_win32/CPointer.cxx b/rfb_win32/CPointer.cxx
index 1cab662..3d0d934 100644
--- a/rfb_win32/CPointer.cxx
+++ b/rfb_win32/CPointer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -16,9 +16,7 @@
  * USA.
  */
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb/LogWriter.h>
 #include <rfb_win32/CPointer.h>
 
@@ -37,21 +35,21 @@
 }
 
 
-void CPointer::pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+void CPointer::pointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
   //
   // - Duplicate Event Filtering
   //
 
   bool maskChanged = buttonMask != currButtonMask;
-  bool posChanged = !Point(x, y).equals(currPos);
+  bool posChanged = !pos.equals(currPos);
   if (!(posChanged || maskChanged))
     return;
 
   // Pass on the event to the event-interval handler
-  threePointerEvent(writer, x, y, buttonMask);
+  threePointerEvent(writer, pos, buttonMask);
 
   // Save the position and mask
-  currPos = Point(x, y);
+  currPos = pos;
   currButtonMask = buttonMask;
 }
 
@@ -66,7 +64,7 @@
   return buttonMask;
 }
 
-void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+void CPointer::threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
   //
   // - 3-Button Mouse Emulation
   //
@@ -84,7 +82,7 @@
         //   expires then we know we should actually send this event
         vlog.debug("emulate3: start timer");
         threeTimer.start(100);
-        threePos = Point(x, y);
+        threePos = pos;
         threeMask = buttonMask;
         return;
 
@@ -94,7 +92,7 @@
         vlog.debug("emulate3: stop timer (state)");
         threeTimer.stop();
         if (threeEmulating == ((buttonMask & 5) == 5))
-          intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+          intervalPointerEvent(writer, threePos, threeMask);
         else
           threeEmulating = ((buttonMask & 5) == 5);
       }
@@ -104,11 +102,11 @@
       if (threeTimer.isActive()) {
         // - We are timing for an emulation event
 
-        if (abs(threePos.x - x) <= 4 || abs(threePos.y - y) <= 4) {
+        if (abs(threePos.x - pos.x) <= 4 || abs(threePos.y - pos.y) <= 4) {
           //   If the mouse has moved too far since the button-change event then flush
           vlog.debug("emulate3: stop timer (moved)");
           threeTimer.stop();
-          intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+          intervalPointerEvent(writer, threePos, threeMask);
 
         } else {
           //   Otherwise, we ignore the new event
@@ -129,14 +127,14 @@
   }
 
   // - Let the event pass through to the next stage of processing
-  intervalPointerEvent(writer, x, y, buttonMask);
+  intervalPointerEvent(writer, pos, buttonMask);
 }
 
-void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+void CPointer::intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
   //
   // - Pointer Event Interval
   //
-  vlog.write(101, "ptrEvent: %d,%d (%lx)", x, y, buttonMask);
+  vlog.write(101, "ptrEvent: %d,%d (%lx)", pos.x, pos.y, buttonMask);
 
   // Send the event immediately if we haven't sent one for a while
   bool sendNow = !intervalTimer.isActive();
@@ -145,14 +143,14 @@
     // If the buttons have changed then flush queued events and send now
     sendNow = true;
     if (intervalQueued)
-      writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+      writer->pointerEvent(intervalPos, intervalMask);
     intervalQueued = false;
   }
 
   if (!sendNow) {
     // If we're not sending now then just queue the event
     intervalQueued = true;
-    intervalPos = Point(x, y);
+    intervalPos = pos;
     intervalMask = buttonMask;
   } else {
     // Start the interval timer if required, and send the event
@@ -160,15 +158,15 @@
     intervalMask = buttonMask;
     if (pointerEventInterval)
       intervalTimer.start(pointerEventInterval);
-    writer->writePointerEvent(x, y, buttonMask);
+    writer->pointerEvent(pos, buttonMask);
   }
 }
 
-void CPointer::handleTimer(CMsgWriter* writer, int timerId) {
+void CPointer::handleTimer(InputHandler* writer, int timerId) {
   if (timerId == intervalTimer.getId()) {
     // Pointer interval has expired - send any queued events
     if (intervalQueued) {
-      writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+      writer->pointerEvent(intervalPos, intervalMask);
       intervalQueued = false;
     } else {
       intervalTimer.stop();
@@ -183,6 +181,6 @@
     if (threeEmulating)
       threeMask = emulate3Mask(threeMask);
 
-    intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+    intervalPointerEvent(writer, threePos, threeMask);
   }
 }
diff --git a/rfb_win32/CPointer.h b/rfb_win32/CPointer.h
index f111de7..b591601 100644
--- a/rfb_win32/CPointer.h
+++ b/rfb_win32/CPointer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,14 +25,12 @@
 
 #include <rdr/Exception.h>
 #include <rfb/Configuration.h>
-#include <rfb/CMsgWriter.h>
+#include <rfb/InputHandler.h>
 #include <rfb/Rect.h>
 #include <rfb_win32/IntervalTimer.h>
 
 namespace rfb {
 
-  class CMsgWriter;
-
   namespace win32 {
 
     class CPointer {
@@ -40,8 +38,8 @@
       CPointer();
       ~CPointer();
 
-      void pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
-      void handleTimer(CMsgWriter* writer, int timerId);
+      void pointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
+      void handleTimer(InputHandler* writer, int timerId);
 
       void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);}
       void setIntervalTimerId(int id) {intervalTimer.setId(id);}
@@ -56,13 +54,13 @@
       bool emulate3;
       int pointerEventInterval;
 
-      void intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      void intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
       IntervalTimer intervalTimer;
       bool intervalQueued;
       Point intervalPos;
       int intervalMask;
 
-      void threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      void threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
       IntervalTimer threeTimer;
       Point threePos;
       int threeMask;
diff --git a/rfb_win32/CleanDesktop.cxx b/rfb_win32/CleanDesktop.cxx
index 9fb8347..39cca11 100644
--- a/rfb_win32/CleanDesktop.cxx
+++ b/rfb_win32/CleanDesktop.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,16 +18,22 @@
 
 // -=- CleanDesktop.cxx
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <wininet.h>
 #include <shlobj.h>
-
 #include <rfb_win32/CleanDesktop.h>
 #include <rfb_win32/CurrentUser.h>
 #include <rfb_win32/Registry.h>
+#include <rfb_win32/OSVersion.h>
 #include <rfb/LogWriter.h>
 #include <rdr/Exception.h>
+#include <set>
+
+#ifdef SPI_GETUIEFFECTS
+#define RFB_HAVE_SPI_UIEFFECTS
+#else
+#pragma message("  NOTE: Not building Get/Set UI Effects support.")
+#endif
 
 using namespace rfb;
 using namespace rfb::win32;
@@ -47,37 +53,93 @@
     if (handle)
       handle->Release();
   }
+
+  // enableItem
+  //   enables or disables the Nth Active Desktop item
+  bool enableItem(int i, bool enable_) {
+    COMPONENT item;
+    memset(&item, 0, sizeof(item));
+    item.dwSize = sizeof(item);
+
+    HRESULT hr = handle->GetDesktopItem(i, &item, 0);
+    if (hr != S_OK) {
+      vlog.error("unable to GetDesktopItem %d: %ld", i, hr);
+      return false;
+    }
+    item.fChecked = enable_;
+    vlog.debug("%sbling %d: \"%s\"", enable_ ? "ena" : "disa", i, (const char*)CStr(item.wszFriendlyName));
+
+    hr = handle->ModifyDesktopItem(&item, COMP_ELEM_CHECKED);
+    return hr == S_OK;
+  }
+  
+  // enable
+  //   Attempts to enable/disable Active Desktop, returns true if the setting changed,
+  //   false otherwise.
+  //   If Active Desktop *can* be enabled/disabled then that is done.
+  //   If Active Desktop is always on (XP/2K3) then instead the individual items are
+  //   disabled, and true is returned to indicate that they need to be restored later.
   bool enable(bool enable_) {
-    // - Get the current Active Desktop options
+    bool modifyComponents = false;
+
+    vlog.debug("ActiveDesktop::enable");
+
+    // - Firstly, try to disable Active Desktop entirely
+    HRESULT hr;
     COMPONENTSOPT adOptions;
     memset(&adOptions, 0, sizeof(adOptions));
     adOptions.dwSize = sizeof(adOptions);
 
-    HRESULT result = handle->GetDesktopItemOptions(&adOptions, 0);
-    if (result != S_OK) {
-      vlog.error("failed to get Active Desktop options: %d", result);
+    // Attempt to actually disable/enable AD
+    hr = handle->GetDesktopItemOptions(&adOptions, 0);
+    if (hr == S_OK) {
+      // If Active Desktop is already in the desired state then return false (no change)
+      // NB: If AD is enabled AND restoreItems is set then we regard it as disabled...
+      if (((adOptions.fActiveDesktop==0) && restoreItems.empty()) == (enable_==false))
+        return false;
+      adOptions.fActiveDesktop = enable_;
+      hr = handle->SetDesktopItemOptions(&adOptions, 0);
+    }
+    // Apply the change, then test whether it actually took effect
+    if (hr == S_OK)
+      hr = handle->ApplyChanges(AD_APPLY_REFRESH);
+    if (hr == S_OK)
+      hr = handle->GetDesktopItemOptions(&adOptions, 0);
+    if (hr == S_OK)
+      modifyComponents = (adOptions.fActiveDesktop==0) != (enable_==false);
+    if (hr != S_OK) {
+      vlog.error("failed to get/set Active Desktop options: %ld", hr);
       return false;
     }
 
-    // - If Active Desktop is active, disable it
-    if ((adOptions.fActiveDesktop==0) != (enable_==0)) {
-      if (enable_)
-        vlog.debug("enabling Active Desktop");
-      else
-        vlog.debug("disabling Active Desktop");
-
-      adOptions.fActiveDesktop = enable_;
-      result = handle->SetDesktopItemOptions(&adOptions, 0);
-      if (result != S_OK) {
-        vlog.error("failed to disable ActiveDesktop: %d", result);
+    if (enable_) {
+      // - We are re-enabling Active Desktop.  If there are components in restoreItems
+      //   then restore them!
+      std::set<int>::const_iterator i;
+      for (i=restoreItems.begin(); i!=restoreItems.end(); i++) {
+        enableItem(*i, true);
+      }
+      restoreItems.clear();
+    } else if (modifyComponents) {
+      // - Disable all currently enabled items, and add the disabled ones to restoreItems
+      int itemCount = 0;
+      hr = handle->GetDesktopItemCount(&itemCount, 0);
+      if (hr != S_OK) {
+        vlog.error("failed to get desktop item count: %ld", hr);
         return false;
       }
-      handle->ApplyChanges(AD_APPLY_REFRESH);
-      return true;
+      for (unsigned int i=0; i<itemCount; i++) {
+        if (enableItem(i, false))
+          restoreItems.insert(i);
+      }
     }
-    return false;
+
+    // - Apply whatever changes we have made, but DON'T save them!
+    hr = handle->ApplyChanges(AD_APPLY_REFRESH);
+    return hr == S_OK;
   }
   IActiveDesktop* handle;
+  std::set<int> restoreItems;
 };
 
 
@@ -91,7 +153,8 @@
 }
 
 
-CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), restoreEffects(false) {
+CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false),
+                               restorePattern(false), restoreEffects(false) {
   CoInitialize(0);
 }
 
@@ -194,13 +257,13 @@
 
 
 void CleanDesktop::disableEffects() {
-#if (WINVER >= 0x500)
   try {
     ImpersonateCurrentUser icu;
 
     vlog.debug("disable desktop effects");
 
     SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE);
+#ifdef RFB_HAVE_SPI_UIEFFECTS
     if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) {
       SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0);
       SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0);
@@ -214,21 +277,23 @@
       SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE);
     } else {
       SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE);
+
+      // We *always* restore UI effects overall, since there is no Windows GUI to do it
+      uiEffects = TRUE;
     }
+#else
+    vlog.debug("  not supported");
+#endif
     restoreEffects = true;
 
   } catch (rdr::Exception& e) {
     vlog.info(e.str());
   }
-#else
-      vlog.info("disableffects not implemented");
-#endif
 }
 
 void CleanDesktop::enableEffects() {
   try {
     if (restoreEffects) {
-#if (WINVER >= 0x500)
       ImpersonateCurrentUser icu;
 
       vlog.debug("restore desktop effects");
@@ -236,6 +301,7 @@
       RegKey desktopCfg;
       desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
       SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE);
+#ifdef RFB_HAVE_SPI_UIEFFECTS
       if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) {
         SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE);
         SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE);
@@ -245,7 +311,7 @@
       }
       restoreEffects = false;
 #else
-      vlog.info("enableEffects not implemented");
+      vlog.info("  not supported");
 #endif
     }
 
diff --git a/rfb_win32/CleanDesktop.h b/rfb_win32/CleanDesktop.h
index 73f4153..22e246f 100644
--- a/rfb_win32/CleanDesktop.h
+++ b/rfb_win32/CleanDesktop.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/Clipboard.cxx b/rfb_win32/Clipboard.cxx
index 867f885..a4c43f0 100644
--- a/rfb_win32/Clipboard.cxx
+++ b/rfb_win32/Clipboard.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -61,15 +61,16 @@
 
 
 //
-// -=- ASCII filter (in-place)
+// -=- ISO-8859-1 (Latin 1) filter (in-place)
 //
 
 void
-removeNonAsciiChars(char* text) {
+removeNonISOLatin1Chars(char* text) {
   int len = strlen(text);
   int i=0, j=0;
   for (; i<len; i++) {
-    if ((text[i] >= 1) && (text[i] <= 127))
+    if (((text[i] >= 1) && (text[i] <= 127)) ||
+        ((text[i] >= 160) && (text[i] <= 255)))
       text[j++] = text[i];
   }
   text[j] = 0;
@@ -126,7 +127,7 @@
               } else {
                 CharArray unix_text;
                 unix_text.buf = dos2unix(clipdata);
-                // removeNonAsciiChars(unix_text.buf);
+                removeNonISOLatin1Chars(unix_text.buf);
                 notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf));
               }
             } else {
@@ -162,7 +163,7 @@
     // - Pre-process the supplied clipboard text into DOS format
     CharArray dos_text;
     dos_text.buf = unix2dos(text);
-    // removeNonAsciiChars(dos_text.buf); 
+    removeNonISOLatin1Chars(dos_text.buf);
     int dos_text_len = strlen(dos_text.buf);
 
     // - Allocate global memory for the data
diff --git a/rfb_win32/Clipboard.h b/rfb_win32/Clipboard.h
index 57297e1..b79768f 100644
--- a/rfb_win32/Clipboard.h
+++ b/rfb_win32/Clipboard.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/CompatibleBitmap.h b/rfb_win32/CompatibleBitmap.h
new file mode 100644
index 0000000..4beed8d
--- /dev/null
+++ b/rfb_win32/CompatibleBitmap.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_COMPAT_BITMAP_H__
+#define __RFB_WIN32_COMPAT_BITMAP_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class CompatibleBitmap {
+    public:
+      CompatibleBitmap(HDC hdc, int width, int height) {
+        hbmp = CreateCompatibleBitmap(hdc, width, height);
+        if (!hbmp)
+          throw rdr::SystemException("CreateCompatibleBitmap() failed", GetLastError());
+      }
+      virtual ~CompatibleBitmap() {
+        if (hbmp) DeleteObject(hbmp);
+      }
+      operator HBITMAP() const {return hbmp;}
+    protected:
+      HBITMAP hbmp;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/ComputerName.h b/rfb_win32/ComputerName.h
new file mode 100644
index 0000000..110caa5
--- /dev/null
+++ b/rfb_win32/ComputerName.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_COMPUTERNAME_H__
+#define __RFB_WIN32_COMPUTERNAME_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+  namespace win32 {
+
+    // Get the computer name
+    struct ComputerName : TCharArray {
+      ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
+        ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
+        if (!GetComputerName(buf, &namelength))
+          _tcscpy(buf, _T(""));
+      }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx
index 1a24485..7562d29 100644
--- a/rfb_win32/CurrentUser.cxx
+++ b/rfb_win32/CurrentUser.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,48 +18,98 @@
 
 // -=- Currentuser.cxx
 
-#include <windows.h>
-#include <lmcons.h>
+#include <stdlib.h>
+#include <rfb/LogWriter.h>
 #include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb_win32/Service.h>
 #include <rfb_win32/OSVersion.h>
+#include <lmcons.h>
 
 using namespace rfb;
 using namespace win32;
 
+static LogWriter vlog("CurrentUser");
 
-CurrentUserToken::CurrentUserToken() : isValid_(false) {
-  // - If the OS doesn't support security, ignore it
-  if (!isServiceProcess()) {
-    // - Running in User-Mode - just get our current token
-    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
-      DWORD err = GetLastError();
-      if (err != ERROR_CALL_NOT_IMPLEMENTED)
-       throw rdr::SystemException("OpenProcessToken failed", GetLastError());
+
+const TCHAR* shellIconClass = _T("Shell_TrayWnd");
+
+BOOL CALLBACK enumWindows(HWND hwnd, LPARAM lParam) {
+  TCHAR className[16];
+  if (GetClassName(hwnd, className, sizeof(className)) &&
+      (_tcscmp(className, shellIconClass) == 0)) {
+    vlog.debug("located tray icon window (%s)", (const char*)CStr(className));
+    DWORD processId = 0;
+    GetWindowThreadProcessId(hwnd, &processId);
+    if (!processId)
+      return TRUE;
+    Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+    if (!process.h)
+      return TRUE;
+    if (!OpenProcessToken(process, MAXIMUM_ALLOWED, (HANDLE*)lParam))
+      return TRUE;
+    vlog.debug("obtained user token");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+BOOL CALLBACK enumDesktops(LPTSTR lpszDesktop, LPARAM lParam) {
+  HDESK desktop = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);
+  vlog.debug("opening \"%s\"", lpszDesktop);
+  if (!desktop) {
+    vlog.info("desktop \"%s\" inaccessible", (const char*)CStr(lpszDesktop));
+    return TRUE;
+  }
+  BOOL result = EnumDesktopWindows(desktop, enumWindows, lParam);
+  if (!CloseDesktop(desktop))
+    vlog.info("unable to close desktop: %ld", GetLastError());
+  return result;
+}
+
+
+CurrentUserToken::CurrentUserToken() : isSafe_(false) {
+  if (isServiceProcess()) {
+    // If the platform is Windows 95/98/Me then we must fake the token's presence
+    if (osVersion.isPlatformWindows) {
+      try {
+        UserName un;
+        h = INVALID_HANDLE_VALUE;
+      } catch (rdr::SystemException& e) {
+        if (e.err != ERROR_NOT_LOGGED_ON)
+          throw;
+        if (FindWindow(shellIconClass, 0))
+          h = INVALID_HANDLE_VALUE;
+      }
+      isSafe_ = (h != 0);
+      return;
     }
-    isValid_ = true;
-  } else {
-    // - Under XP/2003 and above, we can just ask the operating system
+
+    // Try to get the user token using the Terminal Services APIs
+    //   NB: This will only work under XP/2003 and later
     typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
     DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
     if (_WTSQueryUserToken.isValid()) {
       (*_WTSQueryUserToken)(-1, &h);
-      isValid_ = true;
-    } else {
-      // - Under NT/2K we have to resort to a nasty hack...
-      HWND tray = FindWindow(_T("Shell_TrayWnd"), 0);
-      if (!tray)
-        return;
-      DWORD processId = 0;
-      GetWindowThreadProcessId(tray, &processId);
-      if (!processId)
-        return;
-      Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
-      if (!process.h)
-        return;
-      OpenProcessToken(process, MAXIMUM_ALLOWED, &h);
-      isValid_ = true;
+      isSafe_ = true;
+      return;
     }
+
+    // Try to find the Shell Tray Icon window and take its token
+    //   NB: This will only work under NT/2K (and later, but they're dealt with above)
+    //   NB: If the shell is not running then this will return an Unsafe Null token.
+    EnumDesktops(GetProcessWindowStation(), enumDesktops, (LONG)&h);
+    isSafe_ = (h != 0);
+  } else {
+    // Try to open the security token for the User-Mode process
+    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+      DWORD err = GetLastError();
+      if (err != ERROR_CALL_NOT_IMPLEMENTED)
+        throw rdr::SystemException("OpenProcessToken failed", err);
+      // Under Windows 95/98/Me, we fake the handle value...
+      h = INVALID_HANDLE_VALUE;
+    }
+    isSafe_ = true;
   }
 }
 
@@ -68,8 +118,8 @@
   RegCloseKey(HKEY_CURRENT_USER);
   if (!isServiceProcess())
     return;
-  if (!token.isValid())
-    throw rdr::Exception("CurrentUserToken is not valid");
+  if (!token.canImpersonate())
+    throw rdr::Exception("Cannot impersonate unsafe or null token");
   if (!ImpersonateLoggedOnUser(token)) {
     DWORD err = GetLastError();
     if (err != ERROR_CALL_NOT_IMPLEMENTED)
@@ -83,6 +133,7 @@
     if (err != ERROR_CALL_NOT_IMPLEMENTED)
       exit(err);
   }
+  RegCloseKey(HKEY_CURRENT_USER);
 }
 
 
@@ -91,3 +142,11 @@
   if (!GetUserName(buf, &len))
     throw rdr::SystemException("GetUserName failed", GetLastError());
 }
+
+
+UserSID::UserSID() {
+  CurrentUserToken token;
+  if (!token.canImpersonate())
+    return;
+  setSID(Sid::FromToken(token.h));
+}
diff --git a/rfb_win32/CurrentUser.h b/rfb_win32/CurrentUser.h
index 469946b..794f27c 100644
--- a/rfb_win32/CurrentUser.h
+++ b/rfb_win32/CurrentUser.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -26,29 +26,41 @@
 #ifndef __RFB_WIN32_CURRENT_USER_H__
 #define __RFB_WIN32_CURRENT_USER_H__
 
-#include <rfb_win32/Service.h>
-#include <rfb_win32/OSVersion.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Handle.h>
+#include <rfb_win32/Security.h>
 
 namespace rfb {
 
   namespace win32 {
 
     // CurrentUserToken
-    //   h == 0
-    //     if platform is not NT *or* unable to get token
-    //     *or* if process is hosting service.
-    //   h != 0
-    //     if process is hosting service *and*
-    //     if platform is NT *and* able to get token.
-    //   isValid() == true
-    //     if platform is not NT *or* token is valid.
+    //   CurrentUserToken is a Handle containing the security token
+    //   for the currently logged-on user, or null if no user is
+    //   logged on.
+    //
+    //   Under Windows 95/98/Me, which don't support security tokens,
+    //   the token will be INVALID_HANDLE_VALUE if a user is logged on.
+    //
+    //   Under Windows NT/2K, it may be the case that the token is
+    //   null even when a user *is* logged on, because we use some hacks
+    //   to detect the user's token and sometimes they fail.  On these
+    //   platforms, isSafe() will return False if the token is null.
+    //
+    //   Under Windows XP, etc, isSafe() will always be True, and the token
+    //   will always be set to the currently logged on user's token.
+    //
+    //   canImpersonate() tests whether there is a user token that is safe
+    //   to impersonate.
+    //
+    //   noUserLoggedOn() tests whether there is *definitely* no user logged on.
 
     struct CurrentUserToken : public Handle {
       CurrentUserToken();
-      bool isValid() const {return isValid_;};
+      bool isSafe() const { return isSafe_; };
+      bool canImpersonate() const { return h && isSafe(); }
+      bool noUserLoggedOn() const { return !h && isSafe(); }
     private:
-      bool isValid_;
+      bool isSafe_;
     };
 
     // ImpersonateCurrentUser
@@ -66,11 +78,21 @@
 
     // UserName
     //   Returns the name of the user the thread is currently running as.
+    //   Raises a SystemException in case of error.
+    //   NB: Raises a SystemException with err == ERROR_NOT_LOGGED_ON if
+    //       running under Windows 9x/95/Me and no user is logged on.
 
     struct UserName : public TCharArray {
       UserName();
     };
 
+    // UserSID
+    //   Returns the SID of the currently logged-on user (i.e. the session user)
+
+    struct UserSID : public Sid {
+      UserSID();
+    };
+
   }
 
 }
diff --git a/rfb_win32/DIBSectionBuffer.cxx b/rfb_win32/DIBSectionBuffer.cxx
index 026e5ed..18ce3ea 100644
--- a/rfb_win32/DIBSectionBuffer.cxx
+++ b/rfb_win32/DIBSectionBuffer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -15,16 +15,16 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
-#include <rfb/LogWriter.h>
 
 #include <rfb_win32/DIBSectionBuffer.h>
-#include <rfb_win32/Win32Util.h>
-
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/BitmapInfo.h>
+#include <rfb/LogWriter.h>
 
 using namespace rfb;
 using namespace win32;
 
-static LogWriter vlog("DIBSection");
+static LogWriter vlog("DIBSectionBuffer");
 
 
 DIBSectionBuffer::DIBSectionBuffer(HWND window_)
@@ -53,7 +53,7 @@
   format = pf;
   recreateBuffer();
   if ((pf.bpp <= 8) && pf.trueColour) {
-    vlog.debug("creating %d-bit TrueColor palette", pf.depth);
+    vlog.info("creating %d-bit TrueColour palette", pf.depth);
     for (int i=0; i < (1<<(pf.depth)); i++) {
       palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax;
       palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax;
@@ -114,8 +114,6 @@
     bi.mask.green = format.greenMax << format.greenShift;
     bi.mask.blue = format.blueMax << format.blueShift;
 
-    vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp);
-
     // Create a DIBSection to draw into
     if (device)
       new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
@@ -199,10 +197,13 @@
         format.depth++;
         bits = bits >> 1;
       }
+      if (format.depth > format.bpp)
+        throw Exception("Bad DIBSection format (depth exceeds bpp)");
     } else {
       // Set the DIBSection's palette
       refreshPalette();
     }
+
   }
 }
 
diff --git a/rfb_win32/DIBSectionBuffer.h b/rfb_win32/DIBSectionBuffer.h
index 51e2da3..ad1a310 100644
--- a/rfb_win32/DIBSectionBuffer.h
+++ b/rfb_win32/DIBSectionBuffer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,9 +25,7 @@
 #ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__
 #define __RFB_WIN32_DIB_SECTION_BUFFER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb/PixelBuffer.h>
 #include <rfb/Region.h>
 #include <rfb/ColourMap.h>
diff --git a/rfb_win32/DeviceContext.cxx b/rfb_win32/DeviceContext.cxx
new file mode 100644
index 0000000..4f70a1b
--- /dev/null
+++ b/rfb_win32/DeviceContext.cxx
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/CompatibleBitmap.h>
+#include <rfb_win32/BitmapInfo.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+static LogWriter vlog("DeviceContext");
+
+PixelFormat DeviceContext::getPF() const {
+  return getPF(dc);
+}
+
+PixelFormat DeviceContext::getPF(HDC dc) {
+  PixelFormat format;
+  CompatibleBitmap bitmap(dc, 1, 1);
+
+  // -=- Get the bitmap format information
+  BitmapInfo bi;
+  memset(&bi, 0, sizeof(bi));
+  bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bi.bmiHeader.biBitCount = 0;
+  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+    throw rdr::SystemException("unable to determine device pixel format", GetLastError());
+  }
+  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+    throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
+  }
+
+  // Set the initial format information
+  format.trueColour = bi.bmiHeader.biBitCount > 8;
+  format.bigEndian = 0;
+  format.bpp = bi.bmiHeader.biBitCount;
+
+  if (format.trueColour) {
+    DWORD rMask=0, gMask=0, bMask=0;
+
+    // Which true colour format is the DIB section using?
+    switch (bi.bmiHeader.biCompression) {
+    case BI_RGB:
+      // Default RGB layout
+      switch (bi.bmiHeader.biBitCount) {
+      case 16:
+        // RGB 555 - High Colour
+        vlog.info("16-bit High Colour");
+        rMask = 0x7c00;
+        bMask = 0x001f;
+        gMask = 0x03e0;
+        break;
+      case 24:
+      case 32:
+        // RGB 888 - True Colour
+        vlog.info("24/32-bit High Colour");
+        rMask = 0xff0000;
+        gMask = 0x00ff00;
+        bMask = 0x0000ff;
+        break;
+      default:
+        vlog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
+        throw rdr::Exception("unknown bits per pixel specified");
+      };
+      break;
+    case BI_BITFIELDS:
+      // Custom RGB layout
+      rMask = bi.mask.red;
+      gMask = bi.mask.green;
+      bMask = bi.mask.blue;
+      vlog.info("%lu-bit BitFields: (%lx, %lx, %lx)",
+                 bi.bmiHeader.biBitCount, rMask, gMask, bMask);
+      break;
+    };
+
+    // Convert the data we just retrieved
+    initMaxAndShift(rMask, &format.redMax, &format.redShift);
+    initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
+    initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
+
+    // Calculate the depth from the colour shifts
+    format.depth = 0;
+    Pixel bits = rMask | gMask | bMask;
+    while (bits) {
+      format.depth++;
+      bits = bits >> 1;
+    }
+
+    // Check that the depth & bpp are valid
+    if (format.depth > format.bpp) {
+      vlog.error("depth exceeds bits per pixel!");
+      format.bpp = format.depth;
+    }
+
+    // Correct the bits-per-pixel to something we're happy with
+    if (format.bpp <= 16)
+      format.bpp = 16;
+    else if (format.bpp <= 32)
+      format.bpp = 32;
+  } else {
+    // Palettised format - depth reflects number of colours,
+    // but bits-per-pixel is ALWAYS 8
+    format.depth = format.bpp;
+    if (format.bpp < 8)
+      format.bpp = 8;
+    vlog.info("%d-colour palettised", 1<<format.depth);
+  }
+
+  return format;
+}
+
+Rect DeviceContext::getClipBox() const {
+  return getClipBox(dc);
+}
+
+Rect DeviceContext::getClipBox(HDC dc) {
+  // Get the display dimensions
+  RECT cr;
+  if (!GetClipBox(dc, &cr))
+    throw rdr::SystemException("GetClipBox", GetLastError());
+  return Rect(cr.left, cr.top, cr.right, cr.bottom);
+}
+
+
+DeviceDC::DeviceDC(const TCHAR* deviceName) {
+  dc = ::CreateDC(_T("DISPLAY"), deviceName, NULL, NULL);
+  if (!dc)
+    throw rdr::SystemException("failed to create DeviceDC", GetLastError());
+}
+
+DeviceDC::~DeviceDC() {
+  if (dc)
+    DeleteDC(dc);
+}
+
+
+WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
+  dc = GetDC(wnd);
+  if (!dc)
+    throw rdr::SystemException("GetDC failed", GetLastError());
+}
+
+WindowDC::~WindowDC() {
+  if (dc)
+    ReleaseDC(hwnd, dc);
+}
+
+
+CompatibleDC::CompatibleDC(HDC existing) {
+  dc = CreateCompatibleDC(existing);
+  if (!dc)
+    throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
+}
+
+CompatibleDC::~CompatibleDC() {
+  if (dc)
+    DeleteDC(dc);
+}
+
+
+BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
+  oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
+  if (!oldBitmap)
+    throw rdr::SystemException("SelectObject to CompatibleDC failed",
+    GetLastError());
+}
+
+BitmapDC::~BitmapDC() {
+  SelectObject(dc, oldBitmap);
+}
diff --git a/rfb_win32/DeviceContext.h b/rfb_win32/DeviceContext.h
new file mode 100644
index 0000000..9d91cec
--- /dev/null
+++ b/rfb_win32/DeviceContext.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// DeviceContext base class, wrapping Windows HDC, plus some
+// helper classes tailored to particular types of DC, such as
+// window and device DCs.
+
+#ifndef __RFB_WIN32_DEVICECONTEXT_H__
+#define __RFB_WIN32_DEVICECONTEXT_H__
+
+#include <windows.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    // Base class, providing methods to get the bounding (clip) box,
+    // and the pixel format, and access to the HDC itself.
+    class DeviceContext {
+    public:
+      DeviceContext() : dc(0) {}
+      virtual ~DeviceContext() {}
+      operator HDC() const {return dc;}
+      PixelFormat getPF() const;
+      static PixelFormat getPF(HDC dc);
+      Rect getClipBox() const;
+      static Rect getClipBox(HDC dc);
+    protected:
+      HDC dc;
+    };
+
+    // -=- DeviceContext that opens a specific display device
+    class DeviceDC : public DeviceContext {
+    public:
+      DeviceDC(const TCHAR* deviceName);
+      ~DeviceDC();
+    };
+
+    // Get a DC for a particular window's client area.
+    class WindowDC : public DeviceContext {
+    public:
+      WindowDC(HWND wnd);
+      virtual ~WindowDC();
+    protected:
+      HWND hwnd;
+    };
+
+    // Create a new DC, compatible with an existing one.
+    class CompatibleDC : public DeviceContext {
+    public:
+      CompatibleDC(HDC existing);
+      virtual ~CompatibleDC();
+    };
+
+    // Create a new DC, compatible with an existing one, and
+    // select the specified bitmap into it.
+    class BitmapDC : public CompatibleDC {
+    public:
+      BitmapDC(HDC hdc, HBITMAP hbitmap);
+      ~BitmapDC();
+    protected:
+      HBITMAP oldBitmap;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/DeviceFrameBuffer.cxx b/rfb_win32/DeviceFrameBuffer.cxx
index 70975c3..8da894e 100644
--- a/rfb_win32/DeviceFrameBuffer.cxx
+++ b/rfb_win32/DeviceFrameBuffer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -21,25 +21,22 @@
 // The DeviceFrameBuffer class encapsulates the pixel data of the system
 // display.
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <assert.h>
-
 #include <vector>
-
-#include <rfb/LogWriter.h>
-#include <rfb/Exception.h>
-#include <rdr/types.h>
-#include <rfb/VNCServer.h>
 #include <rfb_win32/DeviceFrameBuffer.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/DeviceContext.h>
 #include <rfb_win32/OSVersion.h>
+#include <rfb_win32/IconInfo.h>
+#include <rfb/VNCServer.h>
+#include <rfb/LogWriter.h>
 
-namespace rfb {
+using namespace rfb;
+using namespace win32;
 
-namespace win32 {
+static LogWriter vlog("DeviceFrameBuffer");
 
-static LogWriter vlog("FrameBuffer");
+BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt",
+  "Use a slower capture method that ensures that alpha blended windows appear correctly",
+  true);
 
 
 // -=- DeviceFrameBuffer class
@@ -67,10 +64,7 @@
   // -=- Get the display dimensions and pixel format
 
   // Get the display dimensions
-  RECT cr;
-  if (!GetClipBox(device, &cr))
-    throw rdr::SystemException("GetClipBox", GetLastError());
-  deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom);
+  deviceCoords = DeviceContext::getClipBox(device);
   if (!wRect.is_empty())
     deviceCoords = wRect.translate(deviceCoords.tl);
   int w = deviceCoords.width();
@@ -120,7 +114,7 @@
   // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME
   //       If you try CAPTUREBLT on 98 then you get blank output...
   if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y,
-    osVersion.isPlatformNT ? CAPTUREBLT | SRCCOPY : SRCCOPY)) {
+    (osVersion.isPlatformNT && useCaptureBlt) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) {
     if (ignoreGrabErrors)
       vlog.error("BitBlt failed:%ld", GetLastError());
     else
@@ -176,7 +170,7 @@
   // - If hCursor is null then there is no cursor - clear the old one
 
   if (hCursor == 0) {
-    server->setCursor(0, 0, 0, 0, 0, 0);
+    server->setCursor(0, 0, Point(), 0, 0);
     return;
   }
 
@@ -189,8 +183,10 @@
     BITMAP maskInfo;
     if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
       throw rdr::SystemException("GetObject() failed", GetLastError());
-
-    assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1);
+    if (maskInfo.bmPlanes != 1)
+      throw rdr::Exception("unsupported multi-plane cursor");
+    if (maskInfo.bmBitsPixel != 1)
+      throw rdr::Exception("unsupported cursor mask format");
 
     // - Create the cursor pixel buffer and mask storage
     //   NB: The cursor pixel buffer is NOT used here.  Instead, we
@@ -278,8 +274,7 @@
       memcpy(cursorBm.data, cursor.data, cursor.dataLen());
     }
 
-    server->setCursor(cursor.width(), cursor.height(),
-                      cursor.hotspot.x, cursor.hotspot.y,
+    server->setCursor(cursor.width(), cursor.height(), cursor.hotspot,
                       cursorBm.data, cursor.mask.buf);
   } catch (rdr::Exception& e) {
     vlog.error(e.str());
@@ -292,7 +287,3 @@
   if (!format.trueColour)
     copyDevicePaletteToDIB(device, this);
 }
-
-}; // namespace win32
-
-}; // namespace rfb
diff --git a/rfb_win32/DeviceFrameBuffer.h b/rfb_win32/DeviceFrameBuffer.h
index 5e97b22..7718c33 100644
--- a/rfb_win32/DeviceFrameBuffer.h
+++ b/rfb_win32/DeviceFrameBuffer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -26,13 +26,12 @@
 #ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
 #define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/DIBSectionBuffer.h>
 #include <rfb/Cursor.h>
 #include <rfb/Region.h>
 #include <rfb/Exception.h>
+#include <rfb/Configuration.h>
 
 namespace rfb {
 
@@ -85,6 +84,8 @@
       // Set whether grabRect should ignore errors or throw exceptions
       // Only set this if you are sure you'll capture the errors some other way!
       void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;}
+      
+      static BoolParameter useCaptureBlt;
 
     protected:
       // Translate supplied Desktop coordinates into Device-relative coordinates
@@ -98,22 +99,6 @@
       bool ignoreGrabErrors;
     };
 
-    // -=- createDisplayDeviceFrameBuffer
-    // createDisplayDeviceFrameBuffer must be passed the name of a display device,
-    // and will return a new FrameBuffer object attached to that display
-    // device.
-    // If the device name is not specified then the default display is
-    // returned.
-
-    DeviceFrameBuffer *createDisplayDeviceFrameBuffer(const char *device=0);
-
-    // -=- createDisplayFrameBuffers
-    // Creates a set of framebuffers, one for each available display
-    // device.
-
-    typedef std::vector<DeviceFrameBuffer *> DeviceFrameBuffers;
-    void createDisplayDeviceFrameBuffers(DeviceFrameBuffers *fbs);
-
   };
 
 };
diff --git a/rfb_win32/Dialog.cxx b/rfb_win32/Dialog.cxx
index 157cf5f..398334f 100644
--- a/rfb_win32/Dialog.cxx
+++ b/rfb_win32/Dialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,9 +25,15 @@
 #include <rfb/LogWriter.h>
 #include <rdr/Exception.h>
 #include <rfb_win32/Win32Util.h>
+
 #ifdef _DIALOG_CAPTURE
+#ifdef PropSheet_IndexToId
 #include <rfb_win32/DeviceFrameBuffer.h>
 #include <extra/LoadBMP.cxx>
+#else
+#undef _DIALOG_CAPTURE
+#pragma message("  NOTE: Not building Dialog Capture support.")
+#endif
 #endif
 
 using namespace rfb;
@@ -208,6 +214,33 @@
 }
 
 
+// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure...
+struct DLGTEMPLATEEX {
+  WORD dlgVer;
+  WORD signature;
+  DWORD helpID;
+  DWORD exStyle;
+  DWORD style;
+  WORD cDlgItems;
+  short x;
+  short y;
+  short cx;
+  short cy;
+};
+
+static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) {
+  if (message == PSCB_PRECREATE) {
+    // Remove the context-help style, to remove the titlebar ? button
+    // *** Nasty hack to cope with new & old dialog template formats...
+    if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff)
+      ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP;
+    else
+      ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP;
+  }
+  return TRUE;
+}
+
+
 bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
   if (alreadyShowing) return false;
   alreadyShowing = true;
@@ -227,7 +260,8 @@
     // Initialise and create the PropertySheet itself
     PROPSHEETHEADER header;
     header.dwSize = PROPSHEETHEADER_V1_SIZE;
-    header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) /*| (showCtxtHelp ? 0 : PSH_NOCONTEXTHELP)*/;
+    header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK);
+    header.pfnCallback = removeCtxtHelp;
     header.hwndParent = owner;
     header.hInstance = inst;
     header.pszCaption = title.buf;
@@ -245,9 +279,7 @@
     centerWindow(handle, owner);
     plog.info("created %lx", handle);
 
-#if (WINVER >= 0x0500)
 #ifdef _DIALOG_CAPTURE
-    // *** NOT TESTED
     if (capture) {
       plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
       char* tmpdir = getenv("TEMP");
@@ -264,15 +296,23 @@
             DispatchMessage(&msg);
         }
         fb.grabRect(fb.getRect());
+        TCHAR title[128];
+        if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title)))
+          _stprintf(title, _T("capture%d"), i);
+        CharArray pageTitle(strDup(title));
+        for (int j=0; j<strlen(pageTitle.buf); j++) {
+          if (pageTitle.buf[j] == '/' || pageTitle.buf[j] == '\\' || pageTitle.buf[j] == ':')
+            pageTitle.buf[j] = '-';
+        }
         char filename[256];
-        sprintf(filename, "%s\\capture%d.bmp", tmpdir, i);
+        sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf);
+        vlog.debug("writing to %s", filename);
         saveBMP(filename, &fb);
         i++;
       }
       ReleaseDC(handle, dc);
     } else {
 #endif
-#endif
       try {
         if (owner)
           EnableWindow(owner, FALSE);
@@ -291,11 +331,9 @@
           EnableWindow(owner, TRUE);
         throw;
       }
-#if (WINVER >= 0x0500)
 #ifdef _DIALOG_CAPTURE
     }
 #endif
-#endif
 
     plog.info("finished %lx", handle);
 
diff --git a/rfb_win32/Dialog.h b/rfb_win32/Dialog.h
index d46133a..9784ba4 100644
--- a/rfb_win32/Dialog.h
+++ b/rfb_win32/Dialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,7 +24,6 @@
 #ifndef __RFB_WIN32_DIALOG_H__
 #define __RFB_WIN32_DIALOG_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <prsht.h>
 #include <list>
diff --git a/rfb_win32/DynamicFn.cxx b/rfb_win32/DynamicFn.cxx
new file mode 100644
index 0000000..e933f24
--- /dev/null
+++ b/rfb_win32/DynamicFn.cxx
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DynamicFn");
+
+
+DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
+  dllHandle = LoadLibrary(dllName);
+  if (!dllHandle) {
+    vlog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
+    return;
+  }
+  fnPtr = GetProcAddress(dllHandle, fnName);
+  if (!fnPtr)
+    vlog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
+}
+
+DynamicFnBase::~DynamicFnBase() {
+  if (dllHandle)
+    FreeLibrary(dllHandle);
+}
+
+
diff --git a/rfb_win32/DynamicFn.h b/rfb_win32/DynamicFn.h
new file mode 100644
index 0000000..57fdbec
--- /dev/null
+++ b/rfb_win32/DynamicFn.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// Helper class managing dynamic linkage to DLL functions.
+
+#ifndef __RFB_WIN32_DYNAMICFN_H__
+#define __RFB_WIN32_DYNAMICFN_H__
+
+#include <windows.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class DynamicFnBase {
+    public:
+      DynamicFnBase(const TCHAR* dllName, const char* fnName);
+      ~DynamicFnBase();
+      bool isValid() const {return fnPtr != 0;}
+    protected:
+      void* fnPtr;
+      HMODULE dllHandle;
+    private:
+      DynamicFnBase(const DynamicFnBase&);
+      DynamicFnBase operator=(const DynamicFnBase&);
+    };
+
+    template<class T> class DynamicFn : public DynamicFnBase {
+    public:
+      DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
+      T operator *() const {return (T)fnPtr;};
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/EventManager.cxx b/rfb_win32/EventManager.cxx
new file mode 100644
index 0000000..0f9993b
--- /dev/null
+++ b/rfb_win32/EventManager.cxx
@@ -0,0 +1,103 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/EventManager.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("EventManager");
+
+
+EventManager::EventManager() : eventCount(0) {
+}
+
+EventManager::~EventManager() {
+}
+
+
+bool EventManager::addEvent(HANDLE event, EventHandler* ecb) {
+  if (eventCount >= MAXIMUM_WAIT_OBJECTS-1)
+    return false;
+  events[eventCount] = event;
+  handlers[eventCount] = ecb;
+  eventCount++;
+  return true;
+}
+
+void EventManager::removeEvent(HANDLE event) {
+  for (int i=0; i<eventCount; i++) {
+    if (events[i] == event) {
+      for (int j=i; j<eventCount-1; j++) {
+        events[j] = events[j+1];
+        handlers[j] = handlers[j+1];
+      }
+      eventCount--;
+      return;
+    }
+  }
+  throw rdr::Exception("Event not registered");
+}
+
+
+int EventManager::checkTimeouts() {
+  return 0;
+}
+
+BOOL EventManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+  while (true) {
+    // - Process any pending timeouts
+    DWORD timeout = checkTimeouts();
+    if (timeout == 0)
+      timeout = INFINITE;
+
+    // - Events take precedence over messages
+    DWORD result;
+    if (eventCount) {
+      // - Check whether any events are set
+      result = WaitForMultipleObjects(eventCount, events, FALSE, 0);
+      if (result == WAIT_TIMEOUT) {
+        // - No events are set, so check for messages
+        if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
+          return msg->message != WM_QUIT;
+
+        // - Block waiting for an event to be set, or a message
+        result = MsgWaitForMultipleObjects(eventCount, events, FALSE, timeout,
+                                           QS_ALLINPUT);
+        if (result == WAIT_OBJECT_0 + eventCount) {
+          // - Return the message, if any
+          if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
+            return msg->message != WM_QUIT;
+          continue;
+        }
+      }
+    } else
+      return GetMessage(msg, hwnd, minMsg, maxMsg);
+
+    if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + eventCount))) {
+      // - An event was set - call the handler
+      int index = result - WAIT_OBJECT_0;
+      handlers[index]->processEvent(events[index]);
+    } else if (result == WAIT_FAILED) {
+      // - An error has occurred, so return the error status code
+      return -1;
+    }
+  }
+}
diff --git a/rfb_win32/EventManager.h b/rfb_win32/EventManager.h
new file mode 100644
index 0000000..bb66e34
--- /dev/null
+++ b/rfb_win32/EventManager.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- EventManager.h
+
+// Win32 event manager.  Caller supplies event & handler pairs and
+// then uses getMessage() in place of ::GetMessage() in the main
+// loop.  EventManager calls the event handler whenever the event
+// is set.
+// Ownership of events remains with the caller.
+// It is the responsibility of handlers to reset events.
+
+#ifndef __RFB_WIN32_EVENT_MGR_H__
+#define __RFB_WIN32_EVENT_MGR_H__
+
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class EventHandler {
+    public:
+      virtual ~EventHandler() {}
+      virtual void processEvent(HANDLE event) = 0;
+    };
+
+    class EventManager {
+    public:
+      EventManager();
+      virtual ~EventManager();
+
+      // Add a Win32 event & handler for it
+      //   NB: The handler must call ResetEvent on the event.
+      //   NB: The caller retains ownership of the event.
+      virtual bool addEvent(HANDLE event, EventHandler* ecb);
+
+      // Remove a Win32 event
+      virtual void removeEvent(HANDLE event);
+
+      // getMessage
+      //   Waits for a message to become available on the thread's message queue,
+      //   and returns it.  If any registered events become set while waiting then
+      //   their handlers are called before returning.
+      //   Returns zero if the message is WM_QUIT, -1 in case of error, >0 otherwise.
+      virtual BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+
+    protected:
+      // checkTimeouts
+      //   Derived classes should override this to perform any extra processing,
+      //   returning the maximum number of milliseconds after which the callback
+      //   should be called again.
+      virtual int checkTimeouts();
+
+      HANDLE events[MAXIMUM_WAIT_OBJECTS];
+      EventHandler* handlers[MAXIMUM_WAIT_OBJECTS-1];
+      int eventCount;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/Handle.h b/rfb_win32/Handle.h
new file mode 100644
index 0000000..d3baa58
--- /dev/null
+++ b/rfb_win32/Handle.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// Wrapper for Win32 HANDLEs that can/must be CloseHandle()d.
+
+#ifndef __RFB_WIN32_HANDLE_H__
+#define __RFB_WIN32_HANDLE_H__
+
+#include <windows.h>
+
+namespace rfb {
+  namespace win32 {
+
+
+    class Handle {
+    public:
+      Handle(HANDLE h_=0) : h(h_) {}
+      ~Handle() {
+        if (h) CloseHandle(h);
+      }
+      operator HANDLE() {return h;}
+      HANDLE h;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/IconInfo.h b/rfb_win32/IconInfo.h
new file mode 100644
index 0000000..cb33a42
--- /dev/null
+++ b/rfb_win32/IconInfo.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_ICONINFO_H__
+#define __RFB_WIN32_ICONINFO_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    struct IconInfo : public ICONINFO {
+      IconInfo(HICON icon) {
+        if (!GetIconInfo(icon, this))
+          throw rdr::SystemException("GetIconInfo() failed", GetLastError());
+      }
+      ~IconInfo() {
+        if (hbmColor)
+          DeleteObject(hbmColor);
+        if (hbmMask)
+          DeleteObject(hbmMask);
+      }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/IntervalTimer.h b/rfb_win32/IntervalTimer.h
index 645d0ee..ddfae49 100644
--- a/rfb_win32/IntervalTimer.h
+++ b/rfb_win32/IntervalTimer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/LaunchProcess.cxx b/rfb_win32/LaunchProcess.cxx
index 0a48d60..56a712e 100644
--- a/rfb_win32/LaunchProcess.cxx
+++ b/rfb_win32/LaunchProcess.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -19,9 +19,10 @@
 // -=- LaunchProcess.cxx
 
 #include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb_win32/Win32Util.h>
-
-#include <rfb/util.h>
+#include <rdr/Exception.h>
+#include <stdio.h>
 
 using namespace rfb;
 using namespace win32;
@@ -37,10 +38,11 @@
 }
 
 
-void LaunchProcess::start(HANDLE userToken) {
+void LaunchProcess::start(HANDLE userToken, bool createConsole) {
   if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
     return;
   await();
+  returnCode = STILL_ACTIVE;
 
   // - Create storage for the process startup information
   STARTUPINFO sinfo;
@@ -58,19 +60,15 @@
     exePath.buf = tstrDup(exeName.buf);
   }
 
-  // - Start the VNC server
+  // - Start the process
   // Note: We specify the exe's precise path in the ApplicationName parameter,
   //       AND include the name as the first part of the CommandLine parameter,
   //       because CreateProcess doesn't make ApplicationName argv[0] in C programs.
   TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);
   _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);
-#ifdef _DEBUG
-  DWORD flags = CREATE_NEW_CONSOLE;
-#else
-  DWORD flags = CREATE_NO_WINDOW;
-#endif
+  DWORD flags = createConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW;
   BOOL success;
-  if (userToken)
+  if (userToken != INVALID_HANDLE_VALUE)
     success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
   else
     success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
@@ -81,12 +79,25 @@
   WaitForInputIdle(procInfo.hProcess, 15000);
 }
 
-void LaunchProcess::await() {
+void LaunchProcess::detach()
+{
   if (!procInfo.hProcess)
     return;
-  WaitForSingleObject(procInfo.hProcess, INFINITE);
   CloseHandle(procInfo.hProcess);
   CloseHandle(procInfo.hThread);
   memset(&procInfo, 0, sizeof(procInfo));
 }
 
+bool LaunchProcess::await(DWORD timeoutMs) {
+  if (!procInfo.hProcess)
+    return true;
+  DWORD result = WaitForSingleObject(procInfo.hProcess, timeoutMs);
+  if (result == WAIT_OBJECT_0) {
+    GetExitCodeProcess(procInfo.hProcess, &returnCode);
+    detach();
+    return true;
+  } else if (result == WAIT_FAILED) {
+    throw rdr::SystemException("await() failed", GetLastError());
+  }
+  return false;
+}
diff --git a/rfb_win32/LaunchProcess.h b/rfb_win32/LaunchProcess.h
index 6fd34e9..38521dc 100644
--- a/rfb_win32/LaunchProcess.h
+++ b/rfb_win32/LaunchProcess.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,9 +24,7 @@
 #ifndef __RFB_WIN32_LAUNCHPROCESS_H__
 #define __RFB_WIN32_LAUNCHPROCESS_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/TCharArray.h>
 
 namespace rfb {
@@ -38,14 +36,28 @@
       LaunchProcess(const TCHAR* exeName_, const TCHAR* params);
       ~LaunchProcess();
 
-      // If userToken is 0 then starts as current user, otherwise
-      // starts as the specified user.  userToken must be a primary token.
-      void start(HANDLE userToken);
+      // start() starts the specified process with the supplied
+      //   command-line.
+      //   If userToken is INVALID_HANDLE_VALUE then starts the process
+      //   as the current user, otherwise as the specified user.
+      //   If createConsole is true then CREATE_CONSOLE_WINDOW is passed
+      //   as an extra flag to the process creation call.
+      void start(HANDLE userToken, bool createConsole=false);
 
-      // Wait for the process to quit, and close the handles to it.
-      void await();
+      // Detatch from the child process. After detatching from a child
+      //   process, no other methods should be called on the object
+      //   that started it
+      void detach();
+
+      // Wait for the process to quit, up to the specified timeout, and
+      //   close the handles to it once it has quit.
+      //   If the process quits within the timeout then true is returned
+      //   and returnCode is set. If it has not quit then false is returned.
+      //   If an error occurs then an exception will be thrown.
+      bool await(DWORD timeoutMs=INFINITE);
 
       PROCESS_INFORMATION procInfo;
+      DWORD returnCode;
     protected:
       TCharArray exeName;
       TCharArray params;
diff --git a/rfb_win32/LocalMem.h b/rfb_win32/LocalMem.h
new file mode 100644
index 0000000..a99d324
--- /dev/null
+++ b/rfb_win32/LocalMem.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_LOCALMEM_H__
+#define __RFB_WIN32_LOCALMEM_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    // Allocate and/or manage LocalAlloc memory.
+    struct LocalMem {
+      LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
+        if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
+      }
+      LocalMem(void* p) : ptr(p) {}
+      ~LocalMem() {LocalFree(ptr);}
+      operator void*() {return ptr;}
+      void* takePtr() {
+        void* t = ptr; ptr = 0; return t;
+      }
+      void* ptr;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/LogicalPalette.h b/rfb_win32/LogicalPalette.h
new file mode 100644
index 0000000..204f108
--- /dev/null
+++ b/rfb_win32/LogicalPalette.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_LOGPALETTE_H__
+#define __RFB_WIN32_LOGPALETTE_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class LogicalPalette {
+    public:
+      LogicalPalette() {
+        BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
+        LOGPALETTE* logpal = (LOGPALETTE*)buf;
+        logpal->palVersion = 0x300;
+        logpal->palNumEntries = 256;
+        for (int i=0; i<256;i++) {
+          logpal->palPalEntry[i].peRed = 0;
+          logpal->palPalEntry[i].peGreen = 0;
+          logpal->palPalEntry[i].peBlue = 0;
+          logpal->palPalEntry[i].peFlags = 0;
+        }
+        palette = CreatePalette(logpal);
+        if (!palette)
+          throw rdr::SystemException("failed to CreatePalette", GetLastError());
+      }
+      ~LogicalPalette() {
+        if (palette && !DeleteObject(palette))
+          throw rdr::SystemException("del palette failed", GetLastError());
+      }
+      void setEntries(int start, int count, const Colour* cols) {
+        if (numEntries < count) {
+          ResizePalette(palette, start+count);
+          numEntries = start+count;
+        }
+        PALETTEENTRY* logpal = new PALETTEENTRY[count];
+        for (int i=0; i<count; i++) {
+          logpal[i].peRed = cols[i].r >> 8;
+          logpal[i].peGreen = cols[i].g >> 8;
+          logpal[i].peBlue = cols[i].b >> 8;
+          logpal[i].peFlags = 0;
+        }
+        UnrealizeObject(palette);
+        SetPaletteEntries(palette, start, count, logpal);
+        delete [] logpal;
+      }
+      HPALETTE getHandle() {return palette;}
+    protected:
+      HPALETTE palette;
+      int numEntries;
+    };
+
+    class PaletteSelector {
+    public:
+      PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
+        oldPal = SelectPalette(dc, pal, FALSE);
+        redrawRequired = RealizePalette(dc) > 0;
+      }
+      ~PaletteSelector() {
+        if (oldPal) SelectPalette(device, oldPal, TRUE);
+      }
+      bool isRedrawRequired() {return redrawRequired;}
+    protected:
+      HPALETTE oldPal;
+      HDC device;
+      bool redrawRequired;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/LowLevelKeyEvents.cxx b/rfb_win32/LowLevelKeyEvents.cxx
new file mode 100644
index 0000000..322d1f4
--- /dev/null
+++ b/rfb_win32/LowLevelKeyEvents.cxx
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <windows.h>
+#include <rfb_win32/LowLevelKeyEvents.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+#include <list>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("LowLevelKeyEvents");
+
+
+HHOOK hook = 0;
+std::list<HWND> windows;
+Mutex windowLock;
+
+
+static bool filterKeyEvent(int vkCode) {
+  switch (vkCode) {
+  case VK_LWIN:
+  case VK_RWIN:
+  case VK_SNAPSHOT:
+    return true;
+  case VK_TAB:
+    if (GetAsyncKeyState(VK_MENU) & 0x8000)
+      return true;
+  case VK_ESCAPE:
+    if (GetAsyncKeyState(VK_MENU) & 0x8000)
+      return true;
+    if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+      return true;
+  }
+  return false;
+}
+
+LRESULT CALLBACK LowLevelKeyEventProc(int nCode,
+                                      WPARAM wParam,
+                                      LPARAM lParam) {
+  if (nCode >= 0) {
+    Lock l(windowLock);
+    HWND foreground = GetForegroundWindow();
+    std::list<HWND>::iterator i;
+    for (i=windows.begin(); i!=windows.end(); i++) {
+      if (*i == foreground) {
+        UINT msgType = wParam;
+        KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;
+        if (filterKeyEvent(msgInfo->vkCode)) {
+          vlog.debug("filtered event %lx(%lu) %lu", msgInfo->vkCode, msgInfo->vkCode, wParam);
+          PostMessage(*i, wParam, msgInfo->vkCode, (msgInfo->scanCode & 0xff) << 16);
+          return 1;
+        }
+      }
+    }
+  }
+  return CallNextHookEx(hook, nCode, wParam, lParam);
+}
+
+
+bool rfb::win32::enableLowLevelKeyEvents(HWND hwnd) {
+// ***  return false; // *** THIS CODE IS EXPERIMENTAL, SO DISABLED BY DEFAULT!
+  Lock l(windowLock);
+  if (windows.empty() && !hook)
+    hook = SetWindowsHookEx(WH_KEYBOARD_LL, &LowLevelKeyEventProc, GetModuleHandle(0), 0);
+  if (hook)
+    windows.push_back(hwnd);
+  vlog.debug("enable %p -> %s", hwnd, hook ? "success" : "failure");
+  return hook != 0;
+}
+
+void rfb::win32::disableLowLevelKeyEvents(HWND hwnd) {
+  vlog.debug("disable %p", hwnd);
+  Lock l(windowLock);
+  windows.remove(hwnd);
+  if (windows.empty() && hook) {
+    UnhookWindowsHookEx(hook);
+    hook = 0;
+  }
+}
diff --git a/rfb_win32/LowLevelKeyEvents.h b/rfb_win32/LowLevelKeyEvents.h
new file mode 100644
index 0000000..40d2ecf
--- /dev/null
+++ b/rfb_win32/LowLevelKeyEvents.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- LowLevelKeyEvents.h
+//
+// This interface allows keyboard events destined for a particular window
+// to be intercepted early in the keyboard message queue and posted directly
+// to the window.  This is used to avoid having the operating system process
+// keys such as VK_LWIN, VK_RWIN, etc.
+//
+
+#ifndef __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
+#define __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
+
+namespace rfb {
+
+  namespace win32 {
+
+    // enableLowLevelKeyEvents
+    //   Specifies that keyboard events destined for the specified window should
+    //   be posted directly to the window, rather than being passed via the normal
+    //   Windows keyboard message queue.
+    bool enableLowLevelKeyEvents(HWND hwnd);
+
+    // disableLowLevelKeyEvents
+    //   Causes the specified window to revert to the normal Windows keyboard
+    //   event processing mechanism.
+    void disableLowLevelKeyEvents(HWND hwnd);
+
+  };
+
+};
+
+#endif // __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
diff --git a/rfb_win32/ModuleFileName.h b/rfb_win32/ModuleFileName.h
new file mode 100644
index 0000000..2264e89
--- /dev/null
+++ b/rfb_win32/ModuleFileName.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_MODULE_FILENAME_H__
+#define __RFB_WIN32_MODULE_FILENAME_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+  namespace win32 {
+
+    struct ModuleFileName : public TCharArray {
+      ModuleFileName(HMODULE module=0) : TCharArray(MAX_PATH) {
+        if (!module)
+          module = GetModuleHandle(0);
+        if (!GetModuleFileName(module, buf, MAX_PATH))
+          buf[0] = 0;
+      }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/MonitorInfo.cxx b/rfb_win32/MonitorInfo.cxx
new file mode 100644
index 0000000..03772e9
--- /dev/null
+++ b/rfb_win32/MonitorInfo.cxx
@@ -0,0 +1,205 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("MonitorInfo");
+
+
+// If we are building in multi-monitor support (i.e. the headers support it)
+//   then do dynamic imports of the required system calls, and provide any
+//   other code that wouldn't otherwise compile.
+#ifdef RFB_HAVE_MONITORINFO
+#include <tchar.h>
+typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
+static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
+typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD);
+static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect");
+typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
+static void fillMonitorInfo(HMONITOR monitor, MonitorInfo* mi) {
+  vlog.debug("monitor=%lx", monitor);
+  if (!_GetMonitorInfo.isValid())
+    throw rdr::Exception("no GetMonitorInfo");
+  memset(mi, 0, sizeof(MONITORINFOEXA));
+  mi->cbSize = sizeof(MONITORINFOEXA);
+  if (!(*_GetMonitorInfo)(monitor, mi))
+    throw rdr::SystemException("failed to GetMonitorInfo", GetLastError());
+  vlog.debug("monitor is %d,%d-%d,%d", mi->rcMonitor.left, mi->rcMonitor.top, mi->rcMonitor.right, mi->rcMonitor.bottom);
+  vlog.debug("work area is %d,%d-%d,%d", mi->rcWork.left, mi->rcWork.top, mi->rcWork.right, mi->rcWork.bottom);
+  vlog.debug("device is \"%s\"", mi->szDevice);
+}
+#else
+#pragma message("  NOTE: Not building Multi-Monitor support.")
+#endif
+
+
+MonitorInfo::MonitorInfo(HWND window) {
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+
+#ifdef RFB_HAVE_MONITORINFO
+  try {
+    if (_MonitorFromWindow.isValid()) {
+      HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
+      if (!monitor)
+        throw rdr::SystemException("failed to get monitor", GetLastError());
+      fillMonitorInfo(monitor, this);
+      return;
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error(e.str());
+  }
+#endif
+
+  // Legacy fallbacks - just return the desktop settings
+  vlog.debug("using legacy fall-backs");
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+MonitorInfo::MonitorInfo(const RECT& r) {
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+
+#ifdef RFB_HAVE_MONITORINFO
+  try {
+    if (_MonitorFromRect.isValid()) {
+      HMONITOR monitor = (*_MonitorFromRect)(&r, MONITOR_DEFAULTTONEAREST);
+      if (!monitor)
+        throw rdr::SystemException("failed to get monitor", GetLastError());
+      fillMonitorInfo(monitor, this);
+      return;
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error(e.str());
+  }
+#endif
+
+  // Legacy fallbacks - just return the desktop settings
+  vlog.debug("using legacy fall-backs");
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+
+#ifdef RFB_HAVE_MONITORINFO
+
+struct monitorByNameData {
+  MonitorInfo* info;
+  const char* monitorName;
+};
+
+static BOOL CALLBACK monitorByNameEnumProc(HMONITOR monitor,
+                                    HDC dc,
+                                    LPRECT pos,
+                                    LPARAM d) {
+  monitorByNameData* data = (monitorByNameData*)d;
+  memset(data->info, 0, sizeof(MONITORINFOEXA));
+  data->info->cbSize = sizeof(MONITORINFOEXA);
+  if ((*_GetMonitorInfo)(monitor, data->info)) {
+    if (stricmp(data->monitorName, data->info->szDevice) == 0)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+#endif
+
+MonitorInfo::MonitorInfo(const char* devName) {
+#ifdef RFB_HAVE_MONITORINFO
+  if (!_EnumDisplayMonitors.isValid()) {
+    vlog.debug("EnumDisplayMonitors not found");
+  } else {
+    monitorByNameData data;
+    data.info = this;
+    data.monitorName = devName;
+
+    (*_EnumDisplayMonitors)(0, 0, &monitorByNameEnumProc, (LPARAM)&data);
+    if (stricmp(data.monitorName, szDevice) == 0)
+      return;
+  }
+#endif
+  // If multi-monitor is not built, or not supported by the OS,
+  //   or if the named monitor is not found, revert to the primary monitor.
+  vlog.debug("reverting to primary monitor");
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+void MonitorInfo::moveTo(HWND handle) {
+  vlog.debug("moveTo monitor=%s", szDevice);
+
+#ifdef RFB_HAVE_MONITORINFO
+  MonitorInfo mi(handle);
+  if (strcmp(szDevice, mi.szDevice) != 0) {
+    centerWindow(handle, rcWork);
+    clipTo(handle);
+  }
+#endif
+}
+
+void MonitorInfo::clipTo(RECT* r) {
+  vlog.debug("clipTo monitor=%s", szDevice);
+
+  if (r->top < rcWork.top) {
+    r->bottom += rcWork.top - r->top; r->top = rcWork.top;
+  }
+  if (r->left < rcWork.left) {
+    r->right += rcWork.left - r->left; r->left = rcWork.left;
+  }
+  if (r->bottom > rcWork.bottom) {
+    r->top += rcWork.bottom - r->bottom; r->bottom = rcWork.bottom;
+  }
+  if (r->right > rcWork.right) {
+    r->left += rcWork.right - r->right; r->right = rcWork.right;
+  }
+  r->left = max(r->left, rcWork.left);
+  r->right = min(r->right, rcWork.right);
+  r->top = max(r->top, rcWork.top);
+  r->bottom = min(r->bottom, rcWork.bottom);
+}
+
+void MonitorInfo::clipTo(HWND handle) {
+  RECT r;
+  GetWindowRect(handle, &r);
+  clipTo(&r);
+  SetWindowPos(handle, 0, r.left, r.top, r.right-r.left, r.bottom-r.top,
+               SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+}
+
+
diff --git a/rfb_win32/MonitorInfo.h b/rfb_win32/MonitorInfo.h
new file mode 100644
index 0000000..acf2775
--- /dev/null
+++ b/rfb_win32/MonitorInfo.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// Helper class used to obtain information about a particular monitor.
+// This class wraps the Windows MONITORINFOEX ASCII structure, providing
+// methods that can safely be called on both multi-monitor aware systems
+// and older "legacy" systems.
+
+
+#ifndef __RFB_WIN32_MONITORINFO_H__
+#define __RFB_WIN32_MONITORINFO_H__
+
+#include <windows.h>
+#ifdef MONITOR_DEFAULTTONULL
+#define RFB_HAVE_MONITORINFO
+#endif
+
+namespace rfb {
+  namespace win32 {
+
+    // Structure containing info on the monitor nearest the window.
+    // Copes with multi-monitor OSes and older ones.
+#ifdef RFB_HAVE_MONITORINFO
+    struct MonitorInfo : MONITORINFOEXA {
+#else
+    struct MonitorInfo {
+      DWORD cbSize;
+      RECT rcMonitor;
+      RECT rcWork;
+      DWORD dwFlags;
+      char szDevice[1]; // Always null...
+#endif
+
+      // Constructor: Obtains monitor info for the monitor that has the
+      //   greatest overlap with the supplied window or rectangle.
+      MonitorInfo(HWND hwnd);
+      MonitorInfo(const RECT& r);
+
+      // Constructor: Obtains monitor info for the name monitor.  Monitor
+      //   names should be those obtained from the MonitorInfo
+      //   szDevice field, and usually look like "\\.\DISPLAY<n>"
+      MonitorInfo(const char* devName);
+
+      // Move the specified window to reside on the monitor.
+      void moveTo(HWND handle);
+
+      // Clip the specified rectangle or window to the monitor's working area.
+      //   The rectangle/window is moved so that as much as possible resides
+      //   on the working area of the monitor, and is then intersected with it.
+      void clipTo(HWND handle);
+      void clipTo(RECT* r);
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/MsgBox.h b/rfb_win32/MsgBox.h
new file mode 100644
index 0000000..5957139
--- /dev/null
+++ b/rfb_win32/MsgBox.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_WIN32_MSGBOX_H__
+#define __RFB_WIN32_MSGBOX_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+  namespace win32 {
+
+    // Define rfb::win32::AppName somewhere in the application.
+    // The MsgBox function will use the specified application name
+    // as the prefix for the message box title.
+    // Message box titles are based on the (standard Win32) flags
+    // passed to the MsgBox helper function.
+
+    extern TStr AppName;
+
+    // Wrapper around Win32 MessageBox()
+    static int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
+      const TCHAR* msgType = 0;
+      UINT tflags = flags & 0x70;
+      if (tflags == MB_ICONHAND)
+        msgType = _T("Error");
+      else if (tflags == MB_ICONQUESTION)
+        msgType = _T("Question");
+      else if (tflags == MB_ICONEXCLAMATION)
+        msgType = _T("Warning");
+      else if (tflags == MB_ICONASTERISK)
+        msgType = _T("Information");
+      flags |= MB_TOPMOST | MB_SETFOREGROUND;
+      int len = _tcslen(AppName.buf) + 1;
+      if (msgType) len += _tcslen(msgType) + 3;
+      TCharArray title = new TCHAR[len];
+      _tcscpy(title.buf, AppName.buf);
+      if (msgType) {
+        _tcscat(title.buf, _T(" : "));
+        _tcscat(title.buf, msgType);
+      }
+      return MessageBox(parent, msg, title.buf, flags);
+    }
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/MsgWindow.cxx b/rfb_win32/MsgWindow.cxx
index 519d6ab..95bd523 100644
--- a/rfb_win32/MsgWindow.cxx
+++ b/rfb_win32/MsgWindow.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -113,4 +113,4 @@
 LRESULT
 MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
   return SafeDefWindowProc(getHandle(), msg, wParam, lParam);
-}
\ No newline at end of file
+}
diff --git a/rfb_win32/MsgWindow.h b/rfb_win32/MsgWindow.h
index 94baca3..92b6cf2 100644
--- a/rfb_win32/MsgWindow.h
+++ b/rfb_win32/MsgWindow.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -24,9 +24,7 @@
 #ifndef __RFB_WIN32_MSG_WINDOW_H__
 #define __RFB_WIN32_MSG_WINDOW_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/TCharArray.h>
 
 namespace rfb {
diff --git a/rfb_win32/OSVersion.cxx b/rfb_win32/OSVersion.cxx
index 1976098..3d74c95 100644
--- a/rfb_win32/OSVersion.cxx
+++ b/rfb_win32/OSVersion.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/OSVersion.h b/rfb_win32/OSVersion.h
index 1d52943..18ec003 100644
--- a/rfb_win32/OSVersion.h
+++ b/rfb_win32/OSVersion.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -27,7 +27,6 @@
 #ifndef __RFB_WIN32_OS_VERSION_H__
 #define __RFB_WIN32_OS_VERSION_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace rfb {
diff --git a/rfb_win32/RegConfig.cxx b/rfb_win32/RegConfig.cxx
index fcb309b..dd1c3b0 100644
--- a/rfb_win32/RegConfig.cxx
+++ b/rfb_win32/RegConfig.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -23,7 +23,7 @@
 #include <rfb_win32/RegConfig.h>
 #include <rfb/LogWriter.h>
 #include <rfb/util.h>
-#include <rdr/HexOutStream.h>
+//#include <rdr/HexOutStream.h>
 
 using namespace rfb;
 using namespace rfb::win32;
@@ -32,90 +32,28 @@
 static LogWriter vlog("RegConfig");
 
 
-class rfb::win32::RegReaderThread : public Thread {
-public:
-  RegReaderThread(RegistryReader& reader, const HKEY key);
-  ~RegReaderThread();
-  virtual void run();
-  virtual Thread* join();
-protected:
-  RegistryReader& reader;
-  RegKey key;
-  HANDLE event;
-};
-
-RegReaderThread::RegReaderThread(RegistryReader& reader_, const HKEY key_) : Thread("RegConfig"), reader(reader_), key(key_) {
+RegConfig::RegConfig(EventManager* em) : eventMgr(em), event(CreateEvent(0, TRUE, FALSE, 0)), callback(0) {
+  if (em->addEvent(event, this))
+    eventMgr = em;
 }
 
-RegReaderThread::~RegReaderThread() {
+RegConfig::~RegConfig() {
+  if (eventMgr)
+    eventMgr->removeEvent(event);
 }
 
-void
-RegReaderThread::run() {
-  vlog.debug("RegReaderThread started");
-  while (key) {
-    // - Wait for changes
-    vlog.debug("waiting for changes");
-    key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
-
-    // - Load settings
-    RegistryReader::loadRegistryConfig(key);
-
-    // - Notify specified thread of changes
-    if (reader.notifyThread)
-      PostThreadMessage(reader.notifyThread->getThreadId(),
-        reader.notifyMsg.message, 
-        reader.notifyMsg.wParam, 
-        reader.notifyMsg.lParam);
-    else if (reader.notifyWindow)
-      PostMessage(reader.notifyWindow,
-        reader.notifyMsg.message, 
-        reader.notifyMsg.wParam, 
-        reader.notifyMsg.lParam);
-  }
-}
-
-Thread*
-RegReaderThread::join() {
-  RegKey old_key = key;
-  key.close();
-  if ((HKEY)old_key) {
-    // *** Closing the key doesn't always seem to work
-    //     Writing to it always will, instead...
-    vlog.debug("closing key");
-    old_key.setString(_T("dummy"), _T(""));
-  }
-  return Thread::join();
-}
-
-
-RegistryReader::RegistryReader() : thread(0), notifyThread(0) {
-  memset(&notifyMsg, 0, sizeof(notifyMsg));
-}
-
-RegistryReader::~RegistryReader() {
-  if (thread) delete thread->join();
-}
-
-bool RegistryReader::setKey(const HKEY rootkey, const TCHAR* keyname) {
-  if (thread) delete thread->join();
-  thread = 0;
-  
-  RegKey key;
+bool RegConfig::setKey(const HKEY rootkey, const TCHAR* keyname) {
   try {
     key.createKey(rootkey, keyname);
-    loadRegistryConfig(key);
+    processEvent(event);
+    return true;
   } catch (rdr::Exception& e) {
     vlog.debug(e.str());
     return false;
   }
-  thread = new RegReaderThread(*this, key);
-  if (thread) thread->start();
-  return true;
 }
 
-void
-RegistryReader::loadRegistryConfig(RegKey& key) {
+void RegConfig::loadRegistryConfig(RegKey& key) {
   DWORD i = 0;
   try {
     while (1) {
@@ -131,21 +69,46 @@
   }
 }
 
-bool RegistryReader::setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam, LPARAM lParam) {
-  notifyMsg.message = winMsg;
-  notifyMsg.wParam = wParam;
-  notifyMsg.lParam = lParam;
-  notifyThread = thread;
-  notifyWindow = 0;
-  return true;
+void RegConfig::processEvent(HANDLE event_) {
+  vlog.info("registry changed");
+
+  // Reinstate the registry change notifications
+  ResetEvent(event);
+  key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, event);
+
+  // Load settings
+  loadRegistryConfig(key);
+
+  // Notify the callback, if supplied
+  if (callback)
+    callback->regConfigChanged();
 }
 
-bool RegistryReader::setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam, LPARAM lParam) {
-  notifyMsg.message = winMsg;
-  notifyMsg.wParam = wParam;
-  notifyMsg.lParam = lParam;
-  notifyWindow = window;
-  notifyThread = 0;
-  return true;
+
+RegConfigThread::RegConfigThread() : Thread("RegConfigThread"), config(&eventMgr) {
 }
 
+RegConfigThread::~RegConfigThread() {
+  join();
+}
+
+bool RegConfigThread::start(const HKEY rootKey, const TCHAR* keyname) {
+  if (config.setKey(rootKey, keyname)) {
+    Thread::start();
+    return true;
+  }
+  return false;
+}
+
+void RegConfigThread::run() {
+  DWORD result = 0;
+  MSG msg;
+  while ((result = eventMgr.getMessage(&msg, 0, 0, 0)) > 0) {}
+  if (result < 0)
+    throw rdr::SystemException("RegConfigThread failed", GetLastError());
+}
+
+Thread* RegConfigThread::join() {
+  PostThreadMessage(getThreadId(), WM_QUIT, 0, 0);
+  return Thread::join();
+}
diff --git a/rfb_win32/RegConfig.h b/rfb_win32/RegConfig.h
index 3fced85..e9c01b1 100644
--- a/rfb_win32/RegConfig.h
+++ b/rfb_win32/RegConfig.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -26,27 +26,55 @@
 
 #include <rfb/Threading.h>
 #include <rfb/Configuration.h>
-
 #include <rfb_win32/Registry.h>
+#include <rfb_win32/EventManager.h>
+#include <rfb_win32/Handle.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class RegistryReader {
+    class RegConfig : EventHandler {
     public:
-      RegistryReader();
-      ~RegistryReader();
+      RegConfig(EventManager* em);
+      ~RegConfig();
+
+      // Specify the registry key to read Configuration items from
       bool setKey(const HKEY rootkey, const TCHAR* keyname);
-      bool setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
-      bool setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+
+      // Support for a callback, run in the RegConfig host thread whenever
+      // the registry configuration changes
+      class Callback {
+      public:
+        virtual ~Callback() {}
+        virtual void regConfigChanged() = 0;
+      };
+      void setCallback(Callback* cb) { callback = cb; }
+
+      // Read entries from the specified key into the Configuration
       static void loadRegistryConfig(RegKey& key);
     protected:
-      friend class RegReaderThread;
-      Thread* thread;
-      Thread* notifyThread;
-      HWND notifyWindow;
-      MSG notifyMsg;
+      // EventHandler interface and trigger event
+      virtual void processEvent(HANDLE event);
+
+      EventManager* eventMgr;
+      Handle event;
+      Callback* callback;
+      RegKey key;
+    };
+
+    class RegConfigThread : Thread {
+    public:
+      RegConfigThread();
+      ~RegConfigThread();
+
+      // Start the thread, reading from the specified key
+      bool start(const HKEY rootkey, const TCHAR* keyname);
+    protected:
+      void run();
+      Thread* join();
+      EventManager eventMgr;
+      RegConfig config;
     };
 
   };
diff --git a/rfb_win32/Registry.cxx b/rfb_win32/Registry.cxx
index de9238f..4ece4ba 100644
--- a/rfb_win32/Registry.cxx
+++ b/rfb_win32/Registry.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,14 +18,14 @@
 
 // -=- Registry.cxx
 
-#include <rfb/LogWriter.h>
 #include <rfb_win32/Registry.h>
+#include <rfb_win32/Security.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rdr/MemOutStream.h>
 #include <rdr/HexOutstream.h>
 #include <rdr/HexInStream.h>
-#include <rfb_win32/Security.h>
-
 #include <stdlib.h>
+#include <rfb/LogWriter.h>
 
 // These flags are required to control access control inheritance,
 // but are not defined by VC6's headers.  These definitions comes
@@ -69,6 +69,7 @@
 
 
 void RegKey::setHKEY(HKEY k, bool fK) {
+  vlog.debug("setHKEY(%x,%d)", k, (int)fK);
   close();
   freeKey = fK;
   key = k;
@@ -82,6 +83,7 @@
     vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result);
     throw rdr::SystemException("RegCreateKeyEx", result);
   }
+  vlog.debug("createKey(%x,%s) = %x", root.key, (const char*)CStr(name), key);
   freeKey = true;
   return true;
 }
@@ -91,6 +93,8 @@
   LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key);
   if (result != ERROR_SUCCESS)
     throw rdr::SystemException("RegOpenKeyEx (open)", result);
+  vlog.debug("openKey(%x,%s,%s) = %x", root.key, (const char*)CStr(name),
+	         readOnly ? "ro" : "rw", key);
   freeKey = true;
 }
 
@@ -109,6 +113,7 @@
 
 void RegKey::close() {
   if (freeKey) {
+    vlog.debug("RegCloseKey(%x)", key);
     RegCloseKey(key);
     key = 0;
   }
@@ -126,8 +131,8 @@
     throw rdr::SystemException("RegDeleteValue", result);
 }
 
-void RegKey::awaitChange(bool watchSubTree, DWORD filter) const {
-  LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, 0, FALSE);
+void RegKey::awaitChange(bool watchSubTree, DWORD filter, HANDLE event) const {
+  LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, event, event != 0);
   if (result != ERROR_SUCCESS)
     throw rdr::SystemException("RegNotifyChangeKeyValue", result);
 }
@@ -206,6 +211,16 @@
   return getInt(valname, def ? 1 : 0) > 0;
 }
 
+static inline TCHAR* terminateData(char* data, int length)
+{
+  // We must terminate the string, just to be sure.  Stupid Win32...
+  int len = length/sizeof(TCHAR);
+  TCharArray str(len+1);
+  memcpy(str.buf, data, length);
+  str.buf[len] = 0;
+  return str.takeBuf();
+}
+
 TCHAR* RegKey::getRepresentation(const TCHAR* valname) const {
   DWORD type, length;
   LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length);
@@ -224,12 +239,7 @@
     }
   case REG_SZ:
     if (length) {
-      // We must terminate the string, just to be sure.  Stupid Win32...
-      int len = length/sizeof(TCHAR);
-      TCharArray str(len+1);
-      memcpy(str.buf, data.buf, length);
-      str.buf[len] = 0;
-      return str.takeBuf();
+      return terminateData(data.buf, length);
     } else {
       return tstrDup(_T(""));
     }
@@ -239,6 +249,22 @@
       _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf));
       return tmp.takeBuf();
     }
+  case REG_EXPAND_SZ:
+    {
+    if (length) {
+      TCharArray str(terminateData(data.buf, length));
+      DWORD required = ExpandEnvironmentStrings(str.buf, 0, 0);
+      if (required==0)
+        throw rdr::SystemException("ExpandEnvironmentStrings", GetLastError());
+      TCharArray result(required);
+      length = ExpandEnvironmentStrings(str.buf, result.buf, required);
+      if (required<length)
+        rdr::Exception("unable to expand environment strings");
+      return result.takeBuf();
+    } else {
+      return tstrDup(_T(""));
+    }
+    }
   default:
     throw rdr::Exception("unsupported registry type");
   }
@@ -270,3 +296,21 @@
     throw rdr::SystemException("RegEnumValue", result);
   return valueName.buf;
 }
+
+const TCHAR* RegKey::getKeyName(int i) {
+  DWORD maxValueNameLen;
+  LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0, 0, 0, 0);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegQueryInfoKey", result);
+  if (valueNameBufLen < maxValueNameLen + 1) {
+    valueNameBufLen = maxValueNameLen + 1;
+    delete [] valueName.buf;
+    valueName.buf = new TCHAR[valueNameBufLen];
+  }
+  DWORD length = valueNameBufLen;
+  result = RegEnumKeyEx(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+  if (result == ERROR_NO_MORE_ITEMS) return 0;
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegEnumKey", result);
+  return valueName.buf;
+}
diff --git a/rfb_win32/Registry.h b/rfb_win32/Registry.h
index 1998c49..68d535c 100644
--- a/rfb_win32/Registry.h
+++ b/rfb_win32/Registry.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -22,9 +22,7 @@
 #ifndef __RFB_WIN32_REGISTRY_H__
 #define __RFB_WIN32_REGISTRY_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/Security.h>
 #include <rfb/util.h>
 
@@ -45,9 +43,9 @@
       ~RegKey();
 
       void setHKEY(HKEY key, bool freeKey);
-    protected:
-      HKEY operator=(const RegKey& k);
-      HKEY operator=(HKEY k);
+    private:
+      RegKey& operator=(const RegKey& k);
+      HKEY& operator=(const HKEY& k);
     public:
 
       // Returns true if key was created, false if already existed
@@ -67,7 +65,9 @@
       void deleteValue(const TCHAR* name) const;
 
 
-      void awaitChange(bool watchSubTree, DWORD filter) const;
+      // Block waiting for a registry change, OR return immediately and notify the
+      // event when there is a change, if specified
+      void awaitChange(bool watchSubTree, DWORD filter, HANDLE event=0) const;
 
       void setExpandString(const TCHAR* valname, const TCHAR* s) const;
       void setString(const TCHAR* valname, const TCHAR* s) const;
@@ -91,10 +91,11 @@
 
       bool isValue(const TCHAR* valname) const;
 
-      // Get the name of value number "i"
+      // Get the name of value/key number "i"
       // If there are fewer than "i" values then return 0
       // NAME IS OWNED BY RegKey OBJECT!
       const TCHAR* getValueName(int i);
+      const TCHAR* getKeyName(int i);
 
       operator HKEY() const;
     protected:
diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx
index 4916c48..0af5064 100644
--- a/rfb_win32/SDisplay.cxx
+++ b/rfb_win32/SDisplay.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -20,21 +20,19 @@
 //
 // The SDisplay class encapsulates a particular system display.
 
-#include <assert.h>
-
 #include <rfb_win32/SDisplay.h>
 #include <rfb_win32/Service.h>
-#include <rfb_win32/WMShatter.h>
-#include <rfb_win32/osVersion.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/TsSessions.h>
 #include <rfb_win32/CleanDesktop.h>
-
-#include <rfb/util.h>
-#include <rfb/LogWriter.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb_win32/SDisplayCoreDriver.h>
 #include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
 
-#include <rfb/Configuration.h>
 
 using namespace rdr;
 using namespace rfb;
@@ -44,209 +42,37 @@
 
 // - SDisplay-specific configuration options
 
-BoolParameter rfb::win32::SDisplay::use_hooks("UseHooks",
-  "Set hooks in the operating system to capture display updates more efficiently", true);
+IntParameter rfb::win32::SDisplay::updateMethod("UpdateMethod",
+  "How to discover desktop updates; 0 - Polling, 1 - Application hooking, 2 - Driver hooking.", 1);
 BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs",
   "Disable local keyboard and pointer input while the server is in use", false);
 StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction",
   "Action to perform when all clients have disconnected.  (None, Lock, Logoff)", "None");
-
+StringParameter displayDevice("DisplayDevice",
+  "Display device name of the monitor to be remoted, or empty to export the whole desktop.", "");
 BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper",
-  "Remove the desktop wallpaper when the server in in use.", false);
+  "Remove the desktop wallpaper when the server is in use.", false);
 BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern",
-  "Remove the desktop background pattern when the server in in use.", false);
+  "Remove the desktop background pattern when the server is in use.", false);
 BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects",
   "Disable desktop user interface effects when the server is in use.", false);
 
 
-// - WM_TIMER ID values
-
-#define TIMER_CURSOR 1
-#define TIMER_UPDATE 2
-#define TIMER_UPDATE_AND_POLL 3
-
-
-// -=- Polling settings
-
-const int POLLING_SEGMENTS = 16;
-
-const int FG_POLLING_FPS = 20;
-const int FG_POLLING_FS_INTERVAL = 1000 / FG_POLLING_FPS;
-const int FG_POLLING_INTERVAL = FG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
-
-const int BG_POLLING_FS_INTERVAL = 5000;
-const int BG_POLLING_INTERVAL = BG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// SDisplayCore
-//
-
-// The SDisplay Core object is created by SDisplay's start() method
-// and deleted by its stop() method.
-// The Core must be created in the current input desktop in order
-// to operate - SDisplay is responsible for ensuring that.
-// The structures contained in the Core are manipulated directly
-// by the SDisplay, which is also responsible for detecting when
-// a desktop-switch is required.
-
-class rfb::win32::SDisplayCore : public MsgWindow {
-public:
-  SDisplayCore(SDisplay* display);
-  ~SDisplayCore();
-
-  void setPixelBuffer(DeviceFrameBuffer* pb_);
-
-  bool isRestartRequired();
-
-  virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
-
-  // -=- Timers
-  IntervalTimer pollTimer;
-  IntervalTimer cursorTimer;
-
-  // -=- Input handling
-  rfb::win32::SPointer ptr;
-  rfb::win32::SKeyboard kbd;
-  rfb::win32::Clipboard clipboard;
-
-  // -=- Hook handling objects used outside thread run() method
-  WMCopyRect wm_copyrect;
-  WMPoller wm_poller;
-  WMCursor cursor;
-  WMMonitor wm_monitor;
-  WMHooks wm_hooks;
-  WMBlockInput wm_input;
-
-  // -=- Tidying the desktop
-  CleanDesktop cleanDesktop;
-  bool isWallpaperRemoved;
-  bool isPatternRemoved;
-  bool areEffectsDisabled;
-
-  // -=- Full screen polling
-  int poll_next_y;
-  int poll_y_increment;
-
-  // Are we using hooks?
-  bool use_hooks;
-  bool using_hooks;
-
-  // State of the display object
-  SDisplay* display;
-};
-
-SDisplayCore::SDisplayCore(SDisplay* display_)
-: MsgWindow(_T("SDisplayCore")), display(display_),
-  using_hooks(0), use_hooks(rfb::win32::SDisplay::use_hooks),
-  isWallpaperRemoved(rfb::win32::SDisplay::removeWallpaper),
-  isPatternRemoved(rfb::win32::SDisplay::removePattern),
-  areEffectsDisabled(rfb::win32::SDisplay::disableEffects),
-  pollTimer(getHandle(), TIMER_UPDATE_AND_POLL),
-  cursorTimer(getHandle(), TIMER_CURSOR) {
-  setPixelBuffer(display->pb);
-}
-
-SDisplayCore::~SDisplayCore() {
-}
-
-void SDisplayCore::setPixelBuffer(DeviceFrameBuffer* pb) {
-  poll_y_increment = (display->pb->height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
-  poll_next_y = display->screenRect.tl.y;
-  wm_hooks.setClipRect(display->screenRect);
-  wm_copyrect.setClipRect(display->screenRect);
-  wm_poller.setClipRect(display->screenRect);
-}
-
-
-bool SDisplayCore::isRestartRequired() {
-  // - We must restart the SDesktop if:
-  // 1. We are no longer in the input desktop.
-  // 2. The use_hooks setting has changed.
-
-  // - Check that we are in the input desktop
-  if (rfb::win32::desktopChangeRequired())
-    return true;
-
-  // - Check that the hooks setting hasn't changed
-  // NB: We can't just check using_hooks because that can be false
-  // because they failed, even though use_hooks is true!
-  if (use_hooks != rfb::win32::SDisplay::use_hooks)
-    return true;
-
-  // - Check that the desktop optimisation settings haven't changed
-  //   This isn't very efficient, but it shouldn't change very often!
-  if ((isWallpaperRemoved != rfb::win32::SDisplay::removeWallpaper) ||
-      (isPatternRemoved != rfb::win32::SDisplay::removePattern) ||
-      (areEffectsDisabled != rfb::win32::SDisplay::disableEffects))
-    return true;
-
-  return false;
-}
-
-LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-
-  case WM_TIMER:
-
-    if (display->server && display->server->clientsReadyForUpdate()) {
-
-      // - Check that the SDesktop doesn't need restarting
-      if (isRestartRequired()) {
-        display->restart();
-        return 0;
-      }
-    
-      // - Action depends on the timer message type
-      switch (wParam) {
-
-        // POLL THE SCREEN
-      case TIMER_UPDATE_AND_POLL:
-        // Handle window dragging, polling of consoles, etc.
-        while (wm_poller.processEvent()) {}
-
-        // Poll the next strip of the screen (in Screen coordinates)
-        {
-          Rect pollrect = display->screenRect;
-          if (poll_next_y >= pollrect.br.y) {
-            // Yes.  Reset the counter and return
-            poll_next_y = pollrect.tl.y;
-          } else {
-            // No.  Poll the next section
-            pollrect.tl.y = poll_next_y;
-            poll_next_y += poll_y_increment;
-            pollrect.br.y = min(poll_next_y, pollrect.br.y);
-            display->add_changed(pollrect);
-          }
-        }
-        break;
-
-      case TIMER_CURSOR:
-        display->triggerUpdate();
-        break;
-
-      };
-
-    }
-    return 0;
-
-  };
-
-  return MsgWindow::processMessage(msg, wParam, lParam);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 //
 // SDisplay
 //
 
+typedef BOOL (WINAPI *_LockWorkStation_proto)();
+DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+
 // -=- Constructor/Destructor
 
-SDisplay::SDisplay(const TCHAR* devName)
-  : server(0), change_tracker(true), pb(0),
-    deviceName(tstrDup(devName)), device(0), releaseDevice(false),
-    core(0), statusLocation(0)
+SDisplay::SDisplay()
+  : server(0), pb(0), device(0),
+    core(0), ptr(0), kbd(0), clipboard(0),
+    inputs(0), monitor(0), cleanDesktop(0), cursor(0),
+    statusLocation(0)
 {
   updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
 }
@@ -271,57 +97,14 @@
 void SDisplay::start(VNCServer* vs)
 {
   vlog.debug("starting");
+
+  // Try to make session zero the console session
+  if (!inConsoleSession())
+    setConsoleSession();
+
+  // Start the SDisplay core
   server = vs;
-
-  // Switch to the current input desktop
-  // ***
-  if (rfb::win32::desktopChangeRequired()) {
-    if (!rfb::win32::changeDesktop())
-      throw rdr::Exception("unable to switch into input desktop");
-  }
-
-  // Clear the change tracker
-  change_tracker.clear();
-
-  // Create the framebuffer object
-  recreatePixelBuffer();
-
-  // Create the SDisplayCore
-  core = new SDisplayCore(this);
-  assert(core);
-
-  // Start display monitor and clipboard handler
-  core->wm_monitor.setNotifier(this);
-  core->clipboard.setNotifier(this);
-
-  // Apply desktop optimisations
-  if (removePattern)
-    core->cleanDesktop.disablePattern();
-  if (removeWallpaper)
-    core->cleanDesktop.disableWallpaper();
-  if (disableEffects)
-    core->cleanDesktop.disableEffects();
-
-  // Start hooks
-  core->wm_hooks.setClipRect(screenRect);
-  if (core->use_hooks) {
-    // core->wm_hooks.setDiagnosticRange(0, 0x400-1);
-    core->using_hooks = core->wm_hooks.setUpdateTracker(this);
-    if (!core->using_hooks)
-      vlog.debug("hook subsystem failed to initialise");
-  }
-
-  // Set up timers
-  core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL);
-  core->cursorTimer.start(10);
-
-  // Register an interest in faked copyrect events
-  core->wm_copyrect.setUpdateTracker(&change_tracker);
-  core->wm_copyrect.setClipRect(screenRect);
-
-  // Polling of particular windows on the desktop
-  core->wm_poller.setUpdateTracker(&change_tracker);
-  core->wm_poller.setClipRect(screenRect);
+  startCore();
 
   vlog.debug("started");
 
@@ -331,108 +114,229 @@
 void SDisplay::stop()
 {
   vlog.debug("stopping");
+
+  // If we successfully start()ed then perform the DisconnectAction
   if (core) {
-    // If SDisplay was actually active then perform the disconnect action
+    CurrentUserToken cut;
     CharArray action = disconnectAction.getData();
     if (stricmp(action.buf, "Logoff") == 0) {
-      ExitWindowsEx(EWX_LOGOFF, 0);
-    } else if (stricmp(action.buf, "Lock") == 0) {
-      typedef BOOL (WINAPI *_LockWorkStation_proto)();
-      DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
-      if (_LockWorkStation.isValid())
-        (*_LockWorkStation)();
+      if (!cut.h)
+        vlog.info("ignoring DisconnectAction=Logoff - no current user");
       else
         ExitWindowsEx(EWX_LOGOFF, 0);
+    } else if (stricmp(action.buf, "Lock") == 0) {
+      if (!cut.h) {
+        vlog.info("ignoring DisconnectAction=Lock - no current user");
+      } else {
+        if (_LockWorkStation.isValid())
+          (*_LockWorkStation)();
+        else
+          ExitWindowsEx(EWX_LOGOFF, 0);
+      }
     }
   }
-  delete core;
-  core = 0;
-  delete pb;
-  pb = 0;
-  if (device) {
-    if (releaseDevice)
-      ReleaseDC(0, device);
-    else
-      DeleteDC(device);
-  }
-  device = 0;
+
+  // Stop the SDisplayCore
   if (server)
     server->setPixelBuffer(0);
-
+  stopCore();
   server = 0;
+
   vlog.debug("stopped");
 
   if (statusLocation) *statusLocation = false;
 }
 
-void SDisplay::restart() {
-  vlog.debug("restarting");
-  // Close down the hooks
-  delete core;
-  core = 0;
+
+void SDisplay::startCore() {
+
+  // Currently, we just check whether we're in the console session, and
+  //   fail if not
+  if (!inConsoleSession())
+    throw rdr::Exception("Console is not session zero - oreconnect to restore Console sessin");
+  
+  // Switch to the current input desktop
+  if (rfb::win32::desktopChangeRequired()) {
+    if (!rfb::win32::changeDesktop())
+      throw rdr::Exception("unable to switch into input desktop");
+  }
+
+  // Initialise the change tracker and clipper
+  updates.clear();
+  clipper.setUpdateTracker(server);
+
+  // Create the framebuffer object
+  recreatePixelBuffer(true);
+
+  // Create the SDisplayCore
+  updateMethod_ = updateMethod;
+  int tryMethod = updateMethod_;
+  while (!core) {
+    try {
+      if (tryMethod == 2)
+        core = new SDisplayCoreDriver(this, &updates);
+      else if (tryMethod == 1)
+        core = new SDisplayCoreWMHooks(this, &updates);
+      else
+        core = new SDisplayCorePolling(this, &updates);
+      core->setScreenRect(screenRect);
+    } catch (rdr::Exception& e) {
+      delete core; core = 0;
+      if (tryMethod == 0)
+        throw rdr::Exception("unable to access desktop");
+      tryMethod--;
+      vlog.error(e.str());
+    }
+  }
+  vlog.info("Started %s", core->methodName());
+
+  // Start display monitor, clipboard handler and input handlers
+  monitor = new WMMonitor;
+  monitor->setNotifier(this);
+  clipboard = new Clipboard;
+  clipboard->setNotifier(this);
+  ptr = new SPointer;
+  kbd = new SKeyboard;
+  inputs = new WMBlockInput;
+  cursor = new WMCursor;
+
+  // Apply desktop optimisations
+  cleanDesktop = new CleanDesktop;
+  if (removePattern)
+    cleanDesktop->disablePattern();
+  if (removeWallpaper)
+    cleanDesktop->disableWallpaper();
+  if (disableEffects)
+    cleanDesktop->disableEffects();
+  isWallpaperRemoved = removeWallpaper;
+  isPatternRemoved = removePattern;
+  areEffectsDisabled = disableEffects;
+}
+
+void SDisplay::stopCore() {
+  if (core)
+    vlog.info("Stopping %s", core->methodName());
+  delete core; core = 0;
+  delete pb; pb = 0;
+  delete device; device = 0;
+  delete monitor; monitor = 0;
+  delete clipboard; clipboard = 0;
+  delete inputs; inputs = 0;
+  delete ptr; ptr = 0;
+  delete kbd; kbd = 0;
+  delete cleanDesktop; cleanDesktop = 0;
+  delete cursor; cursor = 0;
+  ResetEvent(updateEvent);
+}
+
+
+bool SDisplay::areHooksAvailable() {
+  return WMHooks::areAvailable();
+}
+
+bool SDisplay::isDriverAvailable() {
+  return SDisplayCoreDriver::isAvailable();
+}
+
+
+bool SDisplay::isRestartRequired() {
+  // - We must restart the SDesktop if:
+  // 1. We are no longer in the input desktop.
+  // 2. The any setting has changed.
+
+  // - Check that our session is the Console
+  if (!inConsoleSession())
+    return true;
+
+  // - Check that we are in the input desktop
+  if (rfb::win32::desktopChangeRequired())
+    return true;
+
+  // - Check that the update method setting hasn't changed
+  //   NB: updateMethod reflects the *selected* update method, not
+  //   necessarily the one in use, since we fall back to simpler
+  //   methods if more advanced ones fail!
+  if (updateMethod_ != updateMethod)
+    return true;
+
+  // - Check that the desktop optimisation settings haven't changed
+  //   This isn't very efficient, but it shouldn't change very often!
+  if ((isWallpaperRemoved != removeWallpaper) ||
+      (isPatternRemoved != removePattern) ||
+      (areEffectsDisabled != disableEffects))
+    return true;
+
+  return false;
+}
+
+
+void SDisplay::restartCore() {
+  vlog.info("restarting");
+
+  // Stop the existing Core  related resources
+  stopCore();
   try {
-    // Re-start the hooks if possible
-    start(server);
-    vlog.debug("restarted");
+    // Start a new Core if possible
+    startCore();
+    vlog.info("restarted");
   } catch (rdr::Exception& e) {
-    // If start() fails then we MUST disconnect all clients,
-    // to cause the server to stop using the desktop.
+    // If startCore() fails then we MUST disconnect all clients,
+    // to cause the server to stop() the desktop.
     // Otherwise, the SDesktop is in an inconsistent state
-    // and the server will crash
+    // and the server will crash.
     server->closeClients(e.str());
   }
 }
 
 
-void SDisplay::pointerEvent(const Point& pos, rdr::U8 buttonmask) {
+void SDisplay::pointerEvent(const Point& pos, int buttonmask) {
   if (pb->getRect().contains(pos)) {
     Point screenPos = pos.translate(screenRect.tl);
-    core->ptr.pointerEvent(screenPos, buttonmask);
+    // - Check that the SDesktop doesn't need restarting
+    if (isRestartRequired())
+      restartCore();
+    if (ptr)
+      ptr->pointerEvent(screenPos, buttonmask);
   }
 }
 
 void SDisplay::keyEvent(rdr::U32 key, bool down) {
-  core->kbd.keyEvent(key, down);
+  // - Check that the SDesktop doesn't need restarting
+  if (isRestartRequired())
+    restartCore();
+  if (kbd)
+    kbd->keyEvent(key, down);
 }
 
 void SDisplay::clientCutText(const char* text, int len) {
   CharArray clip_sz(len+1);
   memcpy(clip_sz.buf, text, len);
   clip_sz.buf[len] = 0;
-  core->clipboard.setClipText(clip_sz.buf);
+  clipboard->setClipText(clip_sz.buf);
 }
 
 
 void SDisplay::framebufferUpdateRequest()
 {
-  triggerUpdate();
+  SetEvent(updateEvent);
 }
 
 Point SDisplay::getFbSize() {
   bool startAndStop = !core;
+
   // If not started, do minimal initialisation to get desktop size.
-  if (startAndStop) recreatePixelBuffer();
+  if (startAndStop)
+    recreatePixelBuffer();
   Point result = Point(pb->width(), pb->height());
+
   // Destroy the initialised structures.
-  if (startAndStop) stop();
+  if (startAndStop)
+    stopCore();
   return result;
 }
 
 
 void
-SDisplay::add_changed(const Region& rgn) {
-  change_tracker.add_changed(rgn);
-  triggerUpdate();
-}
-
-void
-SDisplay::add_copied(const Region& dest, const Point& delta) {
-  change_tracker.add_copied(dest, delta);
-  triggerUpdate();
-}
-
-
-void
 SDisplay::notifyClipboardChanged(const char* text, int len) {
   vlog.debug("clipboard text changed");
   if (server)
@@ -462,36 +366,42 @@
   }
 }
 
-bool
+void
 SDisplay::processEvent(HANDLE event) {
   if (event == updateEvent) {
-    vlog.info("processEvent");
+    vlog.write(120, "processEvent");
     ResetEvent(updateEvent);
 
     // - If the SDisplay isn't even started then quit now
     if (!core) {
       vlog.error("not start()ed");
-      return true;
+      return;
     }
 
     // - Ensure that the disableLocalInputs flag is respected
-    core->wm_input.blockInputs(SDisplay::disableLocalInputs);
+    inputs->blockInputs(disableLocalInputs);
 
     // - Only process updates if the server is ready
     if (server && server->clientsReadyForUpdate()) {
       bool try_update = false;
 
       // - Check that the SDesktop doesn't need restarting
-      if (core->isRestartRequired()) {
-        restart();
-        return true;
+      if (isRestartRequired()) {
+        restartCore();
+        return;
       }
-    
-      // *** window dragging can be improved - more frequent, more cunning about updates
-      while (core->wm_copyrect.processEvent()) {}
-        
+
+      // - Flush any updates from the core
+      try {
+        core->flushUpdates();
+      } catch (rdr::Exception& e) {
+        vlog.error(e.str());
+        restartCore();
+        return;
+      }
+
       // Ensure the cursor is up to date
-      WMCursor::Info info = core->cursor.getCursorInfo();
+      WMCursor::Info info = cursor->getCursorInfo();
       if (old_cursor != info) {
         // Update the cursor shape if the visibility has changed
         bool set_cursor = info.visible != old_cursor.visible;
@@ -505,7 +415,7 @@
         // Update the cursor position
         // NB: First translate from Screen coordinates to Desktop
         Point desktopPos = info.position.translate(screenRect.tl.negate());
-        server->setCursorPos(desktopPos.x, desktopPos.y);
+        server->setCursorPos(desktopPos);
         try_update = true;
 
         old_cursor = info;
@@ -513,100 +423,102 @@
 
       // Flush any changes to the server
       try_update = flushChangeTracker() || try_update;
-      if (try_update)
+      if (try_update) {
         server->tryUpdate();
+      }
     }
-  } else {
-    CloseHandle(event);
-    return false;
+    return;
   }
-  return true;
+  throw rdr::Exception("No such event");
 }
 
 
 // -=- Protected methods
 
 void
-SDisplay::recreatePixelBuffer() {
-  vlog.debug("attaching to device %s", deviceName);
-
+SDisplay::recreatePixelBuffer(bool force) {
   // Open the specified display device
-  HDC new_device;
-  if (deviceName.buf) {
-    new_device = ::CreateDC(_T("DISPLAY"), deviceName.buf, NULL, NULL);
-    releaseDevice = false;
-  } else {
-    // If no device is specified, open entire screen.
-    // Doing this with CreateDC creates problems on multi-monitor systems.
-    new_device = ::GetDC(0);
-    releaseDevice = true;
+  //   If no device is specified, open entire screen using GetDC().
+  //   Opening the whole display with CreateDC doesn't work on multi-monitor
+  //   systems for some reason.
+  DeviceContext* new_device = 0;
+  TCharArray deviceName(displayDevice.getData());
+  if (deviceName.buf[0]) {
+    vlog.info("Attaching to device %s", (const char*)CStr(deviceName.buf));
+    new_device = new DeviceDC(deviceName.buf);
   }
-  if (!new_device)
-    throw SystemException("cannot open the display", GetLastError());
+  if (!new_device) {
+    vlog.info("Attaching to virtual desktop");
+    new_device = new WindowDC(0);
+  }
 
-  // Get the coordinates of the entire virtual display
+  // Get the coordinates of the specified dispay device
   Rect newScreenRect;
-  {
-    WindowDC rootDC(0);
-    RECT r;
-    if (!GetClipBox(rootDC, &r))
-      throw rdr::SystemException("GetClipBox", GetLastError());
-    newScreenRect = Rect(r.left, r.top, r.right, r.bottom);
-  }
-
-  // Create a DeviceFrameBuffer attached to it
-  DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device);
-
-  // Has anything actually changed about the screen or the buffer?
-  if (!pb ||
-      (!newScreenRect.equals(screenRect)) ||
-      (!new_buffer->getPF().equal(pb->getPF())))
-  {
-    // Yes.  Update the buffer state.
-    screenRect = newScreenRect;
-    vlog.debug("creating pixel buffer for device");
-
-    // Flush any existing changes to the server
-    flushChangeTracker();
-
-    // Replace the old PixelBuffer
-    if (pb) delete pb;
-    if (device) DeleteDC(device);
-    pb = new_buffer;
-    device = new_device;
-
-    // Initialise the pixels
-    pb->grabRegion(pb->getRect());
-
-    // Prevent future grabRect operations from throwing exceptions
-    pb->setIgnoreGrabErrors(true);
-
-    // Update the SDisplayCore if required
-    if (core)
-      core->setPixelBuffer(pb);
-
-    // Inform the server of the changes
-    if (server)
-      server->setPixelBuffer(pb);
-
+  if (deviceName.buf[0]) {
+    MonitorInfo info(CStr(deviceName.buf));
+    newScreenRect = Rect(info.rcMonitor.left, info.rcMonitor.top,
+                         info.rcMonitor.right, info.rcMonitor.bottom);
   } else {
-    delete new_buffer;
-    DeleteDC(new_device);
+    newScreenRect = new_device->getClipBox();
   }
+
+  // If nothing has changed & a recreate has not been forced, delete
+  // the new device context and return
+  if (pb && !force &&
+    newScreenRect.equals(screenRect) &&
+    new_device->getPF().equal(pb->getPF())) {
+    delete new_device;
+    return;
+  }
+
+  // Flush any existing changes to the server
+  flushChangeTracker();
+
+  // Delete the old pixelbuffer and device context
+  vlog.debug("deleting old pixel buffer & device");
+  if (pb)
+    delete pb;
+  if (device)
+    delete device;
+
+  // Create a DeviceFrameBuffer attached to the new device
+  vlog.debug("creating pixel buffer");
+  DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device);
+
+  // Replace the old PixelBuffer
+  screenRect = newScreenRect;
+  pb = new_buffer;
+  device = new_device;
+
+  // Initialise the pixels
+  pb->grabRegion(pb->getRect());
+
+  // Prevent future grabRect operations from throwing exceptions
+  pb->setIgnoreGrabErrors(true);
+
+  // Update the clipping update tracker
+  clipper.setClipRect(pb->getRect());
+
+  // Inform the core of the changes
+  if (core)
+    core->setScreenRect(screenRect);
+
+  // Inform the server of the changes
+  if (server)
+    server->setPixelBuffer(pb);
 }
 
 bool SDisplay::flushChangeTracker() {
-  if (change_tracker.is_empty())
+  if (updates.is_empty())
     return false;
-  // Translate the update coordinates from Screen coords to Desktop
-  change_tracker.translate(screenRect.tl.negate());
-  // Flush the updates through
-  change_tracker.get_update(*server);
-  change_tracker.clear();
-  return true;
-}
 
-void SDisplay::triggerUpdate() {
-  if (core)
-    SetEvent(updateEvent);
+  vlog.write(120, "flushChangeTracker");
+
+  // Translate the update coordinates from Screen coords to Desktop
+  updates.translate(screenRect.tl.negate());
+
+  // Clip the updates & flush them to the server
+  updates.copyTo(&clipper);
+  updates.clear();
+  return true;
 }
diff --git a/rfb_win32/SDisplay.h b/rfb_win32/SDisplay.h
index c4c08bf..6dbb50a 100644
--- a/rfb_win32/SDisplay.h
+++ b/rfb_win32/SDisplay.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -20,31 +20,21 @@
 //
 // The SDisplay class encapsulates a system display.
 
-// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
-
 #ifndef __RFB_SDISPLAY_H__
 #define __RFB_SDISPLAY_H__
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
 #include <rfb/SDesktop.h>
 #include <rfb/UpdateTracker.h>
 #include <rfb/Configuration.h>
-#include <rfb/util.h>
-
-#include <winsock2.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/SocketManager.h>
-#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/Handle.h>
+#include <rfb_win32/EventManager.h>
 #include <rfb_win32/SInput.h>
 #include <rfb_win32/Clipboard.h>
-#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/CleanDesktop.h>
 #include <rfb_win32/WMCursor.h>
-#include <rfb_win32/WMHooks.h>
 #include <rfb_win32/WMNotifier.h>
-#include <rfb_win32/WMWindowCopyRect.h>
-#include <rfb_win32/WMPoller.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/DeviceContext.h>
 
 namespace rfb {
 
@@ -54,33 +44,33 @@
     // -=- SDisplay
     //
 
-    class SDisplayCore;
+    class SDisplayCore {
+    public:
+      virtual ~SDisplayCore() {};
+      virtual void setScreenRect(const Rect& screenRect_) = 0;
+      virtual void flushUpdates() = 0;
+      virtual const char* methodName() const = 0;
+    };
 
     class SDisplay : public SDesktop,
       WMMonitor::Notifier,
       Clipboard::Notifier,
-      UpdateTracker,
-      public SocketManager::EventHandler
+      public EventHandler
     {
     public:
-      SDisplay(const TCHAR* device=0);
+      SDisplay();
       virtual ~SDisplay();
 
       // -=- SDesktop interface
 
       virtual void start(VNCServer* vs);
       virtual void stop();
-      virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+      virtual void pointerEvent(const Point& pos, int buttonmask);
       virtual void keyEvent(rdr::U32 key, bool down);
       virtual void clientCutText(const char* str, int len);
       virtual void framebufferUpdateRequest();
       virtual Point getFbSize();
 
-      // -=- UpdateTracker
-
-      virtual void add_changed(const Region& rgn);
-      virtual void add_copied(const Region& dest, const Point& delta);
-
       // -=- Clipboard
       
       virtual void notifyClipboardChanged(const char* text, int len);
@@ -92,7 +82,7 @@
       // -=- EventHandler interface
 
       HANDLE getUpdateEvent() {return updateEvent;}
-      virtual bool processEvent(HANDLE event);
+      virtual void processEvent(HANDLE event);
 
       // -=- Notification of whether or not SDisplay is started
 
@@ -100,38 +90,62 @@
 
       friend class SDisplayCore;
 
-      static BoolParameter use_hooks;
+      static IntParameter updateMethod;
       static BoolParameter disableLocalInputs;
       static StringParameter disconnectAction;
       static BoolParameter removeWallpaper;
       static BoolParameter removePattern;
       static BoolParameter disableEffects;
 
-    protected:
-      void restart();
-      void recreatePixelBuffer();
-      bool flushChangeTracker();  // true if flushed, false if empty
+      // -=- Use by VNC Config to determine whether hooks, driver, etc are available
+      static bool areHooksAvailable();
+      static bool isDriverAvailable();
 
-      void triggerUpdate();
+
+    protected:
+      bool isRestartRequired();
+      void startCore();
+      void stopCore();
+      void restartCore();
+      void recreatePixelBuffer(bool force=false);
+      bool flushChangeTracker();  // true if flushed, false if empty
 
       VNCServer* server;
 
       // -=- Display pixel buffer
       DeviceFrameBuffer* pb;
-      TCharArray deviceName;
-      HDC device;
-      bool releaseDevice;
+      DeviceContext* device;
 
       // -=- The coordinates of Window's entire virtual Screen
       Rect screenRect;
 
-      // -=- All changes are collected in Display coords and merged
-      SimpleUpdateTracker change_tracker;
+      // -=- All changes are collected in UN-CLIPPED Display coords and merged
+      //     When they are to be flushed to the VNCServer, they are changed
+      //     to server coords and clipped appropriately.
+      SimpleUpdateTracker updates;
+      ClippingUpdateTracker clipper;
 
       // -=- Internal SDisplay implementation
       SDisplayCore* core;
+      int updateMethod_;
 
-      // -=- Cursor
+      // Inputs
+      SPointer* ptr;
+      SKeyboard* kbd;
+      Clipboard* clipboard;
+      WMBlockInput* inputs;
+
+      // Desktop state
+      WMMonitor* monitor;
+
+      // Desktop optimisation
+      CleanDesktop* cleanDesktop;
+      bool isWallpaperRemoved;
+      bool isPatternRemoved;
+      bool areEffectsDisabled;
+
+      // Cursor
+      WMCursor* cursor;
       WMCursor::Info old_cursor;
       Region old_cursor_region;
       Point cursor_renderpos;
diff --git a/rfb_win32/SDisplayCoreDriver.h b/rfb_win32/SDisplayCoreDriver.h
new file mode 100644
index 0000000..5fea75c
--- /dev/null
+++ b/rfb_win32/SDisplayCoreDriver.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCoreDriver.h
+//
+// Placeholder for SDisplayCore mirror-driver implementation.
+
+#ifndef __RFB_SDISPLAY_CORE_DRIVER_H__
+#define __RFB_SDISPLAY_CORE_DRIVER_H__
+
+#include <rfb_win32/SDisplay.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class SDisplayCoreDriver: public SDisplayCore {
+    public:
+      SDisplayCoreDriver(SDisplay* display, UpdateTracker* ut) {
+        throw rdr::Exception("Not supported");
+      }
+
+      // - Called by SDisplay to inform Core of the screen size
+      virtual void setScreenRect(const Rect& screenRect_) {}
+
+      // - Called by SDisplay to flush updates to the specified tracker
+      virtual void flushUpdates() {}
+
+      virtual const char* methodName() const { return "VNC Mirror Driver"; }
+
+      // - Determine whether the display driver is installed & usable
+      static bool isAvailable() { return false; }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/SDisplayCorePolling.cxx b/rfb_win32/SDisplayCorePolling.cxx
new file mode 100644
index 0000000..fc57ecd
--- /dev/null
+++ b/rfb_win32/SDisplayCorePolling.cxx
@@ -0,0 +1,81 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCorePolling.cxx
+
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplayCorePolling");
+
+const int POLLING_SEGMENTS = 16;
+
+const int SDisplayCorePolling::pollTimerId = 1;
+
+SDisplayCorePolling::SDisplayCorePolling(SDisplay* d, UpdateTracker* ut, int pollInterval_)
+  : MsgWindow(_T("rfb::win32::SDisplayCorePolling")), updateTracker(ut),
+  pollTimer(getHandle(), pollTimerId), pollNextStrip(false), display(d) {
+  pollInterval = max(10, (pollInterval_ / POLLING_SEGMENTS));
+  copyrect.setUpdateTracker(ut);
+}
+
+SDisplayCorePolling::~SDisplayCorePolling() {
+}
+
+LRESULT SDisplayCorePolling::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  if (msg == WM_TIMER && wParam == pollTimerId) {
+    pollNextStrip = true;
+    SetEvent(display->getUpdateEvent());
+    return 0;
+  }
+  return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+void SDisplayCorePolling::setScreenRect(const Rect& screenRect_) {
+  vlog.info("setScreenRect");
+  screenRect = screenRect_;
+  pollIncrementY = (screenRect.height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
+  pollNextY = screenRect.tl.y;
+  pollTimer.start(pollInterval);
+}
+
+void SDisplayCorePolling::flushUpdates() {
+  vlog.write(120, "flushUpdates");
+
+  // Check for window movement
+  while (copyrect.processEvent()) {}
+
+  if (pollNextStrip) {
+    // Poll the next strip of the screen (in Screen coordinates)
+    pollNextStrip = false;
+    Rect pollrect = screenRect;
+    if (pollNextY >= pollrect.br.y) {
+      // Yes.  Reset the counter and return
+      pollNextY = pollrect.tl.y;
+    } else {
+      // No.  Poll the next section
+      pollrect.tl.y = pollNextY;
+      pollNextY += pollIncrementY;
+      pollrect.br.y = min(pollNextY, pollrect.br.y);
+      updateTracker->add_changed(pollrect);
+    }
+  }
+}
diff --git a/rfb_win32/SDisplayCorePolling.h b/rfb_win32/SDisplayCorePolling.h
new file mode 100644
index 0000000..9e1b5ad
--- /dev/null
+++ b/rfb_win32/SDisplayCorePolling.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCorePolling.h
+//
+// SDisplayCore implementation that simply polls the screen, in sections,
+// in order to detect changes.  This Core will signal the SDisplay's
+// updateEvent regularly, causing it to call the Core back to propagate
+// changes to the VNC Server.
+
+
+#ifndef __RFB_SDISPLAY_CORE_POLLING_H__
+#define __RFB_SDISPLAY_CORE_POLLING_H__
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/WMWindowCopyRect.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class SDisplayCorePolling : public SDisplayCore, protected MsgWindow {
+    public:
+      SDisplayCorePolling(SDisplay* display, UpdateTracker* ut, int pollIntervalMs=50);
+      ~SDisplayCorePolling();
+
+      // - Called by SDisplay to inform Core of the screen size
+      virtual void setScreenRect(const Rect& screenRect_);
+
+      // - Called by SDisplay to flush updates to the specified tracker
+      virtual void flushUpdates();
+
+      virtual const char* methodName() const { return "Polling"; }
+
+    protected:
+      // - MsgWindow overrides
+      //   processMessage is used to service the cursor & polling timers
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // - Hooking subcomponents used to track the desktop state
+      WMCopyRect copyrect;
+
+      // - Background full screen polling fields
+      IntervalTimer pollTimer;
+      static const int pollTimerId;
+      Rect screenRect;
+      int pollInterval;
+      int pollNextY;
+      int pollIncrementY;
+      bool pollNextStrip;
+
+      // - Handle back to the owning SDisplay, and to the UpdateTracker to flush to
+      SDisplay* display;
+      UpdateTracker* updateTracker;
+    };
+
+  };
+};
+
+#endif
\ No newline at end of file
diff --git a/rfb_win32/SDisplayCoreWMHooks.cxx b/rfb_win32/SDisplayCoreWMHooks.cxx
new file mode 100644
index 0000000..10b88e0
--- /dev/null
+++ b/rfb_win32/SDisplayCoreWMHooks.cxx
@@ -0,0 +1,74 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCoreWMHooks.cxx
+
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplayCoreWMHooks");
+
+const int SDisplayCoreWMHooks::cursorTimerId = 2;
+const int SDisplayCoreWMHooks::consolePollTimerId = 3;
+
+
+SDisplayCoreWMHooks::SDisplayCoreWMHooks(SDisplay* d, UpdateTracker* ut)
+  : SDisplayCorePolling(d, ut, 5000),
+  cursorTimer(getHandle(), cursorTimerId),
+  consolePollTimer(getHandle(), consolePollTimerId),
+  pollConsoles(false) {
+  if (!hooks.setEvent(display->getUpdateEvent()))
+    throw rdr::Exception("hook subsystem failed to initialise");
+  poller.setUpdateTracker(updateTracker);
+  cursorTimer.start(20);
+  consolePollTimer.start(200);
+}
+
+SDisplayCoreWMHooks::~SDisplayCoreWMHooks() {
+}
+
+LRESULT SDisplayCoreWMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  if (msg == WM_TIMER) {
+    if (wParam == cursorTimerId) {
+      SetEvent(display->getUpdateEvent());
+      return 0;
+    } else if (wParam == consolePollTimerId) {
+      pollConsoles = true;
+      SetEvent(display->getUpdateEvent());
+      return 0;
+    }
+  }
+  return SDisplayCorePolling::processMessage(msg, wParam, lParam);
+}
+
+void SDisplayCoreWMHooks::flushUpdates() {
+  // Poll any visible console windows
+  if (pollConsoles) {
+    pollConsoles = false;
+    poller.processEvent();
+  }
+
+  // Check for updates from the hooks
+  hooks.getUpdates(updateTracker);
+
+  // Check for updates from the polling Core
+  SDisplayCorePolling::flushUpdates();
+}
diff --git a/rfb_win32/SDisplayCoreWMHooks.h b/rfb_win32/SDisplayCoreWMHooks.h
new file mode 100644
index 0000000..24fa5cd
--- /dev/null
+++ b/rfb_win32/SDisplayCoreWMHooks.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- SDisplayCoreWMHooks.h
+//
+// SDisplayCore implementation that uses WMHooks to capture changes to
+// the display.
+// Whenever changes are detected, the SDisplay's updateEvent is signalled,
+// so that it can perform housekeeping tasks (like ensuring the currently
+// active desktop is the correct one), before flushing changes from the
+// Core to the VNC Server.  The SDisplay will clip the changes before they
+// reach the VNC Server.
+
+
+#ifndef __RFB_SDISPLAY_CORE_WMHOOKS_H__
+#define __RFB_SDISPLAY_CORE_WMHOOKS_H__
+
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/WMPoller.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class SDisplayCoreWMHooks : public SDisplayCorePolling {
+    public:
+      SDisplayCoreWMHooks(SDisplay* display, UpdateTracker* ut);
+      ~SDisplayCoreWMHooks();
+
+      // - Called by SDisplay to flush updates to the specified tracker
+      virtual void flushUpdates();
+
+      virtual const char* methodName() const { return "VNC Hooks"; }
+
+    protected:
+      // - MsgWindow overrides
+      //   processMessage is used to service the cursor & polling timers
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // - Hooking subcomponents used to track the desktop state
+      WMHooks hooks;
+      WMPoller poller;
+      IntervalTimer cursorTimer;
+      IntervalTimer consolePollTimer;
+      bool pollConsoles;
+      static const int consolePollTimerId;
+      static const int cursorTimerId;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/SInput.cxx b/rfb_win32/SInput.cxx
index 457a861..db59287 100644
--- a/rfb_win32/SInput.cxx
+++ b/rfb_win32/SInput.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -26,23 +26,30 @@
 #define XK_CURRENCY
 #include <rfb/keysymdef.h>
 
-// * Force the windows headers to include all the SendInput stuff
-#define _WIN32_WINNT 0x401
-
+#include <tchar.h>
 #include <rfb_win32/SInput.h>
+#include <rfb_win32/MonitorInfo.h>
 #include <rfb_win32/Service.h>
-#include <rfb/LogWriter.h>
 #include <rfb_win32/OSVersion.h>
-#include <rfb_win32/Win32Util.h>
-#include "keymap.h"
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/keymap.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+#if(defined(INPUT_MOUSE) && defined(RFB_HAVE_MONITORINFO))
+#define RFB_HAVE_SENDINPUT
+#else
+#pragma message("  NOTE: Not building SendInput support.")
+#endif
 
 using namespace rfb;
 
 static LogWriter vlog("SInput");
 
-
+#ifdef RFB_HAVE_SENDINPUT
 typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int);
 static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput");
+#endif
 
 //
 // -=- Pointer implementation for Win32
@@ -68,7 +75,7 @@
 }
 
 void
-win32::SPointer::pointerEvent(const Point& pos, rdr::U8 buttonmask)
+win32::SPointer::pointerEvent(const Point& pos, int buttonmask)
 {
   // - We are specifying absolute coordinates
   DWORD flags = MOUSEEVENTF_ABSOLUTE;
@@ -118,7 +125,7 @@
     // The event lies outside the primary monitor.  Under Win2K, we can just use
     // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
     // SendInput is available on all multi-monitor-aware platforms.
-#ifdef SM_CXVIRTUALSCREEN
+#ifdef RFB_HAVE_SENDINPUT
     if (osVersion.isPlatformNT) {
       if (!_SendInput.isValid())
         throw rdr::Exception("SendInput not available");
diff --git a/rfb_win32/SInput.h b/rfb_win32/SInput.h
index dcd779e..2a0b3e6 100644
--- a/rfb_win32/SInput.h
+++ b/rfb_win32/SInput.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -32,8 +32,6 @@
 
 namespace rfb {
 
-  class CMsgWriter;
-
   namespace win32 {
 
     // -=- Pointer event handling
@@ -44,7 +42,7 @@
       // - Create a pointer event at a the given coordinates, with the
       //   specified button state.  The event must be specified using
       //   Screen coordinates.
-      void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+      void pointerEvent(const Point& pos, int buttonmask);
     protected:
       Point last_position;
       rdr::U8 last_buttonmask;
diff --git a/rfb_win32/Security.cxx b/rfb_win32/Security.cxx
new file mode 100644
index 0000000..985f00c
--- /dev/null
+++ b/rfb_win32/Security.cxx
@@ -0,0 +1,192 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Security.cxx
+
+#include <rfb_win32/Security.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/LogWriter.h>
+
+#include <lmcons.h>
+#include <Accctrl.h>
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SecurityWin32");
+
+
+Trustee::Trustee(const TCHAR* name,
+                 TRUSTEE_FORM form,
+                 TRUSTEE_TYPE type) {
+  pMultipleTrustee = 0;
+  MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+  TrusteeForm = form;
+  TrusteeType = type;
+  ptstrName = (TCHAR*)name;
+}
+
+
+ExplicitAccess::ExplicitAccess(const TCHAR* name,
+                               TRUSTEE_FORM type,
+                               DWORD perms,
+                               ACCESS_MODE mode,
+                               DWORD inherit) {
+  Trustee = rfb::win32::Trustee(name, type);
+  grfAccessPermissions = perms;
+  grfAccessMode = mode;
+  grfInheritance = inherit;
+}
+
+
+AccessEntries::AccessEntries() : entries(0), entry_count(0) {}
+
+AccessEntries::~AccessEntries() {
+  delete [] entries;
+}
+
+void AccessEntries::allocMinEntries(int count) {
+  if (count > entry_count) {
+    EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
+    if (entries) {
+      memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
+      delete entries;
+    }
+    entries = new_entries;
+  }
+}
+
+void AccessEntries::addEntry(const TCHAR* trusteeName,
+                             DWORD permissions,
+                             ACCESS_MODE mode) {
+  allocMinEntries(entry_count+1);
+  ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+  entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
+  entry_count++;
+}
+
+void AccessEntries::addEntry(const PSID sid,
+                             DWORD permissions,
+                             ACCESS_MODE mode) {
+  allocMinEntries(entry_count+1);
+  ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+  entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
+  entry_count++;
+}
+
+
+PSID Sid::copySID(const PSID sid) {
+  if (!IsValidSid(sid))
+    throw rdr::Exception("invalid SID in copyPSID");
+  PSID buf = (PSID)new rdr::U8[GetLengthSid(sid)];
+  if (!CopySid(GetLengthSid(sid), buf, sid))
+    throw rdr::SystemException("CopySid failed", GetLastError());
+  return buf;
+}
+
+void Sid::setSID(const PSID sid) {
+  delete [] buf;
+  buf = (rdr::U8*)copySID(sid);
+}
+
+void Sid::getUserNameAndDomain(TCHAR** name, TCHAR** domain) {
+  DWORD nameLen = 0;
+  DWORD domainLen = 0;
+  SID_NAME_USE use;
+  LookupAccountSid(0, (PSID)buf, 0, &nameLen, 0, &domainLen, &use);
+  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+    throw rdr::SystemException("Unable to determine SID name lengths", GetLastError());
+  vlog.info("nameLen=%d, domainLen=%d, use=%d", nameLen, domainLen, use);
+  *name = new TCHAR[nameLen];
+  *domain = new TCHAR[domainLen];
+  if (!LookupAccountSid(0, (PSID)buf, *name, &nameLen, *domain, &domainLen, &use))
+    throw rdr::SystemException("Unable to lookup account SID", GetLastError());
+}
+
+
+Sid::Administrators::Administrators() {
+  PSID sid = 0;
+  SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+  if (!AllocateAndInitializeSid(&ntAuth, 2,
+                                SECURITY_BUILTIN_DOMAIN_RID,
+                                DOMAIN_ALIAS_RID_ADMINS,
+                                0, 0, 0, 0, 0, 0, &sid)) 
+    throw rdr::SystemException("Sid::Administrators", GetLastError());
+  setSID(sid);
+  FreeSid(sid);
+}
+
+Sid::SYSTEM::SYSTEM() {
+  PSID sid = 0;
+  SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+  if (!AllocateAndInitializeSid(&ntAuth, 1,
+                                SECURITY_LOCAL_SYSTEM_RID,
+                                0, 0, 0, 0, 0, 0, 0, &sid))
+          throw rdr::SystemException("Sid::SYSTEM", GetLastError());
+  setSID(sid);
+  FreeSid(sid);
+}
+
+Sid::FromToken::FromToken(HANDLE h) {
+  DWORD required = 0;
+  GetTokenInformation(h, TokenUser, 0, 0, &required);
+  rdr::U8Array tmp(required);
+  if (!GetTokenInformation(h, TokenUser, tmp.buf, required, &required))
+    throw rdr::SystemException("GetTokenInformation", GetLastError());
+  TOKEN_USER* tokenUser = (TOKEN_USER*)tmp.buf;
+  setSID(tokenUser->User.Sid);
+}
+
+
+PACL rfb::win32::CreateACL(const AccessEntries& ae, PACL existing_acl) {
+  typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
+#ifdef UNICODE
+  const char* fnName = "SetEntriesInAclW";
+#else
+  const char* fnName = "SetEntriesInAclA";
+#endif
+  DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
+  if (!_SetEntriesInAcl.isValid())
+    throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
+  PACL new_dacl;
+  DWORD result;
+  if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
+    throw rdr::SystemException("SetEntriesInAcl", result);
+  return new_dacl;
+}
+
+
+PSECURITY_DESCRIPTOR rfb::win32::CreateSdWithDacl(const PACL dacl) {
+  SECURITY_DESCRIPTOR absSD;
+  if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
+    throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
+  Sid::SYSTEM owner;
+  if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
+    throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
+  Sid::Administrators group;
+  if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
+    throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
+  if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
+    throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
+  DWORD sdSize = GetSecurityDescriptorLength(&absSD);
+  SecurityDescriptorPtr sd(sdSize);
+  if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
+    throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
+  return sd.takeSD();
+}
diff --git a/rfb_win32/Security.h b/rfb_win32/Security.h
index d92e314..1e2e906 100644
--- a/rfb_win32/Security.h
+++ b/rfb_win32/Security.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,16 +25,10 @@
 #define __RFB_WIN32_SECURITY_H__
 
 #include <rdr/types.h>
-#include <rdr/Exception.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/LocalMem.h>
 #include <rfb_win32/TCharArray.h>
-
-#include <lmcons.h>
-#include <Accctrl.h>
 #include <aclapi.h>
 
-#include <list>
-
 namespace rfb {
 
   namespace win32 {
@@ -42,14 +36,7 @@
     struct Trustee : public TRUSTEE {
       Trustee(const TCHAR* name,
               TRUSTEE_FORM form=TRUSTEE_IS_NAME,
-              TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN)
-      {
-        pMultipleTrustee = 0;
-        MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
-        TrusteeForm = form;
-        TrusteeType = type;
-        ptstrName = (TCHAR*)name;
-      }
+              TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN);
     };
 
     struct ExplicitAccess : public EXPLICIT_ACCESS {
@@ -57,86 +44,56 @@
                      TRUSTEE_FORM type,
                      DWORD perms,
                      ACCESS_MODE mode,
-                     DWORD inherit=0)
-      {
-        Trustee = rfb::win32::Trustee(name, type);
-        grfAccessPermissions = perms;
-        grfAccessMode = mode;
-        grfInheritance = inherit;
-      }
+                     DWORD inherit=0);
     };
 
     // Helper class for building access control lists
     struct AccessEntries {
-      AccessEntries() : entries(0), entry_count(0) {}
-      ~AccessEntries() {delete [] entries;}
-      void allocMinEntries(int count) {
-        if (count > entry_count) {
-          EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
-          if (entries) {
-            memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
-            delete entries;
-          }
-          entries = new_entries;
-        }
-      }
+      AccessEntries();
+      ~AccessEntries();
+      void allocMinEntries(int count);
       void addEntry(const TCHAR* trusteeName,
                     DWORD permissions,
-                    ACCESS_MODE mode)
-      {
-        allocMinEntries(entry_count+1);
-        ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
-        entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
-        entry_count++;
-      }
-      void addEntry(const PSID sid, DWORD permissions, ACCESS_MODE mode) {
-        allocMinEntries(entry_count+1);
-        ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
-        entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
-        entry_count++;
-      }
+                    ACCESS_MODE mode);
+      void addEntry(const PSID sid,
+                    DWORD permissions,
+                    ACCESS_MODE mode);
 
       EXPLICIT_ACCESS* entries;
       int entry_count;
     };
 
     // Helper class for handling SIDs
-    struct Sid {
-      Sid() : sid(0) {}
-      Sid(PSID sid_) : sid(sid_) {}
-      ~Sid() {
-        if (sid) FreeSid(sid);
-      }
-      operator PSID() const {return sid;}
-      PSID operator=(const PSID sid_) {
-        if (sid) FreeSid(sid);
-        sid = sid_;
-      }
+    struct Sid : rdr::U8Array {
+      Sid() {}
+      operator PSID() const {return (PSID)buf;}
+      PSID takePSID() {PSID r = (PSID)buf; buf = 0; return r;}
 
-      static PSID Administrators() {
-        PSID sid = 0;
-        SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
-        if (!AllocateAndInitializeSid(&ntAuth, 2,
-          SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
-          0, 0, 0, 0, 0, 0,
-          &sid)) 
-          throw rdr::SystemException("Sid::Administrators", GetLastError());
-        return sid;
-      }
-      static PSID SYSTEM() {
-        PSID sid = 0;
-        SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
-        if (!AllocateAndInitializeSid(&ntAuth, 1,
-          SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
-          &sid))
-          throw rdr::SystemException("Sid::SYSTEM", GetLastError());
-        return sid;
-      }
+      static PSID copySID(const PSID sid);
 
-    protected:
-      PSID sid;
+      void setSID(const PSID sid);
+
+      void getUserNameAndDomain(TCHAR** name, TCHAR** domain);
+
+      struct Administrators;
+      struct SYSTEM;
+      struct FromToken;
+
+    private:
+      Sid(const Sid&);
+      Sid& operator=(const Sid&);
     };
       
+    struct Sid::Administrators : public Sid {
+      Administrators();
+    };
+    struct Sid::SYSTEM : public Sid {
+      SYSTEM();
+    };
+    struct Sid::FromToken : public Sid {
+      FromToken(HANDLE h);
+    };
+
     // Helper class for handling & freeing ACLs
     struct AccessControlList : public LocalMem {
       AccessControlList(int size) : LocalMem(size) {}
@@ -145,22 +102,7 @@
     };
 
     // Create a new ACL based on supplied entries and, if supplied, existing ACL 
-    static PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0) {
-      typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
-#ifdef UNICODE
-      const char* fnName = "SetEntriesInAclW";
-#else
-      const char* fnName = "SetEntriesInAclA";
-#endif
-      DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
-      if (!_SetEntriesInAcl.isValid())
-        throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
-      PACL new_dacl;
-      DWORD result;
-      if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
-        throw rdr::SystemException("SetEntriesInAcl", result);
-      return new_dacl;
-    }
+    PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0);
 
     // Helper class for memory-management of self-relative SecurityDescriptors
     struct SecurityDescriptorPtr : LocalMem {
@@ -172,24 +114,7 @@
     // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators,
     //   with the supplied DACL and no SACL.  The returned value can be assigned
     //   to a SecurityDescriptorPtr to be managed.
-    static PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl) {
-      SECURITY_DESCRIPTOR absSD;
-      if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
-        throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
-      Sid owner(Sid::SYSTEM());
-      if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
-        throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
-      Sid group(Sid::Administrators());
-      if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
-        throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
-      if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
-        throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
-      DWORD sdSize = GetSecurityDescriptorLength(&absSD);
-      SecurityDescriptorPtr sd(sdSize);
-      if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
-        throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
-      return sd.takeSD();
-    }
+    PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl);
 
   }
 
diff --git a/rfb_win32/Service.cxx b/rfb_win32/Service.cxx
index b00c290..2b11a22 100644
--- a/rfb_win32/Service.cxx
+++ b/rfb_win32/Service.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -20,15 +20,15 @@
 
 #include <rfb_win32/Service.h>
 #include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb_win32/Registry.h>
-#include <rfb_win32/Win32Util.h>
 #include <rfb_win32/OSVersion.h>
 #include <rfb/Threading.h>
-#include <rfb/LogWriter.h>
-#include <rfb/util.h>
-#include <rdr/Exception.h>
-
 #include <logmessages/messages.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
 
 using namespace rdr;
 using namespace rfb;
@@ -42,22 +42,26 @@
 Service* service = 0;
 
 VOID WINAPI serviceHandler(DWORD control) {
-  vlog.debug("service control %u", control);
   switch (control) {
   case SERVICE_CONTROL_INTERROGATE:
+    vlog.info("cmd: report status");
     service->setStatus();
-    break;
+    return;
   case SERVICE_CONTROL_PARAMCHANGE:
+    vlog.info("cmd: param change");
     service->readParams();
-    break;
+    return;
   case SERVICE_CONTROL_SHUTDOWN:
+    vlog.info("cmd: OS shutdown");
     service->osShuttingDown();
-    break;
+    return;
   case SERVICE_CONTROL_STOP:
+    vlog.info("cmd: stop");
     service->setStatus(SERVICE_STOP_PENDING);
     service->stop();
-    break;
-  }
+    return;
+  };
+  vlog.debug("cmd: unknown %lu", control);
 }
 
 
@@ -87,11 +91,14 @@
 
 VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
   vlog.debug("entering %s serviceProc", service->getName());
+  vlog.info("registering handler...");
   service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
   if (!service->status_handle) {
-    vlog.error("unable to register service control handler");
-    return;
+    DWORD err = GetLastError();
+    vlog.error("failed to register handler: %lu", err);
+    ExitProcess(err);
   }
+  vlog.debug("registered handler (%lx)", service->status_handle);
   service->setStatus(SERVICE_START_PENDING);
   vlog.debug("entering %s serviceMain", service->getName());
   service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
@@ -166,9 +173,9 @@
   status.dwCurrentState = state;
   status.dwCheckPoint++;
   if (!SetServiceStatus(status_handle, &status)) {
+    status.dwCurrentState = SERVICE_STOPPED;
     status.dwWin32ExitCode = GetLastError();
     vlog.error("unable to set service status:%u", status.dwWin32ExitCode);
-    stop();
   }
   vlog.debug("set status to %u(%u)", state, status.dwCheckPoint);
 }
@@ -597,7 +604,7 @@
   return true;
 }
 
-void rfb::win32::printServiceStatus(const TCHAR* name) {
+DWORD rfb::win32::getServiceState(const TCHAR* name) {
   if (osVersion.isPlatformNT) {
     // - Open the SCM
     ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
@@ -614,25 +621,25 @@
     if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
       throw rdr::SystemException("unable to query the service", GetLastError());
 
-    printf("Service is in the ");
-    switch (status.dwCurrentState) {
-    case SERVICE_RUNNING: printf("running"); break;
-    case SERVICE_STOPPED: printf("stopped"); break;
-    case SERVICE_STOP_PENDING: printf("stop pending"); break;
-    default: printf("unknown (%lu)", status.dwCurrentState); break;
-    };
-    printf(" state.\n");
-
+    return status.dwCurrentState;
   } else {
     HWND service_window = findServiceWindow(name);
-    printf("Service is in the ");
-    if (!service_window) printf("stopped");
-    else printf("running");
-    printf(" state.\n");
+    return service_window ? SERVICE_RUNNING : SERVICE_STOPPED;
   }
 }
 
+char* rfb::win32::serviceStateName(DWORD state) {
+  switch (state) {
+  case SERVICE_RUNNING: return strDup("Running");
+  case SERVICE_STOPPED: return strDup("Stopped");
+  case SERVICE_STOP_PENDING: return strDup("Stopping");
+  };
+  CharArray tmp(32);
+  sprintf(tmp.buf, "Unknown (%lu)", state);
+  return tmp.takeBuf();
+}
+
 
 bool rfb::win32::isServiceProcess() {
   return service != 0;
-}
\ No newline at end of file
+}
diff --git a/rfb_win32/Service.h b/rfb_win32/Service.h
index 164381a..00abe10 100644
--- a/rfb_win32/Service.h
+++ b/rfb_win32/Service.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -27,7 +27,6 @@
 #ifndef __RFB_WIN32_SERVICE_H__
 #define __RFB_WIN32_SERVICE_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace rfb {
@@ -61,13 +60,13 @@
       // - Service control notifications
 
       // To get notified when the OS is shutting down
-      virtual void osShuttingDown() = 0;
+      virtual void osShuttingDown() {};
 
       // To get notified when the service parameters change
-      virtual void readParams() = 0;
+      virtual void readParams() {};
 
       // To cause the serviceMain() routine to return
-      virtual void stop() = 0;
+      virtual void stop() {};
 
     public:
       SERVICE_STATUS_HANDLE status_handle;
@@ -111,7 +110,13 @@
 
     bool startService(const TCHAR* name);
     bool stopService(const TCHAR* name);
-    void printServiceStatus(const TCHAR* name);
+
+    // -=- Get the state of the named service (one of the NT service state values)
+    DWORD getServiceState(const TCHAR* name);
+
+    // -=- Convert a supplied service state value to a printable string e.g. Running, Stopped...
+    //     The caller must delete the returned string buffer
+    char* serviceStateName(DWORD state);
 
     // -=- Routine to determine whether the host process is running a service
     bool isServiceProcess();
diff --git a/rfb_win32/SocketManager.cxx b/rfb_win32/SocketManager.cxx
index 6d1980c..1d52bc8 100644
--- a/rfb_win32/SocketManager.cxx
+++ b/rfb_win32/SocketManager.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,10 +18,8 @@
 
 // -=- SocketManager.cxx
 
-#define WIN32_LEAN_AND_MEAN
 #include <winsock2.h>
-#include <assert.h>
-
+#include <list>
 #include <rfb/LogWriter.h>
 #include <rfb_win32/SocketManager.h>
 
@@ -33,218 +31,183 @@
 
 // -=- SocketManager
 
-SocketManager::SocketManager() : sockets(0), events(0), nSockets(0), nAvail(0) {
+SocketManager::SocketManager() {
 }
 
 SocketManager::~SocketManager() {
-  for (int i=0; i<nSockets; i++) {
-    if (!sockets[i].is_event)
-      WSACloseEvent(events[i]);
-  }
-  delete [] events;
-  delete [] sockets;
 }
 
 
-void SocketManager::addListener(network::SocketListener* sock_, network::SocketServer* srvr) {
+static requestAddressChangeEvents(network::SocketListener* sock_) {
+  DWORD dummy = 0;
+  if (WSAIoctl(sock_->getFd(), SIO_ADDRESS_LIST_CHANGE, 0, 0, 0, 0, &dummy, 0, 0) == SOCKET_ERROR) {
+    DWORD err = WSAGetLastError();
+    if (err != WSAEWOULDBLOCK)
+      vlog.error("Unable to track address changes", err);
+  }
+}
+
+
+void SocketManager::addListener(network::SocketListener* sock_,
+                                network::SocketServer* srvr,
+                                AddressChangeNotifier* acn) {
   WSAEVENT event = WSACreateEvent();
-  assert(event != WSA_INVALID_EVENT);
-  addListener(sock_, event, srvr);
-}
+  long flags = FD_ACCEPT | FD_CLOSE;
+  if (acn)
+    flags |= FD_ADDRESS_LIST_CHANGE;
+  try {
+    if (event && (WSAEventSelect(sock_->getFd(), event, flags) == SOCKET_ERROR))
+      throw rdr::SystemException("Unable to select on listener", WSAGetLastError());
 
-void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr) {
-  WSAEVENT event = WSACreateEvent();
-  assert(event != WSA_INVALID_EVENT);
-  addSocket(sock_, event, srvr);
-}
+    // requestAddressChangeEvents MUST happen after WSAEventSelect, so that the socket is non-blocking
+    if (acn)
+      requestAddressChangeEvents(sock_);
 
-
-BOOL SocketManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
-  while (true) {
-    // First check for idle timeout
-
-    network::SocketServer* server = 0;
-    int timeout = 0;
-    for (int i=0; i<nSockets; i++) {
-      if (!sockets[i].is_event &&
-          sockets[i].server != server) {
-        server = sockets[i].server;
-        int t = server->checkTimeouts();
-        if (t > 0 && (timeout == 0 || t < timeout))
-          timeout = t;
-      }
-    }
-    if (timeout == 0)
-      timeout = INFINITE;
-
-    // - Network IO is less common than messages - process it first
-    DWORD result;
-    if (nSockets) {
-      result = WaitForMultipleObjects(nSockets, events, FALSE, 0);
-      if (result == WAIT_TIMEOUT) {
-        if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
-          return msg->message != WM_QUIT;
-
-        result = MsgWaitForMultipleObjects(nSockets, events, FALSE, timeout,
-                                           QS_ALLINPUT);
-        if (result == WAIT_OBJECT_0 + nSockets) {
-          if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
-            return msg->message != WM_QUIT;
-          continue;
-        }
-      }
-    } else
-      return GetMessage(msg, hwnd, minMsg, maxMsg);
-
-    if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + nSockets))) {
-      int index = result - WAIT_OBJECT_0;
-
-      // - Process a socket event
-
-      if (sockets[index].is_event) {
-        // Process a general Win32 event
-        // NB: The handler must reset the event!
-
-        if (!sockets[index].handler->processEvent(events[index])) {
-          removeSocket(index);
-          continue;
-        }
-      } else if (sockets[index].is_conn) {
-        // Process data from an active connection
-
-        // Cancel event notification for this socket
-        if (WSAEventSelect(sockets[index].fd, events[index], 0) == SOCKET_ERROR)
-          vlog.info("unable to disable WSAEventSelect:%u", WSAGetLastError());
-
-        // Reset the event object
-        WSAResetEvent(events[index]);
-
-        // Call the socket server to process the event
-        if (!sockets[index].server->processSocketEvent(sockets[index].sock.conn)) {
-          removeSocket(index);
-          continue;
-        }
-
-        // Re-instate the required socket event
-        // If the read event is still valid, the event object gets set here
-        if (WSAEventSelect(sockets[index].fd, events[index], FD_READ | FD_CLOSE) == SOCKET_ERROR)
-          throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
-
-      } else {
-        // Accept an incoming connection
-        vlog.debug("accepting incoming connection");
-
-        // What kind of event is this?
-        WSANETWORKEVENTS network_events;
-        WSAEnumNetworkEvents(sockets[index].fd, events[index], &network_events);
-        if (network_events.lNetworkEvents & FD_ACCEPT) {
-          network::Socket* new_sock = sockets[index].sock.listener->accept();
-          if ((sockets[index].server)->getDisable()) {
-            delete new_sock;
-            new_sock = 0;
-          }
-          if (new_sock) {
-            sockets[index].server->addClient(new_sock);
-            addSocket(new_sock, sockets[index].server);
-          }
-        } else if (network_events.lNetworkEvents & FD_CLOSE) {
-          vlog.info("deleting listening socket");
-          network::SocketListener* s = sockets[index].sock.listener;
-          removeSocket(index);
-          delete s;
-        } else {
-          vlog.error("unknown network event for listener");
-        }
-
-      }
-    } else if (result == WAIT_FAILED) {
-      throw rdr::SystemException("unable to wait for events", GetLastError());
-    }
+    // addEvent is the last thing we do, so that the event is NOT registered if previous steps fail
+    if (!event || !addEvent(event, this))
+      throw rdr::Exception("Unable to add listener");
+  } catch (rdr::Exception& e) {
+    if (event)
+      WSACloseEvent(event);
+    delete sock_;
+    vlog.error(e.str());
+    throw;
   }
-}
 
-
-void SocketManager::resizeArrays(int numSockets) {
-  if (nAvail >= numSockets) return;
-  while (nAvail < numSockets)
-    nAvail = max(16, nAvail*2);
-
-  SocketInfo* newinfo = new SocketInfo[nAvail];
-  HANDLE* newevents = new HANDLE[nAvail];
-  for (int i=0; i<nSockets; i++) {
-    newinfo[i] = sockets[i];
-    newevents[i] = events[i];
-  }
-  delete [] sockets;
-  delete [] events;
-  sockets = newinfo;
-  events = newevents;
-}
-
-void SocketManager::addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server) {
-  resizeArrays(nSockets+1);
-
-  sockets[nSockets].sock.conn = sock;
-  sockets[nSockets].fd = sock->getFd();
-  sockets[nSockets].server = server;
-  events[nSockets] = event;
-  sockets[nSockets].is_conn = true;
-  sockets[nSockets].is_event = false;
-
-  if (WSAEventSelect(sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
-    throw rdr::SystemException("unable to select on socket", WSAGetLastError());
-  nSockets++;
-}
-
-void SocketManager::addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server) {
-  resizeArrays(nSockets+1);
-
-  sockets[nSockets].sock.listener = sock;
-  sockets[nSockets].fd = sock->getFd();
-  sockets[nSockets].server = server;
-  events[nSockets] = event;
-  sockets[nSockets].is_conn = false;
-  sockets[nSockets].is_event = false;
-
-  if (WSAEventSelect(sock->getFd(), event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
-    throw rdr::SystemException("unable to select on listener", WSAGetLastError());
-  nSockets++;
+  ListenInfo li;
+  li.sock = sock_;
+  li.server = srvr;
+  li.notifier = acn;
+  listeners[event] = li;
 }
 
 void SocketManager::remListener(network::SocketListener* sock) {
-  for (int index=0; index<nSockets; index++) {
-    if (!sockets[index].is_conn &&
-        !sockets[index].is_event) {
-      vlog.debug("removing listening socket");
-      removeSocket(index);
+  std::map<HANDLE,ListenInfo>::iterator i;
+  for (i=listeners.begin(); i!=listeners.end(); i++) {
+    if (i->second.sock == sock) {
+      removeEvent(i->first);
+      WSACloseEvent(i->first);
       delete sock;
+      listeners.erase(i);
+      return;
+    }
+  }
+  throw rdr::Exception("Listener not registered");
+}
+
+
+void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing) {
+  WSAEVENT event = WSACreateEvent();
+  if (!event || !addEvent(event, this) ||
+      (WSAEventSelect(sock_->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)) {
+    if (event)
+      WSACloseEvent(event);
+    delete sock_;
+    vlog.error("Unable to add connection");
+    return;
+  }
+  ConnInfo ci;
+  ci.sock = sock_;
+  ci.server = srvr;
+  connections[event] = ci;
+  srvr->addSocket(sock_, outgoing);
+}
+
+void SocketManager::remSocket(network::Socket* sock_) {
+  std::map<HANDLE,ConnInfo>::iterator i;
+  for (i=connections.begin(); i!=connections.end(); i++) {
+    if (i->second.sock == sock_) {
+      i->second.server->removeSocket(sock_);
+      removeEvent(i->first);
+      WSACloseEvent(i->first);
+      delete sock_;
+      connections.erase(i);
+      return;
+    }
+  }
+  throw rdr::Exception("Socket not registered");
+}
+
+
+int SocketManager::checkTimeouts() {
+  network::SocketServer* server = 0;
+  int timeout = EventManager::checkTimeouts();
+
+  std::map<HANDLE,ListenInfo>::iterator i;
+  for (i=listeners.begin(); i!=listeners.end(); i++)
+    soonestTimeout(&timeout, i->second.server->checkTimeouts());
+
+  std::list<network::Socket*> shutdownSocks;
+  std::map<HANDLE,ConnInfo>::iterator j, j_next;
+  for (j=connections.begin(); j!=connections.end(); j=j_next) {
+    j_next = j; j_next++;
+    if (j->second.sock->isShutdown())
+      shutdownSocks.push_back(j->second.sock);
+  }
+
+  std::list<network::Socket*>::iterator k;
+  for (k=shutdownSocks.begin(); k!=shutdownSocks.end(); k++)
+    remSocket(*k);
+
+  return timeout;
+}
+
+
+void SocketManager::processEvent(HANDLE event) {
+  if (listeners.count(event)) {
+    ListenInfo li = listeners[event];
+
+    // Accept an incoming connection
+    vlog.debug("accepting incoming connection");
+
+    // What kind of event is this?
+    WSANETWORKEVENTS network_events;
+    WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events);
+    if (network_events.lNetworkEvents & FD_ACCEPT) {
+      network::Socket* new_sock = li.sock->accept();
+      if (new_sock && li.server->getDisable()) {
+        delete new_sock;
+        new_sock = 0;
+      }
+      if (new_sock)
+        addSocket(new_sock, li.server, false);
+    } else if (network_events.lNetworkEvents & FD_CLOSE) {
+      vlog.info("deleting listening socket");
+      remListener(li.sock);
+    } else if (network_events.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) {
+      li.notifier->processAddressChange(li.sock);
+      DWORD dummy = 0;
+      requestAddressChangeEvents(li.sock);
+    } else {
+      vlog.error("unknown listener event: %lx", network_events.lNetworkEvents);
+    }
+  } else if (connections.count(event)) {
+    ConnInfo ci = connections[event];
+
+    try {
+      // Process data from an active connection
+
+      // Cancel event notification for this socket
+      if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR)
+        throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError());
+
+      // Reset the event object
+      WSAResetEvent(event);
+
+      // Call the socket server to process the event
+      ci.server->processSocketEvent(ci.sock);
+      if (ci.sock->isShutdown()) {
+        remSocket(ci.sock);
+        return;
+      }
+
+      // Re-instate the required socket event
+      // If the read event is still valid, the event object gets set here
+      if (WSAEventSelect(ci.sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+        throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
+    } catch (rdr::Exception& e) {
+      vlog.error(e.str());
+      remSocket(ci.sock);
     }
   }
 }
-
-void SocketManager::addEvent(HANDLE event, EventHandler* ecb) {
-  resizeArrays(nSockets+1);
-
-  sockets[nSockets].handler = ecb;
-  events[nSockets] = event;
-  sockets[nSockets].is_conn = false;
-  sockets[nSockets].is_event = true;
-
-  nSockets++;
-}
-
-void SocketManager::removeSocket(int index) {
-  if (index >= nSockets)
-    throw rdr::Exception("attempting to remove unregistered socket");
-
-  if (!sockets[index].is_event)
-    WSACloseEvent(events[index]);
-
-  for (int i=index; i<nSockets-1; i++) {
-    sockets[i] = sockets[i+1];
-    events[i] = events[i+1];
-  }
-
-  nSockets--;
-}
-
diff --git a/rfb_win32/SocketManager.h b/rfb_win32/SocketManager.h
index 791370f..ef35974 100644
--- a/rfb_win32/SocketManager.h
+++ b/rfb_win32/SocketManager.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -30,23 +30,32 @@
 #ifndef __RFB_WIN32_SOCKET_MGR_H__
 #define __RFB_WIN32_SOCKET_MGR_H__
 
-#include <list>
-
+#include <map>
 #include <network/Socket.h>
-#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/EventManager.h>
 
 namespace rfb {
-
   namespace win32 {
 
-    class SocketManager {
+    class SocketManager : public EventManager, EventHandler {
     public:
       SocketManager();
       virtual ~SocketManager();
 
+      // AddressChangeNotifier callback interface
+      // If an object implementing this is passed to addListener then it will be
+      // called whenever the SocketListener's address list changes
+      class AddressChangeNotifier {
+      public:
+        virtual ~AddressChangeNotifier() {}
+        virtual void processAddressChange(network::SocketListener* sl) = 0;
+      };
+
       // Add a listening socket.  Incoming connections will be added to the supplied
       // SocketServer.
-      void addListener(network::SocketListener* sock_, network::SocketServer* srvr);
+      void addListener(network::SocketListener* sock_,
+                       network::SocketServer* srvr,
+                       AddressChangeNotifier* acn = 0);
 
       // Remove and delete a listening socket.
       void remListener(network::SocketListener* sock);
@@ -54,50 +63,24 @@
       // Add an already-connected socket.  Socket events will cause the supplied
       // SocketServer to be called.  The socket must ALREADY BE REGISTERED with
       // the SocketServer.
-      void addSocket(network::Socket* sock_, network::SocketServer* srvr);
-
-      // Add a Win32 event & handler for it to the SocketManager
-      // This event will be blocked on along with the registered Sockets, and the
-      // handler called whenever it is discovered to be set.
-      // NB: SocketManager does NOT call ResetEvent on the event!
-      // NB: If processEvent returns false then the event is no longer registered,
-      //     and the event object is assumed to have been closed by processEvent()
-      struct EventHandler {
-        virtual ~EventHandler() {}
-        virtual bool processEvent(HANDLE event) = 0;
-      };
-      void addEvent(HANDLE event, EventHandler* ecb);
-
-      // getMessage
-      //
-      // Either return a message from the thread's message queue or process a socket
-      // event.
-      // Returns whenever a message needs processing.  Returns false if message is
-      // WM_QUIT, true for all other messages.
-      BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+      void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true);
 
     protected:
-      void addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server);
-      void addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server);
-      void resizeArrays(int numSockets);
-      void removeSocket(int index);
-      struct SocketInfo {
-        union {
-          network::Socket* conn;
-          network::SocketListener* listener;
-        } sock;
-        SOCKET fd;
-        bool is_conn;
-        bool is_event;
-        union {
-          network::SocketServer* server;
-          EventHandler* handler;
-        };
+      virtual int checkTimeouts();
+      virtual void processEvent(HANDLE event);
+      virtual void remSocket(network::Socket* sock);
+
+      struct ConnInfo {
+        network::Socket* sock;
+        network::SocketServer* server;
       };
-      SocketInfo* sockets;
-      HANDLE* events;
-      int nSockets;
-      int nAvail;
+      struct ListenInfo {
+        network::SocketListener* sock;
+        network::SocketServer* server;
+        AddressChangeNotifier* notifier;
+      };
+      std::map<HANDLE, ListenInfo> listeners;
+      std::map<HANDLE, ConnInfo> connections;
    };
 
   }
diff --git a/rfb_win32/TCharArray.cxx b/rfb_win32/TCharArray.cxx
index f8f03a6..fd4c078 100644
--- a/rfb_win32/TCharArray.cxx
+++ b/rfb_win32/TCharArray.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/TCharArray.h b/rfb_win32/TCharArray.h
index 399e00a..dde63b7 100644
--- a/rfb_win32/TCharArray.h
+++ b/rfb_win32/TCharArray.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -40,10 +40,10 @@
 #ifndef __RFB_WIN32_TCHARARRAY_H__
 #define __RFB_WIN32_TCHARARRAY_H__
 
-#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
 #include <tchar.h>
-
 #include <rfb/util.h>
+#include <rfb/Password.h>
 
 namespace rfb {
 
@@ -98,6 +98,20 @@
     WCHAR* buf;
   };
 
+  // -=- Wide-character-based password-buffer handler.  Zeroes the password
+  //     buffer when deleted or replaced.
+  class WPlainPasswd : public WCharArray {
+  public:
+    WPlainPasswd() {}
+    WPlainPasswd(WCHAR* str) : WCharArray(str) {}
+    ~WPlainPasswd() {replaceBuf(0);}
+    void replaceBuf(WCHAR* str) {
+      if (buf)
+        memset(buf, 0, sizeof(WCHAR)*wcslen(buf));
+      WCharArray::replaceBuf(str);
+    }
+  };
+    
 #ifdef _UNICODE
 #define tstrDup wstrDup
 #define tstrFree wstrFree
@@ -105,6 +119,7 @@
 #define tstrContains wstrContains
   typedef WCharArray TCharArray;
   typedef WStr TStr;
+  typedef WPlainPasswd TPlainPasswd;
 #else
 #define tstrDup strDup
 #define tstrFree strFree
@@ -112,8 +127,9 @@
 #define tstrContains strContains
   typedef CharArray TCharArray;
   typedef CStr TStr;
+  typedef PlainPasswd TPlainPasswd;
 #endif
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/rfb_win32/Threading.cxx b/rfb_win32/Threading.cxx
new file mode 100644
index 0000000..c41ac38
--- /dev/null
+++ b/rfb_win32/Threading.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Threading.cxx
+// Win32 Threading interface implementation
+
+#include <malloc.h>
+
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rfb_win32/Threading.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Threading");
+
+static DWORD threadStorage = TlsAlloc();
+
+
+inline void logAction(Thread* t, const char* action) {
+  vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t);
+}
+
+inline void logError(Thread* t, const char* err) {
+  vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err);
+}
+
+
+DWORD WINAPI
+Thread::threadProc(LPVOID lpParameter) {
+  Thread* thread = (Thread*) lpParameter;
+  TlsSetValue(threadStorage, thread);
+  logAction(thread, "started");
+  try {
+    thread->run();
+    logAction(thread, "stopped");
+  } catch (rdr::Exception& e) {
+    logError(thread, e.str());
+  }
+  bool deleteThread = false;
+  {
+    Lock l(thread->mutex);
+    thread->state = ThreadStopped;
+    thread->sig->signal();
+    deleteThread = thread->deleteAfterRun;
+  }
+  if (deleteThread)
+    delete thread;
+  return 0;
+}
+
+Thread::Thread(const char* name_) : name(strDup(name_ ? name_ : "Unnamed")), sig(0), deleteAfterRun(false) {
+  sig = new Condition(mutex);
+  cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL);
+  thread.h = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);
+  state = ThreadCreated;
+  logAction(this, "created");
+}
+
+Thread::Thread(HANDLE thread_, DWORD thread_id_) : name(strDup("Native")), sig(0), deleteAfterRun(false),
+  thread(thread_), thread_id(thread_id_) {
+  cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL);
+  state = ThreadNative;
+  logAction(this, "created");
+}
+
+Thread::~Thread() {
+  logAction(this, "destroying");
+  if (!deleteAfterRun && state != ThreadNative)
+    this->join();
+  if (sig)
+    delete sig;
+  logAction(this, "destroyed");
+}
+
+void
+Thread::run() {
+}
+
+void
+Thread::start() {
+  Lock l(mutex);
+  if (state == ThreadCreated) {
+    state = ThreadStarted;
+    sig->signal();
+    ResumeThread(thread);
+  }
+}
+
+Thread*
+Thread::join() {
+  if (deleteAfterRun)
+    throw rdr::Exception("attempt to join() with deleteAfterRun thread");
+  Lock l(mutex);
+  if (state == ThreadJoined) {
+    logAction(this, "already joined");
+  } else {
+    logAction(this, "joining");
+    while (state == ThreadStarted) {
+      sig->wait();
+      logAction(this, "checking");
+    }
+    state = ThreadJoined;
+    logAction(this, "joined");
+  }
+  return this;
+}
+
+const char*
+Thread::getName() const {
+  return name.buf;
+}
+
+ThreadState
+Thread::getState() const {
+  return state;
+}
+
+unsigned long
+Thread::getThreadId() const {
+  return thread_id;
+}
+
+
+Thread*
+Thread::self() {
+  Thread* thread = (Thread*) TlsGetValue(threadStorage);
+  if (!thread) {
+    // *** memory leak - could use GetExitCodeThread to lazily detect when
+    //     to clean up native thread objects
+    thread = new Thread(GetCurrentThread(), GetCurrentThreadId());
+    TlsSetValue(threadStorage, thread);
+  }
+  return thread;
+}
diff --git a/rfb_win32/Threading.h b/rfb_win32/Threading.h
new file mode 100644
index 0000000..850f04d
--- /dev/null
+++ b/rfb_win32/Threading.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Threading_win32.h
+// Win32 Threading interface implementation
+
+#ifndef __RFB_THREADING_IMPL_WIN32
+#define __RFB_THREADING_IMPL_WIN32
+
+#define __RFB_THREADING_IMPL WIN32
+
+#include <rfb_win32/Handle.h>
+#include <rfb/util.h>
+#include <rdr/Exception.h>
+//#include <stdio.h>
+
+
+namespace rfb {
+
+  class Mutex {
+  public:
+    Mutex() {
+      InitializeCriticalSection(&crit);
+    }
+    ~Mutex() {
+      DeleteCriticalSection(&crit);
+    }
+    friend class Lock;
+    friend class Condition;
+  protected:
+    void enter() {EnterCriticalSection(&crit);}
+    void exit() {LeaveCriticalSection(&crit);}
+    CRITICAL_SECTION crit;
+  };
+
+  class Lock {
+  public:
+    Lock(Mutex& m) : mutex(m) {m.enter();}
+    ~Lock() {mutex.exit();}
+  protected:
+    Mutex& mutex;
+  };
+
+  enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadJoined, ThreadNative};
+
+  class Thread {
+  public:
+    Thread(const char* name_=0);
+    virtual ~Thread();
+
+    virtual void run();
+
+    virtual void start();
+    virtual Thread* join();
+
+    const char* getName() const;
+    ThreadState getState() const;
+
+    // Determines whether the thread should delete itself when run() returns
+    // If you set this, you must NEVER call join()!
+    void setDeleteAfterRun() {deleteAfterRun = true;};
+
+    unsigned long getThreadId() const;
+
+    static Thread* self();
+
+    friend class Condition;
+
+  protected:
+    Thread(HANDLE thread_, DWORD thread_id_);
+    static DWORD WINAPI threadProc(LPVOID lpParameter);
+
+    win32::Handle thread;
+    DWORD thread_id;
+    CharArray name;
+    ThreadState state;
+    Condition* sig;
+    Mutex mutex;
+
+    win32::Handle cond_event;
+	  Thread* cond_next;
+
+    bool deleteAfterRun;
+  };
+
+  class Condition {
+  public:
+    Condition(Mutex& m) : mutex(m), waiting(0) {
+    }
+    ~Condition() {
+    }
+
+    // Wake up the specified number of threads that are waiting
+    // on this Condition, or all of them if -1 is specified.
+    void signal(int howMany=1) {
+      Lock l(cond_lock);
+      while (waiting && howMany!=0) {
+        SetEvent(waiting->cond_event);
+        waiting = waiting->cond_next;
+        if (howMany>0) --howMany;
+      }
+    }
+
+    // NB: Must hold "mutex" to call wait()
+    // Wait until either the Condition is signalled or the timeout
+    // expires.
+    void wait(DWORD timeout=INFINITE) {
+      Thread* self = Thread::self();
+      ResetEvent(self->cond_event);
+      { Lock l(cond_lock);
+        self->cond_next = waiting;
+        waiting = self;
+      }
+      mutex.exit();
+      DWORD result = WaitForSingleObject(self->cond_event, timeout);
+      mutex.enter();
+      if (result == WAIT_TIMEOUT) {
+        Lock l(cond_lock);
+        // Remove this thread from the Condition
+        for (Thread** removeFrom = &waiting; *removeFrom; removeFrom = &(*removeFrom)->cond_next) {
+          if (*removeFrom == self) {
+            *removeFrom = self->cond_next;
+            break;
+          }
+        }
+      } else if (result == WAIT_FAILED) {
+        throw rdr::SystemException("failed to wait on Condition", GetLastError());
+      }
+    }
+    
+  protected:
+    Mutex& mutex;
+    Mutex cond_lock;
+	  Thread* waiting;
+  };
+
+};
+
+#endif // __RFB_THREADING_IMPL
diff --git a/rfb_win32/TrayIcon.h b/rfb_win32/TrayIcon.h
index 85680f3..dc5102a 100644
--- a/rfb_win32/TrayIcon.h
+++ b/rfb_win32/TrayIcon.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -23,7 +23,6 @@
 #ifndef __RFB_WIN32_TRAY_ICON_H__
 #define __RFB_WIN32_TRAY_ICON_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <shellapi.h>
 #include <rfb_win32/MsgWindow.h>
diff --git a/rfb_win32/TsSessions.cxx b/rfb_win32/TsSessions.cxx
new file mode 100644
index 0000000..efe7564
--- /dev/null
+++ b/rfb_win32/TsSessions.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb_win32/TsSessions.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+#ifdef ERROR_CTX_WINSTATION_BUSY
+#define RFB_HAVE_WINSTATION_CONNECT
+#else
+#pragma message("  NOTE: Not building WinStationConnect support.")
+#endif
+
+static rfb::LogWriter vlog("TsSessions");
+
+namespace rfb {
+namespace win32 {
+
+  // Windows XP (and later) functions used to handle session Ids
+  typedef BOOLEAN (WINAPI *_WinStationConnect_proto) (HANDLE,ULONG,ULONG,PCWSTR,ULONG);
+  DynamicFn<_WinStationConnect_proto> _WinStationConnect(_T("winsta.dll"), "WinStationConnectW");
+  typedef DWORD (WINAPI *_WTSGetActiveConsoleSessionId_proto) ();
+  DynamicFn<_WTSGetActiveConsoleSessionId_proto> _WTSGetActiveConsoleSessionId(_T("kernel32.dll"), "WTSGetActiveConsoleSessionId");
+  typedef BOOL (WINAPI *_ProcessIdToSessionId_proto) (DWORD, DWORD*);
+  DynamicFn<_ProcessIdToSessionId_proto> _ProcessIdToSessionId(_T("kernel32.dll"), "ProcessIdToSessionId");
+  typedef BOOL (WINAPI *_LockWorkStation_proto)();
+  DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+
+
+  ProcessSessionId::ProcessSessionId(DWORD processId) {
+    id = 0;
+    if (!_ProcessIdToSessionId.isValid())
+      return;
+    if (processId == -1)
+      processId = GetCurrentProcessId();
+    if (!(*_ProcessIdToSessionId)(GetCurrentProcessId(), &id))
+      throw rdr::SystemException("ProcessIdToSessionId", GetLastError());
+  }
+
+  ProcessSessionId mySessionId;
+
+  ConsoleSessionId::ConsoleSessionId() {
+    if (_WTSGetActiveConsoleSessionId.isValid())
+      id = (*_WTSGetActiveConsoleSessionId)();
+    else
+      id = 0;
+  }
+
+  bool inConsoleSession() {
+    ConsoleSessionId console;
+    return console.id == mySessionId.id;
+  }
+
+  void setConsoleSession(DWORD sessionId) {
+#ifdef RFB_HAVE_WINSTATION_CONNECT
+    if (!_WinStationConnect.isValid())
+      throw rdr::Exception("WinSta APIs missing");
+    if (sessionId == -1)
+      sessionId = mySessionId.id;
+
+    // Try to reconnect our session to the console
+    ConsoleSessionId console;
+    vlog.info("Console session is %d", console.id);
+    if (!(*_WinStationConnect)(0, sessionId, console.id, L"", 0))
+      throw rdr::SystemException("Unable to connect session to Console", GetLastError());
+
+    // Lock the newly connected session, for security
+    if (_LockWorkStation.isValid())
+      (*_LockWorkStation)();
+#else
+    throw rdr::Exception("setConsoleSession not implemented");
+#endif
+  }
+
+};
+};
diff --git a/rfb_win32/TsSessions.h b/rfb_win32/TsSessions.h
new file mode 100644
index 0000000..b15ada7
--- /dev/null
+++ b/rfb_win32/TsSessions.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// Windows version-independent Terminal Services Session discovery
+// and manipulation API.  This code will eventually be replaced
+// by the full TS-compatibility scheme.
+
+#ifndef __RFB_WIN32_TSSESSIONS_H__
+#define __RFB_WIN32_TSSESSIONS_H__
+
+#include <windows.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    struct SessionId {
+      DWORD id;
+    };
+
+    // Session Id for a given process
+    struct ProcessSessionId : SessionId {
+      ProcessSessionId(DWORD processId = -1);
+    };
+
+    // Session Id for current process
+    extern ProcessSessionId mySessionId;
+
+    // Current console Session Id
+    struct ConsoleSessionId : SessionId {
+      ConsoleSessionId();
+    };
+
+    // Check whether the process is in the Console session at present
+    bool inConsoleSession();
+
+    // Make the specified session the Console session.
+    //   If sessionId is -1 then the process' session is
+    //   made the Console session.
+    void setConsoleSession(DWORD sessionId = -1);
+
+  };
+
+};
+
+#endif
diff --git a/rfb_win32/WMCursor.cxx b/rfb_win32/WMCursor.cxx
index 871d937..4d696cb 100644
--- a/rfb_win32/WMCursor.cxx
+++ b/rfb_win32/WMCursor.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -20,11 +20,10 @@
 
 // *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6
 // *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE
-#define WINVER 0x0500
 
 #include <rfb_win32/WMCursor.h>
 #include <rfb_win32/OSVersion.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb/Exception.h>
 #include <rfb/LogWriter.h>
 
@@ -35,12 +34,19 @@
 static LogWriter vlog("WMCursor");
 
 
+#ifdef CURSOR_SHOWING
+#define RFB_HAVE_GETCURSORINFO
+#else
+#pragma message("  NOTE: Not building GetCursorInfo support.")
+#endif
+
+#ifdef RFB_HAVE_GETCURSORINFO
 typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci);
 DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo");
+#endif
 
-
-WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) {
-#if (WINVER >= 0x0500)
+WMCursor::WMCursor() : hooks(0), use_getCursorInfo(false), cursor(0) {
+#ifdef RFB_HAVE_GETCURSORINFO
   // Check the OS version
   bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
     (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0));
@@ -48,13 +54,12 @@
 
   // Use GetCursorInfo if OS version is sufficient
   use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid();
-#else
-#pragma message ("not building in GetCursorInfo support")
 #endif
+  cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
   if (!use_getCursorInfo) {
     hooks = new WMCursorHooks();
     if (hooks && hooks->start()) {
-      vlog.info("falling back to cursor hooking");
+      vlog.info("falling back to cursor hooking: %p", hooks);
     } else {
       delete hooks;
       hooks = 0;
@@ -63,18 +68,18 @@
   } else {
     vlog.info("using GetCursorInfo");
   }
-  cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
 }
 
 WMCursor::~WMCursor() {
-  if (hooks) delete hooks;
-  if (library) FreeLibrary(library);
+  vlog.debug("deleting WMCursorHooks (%p)", hooks);
+  if (hooks)
+    delete hooks;
 }
   
 WMCursor::Info
 WMCursor::getCursorInfo() {
   Info result;
-#if (WINVER >= 0x0500)
+#ifdef RFB_HAVE_GETCURSORINFO
   if (use_getCursorInfo) {
     CURSORINFO info;
     info.cbSize = sizeof(CURSORINFO);
@@ -88,7 +93,8 @@
 #endif
   // Fall back to the old way of doing things
   POINT pos;
-  if (hooks) cursor = hooks->getCursor();
+  if (hooks)
+    cursor = hooks->getCursor();
   result.cursor = cursor;
   result.visible = cursor != 0;
   GetCursorPos(&pos);
diff --git a/rfb_win32/WMCursor.h b/rfb_win32/WMCursor.h
index a96822a..41f9ee8 100644
--- a/rfb_win32/WMCursor.h
+++ b/rfb_win32/WMCursor.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,12 +25,10 @@
 #ifndef __RFB_WIN32_WM_CURSOR_H__
 #define __RFB_WIN32_WM_CURSOR_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <rfb_win32/WMHooks.h>
 
 namespace rfb {
-
   namespace win32 {
 
     class WMCursor {
@@ -53,13 +51,11 @@
       Info getCursorInfo();
     protected:
       WMCursorHooks* hooks;
-      HMODULE library;
       bool use_getCursorInfo;
       HCURSOR cursor;
     };
 
   };
-
 };
 
 #endif // __RFB_WIN32_WM_CURSOR_H__
diff --git a/rfb_win32/WMHooks.cxx b/rfb_win32/WMHooks.cxx
index 26a2363..2d69053 100644
--- a/rfb_win32/WMHooks.cxx
+++ b/rfb_win32/WMHooks.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,10 +18,11 @@
 
 // -=- WMHooks.cxx
 
-#include <wm_hooks/wm_hooks.h>
-
 #include <rfb_win32/WMHooks.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/IntervalTimer.h>
 #include <rfb/Threading.h>
 #include <rfb/LogWriter.h>
 
@@ -32,11 +33,29 @@
 
 static LogWriter vlog("WMHooks");
 
+
+typedef UINT (*WM_Hooks_WMVAL_proto)();
+typedef BOOL (*WM_Hooks_Install_proto)(DWORD owner, DWORD thread);
+typedef BOOL (*WM_Hooks_Remove_proto)(DWORD owner);
+typedef BOOL (*WM_Hooks_EnableCursorShape_proto)(BOOL enable);
+#ifdef _DEBUG
+typedef void (*WM_Hooks_SetDiagnosticRange_proto)(UINT min, UINT max);
+DynamicFn<WM_Hooks_SetDiagnosticRange_proto> WM_Hooks_SetDiagnosticRange(_T("wm_hooks.dll"), "WM_Hooks_SetDiagnosticRange");
+#endif
+
+
 class WMHooksThread : public Thread {
 public:
-  WMHooksThread() : Thread("WMHookThread"), active(true) {}
+  WMHooksThread() : Thread("WMHookThread"), active(true),
+    WM_Hooks_Install(_T("wm_hooks.dll"), "WM_Hooks_Install"),
+    WM_Hooks_Remove(_T("wm_hooks.dll"), "WM_Hooks_Remove"),
+    WM_Hooks_EnableCursorShape(_T("wm_hooks.dll"), "WM_Hooks_EnableCursorShape") {
+  }
   virtual void run();
   virtual Thread* join();
+  DynamicFn<WM_Hooks_Install_proto> WM_Hooks_Install;;
+  DynamicFn<WM_Hooks_Remove_proto> WM_Hooks_Remove;
+  DynamicFn<WM_Hooks_EnableCursorShape_proto> WM_Hooks_EnableCursorShape;
 protected:
   bool active;
 };
@@ -48,52 +67,62 @@
 HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
 
 
-bool
-StartHookThread() {
-  if (hook_mgr) return true;
-  vlog.debug("opening hook thread");
+static bool StartHookThread() {
+  if (hook_mgr)
+    return true;
+  vlog.debug("creating thread");
   hook_mgr = new WMHooksThread();
-  if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) {
+  if (!hook_mgr->WM_Hooks_Install.isValid() ||
+      !hook_mgr->WM_Hooks_Remove.isValid()) {
+    vlog.debug("hooks not available");
+    return false;
+  }
+  vlog.debug("installing hooks");
+  if (!(*hook_mgr->WM_Hooks_Install)(hook_mgr->getThreadId(), 0)) {
     vlog.error("failed to initialise hooks");
     delete hook_mgr->join();
     hook_mgr = 0;
     return false;
   }
+  vlog.debug("starting thread");
   hook_mgr->start();
   return true;
 }
 
-void
-StopHookThread() {
-  if (!hook_mgr) return;
-  if (!hooks.empty() || !cursor_hooks.empty()) return;
-  vlog.debug("closing hook thread");
+static void StopHookThread() {
+  if (!hook_mgr)
+    return;
+  if (!hooks.empty() || !cursor_hooks.empty())
+    return;
+  vlog.debug("closing thread");
   delete hook_mgr->join();
   hook_mgr = 0;
 }
 
 
-bool
-AddHook(WMHooks* hook) {
+static bool AddHook(WMHooks* hook) {
   vlog.debug("adding hook");
   Lock l(hook_mgr_lock);
-  if (!StartHookThread()) return false;
+  if (!StartHookThread())
+    return false;
   hooks.push_back(hook);
   return true;
 }
 
-bool
-AddCursorHook(WMCursorHooks* hook) {
+static bool AddCursorHook(WMCursorHooks* hook) {
   vlog.debug("adding cursor hook");
   Lock l(hook_mgr_lock);
-  if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE);
-  if (!StartHookThread()) return false;
+  if (!StartHookThread())
+    return false;
+  if (!hook_mgr->WM_Hooks_EnableCursorShape.isValid())
+    return false;
+  if (cursor_hooks.empty() && !(*hook_mgr->WM_Hooks_EnableCursorShape)(TRUE))
+    return false;
   cursor_hooks.push_back(hook);
   return true;
 }
 
-bool
-RemHook(WMHooks* hook) {
+static bool RemHook(WMHooks* hook) {
   {
     vlog.debug("removing hook");
     Lock l(hook_mgr_lock);
@@ -103,76 +132,107 @@
   return true;
 }
 
-bool
-RemCursorHook(WMCursorHooks* hook) {
+static bool RemCursorHook(WMCursorHooks* hook) {
   {
     vlog.debug("removing cursor hook");
     Lock l(hook_mgr_lock);
     cursor_hooks.remove(hook);
+    if (hook_mgr->WM_Hooks_EnableCursorShape.isValid() &&
+        cursor_hooks.empty())
+      (*hook_mgr->WM_Hooks_EnableCursorShape)(FALSE);
   }
   StopHookThread();
-  if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE);
   return true;
 }
 
-void
-NotifyHooksRegion(const Region& r) {
+static void NotifyHooksRegion(const Region& r) {
   Lock l(hook_mgr_lock);
   std::list<WMHooks*>::iterator i;
-  for (i=hooks.begin(); i!=hooks.end(); i++) {
-    (*i)->new_changes.add_changed(r);
-    if (!(*i)->notified) {
-      (*i)->notified = true;
-      PostMessage((*i)->getHandle(), WM_USER, 0, 0);
-    }
-  }
+  for (i=hooks.begin(); i!=hooks.end(); i++)
+    (*i)->NotifyHooksRegion(r);
 }
 
-void
-NotifyHooksCursor(HCURSOR c) {
+static void NotifyHooksCursor(HCURSOR c) {
   Lock l(hook_mgr_lock);
   hook_cursor = c;
 }
 
+
+static UINT GetMsgVal(DynamicFn<WM_Hooks_WMVAL_proto>& fn) {
+  if (fn.isValid())
+    return (*fn)();
+  return WM_NULL;
+}
+
 void
 WMHooksThread::run() {
-  UINT windowMsg = WM_Hooks_WindowChanged();
-  UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged();
-  UINT borderMsg = WM_Hooks_WindowBorderChanged();
-  UINT rectangleMsg = WM_Hooks_RectangleChanged();
-  UINT cursorMsg = WM_Hooks_CursorChanged();
+  // Obtain message ids for all supported hook messages
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowBorderChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowBorderChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowClientAreaChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowClientAreaChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_RectangleChanged(_T("wm_hooks.dll"), "WM_Hooks_RectangleChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_CursorChanged(_T("wm_hooks.dll"), "WM_Hooks_CursorChanged");
+  UINT windowMsg = GetMsgVal(WM_Hooks_WindowChanged);
+  UINT clientAreaMsg = GetMsgVal(WM_Hooks_WindowClientAreaChanged);
+  UINT borderMsg = GetMsgVal(WM_Hooks_WindowBorderChanged);
+  UINT rectangleMsg = GetMsgVal(WM_Hooks_RectangleChanged);
+  UINT cursorMsg = GetMsgVal(WM_Hooks_CursorChanged);
 #ifdef _DEBUG
-  UINT diagnosticMsg = WM_Hooks_Diagnostic();
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_Diagnostic(_T("wm_hooks.dll"), "WM_Hooks_Diagnostic");
+  UINT diagnosticMsg = GetMsgVal(WM_Hooks_Diagnostic);
 #endif
   MSG msg;
   RECT wrect;
   HWND hwnd;
   int count = 0;
 
+  // Update delay handling
+  //   We delay updates by 40-80ms, so that the triggering application has time to
+  //   actually complete them before we notify the hook callbacks & they go off
+  //   capturing screen state.
+  const int updateDelayMs = 40;
+  MsgWindow updateDelayWnd(_T("WMHooks::updateDelay"));
+  IntervalTimer updateDelayTimer(updateDelayWnd.getHandle(), 1);
+  Region updates[2];
+  int activeRgn = 0;
+
   vlog.debug("starting hook thread");
 
   while (active && GetMessage(&msg, NULL, 0, 0)) {
     count++;
-    if (msg.message == windowMsg) {
+
+    if (msg.message == WM_TIMER) {
+      // Actually notify callbacks of graphical updates
+      NotifyHooksRegion(updates[1-activeRgn]);
+      if (updates[activeRgn].is_empty())
+        updateDelayTimer.stop();
+      activeRgn = 1-activeRgn;
+      updates[activeRgn].clear();
+
+    } else if (msg.message == windowMsg) {
+      // An entire window has (potentially) changed
       hwnd = (HWND) msg.lParam;
       if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
-          GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
-      {
-        NotifyHooksRegion(Rect(wrect.left, wrect.top,
-                               wrect.right, wrect.bottom));
-
+        GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) {
+          updates[activeRgn].assign_union(Rect(wrect.left, wrect.top,
+                                               wrect.right, wrect.bottom));
+          updateDelayTimer.start(updateDelayMs);
       }
+
     } else if (msg.message == clientAreaMsg) {
+      // The client area of a window has (potentially) changed
       hwnd = (HWND) msg.lParam;
       if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
           GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
       {
         POINT pt = {0,0};
         if (ClientToScreen(hwnd, &pt)) {
-          NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y,
-                                 wrect.right+pt.x, wrect.bottom+pt.y));
+          updates[activeRgn].assign_union(Rect(wrect.left+pt.x, wrect.top+pt.y,
+                                               wrect.right+pt.x, wrect.bottom+pt.y));
+          updateDelayTimer.start(updateDelayMs);
         }
       }
+
     } else if (msg.message == borderMsg) {
       hwnd = (HWND) msg.lParam;
       if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
@@ -187,14 +247,19 @@
           changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y,
                                        crect.right+pt.x, crect.bottom+pt.y));
         }
-        NotifyHooksRegion(changed);
+        if (!changed.is_empty()) {
+          updates[activeRgn].assign_union(changed);
+          updateDelayTimer.start(updateDelayMs);
+        }
       }
     } else if (msg.message == rectangleMsg) {
       Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
                     LOWORD(msg.lParam), HIWORD(msg.lParam));
       if (!r.is_empty()) {
-        NotifyHooksRegion(r);
+        updates[activeRgn].assign_union(r);
+        updateDelayTimer.start(updateDelayMs);
       }
+
     } else if (msg.message == cursorMsg) {
       NotifyHooksCursor((HCURSOR)msg.lParam);
 #ifdef _DEBUG
@@ -205,7 +270,7 @@
   }
 
   vlog.debug("stopping hook thread - processed %d events", count);
-  WM_Hooks_Remove(getThreadId());
+  (*WM_Hooks_Remove)(getThreadId());
 }
 
 Thread*
@@ -219,65 +284,52 @@
 
 // -=- WMHooks class
 
-rfb::win32::WMHooks::WMHooks()
-  : clipper(0), new_changes(true), fg_window(0),
-  notified(false), MsgWindow(_T("WMHooks")) {
+rfb::win32::WMHooks::WMHooks() : updateEvent(0) {
 }
 
 rfb::win32::WMHooks::~WMHooks() {
   RemHook(this);
-  if (clipper) delete clipper;
 }
 
-LRESULT
-rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-  case WM_USER:
-    {
-      // *** Yield, to allow the triggering update event to be processed
-      //     BEFORE we try to grab the resulting changes.
-      // *** IMPROVES THINGS NOTICABLY ON WinXP
-      Sleep(0);
-      // ***
-
-      Lock l(hook_mgr_lock);
-      notified = false;
-      new_changes.get_update(*clipper);
-      new_changes.clear();
-    }
-    break;
-  }
-  return MsgWindow::processMessage(msg, wParam, lParam);
+bool rfb::win32::WMHooks::setEvent(HANDLE ue) {
+  if (updateEvent)
+    RemHook(this);
+  updateEvent = ue;
+  return AddHook(this);
 }
 
-bool
-rfb::win32::WMHooks::setClipRect(const Rect& r) {
-  clip_region = r;
-  if (clipper) clipper->set_clip_region(clip_region);
+bool rfb::win32::WMHooks::getUpdates(UpdateTracker* ut) {
+  if (!updatesReady) return false;
+  Lock l(hook_mgr_lock);
+  updates.copyTo(ut);
+  updates.clear();
+  updatesReady = false;
   return true;
 }
 
-bool
-rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
-  if (clipper) delete clipper;
-  clipper = new ClippedUpdateTracker(*ut);
-  clipper->set_clip_region(clip_region);
-  return AddHook(this);
+bool rfb::win32::WMHooks::areAvailable() {
+  WMHooksThread wmht;
+  return wmht.WM_Hooks_Install.isValid();
 }
 
 #ifdef _DEBUG
 void
 rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
-  WM_Hooks_SetDiagnosticRange(min, max);
+  if (WM_Hooks_SetDiagnosticRange.isValid())
+    (*WM_Hooks_SetDiagnosticRange)(min, max);
 }
 #endif
 
+void rfb::win32::WMHooks::NotifyHooksRegion(const Region& r) {
+  // hook_mgr_lock is already held at this point
+  updates.add_changed(r);
+  updatesReady = true;
+  SetEvent(updateEvent);
+}
+
 
 // -=- WMBlockInput class
 
-Mutex blockMutex;
-int blockCount = 0;
-
 rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
 }
 
@@ -285,22 +337,40 @@
   blockInputs(false);
 }
 
-bool rfb::win32::WMBlockInput::blockInputs(bool on) {
-  if (on == active) return true;
-  vlog.debug("blockInput changed");
-  Lock l(blockMutex);
-  int newCount = blockCount;
-  if (on)
-    newCount++;
-  else
-    newCount--;
-  if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) {
-    vlog.debug("set blocking to %d", newCount);
-    blockCount = newCount;
-    active = on;
-    return true;
+typedef BOOL (*WM_Hooks_EnableRealInputs_proto)(BOOL pointer, BOOL keyboard);
+DynamicFn<WM_Hooks_EnableRealInputs_proto>* WM_Hooks_EnableRealInputs = 0;
+static bool blockRealInputs(bool block_) {
+  // NB: Requires blockMutex to be held!
+  if (block_) {
+    if (WM_Hooks_EnableRealInputs)
+      return true;
+    // Enable blocking
+    WM_Hooks_EnableRealInputs = new DynamicFn<WM_Hooks_EnableRealInputs_proto>(_T("wm_hooks.dll"), "WM_Hooks_EnableRealInputs");
+    if (WM_Hooks_EnableRealInputs->isValid() && (**WM_Hooks_EnableRealInputs)(false, false))
+      return true;
   }
-  return false;
+  if (WM_Hooks_EnableRealInputs) {
+    // Clean up the DynamicFn, either if init failed, or block_ is false
+    if (WM_Hooks_EnableRealInputs->isValid())
+      (**WM_Hooks_EnableRealInputs)(true, true);
+    delete WM_Hooks_EnableRealInputs;
+    WM_Hooks_EnableRealInputs = 0;
+  }
+  return block_ == (WM_Hooks_EnableRealInputs != 0);
+}
+
+Mutex blockMutex;
+int blockCount = 0;
+
+bool rfb::win32::WMBlockInput::blockInputs(bool on) {
+  if (active == on) return true;
+  Lock l(blockMutex);
+  int newCount = on ? blockCount+1 : blockCount-1;
+  if (!blockRealInputs(newCount > 0))
+    return false;
+  blockCount = newCount;
+  active = on;
+  return true;
 }
 
 
diff --git a/rfb_win32/WMHooks.h b/rfb_win32/WMHooks.h
index 791df76..4713b41 100644
--- a/rfb_win32/WMHooks.h
+++ b/rfb_win32/WMHooks.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -21,43 +21,50 @@
 #ifndef __RFB_WIN32_WM_HOOKS_H__
 #define __RFB_WIN32_WM_HOOKS_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <rfb/UpdateTracker.h>
 #include <rdr/Exception.h>
-#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/Win32Util.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class WMHooks : public MsgWindow {
+    // -=- WMHooks
+    //     Uses the wm_hooks DLL to intercept window messages, to get _hints_ as
+    //     to what may have changed on-screen.  Updates are notified via a Win32
+    //     event, and retrieved using the getUpdates method, which is thread-safe.
+    class WMHooks {
     public:
       WMHooks();
       ~WMHooks();
 
-      bool setClipRect(const Rect& cr);
-      bool setUpdateTracker(UpdateTracker* ut);
+      // Specify the event object to notify.  Starts the hook subsystem if it is
+      // not already active, and returns false if the hooks fail to start.
+      bool setEvent(HANDLE updateEvent);
 
-      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+      // Copies any new updates to the UpdateTracker.  Returns true if new updates
+      // were added, false otherwise.
+      bool getUpdates(UpdateTracker* ut);
+
+      // Determine whether the hooks DLL is installed on the system
+      static bool areAvailable();
 
 #ifdef _DEBUG
       // Get notifications of any messages in the given range, to any hooked window
       void setDiagnosticRange(UINT min, UINT max);
 #endif
 
+      // * INTERNAL NOTIFICATION FUNCTION * 
+      void NotifyHooksRegion(const Region& r);
     protected:
-      ClippedUpdateTracker* clipper;
-      Region clip_region;
-
-      void* fg_window;
-      Rect fg_window_rect;
-
-    public:
-      SimpleUpdateTracker new_changes;
-      bool notified;
+      HANDLE updateEvent;
+      bool updatesReady;
+      SimpleUpdateTracker updates;
     };
 
+    // -=- Support for filtering out local input events while remote connections are
+    //     active.  Implemented using SetWindowsHookEx for portability.
     class WMBlockInput {
     public:
       WMBlockInput();
diff --git a/rfb_win32/WMNotifier.cxx b/rfb_win32/WMNotifier.cxx
index 9773abf..20a5445 100644
--- a/rfb_win32/WMNotifier.cxx
+++ b/rfb_win32/WMNotifier.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -30,7 +30,7 @@
 static LogWriter vlog("WMMonitor");
 
 
-WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")) {
+WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")), notifier(0) {
 }
 
 WMMonitor::~WMMonitor() {
diff --git a/rfb_win32/WMNotifier.h b/rfb_win32/WMNotifier.h
index 564d176..a760964 100644
--- a/rfb_win32/WMNotifier.h
+++ b/rfb_win32/WMNotifier.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/WMPoller.cxx b/rfb_win32/WMPoller.cxx
index f568b21..f850534 100644
--- a/rfb_win32/WMPoller.cxx
+++ b/rfb_win32/WMPoller.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -35,35 +35,19 @@
 
 // -=- WMPoller class
 
-rfb::win32::WMPoller::WMPoller() : clipper(0) {
-}
-
-rfb::win32::WMPoller::~WMPoller() {
-  if (clipper) delete clipper;
-}
-
 bool
 rfb::win32::WMPoller::processEvent() {
   PollInfo info;
-  if (clipper && poll_console_windows) {
+  if (poll_console_windows && ut) {
     ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info);
-    clipper->add_changed(info.poll_include);
+    ut->add_changed(info.poll_include);
   }
   return false;
 }
 
 bool
-rfb::win32::WMPoller::setClipRect(const Rect& r) {
-  clip_region = r;
-  if (clipper) clipper->set_clip_region(clip_region);
-  return true;
-}
-
-bool
-rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut) {
-  if (clipper) delete clipper;
-  clipper = new ClippedUpdateTracker(*ut);
-  clipper->set_clip_region(clip_region);
+rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut_) {
+  ut = ut_;
   return true;
 }
 
diff --git a/rfb_win32/WMPoller.h b/rfb_win32/WMPoller.h
index 3f3f402..851b69f 100644
--- a/rfb_win32/WMPoller.h
+++ b/rfb_win32/WMPoller.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -28,7 +28,6 @@
 #ifndef __RFB_WIN32_WM_POLLER_H__
 #define __RFB_WIN32_WM_POLLER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <rfb/UpdateTracker.h>
 #include <rfb/Configuration.h>
@@ -39,11 +38,9 @@
 
     class WMPoller {
     public:
-      WMPoller();
-      ~WMPoller();
+      WMPoller() : ut(0) {}
 
       bool processEvent();
-      bool setClipRect(const Rect& cr);
       bool setUpdateTracker(UpdateTracker* ut);
 
       static BoolParameter poll_console_windows;
@@ -55,9 +52,7 @@
       static bool checkPollWindow(HWND w);
       static void pollWindow(HWND w, PollInfo* info);
       static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp);
-
-      ClippedUpdateTracker* clipper;
-      Region clip_region;
+      UpdateTracker* ut;
     };
 
   };
diff --git a/rfb_win32/WMShatter.cxx b/rfb_win32/WMShatter.cxx
index f6a7484..e68abfb 100644
--- a/rfb_win32/WMShatter.cxx
+++ b/rfb_win32/WMShatter.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/WMShatter.h b/rfb_win32/WMShatter.h
index 7b81678..3ea63b1 100644
--- a/rfb_win32/WMShatter.h
+++ b/rfb_win32/WMShatter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -33,11 +33,9 @@
 #ifndef __RFB_WIN32_SHATTER_H__
 #define __RFB_WIN32_SHATTER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace rfb {
-
   namespace win32 {
 
     bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
@@ -47,7 +45,6 @@
     LRESULT SafeDispatchMessage(const MSG* msg);
 
   };
-
 };
 
 #endif // __RFB_WIN32_SHATTER_H__
diff --git a/rfb_win32/WMWindowCopyRect.cxx b/rfb_win32/WMWindowCopyRect.cxx
index 46d85ea..63c1da2 100644
--- a/rfb_win32/WMWindowCopyRect.cxx
+++ b/rfb_win32/WMWindowCopyRect.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -29,55 +29,39 @@
 
 // -=- WMHooks class
 
-rfb::win32::WMCopyRect::WMCopyRect() : clipper(0), fg_window(0) {
-}
-
-rfb::win32::WMCopyRect::~WMCopyRect() {
-  if (clipper) delete clipper;
+rfb::win32::WMCopyRect::WMCopyRect() : ut(0), fg_window(0) {
 }
 
 bool
 rfb::win32::WMCopyRect::processEvent() {
-  if (clipper) {
-    // See if the foreground window has moved
-    HWND window = GetForegroundWindow();
-    if (window) {
-      RECT wrect;
-      if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
-        Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
-        if (fg_window == window) {
-
-          if (!fg_window_rect.tl.equals(winrect.tl)) {
-            // Window has moved - send a copyrect event to the client
-            Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
-            Region copy_dest = winrect;
-            clipper->add_copied(copy_dest, delta);
-            clipper->add_changed(Region(fg_window_rect).subtract(copy_dest));
-          }
+  // See if the foreground window has moved
+  HWND window = GetForegroundWindow();
+  if (window) {
+    RECT wrect;
+    if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
+      Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+      if (fg_window == window) {
+        if (!fg_window_rect.tl.equals(winrect.tl)  && ut) {
+          // Window has moved - send a copyrect event to the client
+          Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
+          Region copy_dest = winrect;
+          ut->add_copied(copy_dest, delta);
+          ut->add_changed(Region(fg_window_rect).subtract(copy_dest));
         }
-        fg_window = window;
-        fg_window_rect = winrect;
-      } else {
-        fg_window = 0;
       }
+      fg_window = window;
+      fg_window_rect = winrect;
     } else {
       fg_window = 0;
     }
+  } else {
+    fg_window = 0;
   }
   return false;
 }
 
 bool
-rfb::win32::WMCopyRect::setClipRect(const Rect& r) {
-  clip_region = r;
-  if (clipper) clipper->set_clip_region(clip_region);
-  return true;
-}
-
-bool
-rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut) {
-  if (clipper) delete clipper;
-  clipper = new ClippedUpdateTracker(*ut);
-  clipper->set_clip_region(clip_region);
+rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut_) {
+  ut = ut_;
   return true;
 }
diff --git a/rfb_win32/WMWindowCopyRect.h b/rfb_win32/WMWindowCopyRect.h
index 0750d86..5a0e876 100644
--- a/rfb_win32/WMWindowCopyRect.h
+++ b/rfb_win32/WMWindowCopyRect.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -36,15 +36,12 @@
     class WMCopyRect {
     public:
       WMCopyRect();
-      ~WMCopyRect();
 
       bool processEvent();
-      bool setClipRect(const Rect& cr);
       bool setUpdateTracker(UpdateTracker* ut);
 
     protected:
-      ClippedUpdateTracker* clipper;
-      Region clip_region;
+      UpdateTracker* ut;
       void* fg_window;
       Rect fg_window_rect;
     };
diff --git a/rfb_win32/Win32Util.cxx b/rfb_win32/Win32Util.cxx
index 28fae2e..ef8039a 100644
--- a/rfb_win32/Win32Util.cxx
+++ b/rfb_win32/Win32Util.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,221 +18,30 @@
 
 // Win32Util.cxx
 
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb_win32/Win32Util.h>
-#include <rdr/Exception.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Handle.h>
 #include <rdr/HexOutStream.h>
-
+#include <rdr/Exception.h>
 
 namespace rfb {
 namespace win32 {
 
-LogicalPalette::LogicalPalette() : palette(0), numEntries(0) {
-  BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
-  LOGPALETTE* logpal = (LOGPALETTE*)buf;
-  logpal->palVersion = 0x300;
-  logpal->palNumEntries = 256;
-  for (int i=0; i<256;i++) {
-    logpal->palPalEntry[i].peRed = 0;
-    logpal->palPalEntry[i].peGreen = 0;
-    logpal->palPalEntry[i].peBlue = 0;
-    logpal->palPalEntry[i].peFlags = 0;
-  }
-  palette = CreatePalette(logpal);
-  if (!palette)
-    throw rdr::SystemException("failed to CreatePalette", GetLastError());
-}
-
-LogicalPalette::~LogicalPalette() {
-  if (palette)
-    if (!DeleteObject(palette))
-      throw rdr::SystemException("del palette failed", GetLastError());
-}
-
-void LogicalPalette::setEntries(int start, int count, const Colour* cols) {
-  if (numEntries < count) {
-    ResizePalette(palette, start+count);
-    numEntries = start+count;
-  }
-  PALETTEENTRY* logpal = new PALETTEENTRY[count];
-  for (int i=0; i<count; i++) {
-    logpal[i].peRed = cols[i].r >> 8;
-    logpal[i].peGreen = cols[i].g >> 8;
-    logpal[i].peBlue = cols[i].b >> 8;
-    logpal[i].peFlags = 0;
-  }
-  UnrealizeObject(palette);
-  SetPaletteEntries(palette, start, count, logpal);
-  delete [] logpal;
-}
-
-
-static LogWriter dcLog("DeviceContext");
-
-PixelFormat DeviceContext::getPF() const {
-  return getPF(dc);
-}
-
-PixelFormat DeviceContext::getPF(HDC dc) {
-  PixelFormat format;
-  CompatibleBitmap bitmap(dc, 1, 1);
-
-  // -=- Get the bitmap format information
-  BitmapInfo bi;
-  memset(&bi, 0, sizeof(bi));
-  bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-  bi.bmiHeader.biBitCount = 0;
-
-  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
-    throw rdr::SystemException("unable to determine device pixel format", GetLastError());
-  }
-  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
-    throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
-  }
-
-  // -=- Munge the bitmap info here
-  switch (bi.bmiHeader.biBitCount) {
-  case 1:
-  case 4:
-    bi.bmiHeader.biBitCount = 8;
-    break;
-  case 24:
-    bi.bmiHeader.biBitCount = 32;
-    break;
-  }
-  bi.bmiHeader.biPlanes = 1;
-
-  format.trueColour = bi.bmiHeader.biBitCount > 8;
-  format.bigEndian = 0;
-  format.bpp = format.depth = bi.bmiHeader.biBitCount;
-
-  if (format.trueColour) {
-    DWORD rMask=0, gMask=0, bMask=0;
-
-    // Which true colour format is the DIB section using?
-    switch (bi.bmiHeader.biCompression) {
-    case BI_RGB:
-      // Default RGB layout
-      switch (bi.bmiHeader.biBitCount) {
-      case 16:
-        // RGB 555 - High Colour
-        dcLog.info("16-bit High Color");
-        rMask = 0x7c00;
-        bMask = 0x001f;
-        gMask = 0x03e0;
-        format.depth = 15;
-        break;
-      case 24:
-      case 32:
-        // RGB 888 - True Colour
-        dcLog.info("24/32-bit High Color");
-        rMask = 0xff0000;
-        gMask = 0x00ff00;
-        bMask = 0x0000ff;
-        format.depth = 24;
-        break;
-      default:
-        dcLog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
-        throw rdr::Exception("unknown bits per pixel specified");
-      };
-      break;
-    case BI_BITFIELDS:
-      // Custom RGB layout
-      rMask = bi.mask.red;
-      gMask = bi.mask.green;
-      bMask = bi.mask.blue;
-      dcLog.info("BitFields format: %lu, (%lx, %lx, %lx)",
-        bi.bmiHeader.biBitCount, rMask, gMask, bMask);
-      if (format.bpp == 32)
-        format.depth = 24; // ...probably
-      break;
-    };
-
-    // Convert the data we just retrieved
-    initMaxAndShift(rMask, &format.redMax, &format.redShift);
-    initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
-    initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
-  }
-
-  return format;
-}
-
-
-WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
-  dc = GetDC(wnd);
-  if (!dc)
-    throw rdr::SystemException("GetDC failed", GetLastError());
-}
-WindowDC::~WindowDC() {
-  if (dc)
-    ReleaseDC(hwnd, dc);
-}
-
-
-CompatibleDC::CompatibleDC(HDC existing) {
-  dc = CreateCompatibleDC(existing);
-  if (!dc)
-    throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
-}
-CompatibleDC::~CompatibleDC() {
-  if (dc)
-    DeleteDC(dc);
-}
-
-
-BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
-  oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
-  if (!oldBitmap)
-    throw rdr::SystemException("SelectObject to CompatibleDC failed",
-    GetLastError());
-}
-BitmapDC::~BitmapDC() {
-  SelectObject(dc, oldBitmap);
-}
-
-
-CompatibleBitmap::CompatibleBitmap(HDC hdc, int width, int height) {
-  hbmp = CreateCompatibleBitmap(hdc, width, height);
-  if (!hbmp)
-    throw rdr::SystemException("CreateCompatibleBitmap() failed", 
-    GetLastError());
-}
-CompatibleBitmap::~CompatibleBitmap() {
-  if (hbmp) DeleteObject(hbmp);
-}
-
-
-PaletteSelector::PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
-  oldPal = SelectPalette(dc, pal, FALSE);
-  redrawRequired = RealizePalette(dc) > 0;
-}
-PaletteSelector::~PaletteSelector() {
-  if (oldPal) SelectPalette(device, oldPal, TRUE);
-}
-
-
-IconInfo::IconInfo(HICON icon) {
-  if (!GetIconInfo(icon, this))
-    throw rdr::SystemException("GetIconInfo() failed", GetLastError());
-}
-IconInfo::~IconInfo() {
-  if (hbmColor)
-    DeleteObject(hbmColor);
-  if (hbmMask)
-    DeleteObject(hbmMask);
-}
-
-
-ModuleFileName::ModuleFileName(HMODULE module) : TCharArray(MAX_PATH) {
-  if (!module) module = GetModuleHandle(0);
-  if (!GetModuleFileName(module, buf, MAX_PATH))
-    buf[0] = 0;
-}
-
 
 FileVersionInfo::FileVersionInfo(const TCHAR* filename) {
   // Get executable name
   ModuleFileName exeName;
-  if (!filename) filename = exeName.buf;
+  if (!filename)
+    filename = exeName.buf;
+
+  // Attempt to open the file, to cause Access Denied, etc, errors
+  // to be correctly reported, since the GetFileVersionInfoXXX calls lie...
+  {
+    Handle file(CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
+	  if (file.h == INVALID_HANDLE_VALUE)
+      throw rdr::SystemException("Failed to open file", GetLastError());
+  }
 
   // Get version info size
   DWORD handle;
@@ -249,7 +58,7 @@
 const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) {
   char langIdBuf[sizeof(langId)];
   for (int i=sizeof(langIdBuf)-1; i>=0; i--) {
-    langIdBuf[i] = langId & 0xff;
+    langIdBuf[i] = (char) (langId & 0xff);
     langId = langId >> 8;
   }
 
@@ -273,173 +82,31 @@
 }
 
 
-static LogWriter dfbLog("DynamicFn");
-
-DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
-  dllHandle = LoadLibrary(dllName);
-  if (!dllHandle) {
-    dfbLog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
-    return;
-  }
-  fnPtr = GetProcAddress(dllHandle, fnName);
-  if (!fnPtr)
-    dfbLog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
-}
-
-DynamicFnBase::~DynamicFnBase() {
-  if (dllHandle)
-    FreeLibrary(dllHandle);
-}
-
-
-static LogWriter miLog("MonitorInfo");
-
-MonitorInfo::MonitorInfo(HWND window) {
-#if (WINVER >= 0x0500)
-  typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
-  rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
-  typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
-  rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
-
-  // Can we dynamically link to the monitor functions?
-  if (_MonitorFromWindow.isValid()) {
-    if (_GetMonitorInfo.isValid()) {
-      HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
-      miLog.debug("monitor=%lx", monitor);
-      if (monitor) {
-        memset(this, 0, sizeof(MONITORINFOEXA));
-        cbSize = sizeof(MONITORINFOEXA);
-        if ((*_GetMonitorInfo)(monitor, this)) {
-          miLog.debug("monitor is %d,%d-%d,%d", rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom);
-          miLog.debug("work area is %d,%d-%d,%d", rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
-          miLog.debug("device is \"%s\"", szDevice);
-          return;
-        }
-        miLog.error("failed to get monitor info: %ld", GetLastError());
-      }
-    } else {
-      miLog.debug("GetMonitorInfo not found");
-    }
-  } else {
-      miLog.debug("MonitorFromWindow not found");
-  }
-#else
-#pragma message ("not building in GetMonitorInfo")
-  cbSize = sizeof(MonitorInfo);
-  szDevice[0] = 0;
-#endif
-
-  // Legacy fallbacks - just return the desktop settings
-  miLog.debug("using legacy fall-backs");
-  HWND desktop = GetDesktopWindow();
-  GetWindowRect(desktop, &rcMonitor);
-  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
-  dwFlags = 0;
-}
-
-
-#if (WINVER >= 0x0500)
-
-struct moveToMonitorData {
-  HWND window;
-  const char* monitorName;
-};
-
-typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
-static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
-
-static BOOL CALLBACK moveToMonitorEnumProc(HMONITOR monitor,
-                                    HDC dc,
-                                    LPRECT pos,
-                                    LPARAM d) {
-  moveToMonitorData* data = (moveToMonitorData*)d;
-  MONITORINFOEXA info;
-  memset(&info, 0, sizeof(info));
-  info.cbSize = sizeof(info);
-
-  if ((*_GetMonitorInfo)(monitor, &info)) {
-    if (stricmp(data->monitorName, info.szDevice) == 0) {
-      SetWindowPos(data->window, 0,
-        info.rcMonitor.left, info.rcMonitor.top,
-        info.rcMonitor.right, info.rcMonitor.bottom,
-        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
-      return FALSE;
-    }
-  }
-
-  return TRUE;
-}
-
-#endif
-
-void moveToMonitor(HWND handle, const char* device) {
-  miLog.debug("moveToMonitor %s", device);
-
-#if (WINVER >= 0x500)
-  typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
-  rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
-  if (!_EnumDisplayMonitors.isValid()) {
-    miLog.debug("EnumDisplayMonitors not found");
-    return;
-  }
-
-  moveToMonitorData data;
-  data.window = handle;
-  data.monitorName = device;
-
-  (*_EnumDisplayMonitors)(0, 0, &moveToMonitorEnumProc, (LPARAM)&data);
-#endif
-}
-
-
-void centerWindow(HWND handle, HWND parent, bool clipToParent) {
+void centerWindow(HWND handle, HWND parent) {
   RECT r;
-  if (parent && IsWindowVisible(parent)) {
-    if (!GetWindowRect(parent, &r)) return;
-  } else {
-    MonitorInfo mi(handle);
+  MonitorInfo mi(parent ? parent : handle);
+  if (!parent || !IsWindowVisible(parent) || !GetWindowRect(parent, &r))
     r=mi.rcWork;
-  }
-  centerWindow(handle, r, clipToParent);
+  centerWindow(handle, r);
+  mi.clipTo(handle);
 }
 
-void centerWindow(HWND handle, const RECT& r, bool clipToRect) {
+void centerWindow(HWND handle, const RECT& r) {
   RECT wr;
   if (!GetWindowRect(handle, &wr)) return;
   int w = wr.right-wr.left;
   int h = wr.bottom-wr.top;
-  if (clipToRect) {
-    w = min(r.right-r.left, w);
-    h = min(r.bottom-r.top, h);
-  }
   int x = (r.left + r.right - w)/2;
   int y = (r.top + r.bottom - h)/2;
-  UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | (clipToRect ? 0 : SWP_NOSIZE);
-  SetWindowPos(handle, 0, x, y, w, h, flags);
+  UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE;
+  SetWindowPos(handle, 0, x, y, 0, 0, flags);
 }
 
-
-int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
-  const TCHAR* msgType = 0;
-  UINT tflags = flags & 0x70;
-  if (tflags == MB_ICONHAND)
-    msgType = _T("Error");
-  else if (tflags == MB_ICONQUESTION)
-    msgType = _T("Question");
-  else if (tflags == MB_ICONEXCLAMATION)
-    msgType = _T("Warning");
-  else if (tflags == MB_ICONASTERISK)
-    msgType = _T("Information");
-  flags |= MB_TOPMOST | MB_SETFOREGROUND;
-  int len = _tcslen(AppName.buf) + 1;
-  if (msgType) len += _tcslen(msgType) + 3;
-  TCharArray title = new TCHAR[len];
-  _tcscpy(title.buf, AppName.buf);
-  if (msgType) {
-    _tcscat(title.buf, _T(" : "));
-    _tcscat(title.buf, msgType);
-  }
-  return MessageBox(parent, msg, title.buf, flags);
+void resizeWindow(HWND handle, int width, int height) {
+  RECT r;
+  GetWindowRect(handle, &r);
+  SetWindowPos(handle, 0, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
+  centerWindow(handle, r);
 }
 
 
diff --git a/rfb_win32/Win32Util.h b/rfb_win32/Win32Util.h
index 5f0ab5a..8cc1a2b 100644
--- a/rfb_win32/Win32Util.h
+++ b/rfb_win32/Win32Util.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -25,105 +25,12 @@
 #ifndef __RFB_WIN32_GDIUTIL_H__
 #define __RFB_WIN32_GDIUTIL_H__
 
-#include <rfb/ColourMap.h>
-#include <rfb/PixelFormat.h>
-#include <rfb/Rect.h>
 #include <rfb_win32/TCharArray.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class LogicalPalette {
-    public:
-      LogicalPalette();
-      ~LogicalPalette();
-      void setEntries(int start, int count, const Colour* cols);
-      HPALETTE getHandle() {return palette;}
-    protected:
-      HPALETTE palette;
-      int numEntries;
-    };
-
-    class DeviceContext {
-    public:
-      DeviceContext() : dc(0) {}
-      virtual ~DeviceContext() {}
-      operator HDC() const {return dc;}
-      PixelFormat getPF() const;
-      static PixelFormat getPF(HDC dc);
-    protected:
-      HDC dc;
-    };
-
-    class WindowDC : public DeviceContext {
-    public:
-      WindowDC(HWND wnd);
-      virtual ~WindowDC();
-    protected:
-      HWND hwnd;
-    };
-
-    class CompatibleDC : public DeviceContext {
-    public:
-      CompatibleDC(HDC existing);
-      virtual ~CompatibleDC();
-    };
-
-    class BitmapDC : public CompatibleDC {
-    public:
-      BitmapDC(HDC hdc, HBITMAP hbitmap);
-      ~BitmapDC();
-    protected:
-      HBITMAP oldBitmap;
-    };
-
-    class CompatibleBitmap {
-    public:
-      CompatibleBitmap(HDC hdc, int width, int height);
-      virtual ~CompatibleBitmap();
-      operator HBITMAP() const {return hbmp;}
-    protected:
-      HBITMAP hbmp;
-    };
-
-    struct BitmapInfo {
-      BITMAPINFOHEADER bmiHeader;
-      union {
-        struct {
-          DWORD red;
-          DWORD green;
-          DWORD blue;
-        } mask;
-        RGBQUAD color[256];
-      };
-    };
-
-    inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
-      for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
-        (*max) = (rdr::U16)mask;
-    }
-
-    class PaletteSelector {
-    public:
-      PaletteSelector(HDC dc, HPALETTE pal);
-      ~PaletteSelector();
-      bool isRedrawRequired() {return redrawRequired;}
-    protected:
-      HPALETTE oldPal;
-      HDC device;
-      bool redrawRequired;
-    };
-
-    struct IconInfo : public ICONINFO {
-      IconInfo(HICON icon);
-      ~IconInfo();
-    };
-
-    struct ModuleFileName : public TCharArray {
-      ModuleFileName(HMODULE module=0);
-    };
-
     struct FileVersionInfo : public TCharArray {
       FileVersionInfo(const TCHAR* filename=0);
       const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0);
@@ -131,84 +38,15 @@
 
     bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file);
 
-    class DynamicFnBase {
-    public:
-      DynamicFnBase(const TCHAR* dllName, const char* fnName);
-      ~DynamicFnBase();
-      bool isValid() const {return fnPtr != 0;}
-    protected:
-      void* fnPtr;
-      HMODULE dllHandle;
-    };
-
-    template<class T> class DynamicFn : public DynamicFnBase {
-    public:
-      DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
-      T operator *() const {return (T)fnPtr;};
-    };
-
-    // Structure containing info on the monitor nearest the window.
-    // Copes with multi-monitor OSes and older ones.
-#if (WINVER >= 0x0500)
-    struct MonitorInfo : MONITORINFOEXA {
-      MonitorInfo(HWND hwnd);
-    };
-#else
-    struct MonitorInfo {
-      MonitorInfo(HWND hwnd);
-      DWORD cbSize;
-      RECT rcMonitor;
-      RECT rcWork;
-      DWORD dwFlags;
-      char szDevice[1]; // Always null...
-    };
-#endif
-    void moveToMonitor(HWND handle, const char* device);
-
-    class Handle {
-    public:
-      Handle(HANDLE h_=0) : h(h_) {}
-      ~Handle() {
-        if (h) CloseHandle(h);
-      }
-      operator HANDLE() {return h;}
-      HANDLE h;
-    };
-
     // Center the window to a rectangle, or to a parent window.
     // Optionally, resize the window to lay within the rect or parent window
     // If the parent window is NULL then the working area if the window's
     // current monitor is used instead.
-    void centerWindow(HWND handle, const RECT& r, bool clipToRect=false);
-    void centerWindow(HWND handle, HWND parent, bool clipToRect=false);
+    void centerWindow(HWND handle, const RECT& r);
+    void centerWindow(HWND handle, HWND parent);
 
-    // MsgBox helper function.  Define rfb::win32::AppName somewhere in your
-    // code and MsgBox will use its value in informational messages.
-    extern TStr AppName;
-    int MsgBox(HWND parent, const TCHAR* message, UINT flags);
-
-    // Get the computer name
-    struct ComputerName : TCharArray {
-      ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
-        ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
-        if (!GetComputerName(buf, &namelength))
-          _tcscpy(buf, _T(""));
-      }
-    };
-
-    // Allocate and/or manage LocalAlloc memory.
-    struct LocalMem {
-      LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
-        if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
-      }
-      LocalMem(void* p) : ptr(p) {}
-      ~LocalMem() {LocalFree(ptr);}
-      operator void*() {return ptr;}
-      void* takePtr() {
-        void* t = ptr; ptr = 0; return t;
-      }
-      void* ptr;
-    };
+    // resizeWindow resizes a window about its center
+    void resizeWindow(HWND handle, int width, int height);
 
   };
 
diff --git a/rfb_win32/keymap.h b/rfb_win32/keymap.h
index 69ce66f..a340d09 100644
--- a/rfb_win32/keymap.h
+++ b/rfb_win32/keymap.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
diff --git a/rfb_win32/rfb_win32.dsp b/rfb_win32/rfb_win32.dsp
index de7f840..984d71b 100644
--- a/rfb_win32/rfb_win32.dsp
+++ b/rfb_win32/rfb_win32.dsp
@@ -134,6 +134,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DeviceContext.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\DeviceFrameBuffer.cxx

 # End Source File

 # Begin Source File

@@ -146,6 +150,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DynamicFn.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\EventManager.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\FolderManager.cxx

 # End Source File

 # Begin Source File

@@ -158,6 +170,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\MonitorInfo.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\MsgWindow.cxx

 # End Source File

 # Begin Source File

@@ -186,6 +202,18 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\SDisplayCorePolling.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\SDisplayCoreWMHooks.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\Security.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\Service.cxx

 # End Source File

 # Begin Source File

@@ -202,10 +230,18 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\Threading.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\ToolBar.cxx

 # End Source File

 # Begin Source File

 

+SOURCE=.\TsSessions.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\Win32Util.cxx

 # End Source File

 # Begin Source File

@@ -242,6 +278,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\BitmapInfo.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\CKeyboard.h

 # End Source File

 # Begin Source File

@@ -254,6 +294,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\CompatibleBitmap.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\ComputerName.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\CPointer.h

 # End Source File

 # Begin Source File

@@ -262,6 +310,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DeviceContext.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\DeviceFrameBuffer.h

 # End Source File

 # Begin Source File

@@ -274,10 +326,26 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DynamicFn.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\EventManager.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\FolderManager.h

 # End Source File

 # Begin Source File

 

+SOURCE=.\Handle.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\IconInfo.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\IntervalTimer.h

 # End Source File

 # Begin Source File

@@ -294,6 +362,26 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\LocalMem.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\LogicalPalette.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\ModuleFileName.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\MonitorInfo.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\MsgBox.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\MsgWindow.h

 # End Source File

 # Begin Source File

@@ -322,6 +410,18 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\SDisplayCoreDriver.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\SDisplayCorePolling.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\SDisplayCoreWMHooks.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\Security.h

 # End Source File

 # Begin Source File

@@ -342,6 +442,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\Threading.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\ToolBar.h

 # End Source File

 # Begin Source File

@@ -350,6 +454,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\TsSessions.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\Win32Util.h

 # End Source File

 # Begin Source File