diff --git a/Graphics/Win32.hs b/Graphics/Win32.hs
--- a/Graphics/Win32.hs
+++ b/Graphics/Win32.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -19,18 +21,23 @@
 -----------------------------------------------------------------------------
 
 module Graphics.Win32 (
-	module System.Win32.Types,
-	module Graphics.Win32.Control,
-	module Graphics.Win32.Dialogue,
-	module Graphics.Win32.GDI,
-	module Graphics.Win32.Icon,
-	module Graphics.Win32.Key,
-	module Graphics.Win32.Menu,
-	module Graphics.Win32.Message,
-	module Graphics.Win32.Misc,
-	module Graphics.Win32.Resource,
-	module Graphics.Win32.Window
-	) where
+        module System.Win32.Types,
+        module Graphics.Win32.Control,
+        module Graphics.Win32.Dialogue,
+        module Graphics.Win32.GDI,
+        module Graphics.Win32.Icon,
+        module Graphics.Win32.Key,
+        module Graphics.Win32.Menu,
+        module Graphics.Win32.Message,
+        module Graphics.Win32.Misc,
+        module Graphics.Win32.Resource,
+        module Graphics.Win32.Window,
+        module Graphics.Win32.LayeredWindow,
+        module Graphics.Win32.Window.AnimateWindow,
+        module Graphics.Win32.Window.ForegroundWindow,
+        module Graphics.Win32.Window.IMM,
+        module Graphics.Win32.Window.PostMessage
+        ) where
 
 import System.Win32.Types
 import Graphics.Win32.Control
@@ -43,3 +50,8 @@
 import Graphics.Win32.Misc
 import Graphics.Win32.Resource
 import Graphics.Win32.Window
+import Graphics.Win32.LayeredWindow
+import Graphics.Win32.Window.AnimateWindow
+import Graphics.Win32.Window.ForegroundWindow
+import Graphics.Win32.Window.IMM
+import Graphics.Win32.Window.PostMessage
diff --git a/Graphics/Win32/Control.hsc b/Graphics/Win32/Control.hsc
--- a/Graphics/Win32/Control.hsc
+++ b/Graphics/Win32/Control.hsc
@@ -1,6 +1,4 @@
-#if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Graphics.Win32.Control
@@ -17,13 +15,17 @@
 
 module Graphics.Win32.Control where
 
-import Graphics.Win32.GDI.Types
-import Graphics.Win32.Window
-import System.Win32.Types
-import Graphics.Win32.Message
+import Data.Bits ((.|.))
+import Graphics.Win32.GDI.Types (HMENU, HWND)
+import Graphics.Win32.Message (WindowMessage)
+import Graphics.Win32.Window (ClassName, Pos, WindowStyle, maybePos)
+import Graphics.Win32.Window (c_CreateWindowEx)
+import System.IO.Unsafe (unsafePerformIO)
+import System.Win32.Types (HANDLE, UINT, maybePtr, newTString, withTString)
+import System.Win32.Types (failIfFalse_, failIfNull, failIfZero)
+import Foreign.Ptr (nullPtr)
 
-import Foreign hiding (unsafePerformIO)
-import System.IO.Unsafe
+##include "windows_cconv.h"
 
 #include <windows.h>
 #include <commctrl.h>
@@ -73,20 +75,20 @@
 checkDlgButton :: HWND -> Int -> ButtonState -> IO ()
 checkDlgButton dialog button check =
   failIfFalse_ "CheckDlgButton" $ c_CheckDlgButton dialog button check
-foreign import stdcall unsafe "windows.h CheckDlgButton"
+foreign import WINDOWS_CCONV unsafe "windows.h CheckDlgButton"
   c_CheckDlgButton :: HWND -> Int -> ButtonState -> IO Bool
 
 checkRadioButton :: HWND -> Int -> Int -> Int -> IO ()
 checkRadioButton dialog first_button last_button check =
   failIfFalse_ "CheckRadioButton" $
     c_CheckRadioButton dialog first_button last_button check
-foreign import stdcall unsafe "windows.h CheckRadioButton"
+foreign import WINDOWS_CCONV unsafe "windows.h CheckRadioButton"
   c_CheckRadioButton :: HWND -> Int -> Int -> Int -> IO Bool
 
 isDlgButtonChecked :: HWND -> Int -> IO ButtonState
 isDlgButtonChecked wnd button =
   failIfZero "IsDlgButtonChecked" $ c_IsDlgButtonChecked wnd button
-foreign import stdcall unsafe "windows.h IsDlgButtonChecked"
+foreign import WINDOWS_CCONV unsafe "windows.h IsDlgButtonChecked"
   c_IsDlgButtonChecked :: HWND -> Int -> IO ButtonState
 
 
@@ -324,7 +326,7 @@
       (maybePos mb_x) (maybePos mb_y) (maybePos mb_w) (maybePos mb_h)
       (maybePtr mb_parent) (maybePtr mb_menu) h nullPtr
 
-foreign import stdcall unsafe "windows.h InitCommonControls"
+foreign import WINDOWS_CCONV unsafe "windows.h InitCommonControls"
   initCommonControls :: IO ()
 
 #endif
diff --git a/Graphics/Win32/Dialogue.hsc b/Graphics/Win32/Dialogue.hsc
--- a/Graphics/Win32/Dialogue.hsc
+++ b/Graphics/Win32/Dialogue.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -26,6 +28,8 @@
 import Foreign
 import Foreign.C
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 type DTemplate = LPCTSTR
@@ -43,7 +47,7 @@
 type ResourceID = Int
 
 mkResource :: ResourceID -> IO (Ptr a)
-mkResource res = return (castUINTToPtr (fromIntegral res))
+mkResource res = return (castUINTPtrToPtr (fromIntegral res))
 
 mkDialogTemplateFromResource :: Int -> IO DTemplate
 mkDialogTemplateFromResource = mkResource
@@ -67,7 +71,7 @@
   c_dia_fn <- mkDialogClosure dia_fn
   failIf (== -1) "DialogBoxParam" $
     c_DialogBoxParam inst template (maybePtr mb_parent) c_dia_fn init_val
-foreign import stdcall "windows.h DialogBoxParamW"
+foreign import WINDOWS_CCONV "windows.h DialogBoxParamW"
   c_DialogBoxParam :: HINSTANCE -> DTemplate -> HWND -> FunPtr DialogProc -> LPARAM -> IO Int
 
 dialogBoxIndirect :: HINSTANCE -> DTemplateMem -> Maybe HWND -> DialogProc -> IO Int
@@ -79,7 +83,7 @@
   c_dia_fn <- mkDialogClosure dia_fn
   failIf (== -1) "DialogBoxIndirectParam" $
     c_DialogBoxIndirectParam inst template (maybePtr mb_parent) c_dia_fn init_val
-foreign import stdcall "windows.h DialogBoxIndirectParamW"
+foreign import WINDOWS_CCONV "windows.h DialogBoxIndirectParamW"
   c_DialogBoxIndirectParam :: HINSTANCE -> DTemplateMem -> HWND -> FunPtr DialogProc -> LPARAM -> IO Int
 
 
@@ -92,7 +96,7 @@
       (Either ResourceID String)  -- class
       (Either ResourceID String)  -- caption
       (Either ResourceID String)  -- fontname
-      Int	 		  -- font height
+      Int                         -- font height
       [DialogControl]
 
 data DialogControl
@@ -102,72 +106,72 @@
       (Either ResourceID String) -- classname
       WindowStyle
       DWORD
-      Int			 -- dia_id
+      Int                        -- dia_id
 
 mkDialogFromTemplate :: DialogTemplate -> IO DTemplateMem
 mkDialogFromTemplate (DialogTemplate x y cx cy
-				     wstyle extstyle
-				     mb_menu mb_class caption
-				     font font_height
-				     controls) = do
+                                     wstyle extstyle
+                                     mb_menu mb_class caption
+                                     font font_height
+                                     controls) = do
   prim_hmenu    <- marshall_res mb_menu
   prim_class    <- marshall_res mb_class
   prim_caption  <- marshall_res caption
   prim_font     <- marshall_res font
   dtemp <- mkDiaTemplate 0 x y cx cy wstyle extstyle
-  			 prim_hmenu prim_class
-			 prim_caption prim_font
-			 font_height
+                         prim_hmenu prim_class
+                         prim_caption prim_font
+                         font_height
   mapM_ (addControl dtemp) controls
   getFinalDialog dtemp
 
 pushButtonControl :: Int -> Int -> Int -> Int
-		  -> DWORD -> DWORD -> Int
-		  -> String
-		  -> DialogControl
+                  -> DWORD -> DWORD -> Int
+                  -> String
+                  -> DialogControl
 pushButtonControl x y cx cy style estyle dia_id lab =
   DialogControl x y cx cy (Left 0x0080) (Right lab)
-  		(style + bS_DEFPUSHBUTTON) estyle dia_id
+                (style + bS_DEFPUSHBUTTON) estyle dia_id
 
 labelControl :: Int -> Int -> Int -> Int
-	     -> DWORD -> DWORD -> Int
-	     -> String
+             -> DWORD -> DWORD -> Int
+             -> String
              -> DialogControl
 labelControl x y cx cy style estyle dia_id lab =
   DialogControl x y cx cy (Left 0x0082) (Right lab)
-  		(style + sS_LEFT) estyle dia_id
+                (style + sS_LEFT) estyle dia_id
 
 listBoxControl :: Int -> Int -> Int -> Int
-	       -> DWORD -> DWORD -> Int
-	       -> String
+               -> DWORD -> DWORD -> Int
+               -> String
                -> DialogControl
 listBoxControl x y cx cy style estyle dia_id lab =
   DialogControl x y cx cy (Left 0x0083) (Right lab)
-  		(style) estyle dia_id
+                (style) estyle dia_id
 
 comboBoxControl :: Int -> Int -> Int -> Int
-	       -> DWORD -> DWORD -> Int
-	       -> String
+               -> DWORD -> DWORD -> Int
+               -> String
                -> DialogControl
 comboBoxControl x y cx cy style estyle dia_id lab =
   DialogControl x y cx cy (Left 0x0085) (Right lab)
-  		(style) estyle dia_id
+                (style) estyle dia_id
 
 editControl :: Int -> Int -> Int -> Int
-	       -> DWORD -> DWORD -> Int
-	       -> String
+               -> DWORD -> DWORD -> Int
+               -> String
                -> DialogControl
 editControl x y cx cy style estyle dia_id lab =
   DialogControl x y cx cy (Left 0x0081) (Right lab)
-  		(style + eS_LEFT) estyle dia_id
+                (style + eS_LEFT) estyle dia_id
 
 scrollBarControl :: Int -> Int -> Int -> Int
-	       -> DWORD -> DWORD -> Int
-	       -> String
+               -> DWORD -> DWORD -> Int
+               -> String
                -> DialogControl
 scrollBarControl x y cx cy style estyle dia_id lab =
   DialogControl x y cx cy (Left 0x0084) (Right lab)
-  		(style) estyle dia_id
+                (style) estyle dia_id
 
 foreign import ccall unsafe "diatemp.h getFinalDialog"
   getFinalDialog :: Ptr DIA_TEMPLATE -> IO DTemplateMem
@@ -178,12 +182,12 @@
 
 addControl :: Ptr DIA_TEMPLATE -> DialogControl -> IO ()
 addControl dtemp (DialogControl x y cx cy mb_text mb_class
-				style exstyle
-				dia_id) = do
+                                style exstyle
+                                dia_id) = do
    prim_text  <- marshall_res mb_text
    prim_class <- marshall_res mb_class
    _ <- addDiaControl dtemp prim_text dia_id prim_class style
-  		 x y cx cy exstyle
+                 x y cx cy exstyle
    return ()
 
 foreign import ccall unsafe "diatemp.h addDiaControl"
@@ -207,7 +211,7 @@
   c_dia_fn <- mkDialogClosure dia_fn
   failIfNull "CreateDialogParam" $
     c_CreateDialogParam inst template (maybePtr mb_parent) c_dia_fn init_val
-foreign import stdcall "windows.h CreateDialogParamW"
+foreign import WINDOWS_CCONV "windows.h CreateDialogParamW"
   c_CreateDialogParam :: HINSTANCE -> DTemplate -> HWND -> FunPtr DialogProc -> LPARAM -> IO HWND
 
 createDialogIndirect :: HINSTANCE -> DTemplateMem -> Maybe HWND -> DialogProc -> IO HWND
@@ -219,31 +223,31 @@
   c_dia_fn <- mkDialogClosure dia_fn
   failIfNull "CreateDialogIndirectParam" $
     c_CreateDialogIndirectParam inst template (maybePtr mb_parent) c_dia_fn init_val
-foreign import stdcall "windows.h CreateDialogIndirectParamW"
+foreign import WINDOWS_CCONV "windows.h CreateDialogIndirectParamW"
   c_CreateDialogIndirectParam :: HINSTANCE -> DTemplateMem -> HWND -> FunPtr DialogProc -> LPARAM -> IO HWND
 
-foreign import stdcall "windows.h DefDlgProcW"
+foreign import WINDOWS_CCONV "windows.h DefDlgProcW"
   defDlgProc :: HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
 
 endDialog :: HWND -> Int -> IO ()
 endDialog dlg res =
   failIfFalse_ "EndDialog" $ c_EndDialog dlg res
-foreign import stdcall "windows.h EndDialog"
+foreign import WINDOWS_CCONV "windows.h EndDialog"
   c_EndDialog :: HWND -> Int -> IO BOOL
 
-foreign import stdcall unsafe "windows.h GetDialogBaseUnits"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDialogBaseUnits"
   getDialogBaseUnits :: IO LONG
 
 getDlgCtrlID :: HWND -> IO Int
 getDlgCtrlID ctl =
   failIfZero "GetDlgCtrlID" $ c_GetDlgCtrlID ctl
-foreign import stdcall unsafe "windows.h GetDlgCtrlID"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDlgCtrlID"
   c_GetDlgCtrlID :: HWND -> IO Int
 
 getDlgItem :: HWND -> Int -> IO HWND
 getDlgItem dlg item =
   failIfNull "GetDlgItem" $ c_GetDlgItem dlg item
-foreign import stdcall unsafe "windows.h GetDlgItem"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDlgItem"
   c_GetDlgItem :: HWND -> Int -> IO HWND
 
 getDlgItemInt :: HWND -> Int -> Bool -> IO Int
@@ -252,7 +256,7 @@
   res <- c_GetDlgItemInt dlg item p_trans signed
   failIfFalse_ "GetDlgItemInt" $ peek p_trans
   return (fromIntegral res)
-foreign import stdcall "windows.h GetDlgItemInt"
+foreign import WINDOWS_CCONV "windows.h GetDlgItemInt"
   c_GetDlgItemInt :: HWND -> Int -> Ptr Bool -> Bool -> IO UINT
 
 getDlgItemText :: HWND -> Int -> Int -> IO String
@@ -260,46 +264,46 @@
   allocaArray size $ \ p_buf -> do
   _ <- failIfZero "GetDlgItemInt" $ c_GetDlgItemText dlg item p_buf size
   peekTString p_buf
-foreign import stdcall "windows.h GetDlgItemTextW"
+foreign import WINDOWS_CCONV "windows.h GetDlgItemTextW"
   c_GetDlgItemText :: HWND -> Int -> LPTSTR -> Int -> IO Int
 
 getNextDlgGroupItem :: HWND -> HWND -> BOOL -> IO HWND
 getNextDlgGroupItem dlg ctl previous =
   failIfNull "GetNextDlgGroupItem" $ c_GetNextDlgGroupItem dlg ctl previous
-foreign import stdcall unsafe "windows.h GetNextDlgGroupItem"
+foreign import WINDOWS_CCONV unsafe "windows.h GetNextDlgGroupItem"
   c_GetNextDlgGroupItem :: HWND -> HWND -> BOOL -> IO HWND
 
 getNextDlgTabItem :: HWND -> HWND -> BOOL -> IO HWND
 getNextDlgTabItem dlg ctl previous =
   failIfNull "GetNextDlgTabItem" $ c_GetNextDlgTabItem dlg ctl previous
-foreign import stdcall unsafe "windows.h GetNextDlgTabItem"
+foreign import WINDOWS_CCONV unsafe "windows.h GetNextDlgTabItem"
   c_GetNextDlgTabItem :: HWND -> HWND -> BOOL -> IO HWND
 
-foreign import stdcall "windows.h IsDialogMessageW"
+foreign import WINDOWS_CCONV "windows.h IsDialogMessageW"
   isDialogMessage :: HWND -> LPMSG -> IO BOOL
 
 mapDialogRect :: HWND -> LPRECT -> IO ()
 mapDialogRect dlg p_rect =
   failIfFalse_ "MapDialogRect" $ c_MapDialogRect dlg p_rect
-foreign import stdcall unsafe "windows.h MapDialogRect"
+foreign import WINDOWS_CCONV unsafe "windows.h MapDialogRect"
   c_MapDialogRect :: HWND -> LPRECT -> IO Bool
 
 -- No MessageBox* funs in here just yet.
 
-foreign import stdcall "windows.h SendDlgItemMessageW"
+foreign import WINDOWS_CCONV "windows.h SendDlgItemMessageW"
   sendDlgItemMessage :: HWND -> Int -> WindowMessage -> WPARAM -> LPARAM -> IO LONG
 
 setDlgItemInt :: HWND -> Int -> UINT -> BOOL -> IO ()
 setDlgItemInt dlg item value signed =
   failIfFalse_ "SetDlgItemInt" $ c_SetDlgItemInt dlg item value signed
-foreign import stdcall "windows.h SetDlgItemInt"
+foreign import WINDOWS_CCONV "windows.h SetDlgItemInt"
   c_SetDlgItemInt :: HWND -> Int -> UINT -> BOOL -> IO Bool
 
 setDlgItemText :: HWND -> Int -> String -> IO ()
 setDlgItemText dlg item str =
   withTString str $ \ c_str ->
   failIfFalse_ "SetDlgItemText" $ c_SetDlgItemText dlg item c_str
-foreign import stdcall "windows.h SetDlgItemTextW"
+foreign import WINDOWS_CCONV "windows.h SetDlgItemTextW"
   c_SetDlgItemText :: HWND -> Int -> LPCTSTR -> IO Bool
 
 #{enum WindowStyle,
diff --git a/Graphics/Win32/GDI.hs b/Graphics/Win32/GDI.hs
--- a/Graphics/Win32/GDI.hs
+++ b/Graphics/Win32/GDI.hs
@@ -1,6 +1,4 @@
-#if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Graphics.Win32.GDI
@@ -18,25 +16,25 @@
 -----------------------------------------------------------------------------
 
 {-# OPTIONS_GHC -w #-}
--- The above warning supression flag is a temporary kludge.
+-- The above warning suppression flag is a temporary kludge.
 -- While working on this module you are encouraged to remove it and fix
 -- any warnings in the module. See
---     http://hackage.haskell.org/trac/ghc/wiki/WorkingConventions#Warnings
+--     https://ghc.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
 -- for details
 
 module Graphics.Win32.GDI (
-	module Graphics.Win32.GDI.Bitmap,
-	module Graphics.Win32.GDI.Brush,
-	module Graphics.Win32.GDI.Clip,
-	module Graphics.Win32.GDI.Font,
-	module Graphics.Win32.GDI.Graphics2D,
-	module Graphics.Win32.GDI.HDC,
-	module Graphics.Win32.GDI.Palette,
-	module Graphics.Win32.GDI.Path,
-	module Graphics.Win32.GDI.Pen,
-	module Graphics.Win32.GDI.Region,
-	module Graphics.Win32.GDI.Types
-	) where
+        module Graphics.Win32.GDI.Bitmap,
+        module Graphics.Win32.GDI.Brush,
+        module Graphics.Win32.GDI.Clip,
+        module Graphics.Win32.GDI.Font,
+        module Graphics.Win32.GDI.Graphics2D,
+        module Graphics.Win32.GDI.HDC,
+        module Graphics.Win32.GDI.Palette,
+        module Graphics.Win32.GDI.Path,
+        module Graphics.Win32.GDI.Pen,
+        module Graphics.Win32.GDI.Region,
+        module Graphics.Win32.GDI.Types
+        ) where
 
 import Graphics.Win32.GDI.Bitmap
 import Graphics.Win32.GDI.Brush
diff --git a/Graphics/Win32/GDI/AlphaBlend.hsc b/Graphics/Win32/GDI/AlphaBlend.hsc
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/GDI/AlphaBlend.hsc
@@ -0,0 +1,74 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.GDI.AlphaBlend
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Provides alpha blending  functionality.
+-}
+module Graphics.Win32.GDI.AlphaBlend where
+import Foreign.Storable         ( Storable(..) )
+import Foreign.Ptr              ( Ptr )
+import Graphics.Win32.GDI.Types ( HDC )
+import System.Win32.Types       ( BOOL, BYTE, UINT )
+
+#include <windows.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+
+foreign import ccall unsafe "alphablend.h"
+  c_AlphaBlend :: HDC -> Int -> Int -> Int -> Int -> HDC -> Int -> Int -> Int -> Int -> PBLENDFUNCTION -> IO BOOL
+{-
+We use C wrapper function to call this API.
+Because foreign stacall/ccall/capi doesn't work with non-pointer user defined type.
+
+We think that capi should support that when user defined type has Storable class instance
+and using CTYPE pragma in the scope.
+
+{-# LANGUAGE CApiFFI #-}
+
+data {-# CTYPE "windows.h" "BLENDFUNCTION" #-} BLENDFUNCTION =
+
+foreign import capi unsafe "windows.h AlphaBlend"
+  c_AlphaBlend :: HDC -> Int -> Int -> Int -> Int -> HDC -> Int -> Int -> Int -> Int -> BLENDFUNCTION -> IO BOOL
+-}
+
+foreign import WINDOWS_CCONV unsafe "windows.h TransparentBlt"
+  c_TransparentBlt :: HDC -> Int -> Int -> Int -> Int -> HDC -> Int -> Int -> Int -> Int -> UINT -> IO BOOL
+
+aC_SRC_OVER :: BYTE
+aC_SRC_OVER = #const AC_SRC_OVER
+
+aC_SRC_ALPHA :: BYTE
+aC_SRC_ALPHA = #const AC_SRC_ALPHA
+
+type PBLENDFUNCTION  = Ptr BLENDFUNCTION
+type LPBLENDFUNCTION = Ptr BLENDFUNCTION
+
+data BLENDFUNCTION = BLENDFUNCTION
+    { blendOp     :: BYTE
+    , blendFlags  :: BYTE
+    , sourceConstantAlpha :: BYTE
+    , alphaFormat :: BYTE
+    } deriving (Show)
+
+instance Storable BLENDFUNCTION where
+    sizeOf = const #size BLENDFUNCTION
+    alignment _ = #alignment BLENDFUNCTION
+    poke buf func = do
+        (#poke BLENDFUNCTION, BlendOp)     buf (blendOp func)
+        (#poke BLENDFUNCTION, BlendFlags)  buf (blendFlags func)
+        (#poke BLENDFUNCTION, SourceConstantAlpha) buf (sourceConstantAlpha func)
+        (#poke BLENDFUNCTION, AlphaFormat) buf (alphaFormat func)
+
+    peek buf = do
+        blendOp'     <- (#peek BLENDFUNCTION, BlendOp) buf
+        blendFlags'  <- (#peek BLENDFUNCTION, BlendFlags) buf
+        sourceConstantAlpha' <-
+            (#peek BLENDFUNCTION, SourceConstantAlpha) buf
+        alphaFormat' <- (#peek BLENDFUNCTION, AlphaFormat) buf
+        return $ BLENDFUNCTION blendOp' blendFlags' sourceConstantAlpha' alphaFormat'
diff --git a/Graphics/Win32/GDI/Bitmap.hsc b/Graphics/Win32/GDI/Bitmap.hsc
--- a/Graphics/Win32/GDI/Bitmap.hsc
+++ b/Graphics/Win32/GDI/Bitmap.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,68 +18,68 @@
 -----------------------------------------------------------------------------
 
 module Graphics.Win32.GDI.Bitmap(
-	RasterOp3,
-	RasterOp4,
-	sRCCOPY,
-	sRCPAINT,
-	sRCAND,
-	sRCINVERT,
-	sRCERASE,
-	nOTSRCCOPY,
-	nOTSRCERASE,
-	mERGECOPY,
-	mERGEPAINT,
-	pATCOPY,
-	pATPAINT,
-	pATINVERT,
-	dSTINVERT,
-	bLACKNESS,
-	wHITENESS,
+        RasterOp3,
+        RasterOp4,
+        sRCCOPY,
+        sRCPAINT,
+        sRCAND,
+        sRCINVERT,
+        sRCERASE,
+        nOTSRCCOPY,
+        nOTSRCERASE,
+        mERGECOPY,
+        mERGEPAINT,
+        pATCOPY,
+        pATPAINT,
+        pATINVERT,
+        dSTINVERT,
+        bLACKNESS,
+        wHITENESS,
 
-	mAKEROP4,
+        mAKEROP4,
 
-	BITMAP,
-	LPBITMAP,
-	setBITMAP,
-	deleteBitmap,
-	createCompatibleBitmap,
-	createBitmap,
-	createBitmapIndirect,
-	createDIBPatternBrushPt,
-	getBitmapDimensionEx,
-	setBitmapDimensionEx,
-	getBitmapInfo,
+        BITMAP,
+        LPBITMAP,
+        setBITMAP,
+        deleteBitmap,
+        createCompatibleBitmap,
+        createBitmap,
+        createBitmapIndirect,
+        createDIBPatternBrushPt,
+        getBitmapDimensionEx,
+        setBitmapDimensionEx,
+        getBitmapInfo,
 
-	BitmapCompression,
-	bI_RGB,
-	bI_RLE8,
-	bI_RLE4,
-	bI_BITFIELDS,
+        BitmapCompression,
+        bI_RGB,
+        bI_RLE8,
+        bI_RLE4,
+        bI_BITFIELDS,
 
-	ColorFormat,
-	dIB_PAL_COLORS,
-	dIB_RGB_COLORS,
+        ColorFormat,
+        dIB_PAL_COLORS,
+        dIB_RGB_COLORS,
 
-	LPBITMAPINFO,
-	BITMAPINFOHEADER,
-	LPBITMAPINFOHEADER,
-	getBITMAPINFOHEADER_,
+        LPBITMAPINFO,
+        BITMAPINFOHEADER,
+        LPBITMAPINFOHEADER,
+        getBITMAPINFOHEADER_,
 
-	BITMAPFILEHEADER,
-	LPBITMAPFILEHEADER,
-	getBITMAPFILEHEADER,
+        BITMAPFILEHEADER,
+        LPBITMAPFILEHEADER,
+        getBITMAPFILEHEADER,
 
-	sizeofBITMAP,
-	sizeofBITMAPINFO,
-	sizeofBITMAPINFOHEADER,
-	sizeofBITMAPFILEHEADER,
-	sizeofLPBITMAPFILEHEADER,
+        sizeofBITMAP,
+        sizeofBITMAPINFO,
+        sizeofBITMAPINFOHEADER,
+        sizeofBITMAPFILEHEADER,
+        sizeofLPBITMAPFILEHEADER,
 
-	createBMPFile,
-	cBM_INIT,
-	getDIBits,
-	setDIBits,
-	createDIBitmap
+        createBMPFile,
+        cBM_INIT,
+        getDIBits,
+        setDIBits,
+        createDIBitmap
 
         ) where
 
@@ -89,6 +91,8 @@
 import Foreign.C
 
 #include "Win32Aux.h"
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 ----------------------------------------------------------------
@@ -212,32 +216,32 @@
 deleteBitmap :: HBITMAP -> IO ()
 deleteBitmap bitmap =
   failIfFalse_ "DeleteBitmap" $ c_DeleteBitmap bitmap
-foreign import stdcall unsafe "windows.h DeleteObject"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteObject"
   c_DeleteBitmap :: HBITMAP -> IO Bool
 
 createBitmap :: INT -> INT -> UINT -> UINT -> Maybe LPVOID -> IO HBITMAP
 createBitmap w h planes bits mb_color_data =
   failIfNull "CreateBitmap" $
     c_CreateBitmap w h planes bits (maybePtr mb_color_data)
-foreign import stdcall unsafe "windows.h CreateBitmap"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateBitmap"
   c_CreateBitmap :: INT -> INT -> UINT -> UINT -> LPVOID -> IO HBITMAP
 
 createBitmapIndirect :: LPBITMAP -> IO HBITMAP
 createBitmapIndirect p_bm =
   failIfNull "CreateBitmapIndirect" $ c_CreateBitmapIndirect p_bm
-foreign import stdcall unsafe "windows.h CreateBitmapIndirect"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateBitmapIndirect"
   c_CreateBitmapIndirect :: LPBITMAP -> IO HBITMAP
 
 createCompatibleBitmap :: HDC -> Int32 -> Int32 -> IO HBITMAP
 createCompatibleBitmap dc w h =
   failIfNull "CreateCompatibleBitmap" $ c_CreateCompatibleBitmap dc w h
-foreign import stdcall unsafe "windows.h CreateCompatibleBitmap"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateCompatibleBitmap"
   c_CreateCompatibleBitmap :: HDC -> Int32 -> Int32 -> IO HBITMAP
 
 createDIBPatternBrushPt :: LPVOID -> ColorFormat -> IO HBRUSH
 createDIBPatternBrushPt bm usage =
   failIfNull "CreateDIBPatternBrushPt" $ c_CreateDIBPatternBrushPt bm usage
-foreign import stdcall unsafe "windows.h CreateDIBPatternBrushPt"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateDIBPatternBrushPt"
   c_CreateDIBPatternBrushPt :: LPVOID -> ColorFormat -> IO HBRUSH
 
 ----------------------------------------------------------------
@@ -249,7 +253,7 @@
   allocaSIZE $ \ p_size -> do
   failIfFalse_ "GetBitmapDimensionEx" $ c_GetBitmapDimensionEx bm p_size
   peekSIZE p_size
-foreign import stdcall unsafe "windows.h GetBitmapDimensionEx"
+foreign import WINDOWS_CCONV unsafe "windows.h GetBitmapDimensionEx"
   c_GetBitmapDimensionEx :: HBITMAP -> Ptr SIZE -> IO Bool
 
 setBitmapDimensionEx :: HBITMAP -> SIZE -> IO SIZE
@@ -258,7 +262,7 @@
   failIfFalse_ "SetBitmapDimensionEx" $ do
     c_SetBitmapDimensionEx bm cx cy p_size
   peekSIZE p_size
-foreign import stdcall unsafe "windows.h SetBitmapDimensionEx"
+foreign import WINDOWS_CCONV unsafe "windows.h SetBitmapDimensionEx"
   c_SetBitmapDimensionEx :: HBITMAP -> LONG -> LONG -> Ptr SIZE -> IO Bool
 
 getBitmapInfo :: HBITMAP -> IO BITMAP
@@ -266,7 +270,7 @@
   allocaBytes (fromIntegral sizeofBITMAP) $ \ p_bm -> do
   failIfFalse_ "GetBitmapInfo" $ c_GetBitmapInfo bm sizeofBITMAP p_bm
   peekBITMAP p_bm
-foreign import stdcall unsafe "windows.h GetObjectW"
+foreign import WINDOWS_CCONV unsafe "windows.h GetObjectW"
   c_GetBitmapInfo :: HBITMAP -> DWORD -> LPBITMAP -> IO Bool
 
 ----------------------------------------------------------------
@@ -398,20 +402,20 @@
 getDIBits dc bm start nlines mb_bits info usage =
   failIfZero "GetDIBits" $
     c_GetDIBits dc bm start nlines (maybePtr mb_bits) info usage
-foreign import stdcall unsafe "windows.h GetDIBits"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDIBits"
   c_GetDIBits :: HDC -> HBITMAP -> INT -> INT -> LPVOID -> LPBITMAPINFO -> ColorFormat -> IO INT
 
 setDIBits :: HDC -> HBITMAP -> INT -> INT -> LPVOID -> LPBITMAPINFO -> ColorFormat -> IO INT
 setDIBits dc bm start nlines bits info use =
   failIfZero "SetDIBits" $ c_SetDIBits dc bm start nlines bits info use
-foreign import stdcall unsafe "windows.h SetDIBits"
+foreign import WINDOWS_CCONV unsafe "windows.h SetDIBits"
   c_SetDIBits :: HDC -> HBITMAP -> INT -> INT -> LPVOID -> LPBITMAPINFO -> ColorFormat -> IO INT
 
 createDIBitmap :: HDC -> LPBITMAPINFOHEADER -> DWORD -> LPVOID -> LPBITMAPINFO -> ColorFormat -> IO HBITMAP
 createDIBitmap dc hdr option init_val info usage =
   failIfNull "CreateDIBitmap" $
     c_CreateDIBitmap dc hdr option init_val info usage
-foreign import stdcall unsafe "windows.h CreateDIBitmap"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateDIBitmap"
   c_CreateDIBitmap :: HDC -> LPBITMAPINFOHEADER -> DWORD -> LPVOID -> LPBITMAPINFO -> ColorFormat -> IO HBITMAP
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Brush.hsc b/Graphics/Win32/GDI/Brush.hsc
--- a/Graphics/Win32/GDI/Brush.hsc
+++ b/Graphics/Win32/GDI/Brush.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -20,6 +22,8 @@
 import System.Win32.Types
 import Graphics.Win32.GDI.Types
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 ----------------------------------------------------------------
@@ -29,25 +33,25 @@
 createSolidBrush :: COLORREF -> IO HBRUSH
 createSolidBrush color =
   failIfNull "CreateSolidBrush" $ c_CreateSolidBrush color
-foreign import stdcall unsafe "windows.h CreateSolidBrush"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateSolidBrush"
   c_CreateSolidBrush :: COLORREF -> IO HBRUSH
 
 createHatchBrush :: HatchStyle -> COLORREF -> IO HBRUSH
 createHatchBrush style color =
   failIfNull "CreateHatchBrush" $ c_CreateHatchBrush style color
-foreign import stdcall unsafe "windows.h CreateHatchBrush"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateHatchBrush"
   c_CreateHatchBrush :: HatchStyle -> COLORREF -> IO HBRUSH
 
 createPatternBrush :: HBITMAP -> IO HBRUSH
 createPatternBrush bitmap =
   failIfNull "CreatePatternBrush" $ c_CreatePatternBrush bitmap
-foreign import stdcall unsafe "windows.h CreatePatternBrush"
+foreign import WINDOWS_CCONV unsafe "windows.h CreatePatternBrush"
   c_CreatePatternBrush :: HBITMAP -> IO HBRUSH
 
 deleteBrush :: HBRUSH -> IO ()
 deleteBrush brush =
   failIfFalse_ "DeleteBrush" $ c_DeleteBrush brush
-foreign import stdcall unsafe "windows.h DeleteObject"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteObject"
   c_DeleteBrush :: HBRUSH -> IO Bool
 
 ----------------------------------------------------------------
@@ -67,7 +71,7 @@
 getStockBrush :: StockBrush -> IO HBRUSH
 getStockBrush sb =
   failIfNull "GetStockBrush" $ c_GetStockBrush sb
-foreign import stdcall unsafe "windows.h GetStockObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetStockObject"
   c_GetStockBrush :: StockBrush -> IO HBRUSH
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Clip.hsc b/Graphics/Win32/GDI/Clip.hsc
--- a/Graphics/Win32/GDI/Clip.hsc
+++ b/Graphics/Win32/GDI/Clip.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -17,11 +19,20 @@
 
 module Graphics.Win32.GDI.Clip where
 
+import Control.Monad
 import Graphics.Win32.GDI.Types
 import System.Win32.Types
+import Graphics.Win32.Message    ( WindowMessage )
 
 import Foreign
 
+##include "windows_cconv.h"
+
+#undef WINVER
+#define WINVER 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+
 #include <windows.h>
 
 type ClipboardFormat = UINT
@@ -50,40 +61,49 @@
  , cF_TEXT              = CF_TEXT
  , cF_WAVE              = CF_WAVE
  , cF_TIFF              = CF_TIFF
+ , cF_DIBV5             = CF_DIBV5
+ , cF_GDIOBJLAST        = CF_GDIOBJLAST
+ , cF_UNICODETEXT       = CF_UNICODETEXT
  }
 
+wM_CLIPBOARDUPDATE :: WindowMessage
+wM_CLIPBOARDUPDATE = 0x031D -- #const WM_CLIPBOARDUPDATE -- Can't use constant due to GHC 7.8.x support.
+
 -- % , CF_UNICODETEXT  -- WinNT only
 
-foreign import stdcall unsafe "windows.h ChangeClipboardChain"
+foreign import WINDOWS_CCONV unsafe "windows.h ChangeClipboardChain"
   changeClipboardChain :: HWND -> HWND -> IO Bool
 
 closeClipboard :: IO ()
 closeClipboard =
   failIfFalse_ "CloseClipboard" c_CloseClipboard
-foreign import stdcall unsafe "windows.h CloseClipboard"
+foreign import WINDOWS_CCONV unsafe "windows.h CloseClipboard"
   c_CloseClipboard :: IO BOOL
 
-foreign import stdcall unsafe "windows.h CountClipboardFormats"
+foreign import WINDOWS_CCONV unsafe "windows.h CountClipboardFormats"
   countClipboardFormats :: IO Int
 
 emptyClipboard :: IO ()
 emptyClipboard =
   failIfFalse_ "EmptyClipboard" c_EmptyClipboard
-foreign import stdcall unsafe "windows.h EmptyClipboard"
+foreign import WINDOWS_CCONV unsafe "windows.h EmptyClipboard"
   c_EmptyClipboard :: IO BOOL
 
 -- original also tested GetLastError() != NO_ERROR
 
 enumClipboardFormats :: ClipboardFormat -> IO ClipboardFormat
-enumClipboardFormats format =
-  failIfZero "EnumClipboardFormats" $ c_EnumClipboardFormats format
-foreign import stdcall unsafe "windows.h EnumClipboardFormats"
+enumClipboardFormats format = do
+  format' <- c_EnumClipboardFormats format
+  when (format' == 0) $
+    failUnlessSuccess "EnumClipboardFormats" getLastError
+  return format'
+foreign import WINDOWS_CCONV unsafe "windows.h EnumClipboardFormats"
   c_EnumClipboardFormats :: ClipboardFormat -> IO ClipboardFormat
 
 getClipboardData :: ClipboardFormat -> IO HANDLE
 getClipboardData format =
   failIfNull "GetClipboardData" $ c_GetClipboardData format
-foreign import stdcall unsafe "windows.h GetClipboardData"
+foreign import WINDOWS_CCONV unsafe "windows.h GetClipboardData"
   c_GetClipboardData :: ClipboardFormat -> IO HANDLE
 
 getClipboardFormatName :: ClipboardFormat -> IO String
@@ -92,25 +112,25 @@
   len <- failIfZero "GetClipboardFormatName" $
     c_GetClipboardFormatName format c_name 256
   peekTStringLen (c_name, len)
-foreign import stdcall unsafe "windows.h GetClipboardFormatNameW"
+foreign import WINDOWS_CCONV unsafe "windows.h GetClipboardFormatNameW"
   c_GetClipboardFormatName :: ClipboardFormat -> LPTSTR -> Int -> IO Int
 
 getClipboardOwner :: IO HWND
 getClipboardOwner =
   failIfNull "GetClipboardOwner" c_GetClipboardOwner
-foreign import stdcall unsafe "windows.h GetClipboardOwner"
+foreign import WINDOWS_CCONV unsafe "windows.h GetClipboardOwner"
   c_GetClipboardOwner :: IO HWND
 
 getClipboardViewer :: IO HWND
 getClipboardViewer =
   failIfNull "GetClipboardViewer" c_GetClipboardViewer
-foreign import stdcall unsafe "windows.h GetClipboardViewer"
+foreign import WINDOWS_CCONV unsafe "windows.h GetClipboardViewer"
   c_GetClipboardViewer :: IO HWND
 
 getOpenClipboardWindow :: IO HWND
 getOpenClipboardWindow =
   failIfNull "GetClipboardWindow" c_GetOpenClipboardWindow
-foreign import stdcall unsafe "windows.h GetOpenClipboardWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h GetOpenClipboardWindow"
   c_GetOpenClipboardWindow :: IO HWND
 
 getPriorityClipboardFormat :: [ClipboardFormat] -> IO Int
@@ -118,16 +138,16 @@
   withArray formats $ \ format_array ->
   failIf (== -1) "GetPriorityClipboardFormat" $
     c_GetPriorityClipboardFormat format_array (length formats)
-foreign import stdcall unsafe "windows.h GetPriorityClipboardFormat"
+foreign import WINDOWS_CCONV unsafe "windows.h GetPriorityClipboardFormat"
   c_GetPriorityClipboardFormat :: Ptr UINT -> Int -> IO Int
 
-foreign import stdcall unsafe "windows.h IsClipboardFormatAvailable"
+foreign import WINDOWS_CCONV unsafe "windows.h IsClipboardFormatAvailable"
   isClipboardFormatAvailable :: ClipboardFormat -> IO BOOL
 
 openClipboard :: HWND -> IO ()
 openClipboard wnd =
   failIfFalse_ "OpenClipboard" $ c_OpenClipboard wnd
-foreign import stdcall unsafe "windows.h OpenClipboard"
+foreign import WINDOWS_CCONV unsafe "windows.h OpenClipboard"
   c_OpenClipboard :: HWND -> IO BOOL
 
 registerClipboardFormat :: String -> IO ClipboardFormat
@@ -135,17 +155,17 @@
   withTString name $ \ c_name ->
   failIfZero "RegisterClipboardFormat" $
     c_RegisterClipboardFormat c_name
-foreign import stdcall unsafe "windows.h RegisterClipboardFormatW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegisterClipboardFormatW"
   c_RegisterClipboardFormat :: LPCTSTR -> IO ClipboardFormat
 
 setClipboardData :: ClipboardFormat -> HANDLE -> IO HANDLE
 setClipboardData format mem =
   failIfNull "SetClipboardData" $ c_SetClipboardData format mem
-foreign import stdcall unsafe "windows.h SetClipboardData"
+foreign import WINDOWS_CCONV unsafe "windows.h SetClipboardData"
   c_SetClipboardData :: ClipboardFormat -> HANDLE -> IO HANDLE
 
 setClipboardViewer :: HWND -> IO HWND
 setClipboardViewer wnd =
   failIfNull "SetClipboardViewer" $ c_SetClipboardViewer wnd
-foreign import stdcall unsafe "windows.h SetClipboardViewer"
+foreign import WINDOWS_CCONV unsafe "windows.h SetClipboardViewer"
   c_SetClipboardViewer :: HWND -> IO HWND
diff --git a/Graphics/Win32/GDI/Font.hsc b/Graphics/Win32/GDI/Font.hsc
--- a/Graphics/Win32/GDI/Font.hsc
+++ b/Graphics/Win32/GDI/Font.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -17,27 +19,28 @@
 
 module Graphics.Win32.GDI.Font
 {-
-	( CharSet
-	, PitchAndFamily
-	, OutPrecision
-	, ClipPrecision
-	, FontQuality
-	, FontWeight
+        ( CharSet
+        , PitchAndFamily
+        , OutPrecision
+        , ClipPrecision
+        , FontQuality
+        , FontWeight
 
-	, createFont, deleteFont
+        , createFont, deleteFont
 
-	, StockFont, getStockFont
-	, oEM_FIXED_FONT, aNSI_FIXED_FONT, aNSI_VAR_FONT, sYSTEM_FONT
-	, dEVICE_DEFAULT_FONT, sYSTEM_FIXED_FONT
-	) where
+        , StockFont, getStockFont
+        , oEM_FIXED_FONT, aNSI_FIXED_FONT, aNSI_VAR_FONT, sYSTEM_FONT
+        , dEVICE_DEFAULT_FONT, sYSTEM_FIXED_FONT
+        ) where
 -}
-	where
+        where
 
 import System.Win32.Types
 import Graphics.Win32.GDI.Types
 
 import Foreign
 
+##include "windows_cconv.h"
 #include <windows.h>
 
 ----------------------------------------------------------------
@@ -148,7 +151,7 @@
   withTString face $ \ c_face ->
   failIfNull "CreateFont" $
     c_CreateFont h w esc orient wt ital under strike cset out clip q pf c_face
-foreign import stdcall unsafe "windows.h CreateFontW"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateFontW"
   c_CreateFont
      :: INT -> INT -> INT -> INT
      -> FontWeight -> Bool -> Bool -> Bool
@@ -165,24 +168,24 @@
 --
 -- createFont_adr (width, height) escapement bold italic family =
 --  createFont height width
--- 		     (round (escapement * 1800/pi))
--- 		     0                     -- orientation
--- 		     weight
--- 		     italic False False    -- italic, underline, strikeout
--- 		     aNSI_CHARSET
--- 		     oUT_DEFAULT_PRECIS
--- 		     cLIP_DEFAULT_PRECIS
--- 		     dEFAULT_QUALITY
--- 		     dEFAULT_PITCH
--- 		     family
+--                   (round (escapement * 1800/pi))
+--                   0                     -- orientation
+--                   weight
+--                   italic False False    -- italic, underline, strikeout
+--                   aNSI_CHARSET
+--                   oUT_DEFAULT_PRECIS
+--                   cLIP_DEFAULT_PRECIS
+--                   dEFAULT_QUALITY
+--                   dEFAULT_PITCH
+--                   family
 --  where
 --   weight | bold      = fW_BOLD
--- 	    | otherwise = fW_NORMAL
+--          | otherwise = fW_NORMAL
 
 
 -- missing CreateFontIndirect from WinFonts.ss; GSL ???
 
-foreign import stdcall unsafe "windows.h DeleteObject"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteObject"
   deleteFont :: HFONT -> IO ()
 
 ----------------------------------------------------------------
@@ -198,7 +201,7 @@
  , sYSTEM_FIXED_FONT   = SYSTEM_FIXED_FONT
  }
 
-foreign import stdcall unsafe "windows.h GetStockObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetStockObject"
   getStockFont :: StockFont -> IO HFONT
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Graphics2D.hs b/Graphics/Win32/GDI/Graphics2D.hs
--- a/Graphics/Win32/GDI/Graphics2D.hs
+++ b/Graphics/Win32/GDI/Graphics2D.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,13 +18,15 @@
 -----------------------------------------------------------------------------
 
 module Graphics.Win32.GDI.Graphics2D
-	where
+        where
 
 import System.Win32.Types
 import Graphics.Win32.GDI.Types
 
 import Foreign
 
+#include "windows_cconv.h"
+
 ----------------------------------------------------------------
 -- Lines and Curves
 ----------------------------------------------------------------
@@ -32,124 +36,124 @@
   allocaPOINT $ \ p_point -> do
   failIfFalse_ "MoveToEx" $ c_MoveToEx dc x y p_point
   peekPOINT p_point
-foreign import stdcall unsafe "windows.h MoveToEx"
+foreign import WINDOWS_CCONV unsafe "windows.h MoveToEx"
   c_MoveToEx :: HDC -> Int32 -> Int32 -> Ptr POINT -> IO Bool
 
 lineTo :: HDC -> Int32 -> Int32 -> IO ()
 lineTo dc x y =
   failIfFalse_ "LineTo" $ c_LineTo dc x y
-foreign import stdcall unsafe "windows.h LineTo"
+foreign import WINDOWS_CCONV unsafe "windows.h LineTo"
   c_LineTo :: HDC -> Int32 -> Int32 -> IO Bool
 
 polyline :: HDC -> [POINT] -> IO ()
 polyline dc points =
   withPOINTArray points $ \ pount_array npoints ->
   failIfFalse_ "Polyline" $ c_Polyline dc pount_array npoints
-foreign import stdcall unsafe "windows.h Polyline"
+foreign import WINDOWS_CCONV unsafe "windows.h Polyline"
   c_Polyline :: HDC -> Ptr POINT -> Int -> IO Bool
 
 polylineTo :: HDC -> [POINT] -> IO ()
 polylineTo dc points =
   withPOINTArray points $ \ pount_array npoints ->
   failIfFalse_ "PolylineTo" $ c_PolylineTo dc pount_array npoints
-foreign import stdcall unsafe "windows.h PolylineTo"
+foreign import WINDOWS_CCONV unsafe "windows.h PolylineTo"
   c_PolylineTo :: HDC -> Ptr POINT -> Int -> IO Bool
 
 polygon :: HDC -> [POINT] -> IO ()
 polygon dc points =
   withPOINTArray points $ \ pount_array npoints ->
   failIfFalse_ "Polygon" $ c_Polygon dc pount_array npoints
-foreign import stdcall unsafe "windows.h Polygon"
+foreign import WINDOWS_CCONV unsafe "windows.h Polygon"
   c_Polygon :: HDC -> Ptr POINT -> Int -> IO Bool
 
 polyBezier :: HDC -> [POINT] -> IO ()
 polyBezier dc points =
   withPOINTArray points $ \ pount_array npoints ->
   failIfFalse_ "PolyBezier" $ c_PolyBezier dc pount_array npoints
-foreign import stdcall unsafe "windows.h PolyBezier"
+foreign import WINDOWS_CCONV unsafe "windows.h PolyBezier"
   c_PolyBezier :: HDC -> Ptr POINT -> Int -> IO Bool
 
 polyBezierTo :: HDC -> [POINT] -> IO ()
 polyBezierTo dc points =
   withPOINTArray points $ \ pount_array npoints ->
   failIfFalse_ "PolyBezierTo" $ c_PolyBezierTo dc pount_array npoints
-foreign import stdcall unsafe "windows.h PolyBezierTo"
+foreign import WINDOWS_CCONV unsafe "windows.h PolyBezierTo"
   c_PolyBezierTo :: HDC -> Ptr POINT -> Int -> IO Bool
 
 arc :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 arc dc left top right bottom x1 y1 x2 y2 =
   failIfFalse_ "Arc" $ c_Arc dc left top right bottom x1 y1 x2 y2
-foreign import stdcall unsafe "windows.h Arc"
+foreign import WINDOWS_CCONV unsafe "windows.h Arc"
   c_Arc :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 arcTo :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 arcTo dc left top right bottom x1 y1 x2 y2 =
   failIfFalse_ "ArcTo" $ c_ArcTo dc left top right bottom x1 y1 x2 y2
-foreign import stdcall unsafe "windows.h ArcTo"
+foreign import WINDOWS_CCONV unsafe "windows.h ArcTo"
   c_ArcTo :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 angleArc :: HDC -> Int32 -> Int32 -> WORD -> Float -> Float -> IO ()
 angleArc dc x y r start sweep =
   failIfFalse_ "AngleArc" $ c_AngleArc dc x y r start sweep
-foreign import stdcall unsafe "windows.h AngleArc"
+foreign import WINDOWS_CCONV unsafe "windows.h AngleArc"
   c_AngleArc :: HDC -> Int32 -> Int32 -> WORD -> Float -> Float -> IO Bool
 
 ----------------------------------------------------------------
 -- Filled Shapes
 ----------------------------------------------------------------
 
--- ToDo: We ought to be able to specify a colour instead of the
+-- TODO: We ought to be able to specify a colour instead of the
 -- Brush by adding 1 to colour number.
 
 fillRect :: HDC -> RECT -> HBRUSH -> IO ()
 fillRect dc rect brush =
   withRECT rect $ \ c_rect ->
   failIfFalse_ "FillRect" $ c_FillRect dc c_rect brush
-foreign import stdcall unsafe "windows.h FillRect"
+foreign import WINDOWS_CCONV unsafe "windows.h FillRect"
   c_FillRect :: HDC -> Ptr RECT -> HBRUSH -> IO Bool
 
 frameRect :: HDC -> RECT -> HBRUSH -> IO ()
 frameRect dc rect brush =
   withRECT rect $ \ c_rect ->
   failIfFalse_ "FrameRect" $ c_FrameRect dc c_rect brush
-foreign import stdcall unsafe "windows.h FrameRect"
+foreign import WINDOWS_CCONV unsafe "windows.h FrameRect"
   c_FrameRect :: HDC -> Ptr RECT -> HBRUSH -> IO Bool
 
 invertRect :: HDC -> RECT -> IO ()
 invertRect dc rect =
   withRECT rect $ \ c_rect ->
   failIfFalse_ "InvertRect" $ c_InvertRect dc c_rect
-foreign import stdcall unsafe "windows.h InvertRect"
+foreign import WINDOWS_CCONV unsafe "windows.h InvertRect"
   c_InvertRect :: HDC -> Ptr RECT -> IO Bool
 
 rectangle :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 rectangle dc left top right bottom =
   failIfFalse_ "Rectangle" $ c_Rectangle dc left top right bottom
-foreign import stdcall unsafe "windows.h Rectangle"
+foreign import WINDOWS_CCONV unsafe "windows.h Rectangle"
   c_Rectangle :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 roundRect :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 roundRect dc left top right bottom w h =
   failIfFalse_ "RoundRect" $ c_RoundRect dc left top right bottom w h
-foreign import stdcall unsafe "windows.h RoundRect"
+foreign import WINDOWS_CCONV unsafe "windows.h RoundRect"
   c_RoundRect :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 ellipse :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 ellipse dc left top right bottom =
   failIfFalse_ "Ellipse" $ c_Ellipse dc left top right bottom
-foreign import stdcall unsafe "windows.h Ellipse"
+foreign import WINDOWS_CCONV unsafe "windows.h Ellipse"
   c_Ellipse :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 chord :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 chord dc left top right bottom x1 y1 x2 y2 =
   failIfFalse_ "Chord" $ c_Chord dc left top right bottom x1 y1 x2 y2
-foreign import stdcall unsafe "windows.h Chord"
+foreign import WINDOWS_CCONV unsafe "windows.h Chord"
   c_Chord :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 pie :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO ()
 pie dc left top right bottom x1 y1 x2 y2 =
   failIfFalse_ "Pie" $ c_Pie dc left top right bottom x1 y1 x2 y2
-foreign import stdcall unsafe "windows.h Pie"
+foreign import WINDOWS_CCONV unsafe "windows.h Pie"
   c_Pie :: HDC -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> Int32 -> IO Bool
 
 ----------------------------------------------------------------
@@ -159,21 +163,21 @@
 bitBlt :: HDC -> INT -> INT -> INT -> INT -> HDC -> INT -> INT -> RasterOp3 -> IO ()
 bitBlt dcDest xDest yDest w h dcSrc xSrc ySrc rop =
   failIfFalse_ "BitBlt" $ c_BitBlt dcDest xDest yDest w h dcSrc xSrc ySrc rop
-foreign import stdcall unsafe "windows.h BitBlt"
+foreign import WINDOWS_CCONV unsafe "windows.h BitBlt"
   c_BitBlt :: HDC -> INT -> INT -> INT -> INT -> HDC -> INT -> INT -> RasterOp3 -> IO Bool
 
 maskBlt :: HDC -> INT -> INT -> INT -> INT -> HDC -> INT -> INT -> HBITMAP -> INT -> INT -> RasterOp4 -> IO ()
 maskBlt dcDest xDest yDest w h dcSrc xSrc ySrc bm xMask yMask rop =
   failIfFalse_ "MaskBlt" $
     c_MaskBlt dcDest xDest yDest w h dcSrc xSrc ySrc bm xMask yMask rop
-foreign import stdcall unsafe "windows.h MaskBlt"
+foreign import WINDOWS_CCONV unsafe "windows.h MaskBlt"
   c_MaskBlt :: HDC -> INT -> INT -> INT -> INT -> HDC -> INT -> INT -> HBITMAP -> INT -> INT -> RasterOp4 -> IO Bool
 
 stretchBlt :: HDC -> INT -> INT -> INT -> INT -> HDC -> INT -> INT -> INT -> INT -> RasterOp3 -> IO ()
 stretchBlt dcDest xDest yDest wDest hDest hdcSrc xSrc ySrc wSrc hSrc rop =
   failIfFalse_ "StretchBlt" $
     c_StretchBlt dcDest xDest yDest wDest hDest hdcSrc xSrc ySrc wSrc hSrc rop
-foreign import stdcall unsafe "windows.h StretchBlt"
+foreign import WINDOWS_CCONV unsafe "windows.h StretchBlt"
   c_StretchBlt :: HDC -> INT -> INT -> INT -> INT -> HDC -> INT -> INT -> INT -> INT -> RasterOp3 -> IO Bool
 
 -- We deviate slightly from the Win32 interface
@@ -188,7 +192,7 @@
   withPOINTArray [p1,p2,p3] $ \ vertices _ ->
   failIfFalse_ "PlgBlt" $
     c_PlgBlt hdDest vertices hdSrc x y w h (maybePtr mb_bm) xMask yMask
-foreign import stdcall unsafe "windows.h PlgBlt"
+foreign import WINDOWS_CCONV unsafe "windows.h PlgBlt"
   c_PlgBlt :: HDC -> Ptr POINT -> HDC -> INT -> INT -> INT -> INT -> HBITMAP -> INT -> INT -> IO Bool
 
 ----------------------------------------------------------------
@@ -199,7 +203,7 @@
 textOut dc x y str =
   withTStringLen str $ \ (c_str, len) ->
   failIfFalse_ "TextOut" $ c_TextOut dc x y c_str len
-foreign import stdcall unsafe "windows.h TextOutW"
+foreign import WINDOWS_CCONV unsafe "windows.h TextOutW"
   c_TextOut :: HDC -> INT -> INT -> LPCTSTR -> Int -> IO Bool
 
 -- missing TabbedTextOut from WinFonts.ss; GSL ???
@@ -211,7 +215,7 @@
   failIfFalse_ "GetTextExtentPoint32" $
     c_GetTextExtentPoint32 dc c_str len p_size
   peekSIZE p_size
-foreign import stdcall unsafe "windows.h GetTextExtentPoint32W"
+foreign import WINDOWS_CCONV unsafe "windows.h GetTextExtentPoint32W"
   c_GetTextExtentPoint32 :: HDC -> LPCTSTR -> Int -> Ptr SIZE -> IO Bool
 
 -- missing getTabbedTextExtent from WinFonts.ss; GSL ???
diff --git a/Graphics/Win32/GDI/HDC.hs b/Graphics/Win32/GDI/HDC.hs
--- a/Graphics/Win32/GDI/HDC.hs
+++ b/Graphics/Win32/GDI/HDC.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,98 +18,124 @@
 -----------------------------------------------------------------------------
 
 module Graphics.Win32.GDI.HDC
-	( module Graphics.Win32.GDI.HDC
-	) where
+        ( module Graphics.Win32.GDI.HDC
+        ) where
 
 import System.Win32.Types
 import Graphics.Win32.GDI.Types
 
 import Foreign
 
-----------------------------------------------------------------
+#include "windows_cconv.h"
 
+{- Note [Overflow checking and fromIntegral]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some windows APIs use the value 0x80000000 to represent failure return
+codes. However, when GHC builds libraries with -XNegativeLiterals
+enabled, it will fail in contexts where the type would suffer from
+signed overflow - such as Int32. (minBound :: Int32 == 0x80000000)
+
+Technically, the frontend is correct that the literal overflows in the
+context it is used in. So instead, we use fromIntegral to convert the
+literal from a Word32 to the necessary type. This isn't any less
+efficient (fromIntegral is optimized away,) and conveys the idea we
+simply want the same representational value.
+-}
+
+----------------------
+-- Implement GetPixel
+----------------------
+
+getPixel :: HDC -> Int -> Int -> IO COLORREF
+getPixel dc x y = c_GetPixel dc x y
+foreign import WINDOWS_CCONV unsafe "windows.h GetPixel"
+  c_GetPixel :: HDC -> Int -> Int -> IO COLORREF
+
+----------------------
+
 setArcDirection :: HDC -> ArcDirection -> IO ArcDirection
 setArcDirection dc dir =
   failIfZero "SetArcDirection" $ c_SetArcDirection dc dir
-foreign import stdcall unsafe "windows.h SetArcDirection"
+foreign import WINDOWS_CCONV unsafe "windows.h SetArcDirection"
   c_SetArcDirection :: HDC ->  ArcDirection  -> IO  ArcDirection
 
 getArcDirection :: HDC -> IO ArcDirection
 getArcDirection dc =
   failIfZero "GetArcDirection" $ c_GetArcDirection dc
-foreign import stdcall unsafe "windows.h GetArcDirection"
+foreign import WINDOWS_CCONV unsafe "windows.h GetArcDirection"
   c_GetArcDirection :: HDC -> IO  ArcDirection
 
 setPolyFillMode :: HDC -> PolyFillMode -> IO PolyFillMode
 setPolyFillMode dc mode =
   failIfZero "SetPolyFillMode" $ c_SetPolyFillMode dc mode
-foreign import stdcall unsafe "windows.h SetPolyFillMode"
+foreign import WINDOWS_CCONV unsafe "windows.h SetPolyFillMode"
   c_SetPolyFillMode :: HDC ->  PolyFillMode  -> IO  PolyFillMode
 
 getPolyFillMode :: HDC -> IO PolyFillMode
 getPolyFillMode dc =
   failIfZero "GetPolyFillMode" $ c_GetPolyFillMode dc
-foreign import stdcall unsafe "windows.h GetPolyFillMode"
+foreign import WINDOWS_CCONV unsafe "windows.h GetPolyFillMode"
   c_GetPolyFillMode :: HDC -> IO  PolyFillMode
 
 setGraphicsMode :: HDC -> GraphicsMode -> IO GraphicsMode
 setGraphicsMode dc mode =
   failIfZero "SetGraphicsMode" $ c_SetGraphicsMode dc mode
-foreign import stdcall unsafe "windows.h SetGraphicsMode"
+foreign import WINDOWS_CCONV unsafe "windows.h SetGraphicsMode"
   c_SetGraphicsMode :: HDC ->  GraphicsMode  -> IO  GraphicsMode
 
 getGraphicsMode :: HDC -> IO GraphicsMode
 getGraphicsMode dc =
   failIfZero "GetGraphicsMode" $ c_GetGraphicsMode dc
-foreign import stdcall unsafe "windows.h GetGraphicsMode"
+foreign import WINDOWS_CCONV unsafe "windows.h GetGraphicsMode"
   c_GetGraphicsMode :: HDC -> IO  GraphicsMode
 
 setStretchBltMode :: HDC -> StretchBltMode -> IO StretchBltMode
 setStretchBltMode dc mode =
   failIfZero "SetStretchBltMode" $ c_SetStretchBltMode dc mode
-foreign import stdcall unsafe "windows.h SetStretchBltMode"
+foreign import WINDOWS_CCONV unsafe "windows.h SetStretchBltMode"
   c_SetStretchBltMode :: HDC ->  StretchBltMode  -> IO  StretchBltMode
 
 getStretchBltMode :: HDC -> IO StretchBltMode
 getStretchBltMode dc =
   failIfZero "GetStretchBltMode" $ c_GetStretchBltMode dc
-foreign import stdcall unsafe "windows.h GetStretchBltMode"
+foreign import WINDOWS_CCONV unsafe "windows.h GetStretchBltMode"
   c_GetStretchBltMode :: HDC -> IO  StretchBltMode
 
 setBkColor :: HDC -> COLORREF -> IO COLORREF
 setBkColor dc color =
   failIfZero "SetBkColor" $ c_SetBkColor dc color
-foreign import stdcall unsafe "windows.h SetBkColor"
+foreign import WINDOWS_CCONV unsafe "windows.h SetBkColor"
   c_SetBkColor :: HDC ->  COLORREF  -> IO  COLORREF
 
 getBkColor :: HDC -> IO COLORREF
 getBkColor dc =
   failIfZero "GetBkColor" $ c_GetBkColor dc
-foreign import stdcall unsafe "windows.h GetBkColor"
+foreign import WINDOWS_CCONV unsafe "windows.h GetBkColor"
   c_GetBkColor :: HDC -> IO  COLORREF
 
 setTextColor :: HDC -> COLORREF -> IO COLORREF
 setTextColor dc color =
   failIf (== cLR_INVALID) "SetTextColor" $ c_SetTextColor dc color
-foreign import stdcall unsafe "windows.h SetTextColor"
+foreign import WINDOWS_CCONV unsafe "windows.h SetTextColor"
   c_SetTextColor :: HDC ->  COLORREF  -> IO  COLORREF
 
 getTextColor :: HDC -> IO COLORREF
 getTextColor dc =
   failIf (== cLR_INVALID) "GetTextColor" $ c_GetTextColor dc
-foreign import stdcall unsafe "windows.h GetTextColor"
+foreign import WINDOWS_CCONV unsafe "windows.h GetTextColor"
   c_GetTextColor :: HDC -> IO  COLORREF
 
 setBkMode :: HDC -> BackgroundMode -> IO BackgroundMode
 setBkMode dc mode =
   failIfZero "SetBkMode" $ c_SetBkMode dc mode
-foreign import stdcall unsafe "windows.h SetBkMode"
+foreign import WINDOWS_CCONV unsafe "windows.h SetBkMode"
   c_SetBkMode :: HDC ->  BackgroundMode  -> IO  BackgroundMode
 
 getBkMode :: HDC -> IO BackgroundMode
 getBkMode dc =
   failIfZero "GetBkMode" $ c_GetBkMode dc
-foreign import stdcall unsafe "windows.h GetBkMode"
+foreign import WINDOWS_CCONV unsafe "windows.h GetBkMode"
   c_GetBkMode :: HDC -> IO  BackgroundMode
 
 setBrushOrgEx :: HDC -> Int -> Int -> IO POINT
@@ -115,7 +143,7 @@
   allocaPOINT $ \ pt -> do
   failIfFalse_ "SetBrushOrgEx" $ c_SetBrushOrgEx dc x y pt
   peekPOINT pt
-foreign import stdcall unsafe "windows.h SetBrushOrgEx"
+foreign import WINDOWS_CCONV unsafe "windows.h SetBrushOrgEx"
   c_SetBrushOrgEx :: HDC -> Int -> Int -> Ptr POINT -> IO Bool
 
 getBrushOrgEx :: HDC -> IO POINT
@@ -123,32 +151,34 @@
   allocaPOINT $ \ pt -> do
   failIfFalse_ "GetBrushOrgEx" $ c_GetBrushOrgEx dc pt
   peekPOINT pt
-foreign import stdcall unsafe "windows.h GetBrushOrgEx"
+foreign import WINDOWS_CCONV unsafe "windows.h GetBrushOrgEx"
   c_GetBrushOrgEx :: HDC -> Ptr POINT -> IO Bool
 
 setTextAlign :: HDC -> TextAlignment -> IO TextAlignment
 setTextAlign dc align =
   failIf (== gDI_ERROR) "SetTextAlign" $ c_SetTextAlign dc align
-foreign import stdcall unsafe "windows.h SetTextAlign"
+foreign import WINDOWS_CCONV unsafe "windows.h SetTextAlign"
   c_SetTextAlign :: HDC ->  TextAlignment  -> IO  TextAlignment
 
 getTextAlign :: HDC -> IO TextAlignment
 getTextAlign dc =
   failIf (== gDI_ERROR) "GetTextAlign" $ c_GetTextAlign dc
-foreign import stdcall unsafe "windows.h GetTextAlign"
+foreign import WINDOWS_CCONV unsafe "windows.h GetTextAlign"
   c_GetTextAlign :: HDC -> IO  TextAlignment
 
 setTextCharacterExtra :: HDC -> Int -> IO Int
 setTextCharacterExtra dc extra =
-  failIf (== 0x80000000) "SetTextCharacterExtra" $
+  -- See Note [Overflow checking and fromIntegral]
+  failIf (== fromIntegral (0x80000000 :: Word32)) "SetTextCharacterExtra" $
     c_SetTextCharacterExtra dc extra
-foreign import stdcall unsafe "windows.h SetTextCharacterExtra"
+foreign import WINDOWS_CCONV unsafe "windows.h SetTextCharacterExtra"
   c_SetTextCharacterExtra :: HDC ->  Int  -> IO  Int
 
 getTextCharacterExtra :: HDC -> IO Int
 getTextCharacterExtra dc =
-  failIf (== 0x80000000) "GetTextCharacterExtra" $ c_GetTextCharacterExtra dc
-foreign import stdcall unsafe "windows.h GetTextCharacterExtra"
+  -- See Note [Overflow checking and fromIntegral]
+  failIf (== fromIntegral (0x80000000 :: Word32)) "GetTextCharacterExtra" $ c_GetTextCharacterExtra dc
+foreign import WINDOWS_CCONV unsafe "windows.h GetTextCharacterExtra"
   c_GetTextCharacterExtra :: HDC -> IO  Int
 
 getMiterLimit :: HDC -> IO Float
@@ -156,7 +186,7 @@
   alloca $ \ p_res -> do
   failIfFalse_ "GetMiterLimit" $ c_GetMiterLimit dc p_res
   peek p_res
-foreign import stdcall unsafe "windows.h GetMiterLimit"
+foreign import WINDOWS_CCONV unsafe "windows.h GetMiterLimit"
   c_GetMiterLimit :: HDC -> Ptr FLOAT -> IO Bool
 
 setMiterLimit :: HDC -> Float -> IO Float
@@ -164,7 +194,7 @@
   alloca $ \ p_old_limit -> do
   failIfFalse_ "SetMiterLimit" $ c_SetMiterLimit dc new_limit p_old_limit
   peek p_old_limit
-foreign import stdcall unsafe "windows.h SetMiterLimit"
+foreign import WINDOWS_CCONV unsafe "windows.h SetMiterLimit"
   c_SetMiterLimit :: HDC -> FLOAT -> Ptr FLOAT -> IO Bool
 
 ----------------------------------------------------------------
@@ -172,13 +202,13 @@
 saveDC :: HDC -> IO Int
 saveDC dc =
   failIfZero "SaveDC" $ c_SaveDC dc
-foreign import stdcall unsafe "windows.h SaveDC"
+foreign import WINDOWS_CCONV unsafe "windows.h SaveDC"
   c_SaveDC :: HDC -> IO Int
 
 restoreDC :: HDC -> Int -> IO ()
 restoreDC dc saved =
   failIfFalse_ "RestoreDC" $ c_RestoreDC dc saved
-foreign import stdcall unsafe "windows.h RestoreDC"
+foreign import WINDOWS_CCONV unsafe "windows.h RestoreDC"
   c_RestoreDC :: HDC -> Int -> IO Bool
 
 ----------------------------------------------------------------
@@ -186,55 +216,55 @@
 getCurrentBitmap :: HDC -> IO HBITMAP
 getCurrentBitmap dc =
   failIfNull "GetCurrentBitmap" $ c_GetCurrentBitmap dc oBJ_BITMAP
-foreign import stdcall unsafe "windows.h GetCurrentObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentObject"
   c_GetCurrentBitmap :: HDC -> UINT -> IO  HBITMAP
 
 getCurrentBrush :: HDC -> IO HBRUSH
 getCurrentBrush dc =
   failIfNull "GetCurrentBrush" $ c_GetCurrentBrush dc oBJ_BRUSH
-foreign import stdcall unsafe "windows.h GetCurrentObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentObject"
   c_GetCurrentBrush :: HDC -> UINT -> IO   HBRUSH
 
 getCurrentFont :: HDC -> IO HFONT
 getCurrentFont dc =
   failIfNull "GetCurrentFont" $ c_GetCurrentFont dc oBJ_FONT
-foreign import stdcall unsafe "windows.h GetCurrentObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentObject"
   c_GetCurrentFont :: HDC -> UINT -> IO    HFONT
 
 getCurrentPalette :: HDC -> IO HPALETTE
 getCurrentPalette dc =
   failIfNull "GetCurrentPalette" $ c_GetCurrentPalette dc oBJ_PAL
-foreign import stdcall unsafe "windows.h GetCurrentObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentObject"
   c_GetCurrentPalette :: HDC -> UINT -> IO     HPALETTE
 
 getCurrentPen :: HDC -> IO HPEN
 getCurrentPen dc =
   failIfNull "GetCurrentPen" $ c_GetCurrentPen dc oBJ_PEN
-foreign import stdcall unsafe "windows.h GetCurrentObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentObject"
   c_GetCurrentPen :: HDC -> UINT -> IO     HPEN
 
 selectBitmap :: HDC -> HBITMAP -> IO HBITMAP
 selectBitmap dc bitmap =
   failIfNull "SelectBitmap" $ c_SelectBitmap dc bitmap
-foreign import stdcall unsafe "windows.h SelectObject"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectObject"
   c_SelectBitmap :: HDC ->   HBITMAP  -> IO   HBITMAP
 
 selectBrush :: HDC -> HBRUSH -> IO HBRUSH
 selectBrush dc brush =
   failIfNull "SelectBrush" $ c_SelectBrush dc brush
-foreign import stdcall unsafe "windows.h SelectObject"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectObject"
   c_SelectBrush :: HDC ->    HBRUSH  -> IO    HBRUSH
 
 selectFont :: HDC -> HFONT -> IO HFONT
 selectFont dc font =
   failIfNull "SelectFont" $ c_SelectFont dc font
-foreign import stdcall unsafe "windows.h SelectObject"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectObject"
   c_SelectFont :: HDC ->     HFONT  -> IO     HFONT
 
 selectPen :: HDC -> HPEN -> IO HPEN
 selectPen dc pen =
   failIfNull "SelectPen" $ c_SelectPen dc pen
-foreign import stdcall unsafe "windows.h SelectObject"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectObject"
   c_SelectPen :: HDC ->      HPEN  -> IO      HPEN
 
 ----------------------------------------------------------------
@@ -244,7 +274,7 @@
 selectPalette :: HDC -> HPALETTE -> Bool -> IO HPALETTE
 selectPalette dc palette force_bg =
   failIfNull "SelectPalette" $ c_SelectPalette dc palette force_bg
-foreign import stdcall unsafe "windows.h SelectPalette"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectPalette"
   c_SelectPalette :: HDC -> HPALETTE -> Bool -> IO HPALETTE
 
 selectRgn :: HDC -> HRGN -> IO RegionType
@@ -260,20 +290,20 @@
 selectClipRgn dc mb_rgn =
   maybeWith withForeignPtr mb_rgn $ \ p_rgn ->
   failIfZero "SelectClipRgn" $ c_SelectClipRgn dc p_rgn
-foreign import stdcall unsafe "windows.h SelectClipRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectClipRgn"
   c_SelectClipRgn :: HDC -> PRGN -> IO RegionType
 
 extSelectClipRgn :: HDC -> Maybe HRGN -> ClippingMode -> IO RegionType
 extSelectClipRgn dc mb_rgn mode =
   maybeWith withForeignPtr mb_rgn $ \ p_rgn ->
   failIfZero "ExtSelectClipRgn" $ c_ExtSelectClipRgn dc p_rgn mode
-foreign import stdcall unsafe "windows.h ExtSelectClipRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h ExtSelectClipRgn"
   c_ExtSelectClipRgn :: HDC -> PRGN -> ClippingMode -> IO RegionType
 
 selectClipPath :: HDC -> ClippingMode -> IO RegionType
 selectClipPath dc mode =
   failIfZero "SelectClipPath" $ c_SelectClipPath dc mode
-foreign import stdcall unsafe "windows.h SelectClipPath"
+foreign import WINDOWS_CCONV unsafe "windows.h SelectClipPath"
   c_SelectClipPath :: HDC -> ClippingMode -> IO RegionType
 
 ----------------------------------------------------------------
@@ -283,19 +313,19 @@
 cancelDC :: HDC -> IO ()
 cancelDC dc =
   failIfFalse_ "CancelDC" $ c_CancelDC dc
-foreign import stdcall unsafe "windows.h CancelDC"
+foreign import WINDOWS_CCONV unsafe "windows.h CancelDC"
   c_CancelDC :: HDC -> IO Bool
 
 createCompatibleDC :: Maybe HDC -> IO HDC
 createCompatibleDC mb_dc =
   failIfNull "CreateCompatibleDC" $ c_CreateCompatibleDC (maybePtr mb_dc)
-foreign import stdcall unsafe "windows.h CreateCompatibleDC"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateCompatibleDC"
   c_CreateCompatibleDC :: HDC -> IO HDC
 
 deleteDC :: HDC -> IO ()
 deleteDC dc =
   failIfFalse_ "DeleteDC" $ c_DeleteDC dc
-foreign import stdcall unsafe "windows.h DeleteDC"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteDC"
   c_DeleteDC :: HDC -> IO Bool
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Palette.hsc b/Graphics/Win32/GDI/Palette.hsc
--- a/Graphics/Win32/GDI/Palette.hsc
+++ b/Graphics/Win32/GDI/Palette.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -20,6 +22,7 @@
 import System.Win32.Types
 import Graphics.Win32.GDI.Types
 
+##include "windows_cconv.h"
 #include <windows.h>
 
 ----------------------------------------------------------------
@@ -35,13 +38,13 @@
 getStockPalette :: StockPalette -> IO HPALETTE
 getStockPalette sp =
   failIfNull "GetStockPalette" $ c_GetStockPalette sp
-foreign import stdcall unsafe "windows.h GetStockObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetStockObject"
   c_GetStockPalette :: StockPalette -> IO HPALETTE
 
 deletePalette :: HPALETTE -> IO ()
 deletePalette p =
   failIfFalse_ "DeletePalette" $ c_DeletePalette p
-foreign import stdcall unsafe "windows.h DeleteObject"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteObject"
   c_DeletePalette :: HPALETTE -> IO Bool
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Path.hs b/Graphics/Win32/GDI/Path.hs
--- a/Graphics/Win32/GDI/Path.hs
+++ b/Graphics/Win32/GDI/Path.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,13 +18,15 @@
 -----------------------------------------------------------------------------
 
 module Graphics.Win32.GDI.Path
-	( beginPath, closeFigure, endPath, fillPath, flattenPath
-	, pathToRegion, strokeAndFillPath, strokePath, widenPath
-	) where
+        ( beginPath, closeFigure, endPath, fillPath, flattenPath
+        , pathToRegion, strokeAndFillPath, strokePath, widenPath
+        ) where
 
 import Graphics.Win32.GDI.Types
 import System.Win32.Types
 
+#include "windows_cconv.h"
+
 ----------------------------------------------------------------
 -- Paths
 ----------------------------------------------------------------
@@ -32,56 +36,56 @@
 beginPath :: HDC -> IO ()
 beginPath dc =
   failIfFalse_ "BeginPath" $ c_BeginPath dc
-foreign import stdcall unsafe "windows.h BeginPath"
+foreign import WINDOWS_CCONV unsafe "windows.h BeginPath"
   c_BeginPath :: HDC -> IO Bool
 
 closeFigure :: HDC -> IO ()
 closeFigure dc =
   failIfFalse_ "CloseFigure" $ c_CloseFigure dc
-foreign import stdcall unsafe "windows.h CloseFigure"
+foreign import WINDOWS_CCONV unsafe "windows.h CloseFigure"
   c_CloseFigure :: HDC -> IO Bool
 
 endPath :: HDC -> IO ()
 endPath dc =
   failIfFalse_ "EndPath" $ c_EndPath dc
-foreign import stdcall unsafe "windows.h EndPath"
+foreign import WINDOWS_CCONV unsafe "windows.h EndPath"
   c_EndPath :: HDC -> IO Bool
 
 fillPath :: HDC -> IO ()
 fillPath dc =
   failIfFalse_ "FillPath" $ c_FillPath dc
-foreign import stdcall unsafe "windows.h FillPath"
+foreign import WINDOWS_CCONV unsafe "windows.h FillPath"
   c_FillPath :: HDC -> IO Bool
 
 flattenPath :: HDC -> IO ()
 flattenPath dc =
   failIfFalse_ "FlattenPath" $ c_FlattenPath dc
-foreign import stdcall unsafe "windows.h FlattenPath"
+foreign import WINDOWS_CCONV unsafe "windows.h FlattenPath"
   c_FlattenPath :: HDC -> IO Bool
 
 pathToRegion :: HDC -> IO HRGN
 pathToRegion dc = do
   ptr <- failIfNull "PathToRegion" $ c_PathToRegion dc
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h PathToRegion"
+foreign import WINDOWS_CCONV unsafe "windows.h PathToRegion"
   c_PathToRegion :: HDC -> IO PRGN
 
 strokeAndFillPath :: HDC -> IO ()
 strokeAndFillPath dc =
   failIfFalse_ "StrokeAndFillPath" $ c_StrokeAndFillPath dc
-foreign import stdcall unsafe "windows.h StrokeAndFillPath"
+foreign import WINDOWS_CCONV unsafe "windows.h StrokeAndFillPath"
   c_StrokeAndFillPath :: HDC -> IO Bool
 
 strokePath :: HDC -> IO ()
 strokePath dc =
   failIfFalse_ "StrokePath" $ c_StrokePath dc
-foreign import stdcall unsafe "windows.h StrokePath"
+foreign import WINDOWS_CCONV unsafe "windows.h StrokePath"
   c_StrokePath :: HDC -> IO Bool
 
 widenPath :: HDC -> IO ()
 widenPath dc =
   failIfFalse_ "WidenPath" $ c_WidenPath dc
-foreign import stdcall unsafe "windows.h WidenPath"
+foreign import WINDOWS_CCONV unsafe "windows.h WidenPath"
   c_WidenPath :: HDC -> IO Bool
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Pen.hsc b/Graphics/Win32/GDI/Pen.hsc
--- a/Graphics/Win32/GDI/Pen.hsc
+++ b/Graphics/Win32/GDI/Pen.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -20,6 +22,8 @@
 import System.Win32.Types
 import Graphics.Win32.GDI.Types
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 ----------------------------------------------------------------
@@ -37,13 +41,13 @@
 getStockPen :: StockPen -> IO HPEN
 getStockPen stockpen =
   failIfNull "GetStockPen" $ c_GetStockPen stockpen
-foreign import stdcall unsafe "windows.h GetStockObject"
+foreign import WINDOWS_CCONV unsafe "windows.h GetStockObject"
   c_GetStockPen :: StockPen -> IO HPEN
 
 deletePen :: HPEN -> IO ()
 deletePen pen =
   failIfFalse_ "DeletePen" $ c_DeletePen pen
-foreign import stdcall unsafe "windows.h DeleteObject"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteObject"
   c_DeletePen :: HPEN -> IO Bool
 
 ----------------------------------------------------------------
@@ -52,7 +56,7 @@
 
 type PenStyle   = INT
 
-#{enum PenStyle,                              // Pick one of these
+#{enum PenStyle,
  , pS_SOLID             = PS_SOLID            // default
  , pS_DASH              = PS_DASH             // -------
  , pS_DOT               = PS_DOT              // .......
@@ -65,14 +69,14 @@
  , pS_STYLE_MASK        = PS_STYLE_MASK       // all the above
  }
 
-#{enum PenStyle ,                             // "or" with one of these
+#{enum PenStyle,
  , pS_ENDCAP_ROUND      = PS_ENDCAP_ROUND     // default
  , pS_ENDCAP_SQUARE     = PS_ENDCAP_SQUARE
  , pS_ENDCAP_FLAT       = PS_ENDCAP_FLAT
  , pS_ENDCAP_MASK       = PS_ENDCAP_MASK      // all the above
  }
 
-#{enum PenStyle,                              // "or" with one of these
+#{enum PenStyle,
  , pS_JOIN_ROUND        = PS_JOIN_ROUND       // default
  , pS_JOIN_BEVEL        = PS_JOIN_BEVEL
  , pS_JOIN_MITER        = PS_JOIN_MITER
@@ -83,7 +87,7 @@
 you'll have to define it.
 -}
 
-#{enum PenStyle,                              // "or" with one of these
+#{enum PenStyle,
  , pS_COSMETIC          = PS_COSMETIC         // default
  , pS_GEOMETRIC         = PS_GEOMETRIC
  , pS_TYPE_MASK         = PS_TYPE_MASK        // all the above
@@ -92,7 +96,7 @@
 createPen :: PenStyle -> INT -> COLORREF -> IO HPEN
 createPen style n color =
   failIfNull "CreatePen" $ c_CreatePen style n color
-foreign import stdcall unsafe "windows.h CreatePen"
+foreign import WINDOWS_CCONV unsafe "windows.h CreatePen"
   c_CreatePen :: PenStyle -> INT -> COLORREF -> IO HPEN
 
 -- Not very well supported on Win'95
diff --git a/Graphics/Win32/GDI/Region.hs b/Graphics/Win32/GDI/Region.hs
--- a/Graphics/Win32/GDI/Region.hs
+++ b/Graphics/Win32/GDI/Region.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -22,6 +24,8 @@
 
 import Foreign
 
+#include "windows_cconv.h"
+
 ----------------------------------------------------------------
 -- Regions
 ----------------------------------------------------------------
@@ -35,56 +39,56 @@
   withForeignPtr src1 $ \ p_src1 ->
   withForeignPtr src2 $ \ p_src2 ->
   failIf badRegion "CombineRgn" $ c_CombineRgn p_dest p_src1 p_src2 mode
-foreign import stdcall unsafe "windows.h CombineRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h CombineRgn"
   c_CombineRgn :: PRGN -> PRGN -> PRGN -> ClippingMode -> IO RegionType
 
 offsetRgn :: HRGN -> INT -> INT -> IO RegionType
 offsetRgn rgn xoff yoff =
   withForeignPtr rgn $ \ p_rgn ->
   failIf badRegion "OffsetRgn" $ c_OffsetRgn p_rgn xoff yoff
-foreign import stdcall unsafe "windows.h OffsetRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h OffsetRgn"
   c_OffsetRgn :: PRGN -> INT -> INT -> IO RegionType
 
 getRgnBox :: HRGN -> LPRECT -> IO RegionType
 getRgnBox rgn p_rect =
   withForeignPtr rgn $ \ p_rgn ->
   failIf badRegion "GetRgnBox" $ c_GetRgnBox p_rgn p_rect
-foreign import stdcall unsafe "windows.h GetRgnBox"
+foreign import WINDOWS_CCONV unsafe "windows.h GetRgnBox"
   c_GetRgnBox :: PRGN -> LPRECT -> IO RegionType
 
 createEllipticRgn :: INT -> INT -> INT -> INT -> IO HRGN
 createEllipticRgn x0 y0 x1 y1 = do
   ptr <- failIfNull "CreateEllipticRgn" $ c_CreateEllipticRgn x0 y0 x1 y1
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h CreateEllipticRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateEllipticRgn"
   c_CreateEllipticRgn :: INT -> INT -> INT -> INT -> IO PRGN
 
 createEllipticRgnIndirect :: LPRECT -> IO HRGN
 createEllipticRgnIndirect rp = do
   ptr <- failIfNull "CreateEllipticRgnIndirect" $ c_CreateEllipticRgnIndirect rp
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h CreateEllipticRgnIndirect"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateEllipticRgnIndirect"
   c_CreateEllipticRgnIndirect :: LPRECT -> IO PRGN
 
 createRectRgn :: INT -> INT -> INT -> INT -> IO HRGN
 createRectRgn x0 y0 x1 y1 = do
   ptr <- failIfNull "CreateRectRgn" $ c_CreateRectRgn x0 y0 x1 y1
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h CreateRectRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateRectRgn"
   c_CreateRectRgn :: INT -> INT -> INT -> INT -> IO PRGN
 
 createRectRgnIndirect :: LPRECT -> IO HRGN
 createRectRgnIndirect rp = do
   ptr <- failIfNull "CreateRectRgnIndirect" $ c_CreateRectRgnIndirect rp
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h CreateRectRgnIndirect"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateRectRgnIndirect"
   c_CreateRectRgnIndirect :: LPRECT -> IO PRGN
 
 createRoundRectRgn :: INT -> INT -> INT -> INT -> INT -> INT -> IO HRGN
 createRoundRectRgn x0 y0 x1 y1 h w = do
   ptr <- failIfNull "CreateRoundRectRgn" $ c_CreateRoundRectRgn x0 y0 x1 y1 h w
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h CreateRoundRectRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateRoundRectRgn"
   c_CreateRoundRectRgn :: INT -> INT -> INT -> INT -> INT -> INT -> IO PRGN
 
 createPolygonRgn :: [POINT] -> PolyFillMode -> IO HRGN
@@ -93,54 +97,54 @@
   ptr <- failIfNull "CreatePolygonRgn" $
     c_CreatePolygonRgn point_array npoints mode
   newForeignHANDLE ptr
-foreign import stdcall unsafe "windows.h CreatePolygonRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h CreatePolygonRgn"
   c_CreatePolygonRgn :: Ptr POINT -> Int -> PolyFillMode -> IO PRGN
 
 -- Needs to do proper error test for EqualRgn; GSL ???
 
-foreign import stdcall unsafe "windows.h EqualRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h EqualRgn"
   equalRgn :: PRGN -> PRGN -> IO Bool
 
 fillRgn :: HDC -> HRGN -> HBRUSH -> IO ()
 fillRgn dc rgn brush =
   withForeignPtr rgn $ \ p_rgn ->
   failIfFalse_ "FillRgn" $ c_FillRgn dc p_rgn brush
-foreign import stdcall unsafe "windows.h FillRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h FillRgn"
   c_FillRgn :: HDC -> PRGN -> HBRUSH -> IO Bool
 
 invertRgn :: HDC -> HRGN -> IO ()
 invertRgn dc rgn =
   withForeignPtr rgn $ \ p_rgn ->
   failIfFalse_ "InvertRgn" $ c_InvertRgn dc p_rgn
-foreign import stdcall unsafe "windows.h InvertRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h InvertRgn"
   c_InvertRgn :: HDC -> PRGN -> IO Bool
 
 paintRgn :: HDC -> HRGN -> IO ()
 paintRgn dc rgn =
   withForeignPtr rgn $ \ p_rgn ->
   failIfFalse_ "PaintRgn" $ c_PaintRgn dc p_rgn
-foreign import stdcall unsafe "windows.h PaintRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h PaintRgn"
   c_PaintRgn :: HDC -> PRGN -> IO Bool
 
 frameRgn :: HDC -> HRGN -> HBRUSH -> Int -> Int -> IO ()
 frameRgn dc rgn brush w h =
   withForeignPtr rgn $ \ p_rgn ->
   failIfFalse_ "FrameRgn" $ c_FrameRgn dc p_rgn brush w h
-foreign import stdcall unsafe "windows.h FrameRgn"
+foreign import WINDOWS_CCONV unsafe "windows.h FrameRgn"
   c_FrameRgn :: HDC -> PRGN -> HBRUSH -> Int -> Int -> IO Bool
 
 ptInRegion :: HRGN -> Int -> Int -> IO Bool
 ptInRegion rgn x y =
   withForeignPtr rgn $ \ p_rgn ->
   c_PtInRegion p_rgn x y
-foreign import stdcall unsafe "windows.h PtInRegion"
+foreign import WINDOWS_CCONV unsafe "windows.h PtInRegion"
   c_PtInRegion :: PRGN -> Int -> Int -> IO Bool
 
 rectInRegion :: HRGN -> RECT -> IO Bool
 rectInRegion rgn rect =
   withForeignPtr rgn $ \ p_rgn ->
   withRECT rect $ c_RectInRegion p_rgn
-foreign import stdcall unsafe "windows.h RectInRegion"
+foreign import WINDOWS_CCONV unsafe "windows.h RectInRegion"
   c_RectInRegion :: PRGN -> Ptr RECT -> IO Bool
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/GDI/Types.hsc b/Graphics/Win32/GDI/Types.hsc
--- a/Graphics/Win32/GDI/Types.hsc
+++ b/Graphics/Win32/GDI/Types.hsc
@@ -1,6 +1,4 @@
-#if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Graphics.Win32.GDI.Types
@@ -17,42 +15,42 @@
 
 module Graphics.Win32.GDI.Types
 {-  -- still incomplete
-	( POINT,        marshall_point, unmarshall_point
-	, ListPOINT,    marshall_ListPOINT_
-	, ListLenPOINT, marshall_ListLenPOINT_
-	, RECT,         marshall_rect, unmarshall_rect
-	, SIZE,         marshall_size, unmarshall_size
-	, nullPtr
-	, HBITMAP	, MbHBITMAP
-	, HFONT		, MbHFONT
-	, HCURSOR	, MbHCURSOR
-	, HICON		, MbHICON
-	, HRGN		, MbHRGN
-	, PRGN
-	, HPALETTE	, MbHPALETTE
-	, HBRUSH	, MbHBRUSH
-	, HPEN		, MbHPEN
-	, HACCEL	--, MbHACCEL
-	, HDC		, MbHDC
-	, HDWP          , MbHDWP
-	, HWND		, MbHWND
-	, HMENU		, MbHMENU
-	, PolyFillMode
-	, ArcDirection
-	, MbArcDirection
-	, GraphicsMode
-	, MbGraphicsMode
-	, BackgroundMode
-	, HatchStyle
-	, StretchBltMode
-	, COLORREF
-	, TextAlignment
-	, ClippingMode
-	, RegionType
-	, gDI_ERROR
-	)
+        ( POINT,        marshall_point, unmarshall_point
+        , ListPOINT,    marshall_ListPOINT_
+        , ListLenPOINT, marshall_ListLenPOINT_
+        , RECT,         marshall_rect, unmarshall_rect
+        , SIZE,         marshall_size, unmarshall_size
+        , nullPtr
+        , HBITMAP       , MbHBITMAP
+        , HFONT         , MbHFONT
+        , HCURSOR       , MbHCURSOR
+        , HICON         , MbHICON
+        , HRGN          , MbHRGN
+        , PRGN
+        , HPALETTE      , MbHPALETTE
+        , HBRUSH        , MbHBRUSH
+        , HPEN          , MbHPEN
+        , HACCEL        --, MbHACCEL
+        , HDC           , MbHDC
+        , HDWP          , MbHDWP
+        , HWND          , MbHWND
+        , HMENU         , MbHMENU
+        , PolyFillMode
+        , ArcDirection
+        , MbArcDirection
+        , GraphicsMode
+        , MbGraphicsMode
+        , BackgroundMode
+        , HatchStyle
+        , StretchBltMode
+        , COLORREF
+        , TextAlignment
+        , ClippingMode
+        , RegionType
+        , gDI_ERROR
+        )
 -}
-	where
+        where
 
 import System.Win32.Types
 
@@ -215,11 +213,11 @@
 type   HWND       = HANDLE
 type MbHWND       = Maybe HWND
 
-#{enum HWND, castUINTToPtr
- , hWND_BOTTOM    = HWND_BOTTOM
- , hWND_NOTOPMOST = HWND_NOTOPMOST
- , hWND_TOP       = HWND_TOP
- , hWND_TOPMOST   = HWND_TOPMOST
+#{enum HWND, castUINTPtrToPtr
+ , hWND_BOTTOM    = (UINT_PTR)HWND_BOTTOM
+ , hWND_NOTOPMOST = (UINT_PTR)HWND_NOTOPMOST
+ , hWND_TOP       = (UINT_PTR)HWND_TOP
+ , hWND_TOPMOST   = (UINT_PTR)HWND_TOPMOST
  }
 
 type   HMENU      = HANDLE
diff --git a/Graphics/Win32/Icon.hs b/Graphics/Win32/Icon.hs
--- a/Graphics/Win32/Icon.hs
+++ b/Graphics/Win32/Icon.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -17,29 +19,38 @@
 
 module Graphics.Win32.Icon where
 
+import Foreign (Ptr)
 import Graphics.Win32.GDI.Types
 import System.Win32.Types
 
+#include "windows_cconv.h"
+
 ----------------------------------------------------------------
 -- Icons
 ----------------------------------------------------------------
 
+createIcon :: HINSTANCE -> Int -> Int -> BYTE -> BYTE -> Ptr BYTE -> Ptr BYTE -> IO HICON
+createIcon instance_ width height planes bitsPixel andBits xorBits =
+    failIfNull "CreateIcon" $ c_CreateIcon instance_ width height planes bitsPixel andBits xorBits
+foreign import WINDOWS_CCONV unsafe "windows.h CreateIcon"
+    c_CreateIcon :: HINSTANCE -> Int -> Int -> BYTE -> BYTE -> Ptr BYTE -> Ptr BYTE -> IO HICON
+
 copyIcon :: HICON -> IO HICON
 copyIcon icon =
   failIfNull "CopyIcon" $ c_CopyIcon icon
-foreign import stdcall unsafe "windows.h CopyIcon"
+foreign import WINDOWS_CCONV unsafe "windows.h CopyIcon"
   c_CopyIcon :: HICON -> IO HICON
 
 drawIcon :: HDC -> Int -> Int -> HICON -> IO ()
 drawIcon dc x y icon =
   failIfFalse_ "DrawIcon" $ c_DrawIcon dc x y icon
-foreign import stdcall unsafe "windows.h DrawIcon"
+foreign import WINDOWS_CCONV unsafe "windows.h DrawIcon"
   c_DrawIcon :: HDC -> Int -> Int -> HICON -> IO Bool
 
 destroyIcon :: HICON -> IO ()
 destroyIcon icon =
   failIfFalse_ "DestroyIcon" $ c_DestroyIcon icon
-foreign import stdcall unsafe "windows.h DestroyIcon"
+foreign import WINDOWS_CCONV unsafe "windows.h DestroyIcon"
   c_DestroyIcon :: HICON -> IO Bool
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/Key.hsc b/Graphics/Win32/Key.hsc
--- a/Graphics/Win32/Key.hsc
+++ b/Graphics/Win32/Key.hsc
@@ -1,10 +1,12 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Graphics.Win32.Key
--- Copyright   :  (c) Alastair Reid, 1997-2003
+-- Copyright   :  (c) Alastair Reid, 1997-2003, 2013 shelarcy
 -- License     :  BSD-style (see the file libraries/base/LICENSE)
 --
 -- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
@@ -17,107 +19,256 @@
 
 module Graphics.Win32.Key where
 
-import Graphics.Win32.GDI.Types
-import System.Win32.Types
-
 import Control.Monad (liftM)
+import Graphics.Win32.GDI.Types (HWND)
+import System.Win32.Types    ( DWORD, UINT, WORD, ptrToMaybe, BOOL, SHORT,
+                               failIfFalse_, failIfZero )
+import Control.Exception     ( bracket )
+import Foreign.Ptr           ( Ptr, nullPtr )
+import Foreign.C.Types       ( CWchar(..) )
+import Foreign.Marshal.Array ( allocaArray, peekArray )
+import System.Win32.String   ( LPTSTR, LPCTSTR
+                             , withTString, withTStringBuffer, peekTString )
+import System.Win32.Thread   ( TID, getCurrentThreadId )
 
+##include "windows_cconv.h"
+
 #include <windows.h>
+#include "winuser_compat.h"
 
 type VKey   = DWORD
 
 #{enum VKey,
- , vK_LBUTTON   = VK_LBUTTON
- , vK_RBUTTON   = VK_RBUTTON
- , vK_CANCEL    = VK_CANCEL
- , vK_MBUTTON   = VK_MBUTTON
- , vK_BACK      = VK_BACK
- , vK_TAB       = VK_TAB
- , vK_CLEAR     = VK_CLEAR
- , vK_RETURN    = VK_RETURN
- , vK_SHIFT     = VK_SHIFT
- , vK_CONTROL   = VK_CONTROL
- , vK_MENU      = VK_MENU
- , vK_PAUSE     = VK_PAUSE
- , vK_CAPITAL   = VK_CAPITAL
- , vK_ESCAPE    = VK_ESCAPE
- , vK_SPACE     = VK_SPACE
- , vK_PRIOR     = VK_PRIOR
- , vK_NEXT      = VK_NEXT
- , vK_END       = VK_END
- , vK_HOME      = VK_HOME
- , vK_LEFT      = VK_LEFT
- , vK_UP        = VK_UP
- , vK_RIGHT     = VK_RIGHT
- , vK_DOWN      = VK_DOWN
- , vK_SELECT    = VK_SELECT
- , vK_EXECUTE   = VK_EXECUTE
- , vK_SNAPSHOT  = VK_SNAPSHOT
- , vK_INSERT    = VK_INSERT
- , vK_DELETE    = VK_DELETE
- , vK_HELP      = VK_HELP
- , vK_NUMPAD0   = VK_NUMPAD0
- , vK_NUMPAD1   = VK_NUMPAD1
- , vK_NUMPAD2   = VK_NUMPAD2
- , vK_NUMPAD3   = VK_NUMPAD3
- , vK_NUMPAD4   = VK_NUMPAD4
- , vK_NUMPAD5   = VK_NUMPAD5
- , vK_NUMPAD6   = VK_NUMPAD6
- , vK_NUMPAD7   = VK_NUMPAD7
- , vK_NUMPAD8   = VK_NUMPAD8
- , vK_NUMPAD9   = VK_NUMPAD9
- , vK_MULTIPLY  = VK_MULTIPLY
- , vK_ADD       = VK_ADD
- , vK_SEPARATOR = VK_SEPARATOR
- , vK_SUBTRACT  = VK_SUBTRACT
- , vK_DECIMAL   = VK_DECIMAL
- , vK_DIVIDE    = VK_DIVIDE
- , vK_F1        = VK_F1
- , vK_F2        = VK_F2
- , vK_F3        = VK_F3
- , vK_F4        = VK_F4
- , vK_F5        = VK_F5
- , vK_F6        = VK_F6
- , vK_F7        = VK_F7
- , vK_F8        = VK_F8
- , vK_F9        = VK_F9
- , vK_F10       = VK_F10
- , vK_F11       = VK_F11
- , vK_F12       = VK_F12
- , vK_F13       = VK_F13
- , vK_F14       = VK_F14
- , vK_F15       = VK_F15
- , vK_F16       = VK_F16
- , vK_F17       = VK_F17
- , vK_F18       = VK_F18
- , vK_F19       = VK_F19
- , vK_F20       = VK_F20
- , vK_F21       = VK_F21
- , vK_F22       = VK_F22
- , vK_F23       = VK_F23
- , vK_F24       = VK_F24
- , vK_NUMLOCK   = VK_NUMLOCK
- , vK_SCROLL    = VK_SCROLL
+ , vK_LBUTTON             = VK_LBUTTON
+ , vK_RBUTTON             = VK_RBUTTON
+ , vK_CANCEL              = VK_CANCEL
+ , vK_MBUTTON             = VK_MBUTTON
+ , vK_BACK                = VK_BACK
+ , vK_TAB                 = VK_TAB
+ , vK_CLEAR               = VK_CLEAR
+ , vK_RETURN              = VK_RETURN
+ , vK_SHIFT               = VK_SHIFT
+ , vK_CONTROL             = VK_CONTROL
+ , vK_MENU                = VK_MENU
+ , vK_PAUSE               = VK_PAUSE
+ , vK_CAPITAL             = VK_CAPITAL
+ , vK_ESCAPE              = VK_ESCAPE
+ , vK_SPACE               = VK_SPACE
+ , vK_PRIOR               = VK_PRIOR
+ , vK_NEXT                = VK_NEXT
+ , vK_END                 = VK_END
+ , vK_HOME                = VK_HOME
+ , vK_LEFT                = VK_LEFT
+ , vK_UP                  = VK_UP
+ , vK_RIGHT               = VK_RIGHT
+ , vK_DOWN                = VK_DOWN
+ , vK_SELECT              = VK_SELECT
+ , vK_EXECUTE             = VK_EXECUTE
+ , vK_SNAPSHOT            = VK_SNAPSHOT
+ , vK_INSERT              = VK_INSERT
+ , vK_DELETE              = VK_DELETE
+ , vK_HELP                = VK_HELP
+ , vK_NUMPAD0             = VK_NUMPAD0
+ , vK_NUMPAD1             = VK_NUMPAD1
+ , vK_NUMPAD2             = VK_NUMPAD2
+ , vK_NUMPAD3             = VK_NUMPAD3
+ , vK_NUMPAD4             = VK_NUMPAD4
+ , vK_NUMPAD5             = VK_NUMPAD5
+ , vK_NUMPAD6             = VK_NUMPAD6
+ , vK_NUMPAD7             = VK_NUMPAD7
+ , vK_NUMPAD8             = VK_NUMPAD8
+ , vK_NUMPAD9             = VK_NUMPAD9
+ , vK_MULTIPLY            = VK_MULTIPLY
+ , vK_ADD                 = VK_ADD
+ , vK_SEPARATOR           = VK_SEPARATOR
+ , vK_SUBTRACT            = VK_SUBTRACT
+ , vK_DECIMAL             = VK_DECIMAL
+ , vK_DIVIDE              = VK_DIVIDE
+ , vK_F1                  = VK_F1
+ , vK_F2                  = VK_F2
+ , vK_F3                  = VK_F3
+ , vK_F4                  = VK_F4
+ , vK_F5                  = VK_F5
+ , vK_F6                  = VK_F6
+ , vK_F7                  = VK_F7
+ , vK_F8                  = VK_F8
+ , vK_F9                  = VK_F9
+ , vK_F10                 = VK_F10
+ , vK_F11                 = VK_F11
+ , vK_F12                 = VK_F12
+ , vK_F13                 = VK_F13
+ , vK_F14                 = VK_F14
+ , vK_F15                 = VK_F15
+ , vK_F16                 = VK_F16
+ , vK_F17                 = VK_F17
+ , vK_F18                 = VK_F18
+ , vK_F19                 = VK_F19
+ , vK_F20                 = VK_F20
+ , vK_F21                 = VK_F21
+ , vK_F22                 = VK_F22
+ , vK_F23                 = VK_F23
+ , vK_F24                 = VK_F24
+ , vK_NUMLOCK             = VK_NUMLOCK
+ , vK_SCROLL              = VK_SCROLL
+  , vK_XBUTTON1           = VK_XBUTTON1
+ , vK_XBUTTON2            = VK_XBUTTON2
+ , vK_KANA                = VK_KANA
+ , vK_HANGUL              = VK_HANGUL
+ , vK_JUNJA               = VK_JUNJA
+ , vK_FINAL               = VK_FINAL
+ , vK_HANJA               = VK_HANJA
+ , vK_KANJI               = VK_KANJI
+ , vK_CONVERT             = VK_CONVERT
+ , vK_NONCONVERT          = VK_NONCONVERT
+ , vK_ACCEPT              = VK_ACCEPT
+ , vK_MODECHANGE          = VK_MODECHANGE
+ , vK_PRINT               = VK_PRINT
+ , vK_APPS                = VK_APPS
+ , vK_SLEEP               = VK_SLEEP
+ , vK_LWIN                = VK_LWIN
+ , vK_RWIN                = VK_RWIN
+ , vK_LSHIFT              = VK_LSHIFT
+ , vK_RSHIFT              = VK_RSHIFT
+ , vK_LCONTROL            = VK_LCONTROL
+ , vK_RCONTROL            = VK_RCONTROL
+ , vK_LMENU               = VK_LMENU
+ , vK_RMENU               = VK_RMENU
+ , vK_BROWSER_BACK        = VK_BROWSER_BACK
+ , vK_BROWSER_FORWARD     = VK_BROWSER_FORWARD
+ , vK_BROWSER_REFRESH     = VK_BROWSER_REFRESH
+ , vK_BROWSER_STOP        = VK_BROWSER_STOP
+ , vK_BROWSER_SEARCH      = VK_BROWSER_SEARCH
+ , vK_BROWSER_FAVORITES   = VK_BROWSER_FAVORITES
+ , vK_BROWSER_HOME        = VK_BROWSER_HOME
+ , vK_VOLUME_MUTE         = VK_VOLUME_MUTE
+ , vK_VOLUME_DOWN         = VK_VOLUME_DOWN
+ , vK_VOLUME_UP           = VK_VOLUME_UP
+ , vK_MEDIA_NEXT_TRACK    = VK_MEDIA_NEXT_TRACK
+ , vK_MEDIA_PREV_TRACK    = VK_MEDIA_PREV_TRACK
+ , vK_MEDIA_STOP          = VK_MEDIA_STOP
+ , vK_MEDIA_PLAY_PAUSE    = VK_MEDIA_PLAY_PAUSE
+ , vK_LAUNCH_MAIL         = VK_LAUNCH_MAIL
+ , vK_LAUNCH_MEDIA_SELECT = VK_LAUNCH_MEDIA_SELECT
+ , vK_LAUNCH_APP1         = VK_LAUNCH_APP1
+ , vK_LAUNCH_APP2         = VK_LAUNCH_APP2
+ , vK_OEM_1               = VK_OEM_1
+ , vK_OEM_PLUS            = VK_OEM_PLUS
+ , vK_OEM_COMMA           = VK_OEM_COMMA
+ , vK_OEM_MINUS           = VK_OEM_MINUS
+ , vK_OEM_PERIOD          = VK_OEM_PERIOD
+ , vK_OEM_2               = VK_OEM_2
+ , vK_OEM_3               = VK_OEM_3
+ , vK_OEM_4               = VK_OEM_4
+ , vK_OEM_5               = VK_OEM_5
+ , vK_OEM_6               = VK_OEM_6
+ , vK_OEM_7               = VK_OEM_7
+ , vK_OEM_8               = VK_OEM_8
+ , vK_OEM_102             = VK_OEM_102
+ , vK_PROCESSKEY          = VK_PROCESSKEY
+ , vK_PACKET              = VK_PACKET
+ , vK_ATTN                = VK_ATTN
+ , vK_CRSEL               = VK_CRSEL
+ , vK_EXSEL               = VK_EXSEL
+ , vK_EREOF               = VK_EREOF
+ , vK_PLAY                = VK_PLAY
+ , vK_ZOOM                = VK_ZOOM
+ , vK_NONAME              = VK_NONAME
+ , vK_PA1                 = VK_PA1
+ , vK_OEM_CLEAR           = VK_OEM_CLEAR
  }
+foreign import WINDOWS_CCONV unsafe "windows.h VkKeyScanExW"
+    c_VkKeyScanEx :: CWchar -> HKL -> IO SHORT
 
-foreign import stdcall unsafe "windows.h EnableWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h MapVirtualKeyW"
+    c_MapVirtualKey :: VKey -> UINT -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "windows.h MapVirtualKeyExW"
+    c_MapVirtualKeyEx :: VKey -> UINT -> HKL -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "windows.h EnableWindow"
   enableWindow :: HWND -> Bool -> IO Bool
 
 getActiveWindow :: IO (Maybe HWND)
 getActiveWindow = liftM ptrToMaybe c_GetActiveWindow
-foreign import stdcall unsafe "windows.h GetActiveWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h GetActiveWindow"
   c_GetActiveWindow :: IO HWND
 
-foreign import stdcall unsafe "windows.h GetAsyncKeyState"
+foreign import WINDOWS_CCONV unsafe "windows.h GetAsyncKeyState"
   getAsyncKeyState :: Int -> IO WORD
 
 getFocus :: IO (Maybe HWND)
 getFocus = liftM ptrToMaybe c_GetFocus
-foreign import stdcall unsafe "windows.h GetFocus"
+foreign import WINDOWS_CCONV unsafe "windows.h GetFocus"
   c_GetFocus :: IO HWND
 
-foreign import stdcall unsafe "windows.h GetKBCodePage"
+foreign import WINDOWS_CCONV unsafe "windows.h GetKBCodePage"
   getKBCodePage :: IO UINT
 
-foreign import stdcall unsafe "windows.h IsWindowEnabled"
+foreign import WINDOWS_CCONV unsafe "windows.h IsWindowEnabled"
   isWindowEnabled :: HWND -> IO Bool
+
+getCurrentKeyboardLayout :: IO HKL
+getCurrentKeyboardLayout = do
+    tid <- getCurrentThreadId
+    c_GetKeyboardLayout tid
+
+getKeyboardLayoutList :: IO [HKL]
+getKeyboardLayoutList = do
+    len' <- failIfZero "GetKeyboardLayoutList" $ c_GetKeyboardLayoutList 0 nullPtr
+    let len = fromIntegral len'
+    allocaArray len $ \buf -> do
+        _ <- failIfZero "GetKeyboardLayoutList" $ c_GetKeyboardLayoutList len  buf
+        peekArray len buf
+
+getKeyboardLayoutName :: IO String
+getKeyboardLayoutName
+  = withTStringBuffer 256 $ \buf -> do
+       failIfFalse_ "GetKeyboardLayoutName" $ c_GetKeyboardLayoutName buf
+       peekTString buf
+
+withLoadKeyboardLayout :: KeyLayoutFlags -> (HKL -> IO a) -> IO a
+withLoadKeyboardLayout flag io
+  = withTStringBuffer 256 $ \buf -> do
+       failIfFalse_ "GetKeyboardLayoutName" $ c_GetKeyboardLayoutName buf
+       bracket (c_LoadKeyboardLayout buf flag)
+               unloadKeyboardLayout
+               io
+
+withLoadKeyboardLayoutWithName :: String -> KeyLayoutFlags -> (HKL -> IO a) -> IO a
+withLoadKeyboardLayoutWithName str flag io
+  = withTString str $ \c_str ->
+      bracket (c_LoadKeyboardLayout c_str flag)
+              unloadKeyboardLayout
+              io
+
+unloadKeyboardLayout :: HKL -> IO ()
+unloadKeyboardLayout
+  = failIfFalse_ "UnloadKeyboardLayout" . c_UnloadKeyboardLayout
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetKeyboardLayout"
+    c_GetKeyboardLayout :: TID -> IO HKL
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetKeyboardLayoutList"
+    c_GetKeyboardLayoutList :: Int -> (Ptr HKL) -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetKeyboardLayoutNameW"
+    c_GetKeyboardLayoutName :: LPTSTR -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h LoadKeyboardLayoutW"
+    c_LoadKeyboardLayout :: LPCTSTR  -> KeyLayoutFlags -> IO HKL
+
+foreign import WINDOWS_CCONV unsafe "windows.h UnloadKeyboardLayout"
+    c_UnloadKeyboardLayout :: HKL -> IO BOOL
+
+type HKL = Ptr ()
+
+type KeyLayoutFlags = UINT
+
+#{enum KeyLayoutFlags,
+ , kLF_ACTIVATE      = KLF_ACTIVATE
+ , kLF_NOTELLSHELL   = KLF_NOTELLSHELL
+ , kLF_REORDER       = KLF_REORDER
+ , kLF_REPLACELANG   = KLF_REPLACELANG
+ , kLF_SUBSTITUTE_OK = KLF_SUBSTITUTE_OK
+ , kLF_SETFORPROCESS = KLF_SETFORPROCESS
+ }
diff --git a/Graphics/Win32/LayeredWindow.hsc b/Graphics/Win32/LayeredWindow.hsc
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/LayeredWindow.hsc
@@ -0,0 +1,57 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.LayeredWindow
+   Copyright   :  2012-2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Provides LayeredWindow functionality.
+-}
+module Graphics.Win32.LayeredWindow (module Graphics.Win32.LayeredWindow, Graphics.Win32.Window.c_GetWindowLongPtr ) where
+import Control.Monad   ( void )
+import Data.Bits       ( (.|.) )
+import Foreign.Ptr     ( Ptr )
+import Graphics.Win32.GDI.AlphaBlend ( BLENDFUNCTION )
+import Graphics.Win32.GDI.Types      ( COLORREF, HDC, SIZE, SIZE, POINT )
+import Graphics.Win32.Window         ( WindowStyleEx, c_GetWindowLongPtr, c_SetWindowLongPtr )
+import System.Win32.Types ( DWORD, HANDLE, BYTE, BOOL, INT )
+
+#include <windows.h>
+##include "windows_cconv.h"
+#include "winuser_compat.h"
+
+toLayeredWindow :: HANDLE -> IO ()
+toLayeredWindow w = do
+  flg <- c_GetWindowLongPtr w gWL_EXSTYLE
+  void $ c_SetWindowLongPtr w gWL_EXSTYLE (flg .|. (fromIntegral wS_EX_LAYERED))
+
+-- test w =  c_SetLayeredWindowAttributes w 0 128 lWA_ALPHA
+
+gWL_EXSTYLE :: INT
+gWL_EXSTYLE = #const GWL_EXSTYLE
+
+wS_EX_LAYERED :: WindowStyleEx
+wS_EX_LAYERED = #const WS_EX_LAYERED
+
+lWA_COLORKEY, lWA_ALPHA :: DWORD
+lWA_COLORKEY = #const LWA_COLORKEY
+lWA_ALPHA    = #const LWA_ALPHA
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetLayeredWindowAttributes"
+  c_SetLayeredWindowAttributes :: HANDLE -> COLORREF -> BYTE -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetLayeredWindowAttributes"
+  c_GetLayeredWindowAttributes :: HANDLE -> COLORREF -> Ptr BYTE -> Ptr DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h UpdateLayeredWindow"
+  c_UpdateLayeredWindow :: HANDLE -> HDC -> Ptr POINT -> Ptr SIZE ->  HDC -> Ptr POINT -> COLORREF -> Ptr BLENDFUNCTION -> DWORD -> IO BOOL
+
+#{enum DWORD,
+ , uLW_ALPHA    = ULW_ALPHA
+ , uLW_COLORKEY = ULW_COLORKEY
+ , uLW_OPAQUE   = ULW_OPAQUE
+ }
+
diff --git a/Graphics/Win32/Menu.hsc b/Graphics/Win32/Menu.hsc
--- a/Graphics/Win32/Menu.hsc
+++ b/Graphics/Win32/Menu.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -69,40 +71,42 @@
 import Foreign
 import Control.Monad (liftM)
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 type MenuName = LPCTSTR
 
 checkMenuItem :: HMENU -> MenuItem -> MenuFlag -> IO Bool
 checkMenuItem menu item check = do
-  rv <- failIf (== -1) "CheckMenuItem" $ c_CheckMenuItem menu item check
+  rv <- failIf (== maxBound) "CheckMenuItem" $ c_CheckMenuItem menu item check
   return (rv == mF_CHECKED)
-foreign import stdcall unsafe "windows.h CheckMenuItem"
+foreign import WINDOWS_CCONV unsafe "windows.h CheckMenuItem"
   c_CheckMenuItem :: HMENU -> UINT -> UINT -> IO DWORD
 
 checkMenuRadioItem :: HMENU -> MenuItem -> MenuItem -> MenuItem -> MenuFlag -> IO ()
 checkMenuRadioItem menu first_id last_id check flags =
   failIfFalse_ "CheckMenuRadioItem" $
     c_CheckMenuRadioItem menu first_id last_id check flags
-foreign import stdcall unsafe "windows.h CheckMenuRadioItem"
+foreign import WINDOWS_CCONV unsafe "windows.h CheckMenuRadioItem"
   c_CheckMenuRadioItem :: HMENU -> UINT -> UINT -> UINT -> UINT -> IO Bool
 
 createMenu :: IO HMENU
 createMenu =
   failIfNull "CreateMenu" $ c_CreateMenu
-foreign import stdcall unsafe "windows.h CreateMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateMenu"
   c_CreateMenu :: IO HMENU
 
 createPopupMenu :: IO HMENU
 createPopupMenu =
   failIfNull "CreatePopupMenu" $ c_CreatePopupMenu
-foreign import stdcall unsafe "windows.h CreatePopupMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h CreatePopupMenu"
   c_CreatePopupMenu :: IO HMENU
 
 drawMenuBar :: HWND -> IO ()
 drawMenuBar wnd =
   failIfFalse_ "DrawMenuBar" $ c_DrawMenuBar wnd
-foreign import stdcall unsafe "windows.h DrawMenuBar"
+foreign import WINDOWS_CCONV unsafe "windows.h DrawMenuBar"
   c_DrawMenuBar :: HWND -> IO Bool
 
 type MenuState = MenuFlag
@@ -110,7 +114,7 @@
 enableMenuItem :: HMENU -> MenuItem -> MenuFlag -> IO MenuState
 enableMenuItem menu item flag =
   failIf (== 0xffffffff) "EnableMenuItem" $ c_EnableMenuItem menu item flag
-foreign import stdcall unsafe "windows.h EnableMenuItem"
+foreign import WINDOWS_CCONV unsafe "windows.h EnableMenuItem"
   c_EnableMenuItem :: HMENU -> UINT -> UINT -> IO MenuState
 
 type GMDIFlag = UINT
@@ -210,56 +214,56 @@
  , sC_SEPARATOR         = SC_SEPARATOR
  }
 
-foreign import stdcall unsafe "windows.h IsMenu" isMenu :: HMENU -> IO Bool
+foreign import WINDOWS_CCONV unsafe "windows.h IsMenu" isMenu :: HMENU -> IO Bool
 
 getSystemMenu :: HWND  -> Bool ->     IO (Maybe HMENU)
 getSystemMenu wnd revert =
   liftM ptrToMaybe $ c_GetSystemMenu wnd revert
-foreign import stdcall unsafe "windows.h GetSystemMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSystemMenu"
   c_GetSystemMenu :: HWND  -> Bool ->     IO HMENU
 
 getMenu :: HWND  ->             IO (Maybe HMENU)
 getMenu wnd =
   liftM ptrToMaybe $ c_GetMenu wnd
-foreign import stdcall unsafe "windows.h GetMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenu"
   c_GetMenu :: HWND  ->             IO HMENU
 
 getMenuDefaultItem :: HMENU -> Bool -> GMDIFlag -> IO MenuItem
 getMenuDefaultItem menu bypos flags =
-  failIf (== -1) "GetMenuDefaultItem" $ c_GetMenuDefaultItem menu bypos flags
-foreign import stdcall unsafe "windows.h GetMenuDefaultItem"
+  failIf (== maxBound) "GetMenuDefaultItem" $ c_GetMenuDefaultItem menu bypos flags
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenuDefaultItem"
   c_GetMenuDefaultItem :: HMENU -> Bool -> UINT -> IO UINT
 
 getMenuState :: HMENU -> MenuItem -> MenuFlag -> IO MenuState
 getMenuState menu item flags =
-  failIf (== -1) "GetMenuState" $ c_GetMenuState menu item flags
-foreign import stdcall unsafe "windows.h GetMenuState"
+  failIf (== maxBound) "GetMenuState" $ c_GetMenuState menu item flags
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenuState"
   c_GetMenuState :: HMENU -> UINT -> UINT -> IO MenuState
 
 getSubMenu :: HMENU -> MenuItem -> IO (Maybe HMENU)
 getSubMenu menu pos =
   liftM ptrToMaybe $ c_GetSubMenu menu pos
-foreign import stdcall unsafe "windows.h GetSubMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSubMenu"
   c_GetSubMenu :: HMENU -> UINT -> IO HMENU
 
 setMenu :: HWND -> HMENU -> IO ()
 setMenu wnd menu =
   failIfFalse_ "SetMenu" $ c_SetMenu wnd menu
-foreign import stdcall unsafe "windows.h SetMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h SetMenu"
   c_SetMenu :: HWND -> HMENU -> IO Bool
 
 getMenuItemCount :: HMENU -> IO Int
 getMenuItemCount menu =
-  failIf (== -1) "GetMenuItemCount" $ c_GetMenuItemCount menu
-foreign import stdcall unsafe "windows.h GetMenuItemCount"
+  failIf (== maxBound) "GetMenuItemCount" $ c_GetMenuItemCount menu
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenuItemCount"
   c_GetMenuItemCount :: HMENU -> IO Int
 
 type MenuID = UINT
 
 getMenuItemID :: HMENU -> MenuItem -> IO MenuID
 getMenuItemID menu item =
-  failIf (== -1) "GetMenuItemID" $ c_GetMenuItemID menu item
-foreign import stdcall unsafe "windows.h GetMenuItemID"
+  failIf (== maxBound) "GetMenuItemID" $ c_GetMenuItemID menu item
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenuItemID"
   c_GetMenuItemID :: HMENU -> UINT -> IO MenuID
 
 data MenuItemInfo
@@ -341,7 +345,7 @@
   pokeFMask p_info mask
   failIfFalse_ "GetMenuItemInfo" $ c_GetMenuItemInfo menu item bypos p_info
   peekMenuItemInfo p_info
-foreign import stdcall unsafe "windows.h GetMenuItemInfoW"
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenuItemInfoW"
   c_GetMenuItemInfo :: HMENU -> UINT -> Bool -> Ptr MenuItemInfo -> IO Bool
 
 getMenuItemRect :: HWND -> HMENU -> MenuItem -> IO RECT
@@ -349,17 +353,17 @@
   allocaRECT $ \ p_rect -> do
   failIfFalse_ "GetMenuItemRect" $ c_GetMenuItemRect wnd menu item p_rect
   peekRECT p_rect
-foreign import stdcall unsafe "windows.h GetMenuItemRect"
+foreign import WINDOWS_CCONV unsafe "windows.h GetMenuItemRect"
   c_GetMenuItemRect :: HWND -> HMENU -> UINT -> LPRECT -> IO Bool
 
-foreign import stdcall unsafe "windows.h HiliteMenuItem"
+foreign import WINDOWS_CCONV unsafe "windows.h HiliteMenuItem"
   hiliteMenuItem :: HWND  -> HMENU -> MenuItem -> MenuFlag -> IO Bool
 
 insertMenuItem :: HMENU -> MenuItem -> Bool -> MenuItemInfo -> IO ()
 insertMenuItem menu item bypos info =
   withMenuItemInfo info $ \ p_info ->
   failIfFalse_ "InsertMenuItem" $ c_InsertMenuItem menu item bypos p_info
-foreign import stdcall unsafe "windows.h InsertMenuItemW"
+foreign import WINDOWS_CCONV unsafe "windows.h InsertMenuItemW"
   c_InsertMenuItem :: HMENU -> UINT -> Bool -> Ptr MenuItemInfo -> IO Bool
 
 type Menu = LPCTSTR
@@ -369,7 +373,7 @@
 loadMenu :: Maybe HINSTANCE -> Menu -> IO HMENU
 loadMenu mb_inst menu =
   failIfNull "LoadMenu" $ c_LoadMenu (maybePtr mb_inst) menu
-foreign import stdcall unsafe "windows.h LoadMenuW"
+foreign import WINDOWS_CCONV unsafe "windows.h LoadMenuW"
   c_LoadMenu :: HINSTANCE -> Menu -> IO HMENU
 
 -- Dealing with mappings to/from structs is a pain in GC,
@@ -385,26 +389,26 @@
 setMenuDefaultItem :: HMENU -> MenuItem -> Bool -> IO ()
 setMenuDefaultItem menu item bypos =
   failIfFalse_ "SetMenuDefaultItem" $ c_SetMenuDefaultItem menu item bypos
-foreign import stdcall unsafe "windows.h SetMenuDefaultItem"
+foreign import WINDOWS_CCONV unsafe "windows.h SetMenuDefaultItem"
   c_SetMenuDefaultItem :: HMENU -> MenuItem -> Bool -> IO Bool
 
 setMenuItemBitmaps :: HMENU -> MenuItem -> MenuFlag -> HBITMAP -> HBITMAP -> IO ()
 setMenuItemBitmaps menu pos flags bm_unchecked bm_checked =
   failIfFalse_ "SetMenuItemBitmaps" $
     c_SetMenuItemBitmaps menu pos flags bm_unchecked bm_checked
-foreign import stdcall unsafe "windows.h SetMenuItemBitmaps"
+foreign import WINDOWS_CCONV unsafe "windows.h SetMenuItemBitmaps"
   c_SetMenuItemBitmaps :: HMENU -> UINT -> UINT -> HBITMAP -> HBITMAP -> IO Bool
 
 destroyMenu :: HMENU -> IO ()
 destroyMenu menu =
   failIfFalse_ "DestroyMenu" $ c_DestroyMenu menu
-foreign import stdcall unsafe "windows.h DestroyMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h DestroyMenu"
   c_DestroyMenu :: HMENU -> IO Bool
 
 deleteMenu :: HMENU -> MenuItem -> MenuFlag -> IO ()
 deleteMenu menu item flag =
   failIfFalse_ "DeleteMenu" $ c_DeleteMenu menu item flag
-foreign import stdcall unsafe "windows.h DeleteMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteMenu"
   c_DeleteMenu :: HMENU -> UINT -> UINT -> IO Bool
 
 setMenuItemInfo :: HMENU -> MenuItem -> Bool -> MenuItemMask -> MenuItemInfo -> IO ()
@@ -412,14 +416,14 @@
   withMenuItemInfo info $ \ p_info -> do
   pokeFMask p_info mask
   failIfFalse_ "SetMenuItemInfo" $ c_SetMenuItemInfo menu item bypos p_info
-foreign import stdcall unsafe "windows.h SetMenuItemInfoW"
+foreign import WINDOWS_CCONV unsafe "windows.h SetMenuItemInfoW"
   c_SetMenuItemInfo :: HMENU -> UINT -> Bool -> Ptr MenuItemInfo -> IO Bool
 
 trackPopupMenu :: HMENU -> TrackMenuFlag -> Int -> Int -> HWND -> RECT -> IO ()
 trackPopupMenu menu flags x y wnd rect =
   withRECT rect $ \ p_rect ->
   failIfFalse_ "TrackPopupMenu" $ c_TrackPopupMenu menu flags x y 0 wnd p_rect
-foreign import stdcall unsafe "windows.h TrackPopupMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h TrackPopupMenu"
   c_TrackPopupMenu :: HMENU -> TrackMenuFlag -> Int -> Int -> Int -> HWND -> LPRECT -> IO Bool
 
 type TPMPARAMS = ()
@@ -436,37 +440,37 @@
 trackPopupMenuEx menu flags x y wnd mb_p_rect =
   maybeWith withTPMPARAMS mb_p_rect $ \ p_ptmp ->
   failIfFalse_ "TrackPopupMenuEx" $ c_TrackPopupMenuEx menu flags x y wnd p_ptmp
-foreign import stdcall unsafe "windows.h TrackPopupMenuEx"
+foreign import WINDOWS_CCONV unsafe "windows.h TrackPopupMenuEx"
   c_TrackPopupMenuEx :: HMENU -> TrackMenuFlag -> Int -> Int -> HWND -> Ptr TPMPARAMS -> IO Bool
 
 -- Note: these 3 assume the flags don't include MF_BITMAP or MF_OWNERDRAW
 -- (which are hidden by this interface)
 
-appendMenu :: HMENU -> MenuFlag -> MenuID -> String -> IO ()
+appendMenu :: HMENU -> MenuFlag -> MenuID -> Maybe String -> IO ()
 appendMenu menu flags id_item name =
-  withTString name $ \ c_name ->
+  maybeWith withTString name $ \ c_name ->
   failIfFalse_ "AppendMenu" $ c_AppendMenu menu flags id_item c_name
-foreign import stdcall unsafe "windows.h AppendMenuW"
+foreign import WINDOWS_CCONV unsafe "windows.h AppendMenuW"
   c_AppendMenu :: HMENU -> UINT -> MenuID -> LPCTSTR -> IO Bool
 
-insertMenu :: HMENU -> MenuItem -> MenuFlag -> MenuID -> String -> IO ()
+insertMenu :: HMENU -> MenuItem -> MenuFlag -> MenuID -> Maybe String -> IO ()
 insertMenu menu item flags id_item name =
-  withTString name $ \ c_name ->
+  maybeWith withTString name $ \ c_name ->
   failIfFalse_ "InsertMenu" $ c_InsertMenu menu item flags id_item c_name
-foreign import stdcall unsafe "windows.h InsertMenuW"
+foreign import WINDOWS_CCONV unsafe "windows.h InsertMenuW"
   c_InsertMenu :: HMENU -> UINT -> UINT -> MenuID -> LPCTSTR -> IO Bool
 
-modifyMenu :: HMENU -> MenuItem -> MenuFlag -> MenuID -> String -> IO ()
+modifyMenu :: HMENU -> MenuItem -> MenuFlag -> MenuID -> Maybe String -> IO ()
 modifyMenu menu item flags id_item name =
-  withTString name $ \ c_name ->
+  maybeWith withTString name $ \ c_name ->
   failIfFalse_ "ModifyMenu" $ c_ModifyMenu menu item flags id_item c_name
-foreign import stdcall unsafe "windows.h ModifyMenuW"
+foreign import WINDOWS_CCONV unsafe "windows.h ModifyMenuW"
   c_ModifyMenu :: HMENU -> UINT -> UINT -> MenuID -> LPCTSTR -> IO Bool
 
 removeMenu :: HMENU -> MenuItem -> MenuFlag -> IO ()
 removeMenu menu pos flags =
   failIfFalse_ "RemoveMenu" $ c_RemoveMenu menu pos flags
-foreign import stdcall unsafe "windows.h RemoveMenu"
+foreign import WINDOWS_CCONV unsafe "windows.h RemoveMenu"
   c_RemoveMenu :: HMENU -> UINT -> UINT -> IO Bool
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/Message.hsc b/Graphics/Win32/Message.hsc
--- a/Graphics/Win32/Message.hsc
+++ b/Graphics/Win32/Message.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -19,6 +21,8 @@
 
 import System.Win32.Types
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 type WindowMessage   = DWORD
@@ -154,12 +158,13 @@
  , wM_QUEUESYNC         = WM_QUEUESYNC
  , wM_USER              = WM_USER
  , wM_APP               = WM_APP
+ , wM_SETICON           = WM_SETICON
  }
 
 registerWindowMessage :: String -> IO WindowMessage
 registerWindowMessage msg =
   withTString msg c_RegisterWindowMessage
-foreign import stdcall unsafe "windows.h RegisterWindowMessageW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegisterWindowMessageW"
   c_RegisterWindowMessage :: LPCTSTR -> IO WindowMessage
 
 -- These are WM_SIZE specific
@@ -169,6 +174,8 @@
  , sIZE_MAXIMIZED       = SIZE_MAXIMIZED
  , sIZE_MAXSHOW         = SIZE_MAXSHOW
  , sIZE_MAXHIDE         = SIZE_MAXHIDE
+ , iCON_SMALL           = ICON_SMALL
+ , iCON_BIG             = ICON_BIG
  }
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/Misc.hsc b/Graphics/Win32/Misc.hsc
--- a/Graphics/Win32/Misc.hsc
+++ b/Graphics/Win32/Misc.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -23,8 +25,9 @@
 import Data.Maybe
 import Foreign
 
+##include "windows_cconv.h"
+
 #include <windows.h>
-#include "gettime.h"
 
 ----------------------------------------------------------------
 -- Resources
@@ -48,39 +51,39 @@
 loadAccelerators :: Maybe HINSTANCE -> Accelerator -> IO HACCEL
 loadAccelerators mb_inst accel =
   failIfNull "LoadAccelerators" $ c_LoadAccelerators (maybePtr mb_inst) accel
-foreign import stdcall unsafe "windows.h LoadAcceleratorsW"
+foreign import WINDOWS_CCONV unsafe "windows.h LoadAcceleratorsW"
   c_LoadAccelerators :: HINSTANCE -> Accelerator -> IO HACCEL
 
 loadCursor :: Maybe HINSTANCE -> Cursor -> IO HCURSOR
 loadCursor mb_inst cursor =
   failIfNull "LoadCursor" $ c_LoadCursor (maybePtr mb_inst) cursor
-foreign import stdcall unsafe "windows.h LoadCursorW"
+foreign import WINDOWS_CCONV unsafe "windows.h LoadCursorW"
   c_LoadCursor :: HINSTANCE -> Cursor -> IO HCURSOR
 
 loadIcon :: Maybe HINSTANCE -> Icon -> IO HICON
 loadIcon mb_inst icon =
   failIfNull "LoadIcon" $ c_LoadIcon (maybePtr mb_inst) icon
-foreign import stdcall unsafe "windows.h LoadIconW"
+foreign import WINDOWS_CCONV unsafe "windows.h LoadIconW"
   c_LoadIcon :: HINSTANCE -> Icon -> IO HICON
 
-#{enum Cursor, castUINTToPtr
- , iDC_ARROW        = (UINT)IDC_ARROW
- , iDC_IBEAM        = (UINT)IDC_IBEAM
- , iDC_WAIT         = (UINT)IDC_WAIT
- , iDC_CROSS        = (UINT)IDC_CROSS
- , iDC_UPARROW      = (UINT)IDC_UPARROW
- , iDC_SIZENWSE     = (UINT)IDC_SIZENWSE
- , iDC_SIZENESW     = (UINT)IDC_SIZENESW
- , iDC_SIZEWE       = (UINT)IDC_SIZEWE
- , iDC_SIZENS       = (UINT)IDC_SIZENS
+#{enum Cursor, castUINTPtrToPtr
+ , iDC_ARROW        = (UINT_PTR)IDC_ARROW
+ , iDC_IBEAM        = (UINT_PTR)IDC_IBEAM
+ , iDC_WAIT         = (UINT_PTR)IDC_WAIT
+ , iDC_CROSS        = (UINT_PTR)IDC_CROSS
+ , iDC_UPARROW      = (UINT_PTR)IDC_UPARROW
+ , iDC_SIZENWSE     = (UINT_PTR)IDC_SIZENWSE
+ , iDC_SIZENESW     = (UINT_PTR)IDC_SIZENESW
+ , iDC_SIZEWE       = (UINT_PTR)IDC_SIZEWE
+ , iDC_SIZENS       = (UINT_PTR)IDC_SIZENS
  }
 
-#{enum Icon, castUINTToPtr
- , iDI_APPLICATION  = (UINT)IDI_APPLICATION
- , iDI_HAND         = (UINT)IDI_HAND
- , iDI_QUESTION     = (UINT)IDI_QUESTION
- , iDI_EXCLAMATION  = (UINT)IDI_EXCLAMATION
- , iDI_ASTERISK     = (UINT)IDI_ASTERISK
+#{enum Icon, castUINTPtrToPtr
+ , iDI_APPLICATION  = (UINT_PTR)IDI_APPLICATION
+ , iDI_HAND         = (UINT_PTR)IDI_HAND
+ , iDI_QUESTION     = (UINT_PTR)IDI_QUESTION
+ , iDI_EXCLAMATION  = (UINT_PTR)IDI_EXCLAMATION
+ , iDI_ASTERISK     = (UINT_PTR)IDI_ASTERISK
  }
 
 ----------------------------------------------------------------
@@ -125,12 +128,12 @@
 
 -- Note: if the error is ever raised, we're in a very sad way!
 
-messageBox :: HWND -> String -> String -> MBStyle -> IO MBStatus
+messageBox :: Maybe HWND -> String -> String -> MBStyle -> IO MBStatus
 messageBox wnd text caption style =
   withTString text $ \ c_text ->
   withTString caption $ \ c_caption ->
-  failIfZero "MessageBox" $ c_MessageBox wnd c_text c_caption style
-foreign import stdcall unsafe "windows.h MessageBoxW"
+  failIfZero "MessageBox" $ c_MessageBox (maybePtr wnd) c_text c_caption style
+foreign import WINDOWS_CCONV safe "windows.h MessageBoxW"
   c_MessageBox :: HWND -> LPCTSTR -> LPCTSTR -> MBStyle -> IO MBStatus
 
 ----------------------------------------------------------------
@@ -148,7 +151,7 @@
 getStdHandle :: StdHandleId -> IO HANDLE
 getStdHandle hid =
   failIf (== iNVALID_HANDLE_VALUE) "GetStdHandle" $ c_GetStdHandle hid
-foreign import stdcall unsafe "windows.h GetStdHandle"
+foreign import WINDOWS_CCONV unsafe "windows.h GetStdHandle"
   c_GetStdHandle :: StdHandleId -> IO HANDLE
 
 ----------------------------------------------------------------
@@ -181,20 +184,20 @@
   allocaPOINT $ \ p_pt -> do
   failIfFalse_ "GetCursorPos" $ c_GetCursorPos p_pt
   peekPOINT p_pt
-foreign import stdcall unsafe "windows.h GetCursorPos"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCursorPos"
   c_GetCursorPos :: Ptr POINT -> IO Bool
 
 setCursorPos :: POINT -> IO ()
 setCursorPos (x,y) =
   failIfFalse_ "setCursorPos" $ c_SetCursorPos x y
-foreign import stdcall unsafe "windows.h SetCursorPos"
+foreign import WINDOWS_CCONV unsafe "windows.h SetCursorPos"
   c_SetCursorPos :: LONG -> LONG -> IO Bool
 
 clipCursor :: RECT -> IO ()
 clipCursor rect =
   withRECT rect $ \ p_rect ->
   failIfFalse_ "ClipCursor" $ c_ClipCursor p_rect
-foreign import stdcall unsafe "windows.h ClipCursor"
+foreign import WINDOWS_CCONV unsafe "windows.h ClipCursor"
   c_ClipCursor :: Ptr RECT -> IO Bool
 
 getClipCursor :: IO RECT
@@ -202,7 +205,7 @@
   allocaRECT $ \ p_rect -> do
   failIfFalse_ "GetClipCursor" $ c_GetClipCursor p_rect
   peekRECT p_rect
-foreign import stdcall unsafe "windows.h GetClipCursor"
+foreign import WINDOWS_CCONV unsafe "windows.h GetClipCursor"
   c_GetClipCursor :: Ptr RECT -> IO Bool
 
 ----------------------------------------------------------------
@@ -222,7 +225,7 @@
 exitWindowsEx :: ExitOption -> IO ()
 exitWindowsEx opt =
   failIfFalse_ "ExitWindowsEx" $ c_ExitWindowsEx opt 0
-foreign import stdcall unsafe "windows.h ExitWindowsEx"
+foreign import WINDOWS_CCONV unsafe "windows.h ExitWindowsEx"
   c_ExitWindowsEx :: ExitOption -> DWORD -> IO Bool
 
 exitWindows :: IO ()
@@ -248,13 +251,13 @@
 messageBeep :: Maybe Beep -> IO ()
 messageBeep mb_beep =
   c_MessageBeep (maybeBeep mb_beep)
-foreign import stdcall unsafe "windows.h MessageBeep"
+foreign import WINDOWS_CCONV unsafe "windows.h MessageBeep"
   c_MessageBeep :: Beep -> IO ()
 
 beep :: WORD -> MbDuration -> IO ()
 beep freq mb_dur =
   failIfFalse_ "Beep" $ c_Beep freq (maybeDuration mb_dur)
-foreign import stdcall unsafe "windows.h Beep"
+foreign import WINDOWS_CCONV unsafe "windows.h Beep"
   c_Beep :: WORD -> Duration -> IO Bool
 
 ----------------------------------------------------------------
@@ -265,26 +268,26 @@
 
 type TIMERPROC = FunPtr (HWND -> UINT -> TimerId -> DWORD -> IO ())
 
--- ToDo: support the other two forms of timer initialisation
+-- TODO: support the other two forms of timer initialisation
 
 -- Cause WM_TIMER events to be sent to window callback
 
 setWinTimer :: HWND -> TimerId -> UINT -> IO TimerId
 setWinTimer wnd timer elapse =
   failIfZero "SetTimer" $ c_SetTimer wnd timer elapse nullFunPtr
-foreign import stdcall unsafe "windows.h SetTimer"
+foreign import WINDOWS_CCONV unsafe "windows.h SetTimer"
   c_SetTimer :: HWND -> TimerId -> UINT -> TIMERPROC -> IO TimerId
 
 killTimer :: Maybe HWND -> TimerId -> IO ()
 killTimer mb_wnd timer =
   failIfFalse_ "KillTimer" $ c_KillTimer (maybePtr mb_wnd) timer
-foreign import stdcall unsafe "windows.h KillTimer"
+foreign import WINDOWS_CCONV unsafe "windows.h KillTimer"
   c_KillTimer :: HWND -> TimerId -> IO Bool
 
 -- For documentation purposes:
 type MilliSeconds = DWORD
 
-foreign import stdcall unsafe "windows.h timeGetTime"
+foreign import WINDOWS_CCONV unsafe "windows.h timeGetTime"
   timeGetTime :: IO MilliSeconds
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/Resource.hsc b/Graphics/Win32/Resource.hsc
--- a/Graphics/Win32/Resource.hsc
+++ b/Graphics/Win32/Resource.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -21,13 +23,15 @@
 
 import Foreign
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 beginUpdateResource :: String -> Bool -> IO HANDLE
 beginUpdateResource name del =
   withTString name $ \ c_name ->
   failIfNull "BeginUpdateResource" $ c_BeginUpdateResource c_name del
-foreign import stdcall unsafe "windows.h BeginUpdateResourceW"
+foreign import WINDOWS_CCONV unsafe "windows.h BeginUpdateResourceW"
   c_BeginUpdateResource :: LPCTSTR -> Bool -> IO HANDLE
 
 type ResourceImageType = UINT
@@ -45,42 +49,42 @@
 copyImage :: HANDLE -> ResourceImageType -> Int -> Int -> UINT -> IO HANDLE
 copyImage h ty x y flags =
   failIfNull "CopyImage" $ c_CopyImage h ty x y flags
-foreign import stdcall unsafe "windows.h CopyImage"
+foreign import WINDOWS_CCONV unsafe "windows.h CopyImage"
   c_CopyImage :: HANDLE -> ResourceImageType -> Int -> Int -> UINT -> IO HANDLE
 
 endUpdateResource :: HANDLE -> BOOL -> IO ()
 endUpdateResource h discard =
   failIfFalse_ "EndUpdateResource" $ c_EndUpdateResource h discard
-foreign import stdcall unsafe "windows.h EndUpdateResourceW"
+foreign import WINDOWS_CCONV unsafe "windows.h EndUpdateResourceW"
   c_EndUpdateResource :: HANDLE -> BOOL -> IO Bool
 
 type ResourceType = LPCTSTR
 
-#{enum ResourceType, castUINTToPtr
- , rT_ACCELERATOR  = (UINT)RT_ACCELERATOR // Accelerator table
- , rT_ANICURSOR    = (UINT)RT_ANICURSOR // Animated cursor
- , rT_ANIICON      = (UINT)RT_ANIICON   // Animated icon
- , rT_BITMAP       = (UINT)RT_BITMAP    // Bitmap resource
- , rT_CURSOR       = (UINT)RT_CURSOR    // Hardware-dependent cursor resource
- , rT_DIALOG       = (UINT)RT_DIALOG    // Dialog box
- , rT_FONT         = (UINT)RT_FONT      // Font resource
- , rT_FONTDIR      = (UINT)RT_FONTDIR    // Font directory resource
- , rT_GROUP_CURSOR = (UINT)RT_GROUP_CURSOR // Hardware-independent cursor resource
- , rT_GROUP_ICON   = (UINT)RT_GROUP_ICON // Hardware-independent icon resource
- , rT_HTML         = (UINT)RT_HTML      // HTML document
- , rT_ICON         = (UINT)RT_ICON      // Hardware-dependent icon resource
- , rT_MENU         = (UINT)RT_MENU      // Menu resource
- , rT_MESSAGETABLE = (UINT)RT_MESSAGETABLE // Message-table entry
- , rT_RCDATA       = (UINT)RT_RCDATA    // Application-defined resource (raw data)
- , rT_STRING       = (UINT)RT_STRING    // String-table entry
- , rT_VERSION      = (UINT)RT_VERSION   // Version resource
+#{enum ResourceType, castUINTPtrToPtr
+ , rT_ACCELERATOR  = (UINT_PTR)RT_ACCELERATOR // Accelerator table
+ , rT_ANICURSOR    = (UINT_PTR)RT_ANICURSOR // Animated cursor
+ , rT_ANIICON      = (UINT_PTR)RT_ANIICON   // Animated icon
+ , rT_BITMAP       = (UINT_PTR)RT_BITMAP    // Bitmap resource
+ , rT_CURSOR       = (UINT_PTR)RT_CURSOR    // Hardware-dependent cursor resource
+ , rT_DIALOG       = (UINT_PTR)RT_DIALOG    // Dialog box
+ , rT_FONT         = (UINT_PTR)RT_FONT      // Font resource
+ , rT_FONTDIR      = (UINT_PTR)RT_FONTDIR    // Font directory resource
+ , rT_GROUP_CURSOR = (UINT_PTR)RT_GROUP_CURSOR // Hardware-independent cursor resource
+ , rT_GROUP_ICON   = (UINT_PTR)RT_GROUP_ICON // Hardware-independent icon resource
+ , rT_HTML         = (UINT_PTR)RT_HTML      // HTML document
+ , rT_ICON         = (UINT_PTR)RT_ICON      // Hardware-dependent icon resource
+ , rT_MENU         = (UINT_PTR)RT_MENU      // Menu resource
+ , rT_MESSAGETABLE = (UINT_PTR)RT_MESSAGETABLE // Message-table entry
+ , rT_RCDATA       = (UINT_PTR)RT_RCDATA    // Application-defined resource (raw data)
+ , rT_STRING       = (UINT_PTR)RT_STRING    // String-table entry
+ , rT_VERSION      = (UINT_PTR)RT_VERSION   // Version resource
  }
 
 findResource :: HMODULE -> String -> ResourceType -> IO HRSRC
 findResource hmod name ty =
   withTString name $ \ c_name ->
   failIfNull "FindResource" $ c_FindResource hmod c_name ty
-foreign import stdcall unsafe "windows.h FindResourceW"
+foreign import WINDOWS_CCONV unsafe "windows.h FindResourceW"
   c_FindResource :: HMODULE -> LPCTSTR -> LPCTSTR -> IO HRSRC
 
 -- was: LPCTSTR_
@@ -88,7 +92,7 @@
 findResourceEx hmod name ty lang =
   withTString name $ \ c_name ->
   failIfNull "FindResourceEx" $ c_FindResourceEx hmod c_name ty lang
-foreign import stdcall unsafe "windows.h FindResourceExW"
+foreign import WINDOWS_CCONV unsafe "windows.h FindResourceExW"
   c_FindResourceEx :: HMODULE -> LPCTSTR -> LPCTSTR -> WORD -> IO HRSRC
 
 type ResourceSize = Int
@@ -115,25 +119,25 @@
 loadImage inst name ty x y load =
   withTString name $ \ c_name ->
   failIfNull "LoadImage" $ c_LoadImage inst c_name ty x y load
-foreign import stdcall unsafe "windows.h LoadImageW"
+foreign import WINDOWS_CCONV unsafe "windows.h LoadImageW"
   c_LoadImage :: HINSTANCE -> LPCTSTR -> ResourceImageType -> ResourceSize -> ResourceSize -> LoadImageFlags -> IO HANDLE
 
 loadResource :: HMODULE -> HRSRC -> IO HGLOBAL
 loadResource hmod res =
   failIfNull "LoadResource" $ c_LoadResource hmod res
-foreign import stdcall unsafe "windows.h LoadResource"
+foreign import WINDOWS_CCONV unsafe "windows.h LoadResource"
   c_LoadResource :: HMODULE -> HRSRC -> IO HGLOBAL
 
 lockResource :: HGLOBAL -> IO Addr
 lockResource res =
   failIfNull "LockResource" $ c_LockResource res
-foreign import stdcall unsafe "windows.h LockResource"
+foreign import WINDOWS_CCONV unsafe "windows.h LockResource"
   c_LockResource :: HGLOBAL -> IO Addr
 
 sizeofResource :: HMODULE -> HRSRC -> IO DWORD
 sizeofResource hmod res =
   failIfZero "SizeofResource" $ c_SizeofResource hmod res
-foreign import stdcall unsafe "windows.h SizeofResource"
+foreign import WINDOWS_CCONV unsafe "windows.h SizeofResource"
   c_SizeofResource :: HMODULE -> HRSRC -> IO DWORD
 
 -- was: LPCTSTR_
@@ -142,5 +146,5 @@
   withTString name $ \ c_name ->
   failIfFalse_ "UpdateResource" $
     c_UpdateResource h ty c_name lang p_data data_len
-foreign import stdcall unsafe "windows.h UpdateResourceW"
+foreign import WINDOWS_CCONV unsafe "windows.h UpdateResourceW"
   c_UpdateResource :: HANDLE -> LPCTSTR -> LPCTSTR -> WORD -> Addr -> DWORD -> IO Bool
diff --git a/Graphics/Win32/Window.hsc b/Graphics/Win32/Window.hsc
--- a/Graphics/Win32/Window.hsc
+++ b/Graphics/Win32/Window.hsc
@@ -1,6 +1,5 @@
-#if __GLASGOW_HASKELL__ >= 701
+{-# LANGUAGE CApiFFI #-}
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Graphics.Win32.Window
@@ -17,14 +16,31 @@
 
 module Graphics.Win32.Window where
 
-import System.Win32.Types
-import Graphics.Win32.GDI.Types
-import Graphics.Win32.Message
+import Control.Monad (liftM, when, unless)
+import Data.Maybe (fromMaybe)
+import Data.Int (Int32)
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Marshal.Utils (maybeWith)
+import Foreign.Marshal.Alloc (allocaBytes)
+import Foreign.Marshal.Array (allocaArray)
+import Foreign.Ptr (FunPtr, Ptr, castFunPtrToPtr, nullPtr)
+import Foreign.Ptr (intPtrToPtr, castPtrToFunPtr, freeHaskellFunPtr,ptrToIntPtr)
+import Foreign.Storable (pokeByteOff)
+import Foreign.C.Types (CIntPtr(..))
+import Graphics.Win32.GDI.Types (HBITMAP, HCURSOR, HDC, HDWP, HRGN, HWND, PRGN)
+import Graphics.Win32.GDI.Types (HBRUSH, HICON, HMENU, prim_ChildWindowFromPoint)
+import Graphics.Win32.GDI.Types (LPRECT, RECT, allocaRECT, peekRECT, withRECT)
+import Graphics.Win32.GDI.Types (POINT, allocaPOINT, peekPOINT, withPOINT)
+import Graphics.Win32.GDI.Types (prim_ChildWindowFromPointEx)
+import Graphics.Win32.Message (WindowMessage, wM_NCDESTROY)
+import System.IO.Unsafe (unsafePerformIO)
+import System.Win32.Types (ATOM, maybePtr, newTString, ptrToMaybe, numToMaybe)
+import System.Win32.Types (Addr, BOOL, DWORD, INT, LONG, LRESULT, UINT, WPARAM)
+import System.Win32.Types (HANDLE, HINSTANCE, LONG_PTR, LPARAM, LPCTSTR)
+import System.Win32.Types (LPTSTR, LPVOID, withTString, peekTString)
+import System.Win32.Types (failIf, failIf_, failIfFalse_, failIfNull, maybeNum, failUnlessSuccess, getLastError, errorWin)
 
-import Control.Monad
-import Data.Maybe
-import Foreign hiding (unsafePerformIO)
-import System.IO.Unsafe
+##include "windows_cconv.h"
 
 #include <windows.h>
 
@@ -38,7 +54,7 @@
 
 type ClassName   = LPCTSTR
 
--- Note: this is one of those rare functions which doesnt free all
+-- Note: this is one of those rare functions which doesn't free all
 -- its String arguments.
 
 mkClassName :: String -> ClassName
@@ -91,7 +107,7 @@
   #{poke WNDCLASS,lpszClassName} p cls
   f p
 
-foreign import stdcall unsafe "WndProc.h &genericWndProc"
+foreign import WINDOWS_CCONV unsafe "WndProc.h &genericWndProc"
   genericWndProc_p :: FunPtr WindowClosure
 
 {-# CFILES cbits/WndProc.c #-}
@@ -100,10 +116,10 @@
 registerClass cls =
   withWNDCLASS cls $ \ p ->
   liftM numToMaybe $ c_RegisterClass p
-foreign import stdcall unsafe "windows.h RegisterClassW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegisterClassW"
   c_RegisterClass :: Ptr WNDCLASS -> IO ATOM
 
-foreign import stdcall unsafe "windows.h UnregisterClassW"
+foreign import WINDOWS_CCONV unsafe "windows.h UnregisterClassW"
   unregisterClass :: ClassName -> HINSTANCE -> IO ()
 
 ----------------------------------------------------------------
@@ -168,8 +184,13 @@
  , wS_EX_PALETTEWINDOW  = WS_EX_PALETTEWINDOW
  }
 
+
 cW_USEDEFAULT :: Pos
-cW_USEDEFAULT = #{const CW_USEDEFAULT}
+-- See Note [Overflow checking and fromIntegral] in Graphics/Win32/GDI/HDC.hs
+-- Weird way to essentially get a value with the top bit set. But GHC 7.8.4 was
+-- rejecting all other sane attempts.
+cW_USEDEFAULT = let val = negate (#{const CW_USEDEFAULT}) :: Integer
+                in fromIntegral (fromIntegral val :: Int32) :: Pos
 
 type Pos = Int
 
@@ -180,17 +201,64 @@
 
 type WindowClosure = HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
 
-foreign import stdcall "wrapper"
+foreign import WINDOWS_CCONV "wrapper"
   mkWindowClosure :: WindowClosure -> IO (FunPtr WindowClosure)
 
-setWindowClosure :: HWND -> WindowClosure -> IO ()
+mkCIntPtr :: FunPtr a -> CIntPtr
+mkCIntPtr = fromIntegral . ptrToIntPtr . castFunPtrToPtr
+
+-- | The standard C wndproc for every window class registered by
+-- 'registerClass' is a C function pointer provided with this library. It in
+-- turn delegates to a Haskell function pointer stored in 'gWLP_USERDATA'.
+-- This action creates that function pointer. All Haskell function pointers
+-- must be freed in order to allow the objects they close over to be garbage
+-- collected. Consequently, if you are replacing a window closure previously
+-- set via this method or indirectly with 'createWindow' or 'createWindowEx'
+-- you must free it. This action returns a function pointer to the old window
+-- closure for you to free. The current window closure is freed automatically
+-- by 'defWindowProc' when it receives 'wM_NCDESTROY'.
+setWindowClosure :: HWND -> WindowClosure -> IO (Maybe (FunPtr WindowClosure))
 setWindowClosure wnd closure = do
   fp <- mkWindowClosure closure
-  _ <- c_SetWindowLong wnd (#{const GWL_USERDATA}) (castFunPtrToLONG fp)
-  return ()
-foreign import stdcall unsafe "windows.h SetWindowLongW"
-  c_SetWindowLong :: HWND -> INT -> LONG -> IO LONG
+  fpOld <- c_SetWindowLongPtr wnd (#{const GWLP_USERDATA})
+                              (mkCIntPtr fp)
+  if fpOld == 0
+     then return Nothing
+     else return $ Just $ castPtrToFunPtr $ intPtrToPtr $ fromIntegral fpOld
 
+{- Note [SetWindowLongPtrW]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Windows.h defines SetWindowLongPtrW as SetWindowLongW for 32-bit platforms
+(i386_HOST_ARCH for our mingw32 environment). Unfortunately since the foreign
+import name is given inside of a string, the macro will not be expanded.
+
+Until a better solution is presented each version is provided explicitly here.
+
+-}
+#if defined(i386_HOST_ARCH)
+foreign import WINDOWS_CCONV unsafe "windows.h SetWindowLongW"
+#elif defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)
+foreign import WINDOWS_CCONV unsafe "windows.h SetWindowLongPtrW"
+#else
+# error Unknown mingw32 arch
+#endif
+  c_SetWindowLongPtr :: HWND -> INT -> LONG_PTR -> IO (LONG_PTR)
+
+#if defined(i386_HOST_ARCH)
+foreign import WINDOWS_CCONV unsafe "windows.h GetWindowLongW"
+#elif defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)
+foreign import WINDOWS_CCONV unsafe "windows.h GetWindowLongPtrW"
+#else
+# error Unknown mingw32 arch
+#endif
+  c_GetWindowLongPtr :: HANDLE -> INT -> IO LONG_PTR
+
+
+-- | Creates a window with a default extended window style. If you create many
+-- windows over the life of your program, WindowClosure may leak memory. Be
+-- sure to delegate to 'defWindowProc' for 'wM_NCDESTROY' and see
+-- 'defWindowProc' and 'setWindowClosure' for details.
 createWindow
   :: ClassName -> String -> WindowStyle ->
      Maybe Pos -> Maybe Pos -> Maybe Pos -> Maybe Pos ->
@@ -199,6 +267,10 @@
 createWindow = createWindowEx 0
 -- apparently CreateWindowA/W are just macros for CreateWindowExA/W
 
+-- | Creates a window and allows your to specify the  extended window style. If
+-- you create many windows over the life of your program, WindowClosure may
+-- leak memory. Be sure to delegate to 'defWindowProc' for 'wM_NCDESTROY' and see
+-- 'defWindowProc' and 'setWindowClosure' for details.
 createWindowEx
   :: WindowStyle -> ClassName -> String -> WindowStyle
   -> Maybe Pos -> Maybe Pos -> Maybe Pos -> Maybe Pos
@@ -213,9 +285,9 @@
     c_CreateWindowEx estyle cname c_wname wstyle
       (maybePos mb_x) (maybePos mb_y) (maybePos mb_w) (maybePos mb_h)
       (maybePtr mb_parent) (maybePtr mb_menu) inst nullPtr
-  setWindowClosure wnd closure
+  _ <- setWindowClosure wnd closure
   return wnd
-foreign import stdcall "windows.h CreateWindowExW"
+foreign import WINDOWS_CCONV "windows.h CreateWindowExW"
   c_CreateWindowEx
     :: WindowStyle -> ClassName -> LPCTSTR -> WindowStyle
     -> Pos -> Pos -> Pos -> Pos
@@ -224,12 +296,43 @@
 
 ----------------------------------------------------------------
 
+-- | Delegates to the Win32 default window procedure. If you are using a
+-- window created by 'createWindow', 'createWindowEx' or on which you have
+-- called 'setWindowClosure', please note that the window will leak memory once
+-- it is destroyed unless you call 'freeWindowProc' when it receives
+-- 'wM_NCDESTROY'. If you wish to do this, instead of using this function
+-- directly, you can delegate to 'defWindowProcSafe' which will handle it for
+-- you. As an alternative, you can manually retrieve the window closure
+-- function pointer and free it after the window has been destroyed. Check the
+-- implementation of 'freeWindowProc' for a guide.
 defWindowProc :: Maybe HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
 defWindowProc mb_wnd msg w l =
   c_DefWindowProc (maybePtr mb_wnd) msg w l
-foreign import stdcall "windows.h DefWindowProcW"
+
+-- | Delegates to the standard default window procedure, but if it receives the
+-- 'wM_NCDESTROY' message it first frees the window closure to allow the
+-- closure and any objects it closes over to be garbage collected. 'wM_NCDESTROY' is
+-- the last message a window receives prior to being deleted.
+defWindowProcSafe :: Maybe HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
+defWindowProcSafe mb_wnd msg w l = do
+  when (msg == wM_NCDESTROY) (maybe (return ()) freeWindowProc mb_wnd)
+  defWindowProc mb_wnd msg w l
+
+foreign import WINDOWS_CCONV "windows.h DefWindowProcW"
   c_DefWindowProc :: HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
 
+-- | Frees a function pointer to the window closure which has been set
+-- directly by 'setWindowClosure' or indirectly by 'createWindowEx'. You
+-- should call this function in your window closure's 'wM_NCDESTROY' case
+-- unless you delegate that case to 'defWindowProc' (e.g. as part of the
+-- default).
+freeWindowProc :: HWND -> IO ()
+freeWindowProc hwnd = do
+   fp <- c_GetWindowLongPtr hwnd (#{const GWLP_USERDATA})
+   unless (fp == 0) $ 
+      freeHaskellFunPtr $ castPtrToFunPtr . intPtrToPtr . fromIntegral $ fp
+
+
 ----------------------------------------------------------------
 
 getClientRect :: HWND -> IO RECT
@@ -237,7 +340,7 @@
   allocaRECT $ \ p_rect -> do
   failIfFalse_ "GetClientRect" $ c_GetClientRect wnd p_rect
   peekRECT p_rect
-foreign import stdcall unsafe "windows.h GetClientRect"
+foreign import WINDOWS_CCONV unsafe "windows.h GetClientRect"
   c_GetClientRect :: HWND -> Ptr RECT -> IO Bool
 
 getWindowRect :: HWND -> IO RECT
@@ -245,7 +348,7 @@
   allocaRECT $ \ p_rect -> do
   failIfFalse_ "GetWindowRect" $ c_GetWindowRect wnd p_rect
   peekRECT p_rect
-foreign import stdcall unsafe "windows.h GetWindowRect"
+foreign import WINDOWS_CCONV unsafe "windows.h GetWindowRect"
   c_GetWindowRect :: HWND -> Ptr RECT -> IO Bool
 
 -- Should it be Maybe RECT instead?
@@ -254,7 +357,7 @@
 invalidateRect wnd p_mb_rect erase =
   failIfFalse_ "InvalidateRect" $
     c_InvalidateRect (maybePtr wnd) (maybePtr p_mb_rect) erase
-foreign import stdcall "windows.h InvalidateRect"
+foreign import WINDOWS_CCONV "windows.h InvalidateRect"
   c_InvalidateRect :: HWND -> LPRECT -> Bool -> IO Bool
 
 screenToClient :: HWND -> POINT -> IO POINT
@@ -262,7 +365,7 @@
   withPOINT pt $ \ p_pt -> do
   failIfFalse_ "ScreenToClient" $ c_ScreenToClient wnd p_pt
   peekPOINT p_pt
-foreign import stdcall unsafe "windows.h ScreenToClient"
+foreign import WINDOWS_CCONV unsafe "windows.h ScreenToClient"
   c_ScreenToClient :: HWND -> Ptr POINT -> IO Bool
 
 clientToScreen :: HWND -> POINT -> IO POINT
@@ -270,7 +373,7 @@
   withPOINT pt $ \ p_pt -> do
   failIfFalse_ "ClientToScreen" $ c_ClientToScreen wnd p_pt
   peekPOINT p_pt
-foreign import stdcall unsafe "windows.h ClientToScreen"
+foreign import WINDOWS_CCONV unsafe "windows.h ClientToScreen"
   c_ClientToScreen :: HWND -> Ptr POINT -> IO Bool
 
 ----------------------------------------------------------------
@@ -282,10 +385,45 @@
 setWindowText wnd text =
   withTString text $ \ c_text ->
   failIfFalse_ "SetWindowText" $ c_SetWindowText wnd c_text
-foreign import stdcall "windows.h SetWindowTextW"
+foreign import WINDOWS_CCONV "windows.h SetWindowTextW"
   c_SetWindowText :: HWND -> LPCTSTR -> IO Bool
 
 ----------------------------------------------------------------
+-- Getting window text/label
+----------------------------------------------------------------
+-- For getting the title bar text. 
+-- If the window has no title bar or text, if the title bar is empty,
+-- or if the window or control handle is invalid, the return value is zero.
+-- If invalid handle throws exception.
+-- If size <= 0 throws exception.
+
+getWindowText :: HWND -> Int -> IO String
+getWindowText wnd size 
+  | size <= 0 = errorWin "GetWindowTextW"
+  | otherwise  = do
+      allocaArray size $ \ p_buf -> do
+      _ <- c_GetWindowText wnd p_buf size
+      failUnlessSuccess "GetWindowTextW" getLastError
+      peekTString p_buf
+foreign import WINDOWS_CCONV "windows.h GetWindowTextW"
+  c_GetWindowText :: HWND -> LPTSTR -> Int -> IO Int
+
+----------------------------------------------------------------
+-- Getting window text length
+----------------------------------------------------------------
+-- For getting the title bar text length. 
+-- If the window has no text, the return value is zero.
+-- If invalid handle throws exception.
+  
+getWindowTextLength :: HWND -> IO Int
+getWindowTextLength wnd = do
+  size' <- c_GetWindowTextLength wnd
+  failUnlessSuccess "GetWindowTextLengthW" getLastError
+  return size'
+foreign import WINDOWS_CCONV "windows.h GetWindowTextLengthW"
+  c_GetWindowTextLength :: HWND -> IO Int
+  
+----------------------------------------------------------------
 -- Paint struct
 ----------------------------------------------------------------
 
@@ -306,10 +444,10 @@
 beginPaint :: HWND -> LPPAINTSTRUCT -> IO HDC
 beginPaint wnd paint =
   failIfNull "BeginPaint" $ c_BeginPaint wnd paint
-foreign import stdcall "windows.h BeginPaint"
+foreign import WINDOWS_CCONV "windows.h BeginPaint"
   c_BeginPaint :: HWND -> LPPAINTSTRUCT -> IO HDC
 
-foreign import stdcall "windows.h EndPaint"
+foreign import WINDOWS_CCONV "windows.h EndPaint"
   endPaint :: HWND -> LPPAINTSTRUCT -> IO ()
 -- Apparently always succeeds (return non-zero)
 
@@ -333,9 +471,12 @@
  , sW_RESTORE           = SW_RESTORE
  }
 
-foreign import stdcall "windows.h ShowWindow"
+foreign import WINDOWS_CCONV "windows.h ShowWindow"
   showWindow :: HWND  -> ShowWindowControl  -> IO Bool
 
+foreign import WINDOWS_CCONV "windows.h IsWindowVisible"
+  isWindowVisible :: HWND -> IO Bool
+
 ----------------------------------------------------------------
 -- Misc
 ----------------------------------------------------------------
@@ -345,7 +486,7 @@
   withRECT rect $ \ p_rect -> do
   failIfFalse_ "AdjustWindowRect" $ c_AdjustWindowRect p_rect style menu
   peekRECT p_rect
-foreign import stdcall unsafe "windows.h AdjustWindowRect"
+foreign import WINDOWS_CCONV unsafe "windows.h AdjustWindowRect"
   c_AdjustWindowRect :: Ptr RECT -> WindowStyle -> Bool -> IO Bool
 
 adjustWindowRectEx :: RECT -> WindowStyle -> Bool -> WindowStyleEx -> IO RECT
@@ -354,7 +495,7 @@
   failIfFalse_ "AdjustWindowRectEx" $
     c_AdjustWindowRectEx p_rect style menu exstyle
   peekRECT p_rect
-foreign import stdcall unsafe "windows.h AdjustWindowRectEx"
+foreign import WINDOWS_CCONV unsafe "windows.h AdjustWindowRectEx"
   c_AdjustWindowRectEx :: Ptr RECT -> WindowStyle -> Bool -> WindowStyleEx -> IO Bool
 
 -- Win2K and later:
@@ -381,25 +522,25 @@
 -- %code BOOL success = AnimateWindow(arg1,arg2,arg3)
 -- %fail { !success } { ErrorWin("AnimateWindow") }
 
-foreign import stdcall unsafe "windows.h AnyPopup"
+foreign import WINDOWS_CCONV unsafe "windows.h AnyPopup"
   anyPopup :: IO Bool
 
 arrangeIconicWindows :: HWND -> IO ()
 arrangeIconicWindows wnd =
   failIfFalse_ "ArrangeIconicWindows" $ c_ArrangeIconicWindows wnd
-foreign import stdcall unsafe "windows.h ArrangeIconicWindows"
+foreign import WINDOWS_CCONV unsafe "windows.h ArrangeIconicWindows"
   c_ArrangeIconicWindows :: HWND -> IO Bool
 
 beginDeferWindowPos :: Int -> IO HDWP
 beginDeferWindowPos n =
   failIfNull "BeginDeferWindowPos" $ c_BeginDeferWindowPos n
-foreign import stdcall unsafe "windows.h BeginDeferWindowPos"
+foreign import WINDOWS_CCONV unsafe "windows.h BeginDeferWindowPos"
   c_BeginDeferWindowPos :: Int -> IO HDWP
 
 bringWindowToTop :: HWND -> IO ()
 bringWindowToTop wnd =
   failIfFalse_ "BringWindowToTop" $ c_BringWindowToTop wnd
-foreign import stdcall "windows.h BringWindowToTop"
+foreign import WINDOWS_CCONV "windows.h BringWindowToTop"
   c_BringWindowToTop :: HWND -> IO Bool
 
 -- Can't pass structs with current FFI, so use a C wrapper (in Types)
@@ -421,63 +562,68 @@
 deferWindowPos :: HDWP -> HWND -> HWND -> Int -> Int -> Int -> Int -> SetWindowPosFlags -> IO HDWP
 deferWindowPos wp wnd after x y cx cy flags =
   failIfNull "DeferWindowPos" $ c_DeferWindowPos wp wnd after x y cx cy flags
-foreign import stdcall unsafe "windows.h DeferWindowPos"
+foreign import WINDOWS_CCONV unsafe "windows.h DeferWindowPos"
   c_DeferWindowPos :: HDWP -> HWND -> HWND -> Int -> Int -> Int -> Int -> SetWindowPosFlags -> IO HDWP
 
 destroyWindow :: HWND -> IO ()
 destroyWindow wnd =
   failIfFalse_ "DestroyWindow" $ c_DestroyWindow wnd
-foreign import stdcall "windows.h DestroyWindow"
+foreign import WINDOWS_CCONV "windows.h DestroyWindow"
   c_DestroyWindow :: HWND -> IO Bool
 
 endDeferWindowPos :: HDWP -> IO ()
 endDeferWindowPos pos =
   failIfFalse_ "EndDeferWindowPos" $ c_EndDeferWindowPos pos
-foreign import stdcall unsafe "windows.h EndDeferWindowPos"
+foreign import WINDOWS_CCONV unsafe "windows.h EndDeferWindowPos"
   c_EndDeferWindowPos :: HDWP -> IO Bool
 
-findWindow :: String -> String -> IO (Maybe HWND)
+findWindow :: Maybe String -> Maybe String -> IO (Maybe HWND)
 findWindow cname wname =
-  withTString cname $ \ c_cname ->
-  withTString wname $ \ c_wname ->
+  maybeWith withTString cname $ \ c_cname ->
+  maybeWith withTString wname $ \ c_wname ->
   liftM ptrToMaybe $ c_FindWindow c_cname c_wname
-foreign import stdcall unsafe "windows.h FindWindowW"
+
+{-# DEPRECATED findWindowByName "Use 'findWindow Nothing' instead." #-}
+findWindowByName :: String -> IO (Maybe HWND)
+findWindowByName wname = findWindow Nothing $ Just wname
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindWindowW"
   c_FindWindow :: LPCTSTR -> LPCTSTR -> IO HWND
 
-findWindowEx :: HWND -> HWND -> String -> String -> IO (Maybe HWND)
+findWindowEx :: Maybe HWND -> Maybe HWND -> Maybe String -> Maybe String -> IO (Maybe HWND)
 findWindowEx parent after cname wname =
-  withTString cname $ \ c_cname ->
-  withTString wname $ \ c_wname ->
-  liftM ptrToMaybe $ c_FindWindowEx parent after c_cname c_wname
-foreign import stdcall unsafe "windows.h FindWindowExW"
+  maybeWith withTString cname $ \ c_cname ->
+  maybeWith withTString wname $ \ c_wname ->
+  liftM ptrToMaybe $ c_FindWindowEx (maybePtr parent) (maybePtr after) c_cname c_wname
+foreign import WINDOWS_CCONV unsafe "windows.h FindWindowExW"
   c_FindWindowEx :: HWND -> HWND -> LPCTSTR -> LPCTSTR -> IO HWND
 
-foreign import stdcall unsafe "windows.h FlashWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h FlashWindow"
   flashWindow :: HWND -> Bool -> IO Bool
 -- No error code
 
 moveWindow :: HWND -> Int -> Int -> Int -> Int -> Bool -> IO ()
 moveWindow wnd x y w h repaint =
   failIfFalse_ "MoveWindow" $ c_MoveWindow wnd x y w h repaint
-foreign import stdcall "windows.h MoveWindow"
+foreign import WINDOWS_CCONV "windows.h MoveWindow"
   c_MoveWindow :: HWND -> Int -> Int -> Int -> Int -> Bool -> IO Bool
 
-foreign import stdcall unsafe "windows.h GetDesktopWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDesktopWindow"
   getDesktopWindow :: IO HWND
 
-foreign import stdcall unsafe "windows.h GetForegroundWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h GetForegroundWindow"
   getForegroundWindow :: IO HWND
-
+  
 getParent :: HWND -> IO HWND
 getParent wnd =
   failIfNull "GetParent" $ c_GetParent wnd
-foreign import stdcall unsafe "windows.h GetParent"
+foreign import WINDOWS_CCONV unsafe "windows.h GetParent"
   c_GetParent :: HWND -> IO HWND
 
 getTopWindow :: HWND -> IO HWND
 getTopWindow wnd =
   failIfNull "GetTopWindow" $ c_GetTopWindow wnd
-foreign import stdcall unsafe "windows.h GetTopWindow"
+foreign import WINDOWS_CCONV unsafe "windows.h GetTopWindow"
   c_GetTopWindow :: HWND -> IO HWND
 
 
@@ -522,25 +668,25 @@
 getDCEx wnd rgn flags =
   withForeignPtr rgn $ \ p_rgn ->
   failIfNull "GetDCEx" $ c_GetDCEx wnd p_rgn flags
-foreign import stdcall unsafe "windows.h GetDCEx"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDCEx"
   c_GetDCEx :: HWND -> PRGN -> GetDCExFlags -> IO HDC
 
 getDC :: Maybe HWND -> IO HDC
 getDC mb_wnd =
   failIfNull "GetDC" $ c_GetDC (maybePtr mb_wnd)
-foreign import stdcall unsafe "windows.h GetDC"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDC"
   c_GetDC :: HWND -> IO HDC
 
 getWindowDC :: Maybe HWND -> IO HDC
 getWindowDC mb_wnd =
   failIfNull "GetWindowDC" $ c_GetWindowDC (maybePtr mb_wnd)
-foreign import stdcall unsafe "windows.h GetWindowDC"
+foreign import WINDOWS_CCONV unsafe "windows.h GetWindowDC"
   c_GetWindowDC :: HWND -> IO HDC
 
 releaseDC :: Maybe HWND -> HDC -> IO ()
 releaseDC mb_wnd dc =
   failIfFalse_ "ReleaseDC" $ c_ReleaseDC (maybePtr mb_wnd) dc
-foreign import stdcall unsafe "windows.h ReleaseDC"
+foreign import WINDOWS_CCONV unsafe "windows.h ReleaseDC"
   c_ReleaseDC :: HWND -> HDC -> IO Bool
 
 getDCOrgEx :: HDC -> IO POINT
@@ -548,7 +694,7 @@
   allocaPOINT $ \ p_pt -> do
   failIfFalse_ "GetDCOrgEx" $ c_GetDCOrgEx dc p_pt
   peekPOINT p_pt
-foreign import stdcall unsafe "windows.h GetDCOrgEx"
+foreign import WINDOWS_CCONV unsafe "windows.h GetDCOrgEx"
   c_GetDCOrgEx :: HDC -> Ptr POINT -> IO Bool
 
 ----------------------------------------------------------------
@@ -558,13 +704,13 @@
 hideCaret :: HWND -> IO ()
 hideCaret wnd =
   failIfFalse_ "HideCaret" $ c_HideCaret wnd
-foreign import stdcall unsafe "windows.h HideCaret"
+foreign import WINDOWS_CCONV unsafe "windows.h HideCaret"
   c_HideCaret :: HWND -> IO Bool
 
 showCaret :: HWND -> IO ()
 showCaret wnd =
   failIfFalse_ "ShowCaret" $ c_ShowCaret wnd
-foreign import stdcall unsafe "windows.h ShowCaret"
+foreign import WINDOWS_CCONV unsafe "windows.h ShowCaret"
   c_ShowCaret :: HWND -> IO Bool
 
 -- ToDo: allow arg2 to be NULL or {(HBITMAP)1}
@@ -573,13 +719,13 @@
 createCaret wnd bm mb_w mb_h =
   failIfFalse_ "CreateCaret" $
     c_CreateCaret wnd bm (maybeNum mb_w) (maybeNum mb_h)
-foreign import stdcall unsafe "windows.h CreateCaret"
+foreign import WINDOWS_CCONV unsafe "windows.h CreateCaret"
   c_CreateCaret :: HWND -> HBITMAP -> INT -> INT -> IO Bool
 
 destroyCaret :: IO ()
 destroyCaret =
   failIfFalse_ "DestroyCaret" $ c_DestroyCaret
-foreign import stdcall unsafe "windows.h DestroyCaret"
+foreign import WINDOWS_CCONV unsafe "windows.h DestroyCaret"
   c_DestroyCaret :: IO Bool
 
 getCaretPos :: IO POINT
@@ -587,13 +733,13 @@
   allocaPOINT $ \ p_pt -> do
   failIfFalse_ "GetCaretPos" $ c_GetCaretPos p_pt
   peekPOINT p_pt
-foreign import stdcall unsafe "windows.h GetCaretPos"
+foreign import WINDOWS_CCONV unsafe "windows.h GetCaretPos"
   c_GetCaretPos :: Ptr POINT -> IO Bool
 
 setCaretPos :: POINT -> IO ()
 setCaretPos (x,y) =
   failIfFalse_ "SetCaretPos" $ c_SetCaretPos x y
-foreign import stdcall unsafe "windows.h SetCaretPos"
+foreign import WINDOWS_CCONV unsafe "windows.h SetCaretPos"
   c_SetCaretPos :: LONG -> LONG -> IO Bool
 
 -- The remarks on SetCaretBlinkTime are either highly risible or very sad -
@@ -609,7 +755,7 @@
 --   UpdateWindow   (I think)
 --   RedrawWindow   (I think)
 --
--- The following dont have to be reentrant (according to documentation)
+-- The following don't have to be reentrant (according to documentation)
 --
 --   GetMessage
 --   PeekMessage
@@ -643,7 +789,7 @@
   res <- failIf (== -1) "GetMessage" $
     c_GetMessage msg (maybePtr mb_wnd) 0 0
   return (res /= 0)
-foreign import stdcall "windows.h GetMessageW"
+foreign import WINDOWS_CCONV "windows.h GetMessageW"
   c_GetMessage :: LPMSG -> HWND -> UINT -> UINT -> IO LONG
 
 -- A NULL window requests messages for any window belonging to this thread.
@@ -654,26 +800,26 @@
 peekMessage msg mb_wnd filterMin filterMax remove = do
   failIf_ (== -1) "PeekMessage" $
     c_PeekMessage msg (maybePtr mb_wnd) filterMin filterMax remove
-foreign import stdcall "windows.h PeekMessageW"
+foreign import WINDOWS_CCONV "windows.h PeekMessageW"
   c_PeekMessage :: LPMSG -> HWND -> UINT -> UINT -> UINT -> IO LONG
 
 -- Note: you're not supposed to call this if you're using accelerators
 
-foreign import stdcall "windows.h TranslateMessage"
+foreign import WINDOWS_CCONV "windows.h TranslateMessage"
   translateMessage :: LPMSG -> IO BOOL
 
 updateWindow :: HWND -> IO ()
 updateWindow wnd =
   failIfFalse_ "UpdateWindow" $ c_UpdateWindow wnd
-foreign import stdcall "windows.h UpdateWindow"
+foreign import WINDOWS_CCONV "windows.h UpdateWindow"
   c_UpdateWindow :: HWND -> IO Bool
 
 -- Return value of DispatchMessage is usually ignored
 
-foreign import stdcall "windows.h DispatchMessageW"
+foreign import WINDOWS_CCONV "windows.h DispatchMessageW"
   dispatchMessage :: LPMSG -> IO LONG
 
-foreign import stdcall "windows.h SendMessageW"
+foreign import WINDOWS_CCONV "windows.h SendMessageW"
   sendMessage :: HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
 
 ----------------------------------------------------------------
diff --git a/Graphics/Win32/Window/AnimateWindow.hsc b/Graphics/Win32/Window/AnimateWindow.hsc
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/Window/AnimateWindow.hsc
@@ -0,0 +1,40 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.Window.AnimateWindow
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Provide AnimatedWindow function and flags.
+-}
+module Graphics.Win32.Window.AnimateWindow where
+import Graphics.Win32.GDI.Types ( HWND )
+import System.Win32.Types       ( DWORD, BOOL, failIfFalse_ )
+
+#include <windows.h>
+##include "windows_cconv.h"
+#include "winuser_compat.h"
+
+type AnimateWindowType = DWORD
+
+#{enum AnimateWindowType,
+ , aW_SLIDE        = AW_SLIDE
+ , aW_ACTIVATE     = AW_ACTIVATE
+ , aW_BLEND        = AW_BLEND
+ , aW_HIDE         = AW_HIDE
+ , aW_CENTER       = AW_CENTER
+ , aW_HOR_POSITIVE = AW_HOR_POSITIVE
+ , aW_HOR_NEGATIVE = AW_HOR_NEGATIVE
+ , aW_VER_POSITIVE = AW_VER_POSITIVE
+ , aW_VER_NEGATIVE = AW_VER_NEGATIVE
+ }
+
+animateWindow :: HWND -> DWORD -> AnimateWindowType -> IO ()
+animateWindow hwnd dwTime dwFlags
+  = failIfFalse_ "AnimateWindow" $ c_AnimateWindow hwnd dwTime dwFlags
+
+foreign import WINDOWS_CCONV "windows.h AnimateWindow"
+    c_AnimateWindow :: HWND -> DWORD -> AnimateWindowType -> IO BOOL
diff --git a/Graphics/Win32/Window/ForegroundWindow.hs b/Graphics/Win32/Window/ForegroundWindow.hs
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/Window/ForegroundWindow.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.Window.ForegroundWindow
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Get/Set Foreground Window.
+-}
+
+module Graphics.Win32.Window.ForegroundWindow
+  ( getForegroundWindow
+  , setForegroundWindow
+  , c_SetForegroundWindow
+  , allowSetForegroundWindow
+  , c_AllowSetForegroundWindow
+  ) where
+
+import Control.Monad            ( void )
+import Graphics.Win32.GDI.Types ( HWND )
+import Graphics.Win32.Window    ( getForegroundWindow )
+import System.Win32.Process     ( ProcessId )
+
+#include "windows_cconv.h"
+
+----------------------------------------------------------------
+-- | Setting Window to Foreground.
+-- See: <https://github.com/haskell/win32/pull/9>,
+-- <http://stackoverflow.com/questions/14297146/win32-setforegroundwindow-in-haskell>.
+----------------------------------------------------------------
+setForegroundWindow :: HWND -> IO Bool
+setForegroundWindow = c_SetForegroundWindow
+
+foreign import WINDOWS_CCONV safe "windows.h SetForegroundWindow"
+    c_SetForegroundWindow :: HWND -> IO Bool
+
+----------------------------------------------------------------
+-- | Allow other process to set Window to Foreground
+-- by using 'setForegroundWindow' function.
+allowSetForegroundWindow :: ProcessId -> IO ()
+allowSetForegroundWindow = void . c_AllowSetForegroundWindow
+
+foreign import WINDOWS_CCONV safe "windows.h AllowSetForegroundWindow"
+    c_AllowSetForegroundWindow :: ProcessId -> IO Bool
diff --git a/Graphics/Win32/Window/HotKey.hsc b/Graphics/Win32/Window/HotKey.hsc
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/Window/HotKey.hsc
@@ -0,0 +1,65 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.Window.HotKey
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   An FFI binding to the hot key part of the Win32 API.
+-}
+module Graphics.Win32.Window.HotKey where
+
+import Data.Bits                 ( (.|.) )
+import Graphics.Win32.GDI.Types  ( HWND, MbHWND )
+import Graphics.Win32.Key        ( VKey )
+import Graphics.Win32.Message    ( WindowMessage )
+import System.Win32.Types        ( UINT, BOOL, maybePtr, failIfFalse_ )
+import System.Win32.Exception.Unsupported ( unsupportedVal, upgradeWindowsOS )
+import System.Win32.Info.Version ( is7OrLater )
+
+#include <windows.h>
+##include "windows_cconv.h"
+
+type FsModifiers = [FsModifier]
+type FsModifier  = UINT
+
+#{enum FsModifier,
+ , mOD_ALT      = MOD_ALT
+ , mOD_CONTROL  = MOD_CONTROL
+ , mOD_SHIFT    = MOD_SHIFT
+ , mOD_WIN      = MOD_WIN
+}
+
+-- | This parameter requires to use Windows 7 or later.
+mOD_NOREPEAT :: FsModifier
+mOD_NOREPEAT
+  = unsupportedVal "MOD_NOREPEAT"
+      is7OrLater (upgradeWindowsOS "Windows 7") 0x4000
+{-
+ , mOD_NOREPEAT = MOD_NOREPEAT
+-}
+
+wM_HOTKEY :: WindowMessage
+wM_HOTKEY = #const WM_HOTKEY
+
+joinModifiers :: FsModifiers -> FsModifier
+joinModifiers = foldr (.|.) 0
+
+registerHotKey :: MbHWND -> Int -> FsModifier -> VKey -> IO ()
+registerHotKey mb_wnd kid md vkey =
+  failIfFalse_ (unwords ["RegisterHotKey", show mb_wnd, show kid, show md, show vkey])
+    $ c_RegisterHotKey (maybePtr mb_wnd) kid md vkey
+
+foreign import WINDOWS_CCONV "windows.h RegisterHotKey"
+  c_RegisterHotKey :: HWND -> Int -> UINT -> VKey -> IO BOOL
+
+unregisterHotKey :: MbHWND -> Int -> IO ()
+unregisterHotKey mb_wnd kid =
+  failIfFalse_ (unwords ["UnregisterHotKey", show mb_wnd, show kid])
+    $ c_UnregisterHotKey (maybePtr mb_wnd) kid
+
+foreign import WINDOWS_CCONV "windows.h UnregisterHotKey"
+  c_UnregisterHotKey :: HWND -> Int -> IO BOOL
diff --git a/Graphics/Win32/Window/IMM.hsc b/Graphics/Win32/Window/IMM.hsc
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/Window/IMM.hsc
@@ -0,0 +1,110 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.Window.IMM
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   An FFI binding to the IMM (Input Method Manager) part of the Win32 API.
+-}
+module Graphics.Win32.Window.IMM where
+import Foreign.Marshal.Alloc    ( alloca )
+import Foreign.Marshal.Utils    ( fromBool )
+import Foreign.Ptr              ( Ptr )
+import Foreign.Storable         ( peek )
+import Graphics.Win32.GDI.Types ( HWND )
+import Graphics.Win32.Key       ( VKey )
+import System.Win32.Types       ( UINT, DWORD, LPDWORD, BOOL, failIfFalse_ )
+
+#include <windows.h>
+##include "windows_cconv.h"
+
+type HIMC = Ptr ()
+
+foreign import WINDOWS_CCONV "windows.h ImmGetContext"
+  immGetContext     :: HWND -> IO HIMC
+
+foreign import WINDOWS_CCONV "windows.h ImmGetOpenStatus"
+  immGetOpenStatus :: HIMC -> IO BOOL
+
+immSetOpenStatus :: HIMC -> BOOL -> IO ()
+immSetOpenStatus imc flg =
+  failIfFalse_ (unwords ["ImmSetOpenStatus", show imc, show flg])
+    $ c_ImmSetOpenStatus imc (fromBool flg)
+
+foreign import WINDOWS_CCONV "windows.h ImmSetOpenStatus"
+  c_ImmSetOpenStatus  :: HIMC -> UINT -> IO BOOL
+
+
+data IMEMode = IMEMode DWORD DWORD
+
+immGetConversionStatus :: HIMC -> IO IMEMode
+immGetConversionStatus imc =
+  alloca $ \lpConv ->
+  alloca $ \lpStnc -> do
+    failIfFalse_ (unwords ["ImmGetConversionStatus", show imc, show lpConv, show lpStnc]) $
+      c_ImmGetConversionStatus imc lpConv lpStnc
+    conv <- peek lpConv
+    stnc <- peek lpStnc
+    return $ IMEMode conv stnc
+
+foreign import WINDOWS_CCONV "windows.h ImmGetConversionStatus"
+  c_ImmGetConversionStatus :: HIMC -> LPDWORD ->  LPDWORD -> IO BOOL
+
+immSetConversionStatus :: HIMC -> IMEMode -> IO ()
+immSetConversionStatus imc (IMEMode conv stnc) =
+  failIfFalse_ (unwords ["ImmSetConversionStatus", show imc, show conv, show stnc])
+    $ c_ImmSetConversionStatus imc conv stnc
+
+foreign import WINDOWS_CCONV "windows.h ImmSetConversionStatus"
+  c_ImmSetConversionStatus :: HIMC -> DWORD -> DWORD -> IO BOOL
+
+-- iMN_SETCONVERSIONSTATUS = #const IMN_SETCONVERSIONSTATUS
+
+#{enum DWORD,
+ , iME_CMODE_ALPHANUMERIC  = IME_CMODE_ALPHANUMERIC
+ , iME_CMODE_CHARCODE      = IME_CMODE_CHARCODE
+ , iME_CMODE_EUDC          = IME_CMODE_EUDC
+ , iME_CMODE_FIXED         = IME_CMODE_FIXED
+ , iME_CMODE_FULLSHAPE     = IME_CMODE_FULLSHAPE
+ , iME_CMODE_HANJACONVERT  = IME_CMODE_HANJACONVERT
+ , iME_CMODE_KATAKANA      = IME_CMODE_KATAKANA
+ , iME_CMODE_NATIVE        = IME_CMODE_NATIVE
+ , iME_CMODE_NOCONVERSION  = IME_CMODE_NOCONVERSION
+ , iME_CMODE_ROMAN         = IME_CMODE_ROMAN
+ , iME_CMODE_SOFTKBD       = IME_CMODE_SOFTKBD
+ , iME_CMODE_SYMBOL        = IME_CMODE_SYMBOL
+ }
+
+#{enum DWORD,
+ , iME_SMODE_AUTOMATIC     = IME_SMODE_AUTOMATIC
+ , iME_SMODE_NONE          = IME_SMODE_NONE
+ , iME_SMODE_PHRASEPREDICT = IME_SMODE_PHRASEPREDICT
+ , iME_SMODE_PLAURALCLAUSE = IME_SMODE_PLAURALCLAUSE
+ , iME_SMODE_SINGLECONVERT = IME_SMODE_SINGLECONVERT
+ }
+{-
+ , iME_SMODE_CONVERSATION  = IME_SMODE_CONVERSATION
+-}
+
+immReleaseContext :: HWND -> HIMC -> IO ()
+immReleaseContext wnd imc =
+  failIfFalse_ (unwords ["ImmSetOpenStatus", show wnd, show imc])
+    $ c_ImmReleaseContext wnd imc
+
+foreign import WINDOWS_CCONV "windows.h ImmReleaseContext"
+  c_ImmReleaseContext :: HWND -> HIMC -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h ImmGetVirtualKey"
+  immGetVirtualKey :: HWND -> IO VKey
+
+immSimulateHotKey :: HWND -> DWORD -> IO ()
+immSimulateHotKey hwd hkey =
+  failIfFalse_ (unwords ["ImmSimulateHotKey", show hwd, show hkey])
+    $ c_ImmSimulateHotKey hwd hkey
+
+foreign import WINDOWS_CCONV "windows.h ImmSimulateHotKey"
+  c_ImmSimulateHotKey :: HWND -> DWORD -> IO BOOL
diff --git a/Graphics/Win32/Window/PostMessage.hsc b/Graphics/Win32/Window/PostMessage.hsc
new file mode 100644
--- /dev/null
+++ b/Graphics/Win32/Window/PostMessage.hsc
@@ -0,0 +1,48 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Graphics.Win32.Window.PostMessage
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Provide PostMessage function and friends.
+-}
+module Graphics.Win32.Window.PostMessage where
+
+import Foreign.C.Types ( CIntPtr(..) )
+import Graphics.Win32.GDI.Types ( HWND, MbHWND )
+import Graphics.Win32.Message   ( WindowMessage )
+import System.Win32.Types       ( DWORD, WPARAM, LPARAM, BOOL
+                                , maybePtr, castUINTPtrToPtr, failIfFalse_ )
+
+#include <windows.h>
+##include "windows_cconv.h"
+
+postMessage :: MbHWND -> WindowMessage -> WPARAM -> LPARAM -> IO ()
+postMessage mb_wnd msg w l =
+  failIfFalse_ (unwords ["PostMessage", show mb_wnd, show msg, show w, show l]) $
+    c_PostMessage (maybePtr mb_wnd) msg w l
+
+foreign import WINDOWS_CCONV "windows.h PostMessageW"
+  c_PostMessage :: HWND -> WindowMessage -> WPARAM -> LPARAM -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h PostQuitMessage"
+  postQuitMessage :: Int -> IO ()
+
+postThreadMessage :: DWORD -> WindowMessage -> WPARAM -> LPARAM -> IO ()
+postThreadMessage tId msg w l =
+  failIfFalse_ (unwords ["PostThreadMessage", show tId, show msg, show w, show l]) $
+    c_PostThreadMessage tId msg w l
+
+foreign import WINDOWS_CCONV "windows.h PostThreadMessageW"
+  c_PostThreadMessage :: DWORD -> WindowMessage -> WPARAM -> LPARAM -> IO BOOL
+
+#{enum HWND, castUINTPtrToPtr
+ , hWND_BROADCAST = (UINT_PTR)HWND_BROADCAST
+ }
+
+foreign import WINDOWS_CCONV "windows.h InSendMessage"
+  inSendMessage :: IO Bool
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,6 @@
 Copyright (c) 1997-2003, Alastair Reid
 Copyright (c) 2006, Esa Ilari Vuokko
+Copyright (c) 2012-2013, shelarcy
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/Media/Win32.hs b/Media/Win32.hs
new file mode 100644
--- /dev/null
+++ b/Media/Win32.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  Media.Win32
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Multimedia API. TODO: provide more functions ...
+-}
+
+module Media.Win32
+  ( module Media.Win32
+  ) where
+import Control.Monad                ( unless )
+import Prelude               hiding ( ioError, userError )
+import System.IO.Error              ( ioError, userError )
+import System.Win32.Encoding        ( encodeMultiByte, getCurrentCodePage )
+import System.Win32.Types
+import System.Win32.String          ( withTStringBufferLen )
+
+type MCIERROR = DWORD
+
+#include "windows_cconv.h"
+
+mciSendString :: String -> IO ()
+mciSendString cmd
+ = withTString cmd $ \sendCmd -> do
+     err <- c_mciSendString sendCmd nullPtr 0 nullPtr
+     unless (err == 0)
+       $ mciGetErrorString err
+
+foreign import WINDOWS_CCONV safe "windows.h mciSendStringW"
+  c_mciSendString :: LPCTSTR -> LPTSTR -> UINT -> HANDLE -> IO MCIERROR
+
+mciGetErrorString :: MCIERROR -> IO ()
+mciGetErrorString err
+  = withTStringBufferLen 256 $ \(cstr, len) ->  do
+      failIfFalse_ (unwords ["mciGetErrorString", show err]) $
+        c_mciGetErrorString err cstr $ fromIntegral len
+      msg <- peekTString cstr
+      cp  <- getCurrentCodePage
+      ioError $ userError $ encodeMultiByte cp msg
+
+foreign import WINDOWS_CCONV unsafe "windows.h mciGetErrorStringW"
+  c_mciGetErrorString :: MCIERROR -> LPTSTR -> UINT -> IO BOOL
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,6 +0,0 @@
-module Main (main) where
-
-import Distribution.Simple
-
-main :: IO ()
-main = defaultMainWithHooks defaultUserHooks
diff --git a/System/Win32.hs b/System/Win32.hs
--- a/System/Win32.hs
+++ b/System/Win32.hs
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,26 +18,35 @@
 -----------------------------------------------------------------------------
 
 module System.Win32
-	( module System.Win32.DLL
-	, module System.Win32.File
-	, module System.Win32.FileMapping
-	, module System.Win32.Info
-	, module System.Win32.Mem
-	, module System.Win32.NLS
-	, module System.Win32.Process
-	, module System.Win32.Registry
-	, module System.Win32.Time
-	, module System.Win32.Console
-	, module System.Win32.Security
-	, module System.Win32.Types
-	, module System.Win32.Shell
-	) where
+        ( module System.Win32.DLL
+        , module System.Win32.Event
+        , module System.Win32.File
+        , module System.Win32.FileMapping
+        , module System.Win32.Info
+        , module System.Win32.Mem
+        , module System.Win32.MinTTY
+        , module System.Win32.NLS
+        , module System.Win32.Process
+        , module System.Win32.Registry
+        , module System.Win32.Time
+        , module System.Win32.Console
+        , module System.Win32.Security
+        , module System.Win32.Types
+        , module System.Win32.Shell
+        , module System.Win32.Automation
+        , module System.Win32.HardLink
+        , module System.Win32.SymbolicLink
+        , module System.Win32.Thread
+        , module System.Win32.Utils
+        ) where
 
 import System.Win32.DLL
+import System.Win32.Event
 import System.Win32.File
 import System.Win32.FileMapping
 import System.Win32.Info
 import System.Win32.Mem
+import System.Win32.MinTTY
 import System.Win32.NLS hiding  ( LCID, LANGID, SortID, SubLANGID
                                 , PrimaryLANGID, mAKELCID, lANGIDFROMLCID
                                 , sORTIDFROMLCID, mAKELANGID, pRIMARYLANGID
@@ -47,6 +58,12 @@
 import System.Win32.Types
 import System.Win32.Security
 import System.Win32.Shell
+
+import System.Win32.Automation
+import System.Win32.HardLink
+import System.Win32.SymbolicLink
+import System.Win32.Thread
+import System.Win32.Utils hiding ( try )
 
 ----------------------------------------------------------------
 -- End
diff --git a/System/Win32/Automation.hs b/System/Win32/Automation.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Automation.hs
@@ -0,0 +1,15 @@
+{- |
+   Module      :  System.Win32.Automation
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Provide sendInput function and INPUT types.
+-}
+module System.Win32.Automation
+  ( module System.Win32.Automation.Input
+  ) where
+import System.Win32.Automation.Input
diff --git a/System/Win32/Automation/Input.hsc b/System/Win32/Automation/Input.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Automation/Input.hsc
@@ -0,0 +1,130 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Automation.Input
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Provide sendInput function and INPUT types.
+-}
+module System.Win32.Automation.Input
+  ( sendInput
+  , sendInputPtr
+  , makeKeyboardInput
+  , PINPUT
+  , LPINPUT
+  , INPUT(..)
+  , PHARDWAREINPUT
+  , HARDWAREINPUT(..)
+  , getMessageExtraInfo
+  , setMessageExtraInfo
+  , module System.Win32.Automation.Input.Key
+  , module System.Win32.Automation.Input.Mouse
+  ) where
+
+import Data.Bits                 ( (.|.) )
+import Foreign.Ptr               ( Ptr )
+import Foreign.Storable          ( Storable(..) )
+import Foreign.Marshal.Array     ( withArrayLen )
+import Foreign.C.Types           ( CIntPtr(..) )
+import Graphics.Win32.Key        ( VKey, c_MapVirtualKey )
+import System.Win32.Automation.Input.Key
+import System.Win32.Automation.Input.Mouse ( MOUSEINPUT )
+import System.Win32.Automation.Input.Mouse hiding ( MOUSEINPUT(..) )
+import System.Win32.Types        ( UINT, LPARAM, failIfZero )
+import System.Win32.Word         ( DWORD, WORD )
+
+#include <windows.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+#include "winuser_compat.h"
+
+sendInput :: [INPUT] -> IO UINT
+sendInput input
+  = withArrayLen input $ \len c_input ->
+      sendInputPtr len c_input
+
+{-# INLINE sendInputPtr #-}
+-- | Raw pointer of array version of 'sendInput'.
+-- Use this function to support non-list sequence.
+sendInputPtr :: Int -> Ptr INPUT -> IO UINT
+sendInputPtr len c_input
+  = failIfZero "SendInput" $
+      c_SendInput (fromIntegral len) c_input $ sizeOf (undefined :: INPUT)
+
+foreign import WINDOWS_CCONV unsafe "windows.h SendInput"
+    c_SendInput :: UINT -> LPINPUT -> Int -> IO UINT
+
+makeKeyboardInput :: VKey -> Maybe DWORD -> IO INPUT
+makeKeyboardInput vkey flag = do
+    let flag' = maybe kEYEVENTF_EXTENDEDKEY (kEYEVENTF_EXTENDEDKEY .|.) flag
+    scan         <- c_MapVirtualKey vkey 0
+    dwExtraInfo' <- getMessageExtraInfo
+    return $ Keyboard
+           $ KEYBDINPUT {
+                 wVk   = fromIntegral vkey
+               , wScan = fromIntegral scan
+               , dwFlags = flag'
+               , time = 0
+               , dwExtraInfo = fromIntegral $ dwExtraInfo'
+               }
+
+type PINPUT = Ptr INPUT
+type LPINPUT = Ptr INPUT
+
+data INPUT = Mouse MOUSEINPUT | Keyboard KEYBDINPUT | OtherHardware HARDWAREINPUT
+     deriving Show
+
+instance Storable INPUT where
+    sizeOf = const #{size INPUT}
+    alignment _ = #alignment INPUT
+
+    poke buf (Mouse mouse) = do
+        (#poke INPUT, type) buf (#{const INPUT_MOUSE}:: DWORD)
+        (#poke INPUT, mi) buf mouse
+    poke buf (Keyboard key) = do
+        (#poke INPUT, type) buf (#{const INPUT_KEYBOARD} :: DWORD)
+        (#poke INPUT, ki) buf key
+    poke buf (OtherHardware hard) = do
+        (#poke INPUT, type) buf (#{const INPUT_HARDWARE} :: DWORD)
+        (#poke INPUT, hi) buf hard
+
+    peek buf = do
+        type'  <- (#peek INPUT, type) buf :: IO DWORD
+        case type' of
+          #{const INPUT_MOUSE} ->
+              Mouse `fmap` (#peek INPUT, mi) buf
+          #{const INPUT_KEYBOARD} ->
+              Keyboard `fmap` (#peek INPUT, ki) buf
+          _ -> OtherHardware `fmap` (#peek INPUT, hi) buf
+
+
+type PHARDWAREINPUT = Ptr HARDWAREINPUT
+
+data HARDWAREINPUT = HARDWAREINPUT
+     { uMsg    :: DWORD
+     , wParamL :: WORD
+     , wParamH :: WORD
+     } deriving Show
+
+instance Storable HARDWAREINPUT where
+    sizeOf = const #{size HARDWAREINPUT}
+    alignment _ = #alignment HARDWAREINPUT
+    poke buf input = do
+        (#poke HARDWAREINPUT, uMsg)    buf (uMsg input)
+        (#poke HARDWAREINPUT, wParamL) buf (wParamL input)
+        (#poke HARDWAREINPUT, wParamH) buf (wParamH input)
+    peek buf = do
+        uMsg'    <- (#peek HARDWAREINPUT, uMsg) buf
+        wParamL' <- (#peek HARDWAREINPUT, wParamL) buf
+        wParamH' <- (#peek HARDWAREINPUT, wParamH) buf
+        return $ HARDWAREINPUT uMsg' wParamL' wParamH'
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetMessageExtraInfo"
+    getMessageExtraInfo :: IO LPARAM
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetMessageExtraInfo"
+    setMessageExtraInfo :: LPARAM -> IO LPARAM
diff --git a/System/Win32/Automation/Input/Key.hsc b/System/Win32/Automation/Input/Key.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Automation/Input/Key.hsc
@@ -0,0 +1,55 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Automation.Input.Key
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Keyboard input events
+-}
+module System.Win32.Automation.Input.Key where
+import Foreign.Ptr        ( Ptr )
+import Foreign.Storable   ( Storable(..) )
+import System.Win32.Types ( ULONG_PTR )
+import System.Win32.Word  ( DWORD, WORD )
+
+#include <windows.h>
+#include "winuser_compat.h"
+#include "alignment.h"
+
+type PKEYBDINPUT = Ptr KEYBDINPUT
+
+data KEYBDINPUT = KEYBDINPUT
+     { wVk         :: WORD
+     , wScan       :: WORD
+     , dwFlags     :: DWORD
+     , time        :: DWORD
+     , dwExtraInfo :: ULONG_PTR
+     } deriving Show
+
+instance Storable KEYBDINPUT where
+    sizeOf = const #{size KEYBDINPUT}
+    alignment _ = #alignment KEYBDINPUT
+    poke buf input = do
+        (#poke KEYBDINPUT, wVk)     buf (wVk input)
+        (#poke KEYBDINPUT, wScan)   buf (wScan input)
+        (#poke KEYBDINPUT, dwFlags) buf (dwFlags input)
+        (#poke KEYBDINPUT, time)    buf (time input)
+        (#poke KEYBDINPUT, dwExtraInfo) buf (dwExtraInfo input)
+    peek buf = do
+        wVk'         <- (#peek KEYBDINPUT, wVk) buf
+        wScan'       <- (#peek KEYBDINPUT, wScan) buf
+        dwFlags'     <- (#peek KEYBDINPUT, dwFlags) buf
+        time'        <- (#peek KEYBDINPUT, time) buf
+        dwExtraInfo' <- (#peek KEYBDINPUT, dwExtraInfo) buf
+        return $ KEYBDINPUT wVk' wScan' dwFlags' time' dwExtraInfo'
+
+#{enum DWORD,
+ , kEYEVENTF_EXTENDEDKEY = KEYEVENTF_EXTENDEDKEY
+ , kEYEVENTF_KEYUP       = KEYEVENTF_KEYUP
+ , kEYEVENTF_SCANCODE    = KEYEVENTF_SCANCODE
+ , kEYEVENTF_UNICODE     = KEYEVENTF_UNICODE
+ }
diff --git a/System/Win32/Automation/Input/Mouse.hsc b/System/Win32/Automation/Input/Mouse.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Automation/Input/Mouse.hsc
@@ -0,0 +1,76 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Automation.Input.Mouse
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Mouse input events
+-}
+module System.Win32.Automation.Input.Mouse where
+import Foreign.Ptr               ( Ptr )
+import Foreign.Storable          ( Storable(..) )
+import System.Win32.Types        ( LONG, ULONG_PTR )
+import System.Win32.Word         ( DWORD )
+
+#include <windows.h>
+#include "winuser_compat.h"
+#include "alignment.h"
+
+type PMOUSEINPUT = Ptr MOUSEINPUT
+
+data MOUSEINPUT = MOUSEINPUT
+     { dx           :: LONG
+     , dy           :: LONG
+     , mouseData    :: DWORD
+     , dwFlags      :: DWORD
+     , time         :: DWORD
+     , dwExtraInfo :: ULONG_PTR
+     } deriving Show
+
+instance Storable MOUSEINPUT where
+    sizeOf = const #{size MOUSEINPUT}
+    alignment _ = #alignment MOUSEINPUT
+    poke buf input = do
+        (#poke MOUSEINPUT, dx) buf (dx input)
+        (#poke MOUSEINPUT, dy) buf (dy input)
+        (#poke MOUSEINPUT, mouseData)   buf (mouseData input)
+        (#poke MOUSEINPUT, dwFlags)     buf (dwFlags input)
+        (#poke MOUSEINPUT, time)        buf (time input)
+        (#poke MOUSEINPUT, dwExtraInfo) buf (dwExtraInfo input)
+    peek buf = do
+        dx' <- (#peek MOUSEINPUT, dx) buf
+        dy' <- (#peek MOUSEINPUT, dy) buf
+        mouseData'   <- (#peek MOUSEINPUT, mouseData) buf
+        dwFlags'     <- (#peek MOUSEINPUT, dwFlags) buf
+        time'        <- (#peek MOUSEINPUT, time) buf
+        dwExtraInfo' <- (#peek MOUSEINPUT, dwExtraInfo) buf
+        return $ MOUSEINPUT dx' dy' mouseData' dwFlags' time' dwExtraInfo'
+
+#{enum DWORD,
+ , xBUTTON1 = XBUTTON1
+ , xBUTTON2 = XBUTTON2
+ }
+
+#{enum DWORD,
+ , mOUSEEVENTF_ABSOLUTE    = MOUSEEVENTF_ABSOLUTE
+ , mOUSEEVENTF_MOVE        = MOUSEEVENTF_MOVE
+ , mOUSEEVENTF_LEFTDOWN    = MOUSEEVENTF_LEFTDOWN
+ , mOUSEEVENTF_LEFTUP      = MOUSEEVENTF_LEFTUP
+ , mOUSEEVENTF_RIGHTDOWN   = MOUSEEVENTF_RIGHTDOWN
+ , mOUSEEVENTF_RIGHTUP     = MOUSEEVENTF_RIGHTUP
+ , mOUSEEVENTF_MIDDLEDOWN  = MOUSEEVENTF_MIDDLEDOWN
+ , mOUSEEVENTF_MIDDLEUP    = MOUSEEVENTF_MIDDLEUP
+ , mOUSEEVENTF_WHEEL       = MOUSEEVENTF_WHEEL
+ , mOUSEEVENTF_XDOWN       = MOUSEEVENTF_XDOWN
+ , mOUSEEVENTF_XUP         = MOUSEEVENTF_XUP
+ }
+
+{-
+ , mOUSEEVENTF_VIRTUALDESK = MOUSEEVENTF_VIRTUALDESK -- I don't know why we can't find this
+ , mOUSEEVENTF_HWHEEL      = MOUSEEVENTF_HWHEEL
+ , mOUSEEVENTF_MOVE_NOCOALESCE = MOUSEEVENTF_MOVE_NOCOALESCE
+-}
diff --git a/System/Win32/Console.hsc b/System/Win32/Console.hsc
--- a/System/Win32/Console.hsc
+++ b/System/Win32/Console.hsc
@@ -1,5 +1,7 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
 {-# LANGUAGE Trustworthy #-}
+#else
+{-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
 -- |
@@ -16,35 +18,108 @@
 -----------------------------------------------------------------------------
 
 module System.Win32.Console (
-	-- * Console code pages
-	getConsoleCP,
-	setConsoleCP,
-	getConsoleOutputCP,
-	setConsoleOutputCP,
-	-- * Ctrl events
-	CtrlEvent, cTRL_C_EVENT, cTRL_BREAK_EVENT,
-	generateConsoleCtrlEvent
+        -- * Console mode
+        getConsoleMode,
+        setConsoleMode,
+        eNABLE_ECHO_INPUT,
+        eNABLE_EXTENDED_FLAGS,
+        eNABLE_INSERT_MODE,
+        eNABLE_LINE_INPUT,
+        eNABLE_MOUSE_INPUT,
+        eNABLE_PROCESSED_INPUT,
+        eNABLE_QUICK_EDIT_MODE,
+        eNABLE_WINDOW_INPUT,
+        eNABLE_VIRTUAL_TERMINAL_INPUT,
+        eNABLE_PROCESSED_OUTPUT,
+        eNABLE_WRAP_AT_EOL_OUTPUT,
+        eNABLE_VIRTUAL_TERMINAL_PROCESSING,
+        dISABLE_NEWLINE_AUTO_RETURN,
+        eNABLE_LVB_GRID_WORLDWIDE,
+        -- * Console code pages
+        getConsoleCP,
+        setConsoleCP,
+        getConsoleOutputCP,
+        setConsoleOutputCP,
+        -- * Ctrl events
+        CtrlEvent, cTRL_C_EVENT, cTRL_BREAK_EVENT,
+        generateConsoleCtrlEvent,
+        -- * Command line
+        commandLineToArgv,
+        getCommandLineW,
+        getArgs,
+        -- * Screen buffer
+        CONSOLE_SCREEN_BUFFER_INFO(..),
+        CONSOLE_SCREEN_BUFFER_INFOEX(..),
+        COORD(..),
+        SMALL_RECT(..),
+        COLORREF,
+        getConsoleScreenBufferInfo,
+        getCurrentConsoleScreenBufferInfo,
+        getConsoleScreenBufferInfoEx,
+        getCurrentConsoleScreenBufferInfoEx,
+
+        -- * Env
+        getEnv,
+        getEnvironment,
+        -- * Console I/O
+        KEY_EVENT_RECORD(..),
+        MOUSE_EVENT_RECORD(..),
+        WINDOW_BUFFER_SIZE_RECORD(..),
+        MENU_EVENT_RECORD(..),
+        FOCUS_EVENT_RECORD(..),
+        INPUT_RECORD(..),
+        readConsoleInput
   ) where
 
+#include <windows.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+#include "wincon_compat.h"
+
+import Data.Char (chr)
 import System.Win32.Types
+import System.Win32.String
+import System.Win32.Console.Internal
+import Graphics.Win32.Misc
+import Graphics.Win32.GDI.Types (COLORREF)
 
-foreign import stdcall unsafe "windows.h GetConsoleCP"
-	getConsoleCP :: IO UINT
+import GHC.IO (bracket)
+import GHC.IO.Exception (IOException(..), IOErrorType(OtherError))
+import Foreign.Ptr (plusPtr, Ptr)
+import Foreign.C.Types (CWchar)
+import Foreign.C.String (withCWString, CWString)
+import Foreign.Storable (Storable(..))
+import Foreign.Marshal.Array (peekArray, peekArray0)
+import Foreign.Marshal.Alloc (alloca)
 
-foreign import stdcall unsafe "windows.h SetConsoleCP"
-	setConsoleCP :: UINT -> IO ()
 
-foreign import stdcall unsafe "windows.h GetConsoleOutputCP"
-	getConsoleOutputCP :: IO UINT
+getConsoleMode :: HANDLE -> IO DWORD
+getConsoleMode h = alloca $ \ptr -> do
+    failIfFalse_ "GetConsoleMode" $ c_GetConsoleMode h ptr
+    peek ptr
 
-foreign import stdcall unsafe "windows.h SetConsoleOutputCP"
-	setConsoleOutputCP :: UINT -> IO ()
+setConsoleMode :: HANDLE -> DWORD -> IO ()
+setConsoleMode h mode = failIfFalse_ "SetConsoleMode" $ c_SetConsoleMode h mode
 
-type CtrlEvent = DWORD
-#{enum CtrlEvent,
-    , cTRL_C_EVENT      = 0
-    , cTRL_BREAK_EVENT  = 1
-    }
+eNABLE_ECHO_INPUT, eNABLE_EXTENDED_FLAGS, eNABLE_INSERT_MODE, eNABLE_LINE_INPUT,
+    eNABLE_MOUSE_INPUT, eNABLE_PROCESSED_INPUT, eNABLE_QUICK_EDIT_MODE,
+    eNABLE_WINDOW_INPUT, eNABLE_VIRTUAL_TERMINAL_INPUT, eNABLE_PROCESSED_OUTPUT,
+    eNABLE_WRAP_AT_EOL_OUTPUT, eNABLE_VIRTUAL_TERMINAL_PROCESSING,
+    dISABLE_NEWLINE_AUTO_RETURN, eNABLE_LVB_GRID_WORLDWIDE :: DWORD
+eNABLE_ECHO_INPUT = 4
+eNABLE_EXTENDED_FLAGS = 128
+eNABLE_INSERT_MODE = 32
+eNABLE_LINE_INPUT = 2
+eNABLE_MOUSE_INPUT = 16
+eNABLE_PROCESSED_INPUT = 1
+eNABLE_QUICK_EDIT_MODE = 64
+eNABLE_WINDOW_INPUT = 8
+eNABLE_VIRTUAL_TERMINAL_INPUT = 512
+eNABLE_PROCESSED_OUTPUT = 1
+eNABLE_WRAP_AT_EOL_OUTPUT = 2
+eNABLE_VIRTUAL_TERMINAL_PROCESSING = 4
+dISABLE_NEWLINE_AUTO_RETURN = 8
+eNABLE_LVB_GRID_WORLDWIDE = 16
 
 generateConsoleCtrlEvent :: CtrlEvent -> DWORD -> IO ()
 generateConsoleCtrlEvent e p
@@ -52,7 +127,126 @@
         "generateConsoleCtrlEvent"
         $ c_GenerateConsoleCtrlEvent e p
 
-foreign import stdcall safe "windows.h GenerateConsoleCtrlEvent"
-    c_GenerateConsoleCtrlEvent :: CtrlEvent -> DWORD -> IO BOOL
+-- | This function can be used to parse command line arguments and return
+--   the split up arguments as elements in a list.
+commandLineToArgv :: String -> IO [String]
+commandLineToArgv []  = return []
+commandLineToArgv arg =
+  do withCWString arg $ \c_arg -> do
+       alloca $ \c_size -> do
+         res <- c_CommandLineToArgvW c_arg c_size
+         size <- peek c_size
+         args <- peekArray (fromIntegral size) res
+         _ <- localFree res
+         mapM peekTString args
 
--- ToDo: lots more
+-- | Based on 'GetCommandLineW'. This behaves slightly different
+-- than 'System.Environment.getArgs'. See the online documentation:
+-- <https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getcommandlinew>
+getArgs :: IO [String]
+getArgs = do
+  getCommandLineW >>= peekTString >>= commandLineToArgv
+
+getConsoleScreenBufferInfo :: HANDLE -> IO CONSOLE_SCREEN_BUFFER_INFO
+getConsoleScreenBufferInfo h = alloca $ \ptr -> do
+    failIfFalse_ "GetConsoleScreenBufferInfo" $ c_GetConsoleScreenBufferInfo h ptr
+    peek ptr
+
+getCurrentConsoleScreenBufferInfo :: IO CONSOLE_SCREEN_BUFFER_INFO
+getCurrentConsoleScreenBufferInfo = do
+    h <- failIf (== nullHANDLE) "getStdHandle" $ getStdHandle sTD_OUTPUT_HANDLE
+    getConsoleScreenBufferInfo h
+
+getConsoleScreenBufferInfoEx :: HANDLE -> IO CONSOLE_SCREEN_BUFFER_INFOEX
+getConsoleScreenBufferInfoEx h = alloca $ \ptr -> do
+    -- The cbSize member must be set or GetConsoleScreenBufferInfoEx fails with
+    -- ERROR_INVALID_PARAMETER (87).
+    (#poke CONSOLE_SCREEN_BUFFER_INFOEX, cbSize) ptr cbSize
+    failIfFalse_ "GetConsoleScreenBufferInfoEx" $ c_GetConsoleScreenBufferInfoEx h ptr
+    peek ptr
+  where
+    cbSize :: ULONG
+    cbSize = #{size CONSOLE_SCREEN_BUFFER_INFOEX}
+
+getCurrentConsoleScreenBufferInfoEx :: IO CONSOLE_SCREEN_BUFFER_INFOEX
+getCurrentConsoleScreenBufferInfoEx = do
+    h <- failIf (== nullHANDLE) "getStdHandle" $ getStdHandle sTD_OUTPUT_HANDLE
+    getConsoleScreenBufferInfoEx h
+
+
+-- c_GetEnvironmentVariableW :: LPCWSTR -> LPWSTR -> DWORD -> IO DWORD
+getEnv :: String -> IO (Maybe String)
+getEnv name =
+  withCWString name $ \c_name -> withTStringBufferLen maxLength $ \(buf, len) -> do
+    let c_len = fromIntegral len
+    c_len' <- c_GetEnvironmentVariableW c_name buf c_len
+    case c_len' of
+      0 -> do
+        err_code <- getLastError
+        if err_code  == eERROR_ENVVAR_NOT_FOUND
+        then return Nothing
+        else errorWin "GetEnvironmentVariableW"
+      _ | c_len' > fromIntegral maxLength ->
+            -- shouldn't happen, because we provide maxLength
+            ioError (IOError Nothing OtherError "GetEnvironmentVariableW" ("Unexpected return code: " <> show c_len') Nothing Nothing)
+        | otherwise -> do
+            let len' = fromIntegral c_len'
+            Just <$> peekTStringLen (buf, len')
+ where
+  -- according to https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
+  -- max characters (wide chars): 32767
+  -- => bytes = 32767 * 2 = 65534
+  -- +1 byte for NUL (although not needed I think)
+  maxLength :: Int
+  maxLength = 65535
+
+
+getEnvironment :: IO [(String, String)]
+getEnvironment = bracket c_GetEnvironmentStringsW c_FreeEnvironmentStrings $ \lpwstr -> do
+    strs <- builder lpwstr
+    return (divvy <$> strs)
+ where
+  divvy :: String -> (String, String)
+  divvy str =
+    case break (=='=') str of
+      (xs,[])        -> (xs,[]) -- don't barf (like Posix.getEnvironment)
+      (name,_:value) -> (name,value)
+
+  builder :: LPWSTR -> IO [String]
+  builder ptr = go 0
+   where
+    go :: Int -> IO [String]
+    go off = do
+      (str, l) <- peekCWStringOff ptr off
+      if l == 0
+      then pure []
+      else (str:) <$> go (((l + 1) * 2) + off)
+
+
+peekCWStringOff :: CWString -> Int -> IO (String, Int)
+peekCWStringOff cp off = do
+  cs <- peekArray0 wNUL (cp `plusPtr` off)
+  return (cWcharsToChars cs, length cs)
+
+wNUL :: CWchar
+wNUL = 0
+
+cWcharsToChars :: [CWchar] -> [Char]
+cWcharsToChars = map chr . fromUTF16 . map fromIntegral
+ where
+  fromUTF16 (c1:c2:wcs)
+    | 0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff =
+      ((c1 - 0xd800)*0x400 + (c2 - 0xdc00) + 0x10000) : fromUTF16 wcs
+  fromUTF16 (c:wcs) = c : fromUTF16 wcs
+  fromUTF16 [] = []
+
+-- | Reads all available input records up to the amount specified by the
+-- len parameter.
+readConsoleInput :: HANDLE -> Int -> Ptr INPUT_RECORD -> IO Int
+readConsoleInput handle len inputRecordPtr =
+  alloca $ \numEventsReadPtr -> do
+    poke numEventsReadPtr 0
+    failIfFalse_ "ReadConsoleInput" $
+      c_ReadConsoleInput handle inputRecordPtr (fromIntegral len) numEventsReadPtr
+    numEvents <- peek numEventsReadPtr
+    return $ fromIntegral numEvents
diff --git a/System/Win32/Console/CtrlHandler.hs b/System/Win32/Console/CtrlHandler.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Console/CtrlHandler.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Console.CtrlHandler
+   Copyright   :  2008-2013 Judah Jacobson, 2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Set handlers of console Ctrl events.
+-}
+module System.Win32.Console.CtrlHandler 
+  ( CtrlEvent, Handler, PHANDLER_ROUTINE
+  , withConsoleCtrlHandler
+  , setConsoleCtrlHandler, c_SetConsoleCtrlHandler
+  , mkHandler
+  , cTRL_C_EVENT, cTRL_BREAK_EVENT
+  ) where
+
+import Control.Exception    ( bracket )
+import Control.Monad        ( void )
+import Foreign.Ptr          ( FunPtr )
+import System.Win32.Console ( CtrlEvent, cTRL_C_EVENT, cTRL_BREAK_EVENT )
+import System.Win32.Types   ( BOOL, failIfFalse_ )
+
+#include "windows_cconv.h"
+
+type Handler = CtrlEvent -> IO BOOL
+-- type HandlerRoutine = Handler
+type PHANDLER_ROUTINE = FunPtr Handler
+
+withConsoleCtrlHandler :: Handler -> IO a -> IO a
+withConsoleCtrlHandler handler io
+  = bracket (do hd <- mkHandler handler
+                -- don't fail if we can't set the Ctrl-C handler
+                -- for example, we might not be attached to a console?
+                void $ c_SetConsoleCtrlHandler hd True
+                return hd)
+            (\hd -> void $ c_SetConsoleCtrlHandler hd False)
+            $ const io
+
+-- | This function isn't suitable when we want to set the cTRL_C_EVENT handler.
+-- If you want to set the cTRL_C_EVENT handler, use 'c_SetConsoleCtrlHandler' instead.
+setConsoleCtrlHandler :: PHANDLER_ROUTINE -> BOOL -> IO ()
+setConsoleCtrlHandler handler flag
+  = failIfFalse_ "SetConsoleCtrlHandler"
+      $ c_SetConsoleCtrlHandler handler flag
+
+foreign import WINDOWS_CCONV "wrapper" mkHandler :: Handler -> IO PHANDLER_ROUTINE
+foreign import WINDOWS_CCONV "windows.h SetConsoleCtrlHandler"
+  c_SetConsoleCtrlHandler :: PHANDLER_ROUTINE -> BOOL -> IO BOOL
diff --git a/System/Win32/Console/HWND.hs b/System/Win32/Console/HWND.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Console/HWND.hs
@@ -0,0 +1,34 @@
+{- |
+   Module      :  System.Win32.Console.HWND
+   Copyright   :  2009 Balazs Komuves, 2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Get the handle of the current console window.
+-}
+module System.Win32.Console.HWND (getConsoleHWND) where
+import Control.Concurrent           ( threadDelay )
+import Control.Exception            ( bracket )
+import Foreign.Ptr                  ( nullPtr )
+import Graphics.Win32.Window        ( c_FindWindow )
+import Graphics.Win32.GDI.Types     ( HWND )
+import System.Win32.Console.Title   ( getConsoleTitle, setConsoleTitle )
+import System.Win32.Process ( getCurrentProcessId )
+import System.Win32.String          ( withTString )
+import System.Win32.Time            ( getTickCount )
+
+-- | Get the handle of the current console window by using window's title.
+-- See: <http://support.microsoft.com/kb/124103>
+getConsoleHWND :: IO HWND
+getConsoleHWND
+  = bracket getConsoleTitle setConsoleTitle $ \_ -> do
+        time   <- getTickCount
+        pid    <- getCurrentProcessId
+        let unique = show time ++ show pid
+        setConsoleTitle unique
+        threadDelay (42*1000)
+        withTString unique $ \punique ->
+            c_FindWindow nullPtr punique
diff --git a/System/Win32/Console/Internal.hsc b/System/Win32/Console/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Console/Internal.hsc
@@ -0,0 +1,336 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Console.Internal
+-- Copyright   :  (c) University of Glasgow 2023
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- Internals for Console modules.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Console.Internal where
+
+#include <windows.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+#include "wincon_compat.h"
+
+import System.Win32.Types
+import Graphics.Win32.GDI.Types (COLORREF)
+
+import Foreign.C.Types (CInt(..), CWchar)
+import Foreign.C.String (CWString)
+import Foreign.Ptr (Ptr, plusPtr)
+import Foreign.Storable (Storable(..))
+import Foreign.Marshal.Array (peekArray, pokeArray)
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetConsoleMode"
+        c_GetConsoleMode :: HANDLE -> LPDWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetConsoleMode"
+        c_SetConsoleMode :: HANDLE -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetConsoleCP"
+        getConsoleCP :: IO UINT
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetConsoleCP"
+        setConsoleCP :: UINT -> IO ()
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetConsoleOutputCP"
+        getConsoleOutputCP :: IO UINT
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetConsoleOutputCP"
+        setConsoleOutputCP :: UINT -> IO ()
+
+type CtrlEvent = DWORD
+#{enum CtrlEvent,
+    , cTRL_C_EVENT      = 0
+    , cTRL_BREAK_EVENT  = 1
+    }
+
+foreign import WINDOWS_CCONV safe "windows.h GenerateConsoleCtrlEvent"
+    c_GenerateConsoleCtrlEvent :: CtrlEvent -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "Shellapi.h CommandLineToArgvW"
+     c_CommandLineToArgvW :: CWString -> Ptr CInt -> IO (Ptr CWString)
+
+foreign import WINDOWS_CCONV unsafe "processenv.h GetCommandLineW"
+        getCommandLineW :: IO LPWSTR
+
+foreign import WINDOWS_CCONV unsafe "processenv.h GetEnvironmentVariableW"
+        c_GetEnvironmentVariableW :: LPCWSTR -> LPWSTR -> DWORD -> IO DWORD
+
+foreign import WINDOWS_CCONV unsafe "processenv.h GetEnvironmentStringsW"
+        c_GetEnvironmentStringsW :: IO LPWSTR
+
+foreign import WINDOWS_CCONV unsafe "processenv.h FreeEnvironmentStringsW"
+  c_FreeEnvironmentStrings :: LPWSTR -> IO Bool
+
+data CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO
+    { dwSize              :: COORD
+    , dwCursorPosition    :: COORD
+    , wAttributes         :: WORD
+    , srWindow            :: SMALL_RECT
+    , dwMaximumWindowSize :: COORD
+    } deriving (Show, Eq)
+
+instance Storable CONSOLE_SCREEN_BUFFER_INFO where
+    sizeOf = const #{size CONSOLE_SCREEN_BUFFER_INFO}
+    alignment _ = #alignment CONSOLE_SCREEN_BUFFER_INFO
+    peek buf = do
+        dwSize'              <- (#peek CONSOLE_SCREEN_BUFFER_INFO, dwSize) buf
+        dwCursorPosition'    <- (#peek CONSOLE_SCREEN_BUFFER_INFO, dwCursorPosition) buf
+        wAttributes'         <- (#peek CONSOLE_SCREEN_BUFFER_INFO, wAttributes) buf
+        srWindow'            <- (#peek CONSOLE_SCREEN_BUFFER_INFO, srWindow) buf
+        dwMaximumWindowSize' <- (#peek CONSOLE_SCREEN_BUFFER_INFO, dwMaximumWindowSize) buf
+        return $ CONSOLE_SCREEN_BUFFER_INFO dwSize' dwCursorPosition' wAttributes' srWindow' dwMaximumWindowSize'
+    poke buf info = do
+        (#poke CONSOLE_SCREEN_BUFFER_INFO, dwSize) buf (dwSize info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFO, dwCursorPosition) buf (dwCursorPosition info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFO, wAttributes) buf (wAttributes info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFO, srWindow) buf (srWindow info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFO, dwMaximumWindowSize) buf (dwMaximumWindowSize info)
+
+data CONSOLE_SCREEN_BUFFER_INFOEX = CONSOLE_SCREEN_BUFFER_INFOEX
+    { dwSizeEx              :: COORD
+    , dwCursorPositionEx    :: COORD
+    , wAttributesEx         :: WORD
+    , srWindowEx            :: SMALL_RECT
+    , dwMaximumWindowSizeEx :: COORD
+    , wPopupAttributes      :: WORD
+    , bFullscreenSupported  :: BOOL
+    , colorTable            :: [COLORREF]
+      -- ^ Only the first 16 'COLORREF' values passed to the Windows Console
+      -- API. If fewer than 16 values, the remainder are padded with @0@ when
+      -- passed to the API.
+    } deriving (Show, Eq)
+
+instance Storable CONSOLE_SCREEN_BUFFER_INFOEX where
+    sizeOf = const #{size CONSOLE_SCREEN_BUFFER_INFOEX}
+    alignment = const #{alignment CONSOLE_SCREEN_BUFFER_INFOEX}
+    peek buf = do
+        dwSize'               <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, dwSize) buf
+        dwCursorPosition'     <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, dwCursorPosition) buf
+        wAttributes'          <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, wAttributes) buf
+        srWindow'             <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, srWindow) buf
+        dwMaximumWindowSize'  <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, dwMaximumWindowSize) buf
+        wPopupAttributes'     <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, wPopupAttributes) buf
+        bFullscreenSupported' <- (#peek CONSOLE_SCREEN_BUFFER_INFOEX, bFullscreenSupported) buf
+        colorTable'           <- peekArray 16 ((#ptr CONSOLE_SCREEN_BUFFER_INFOEX, ColorTable) buf)
+        return $ CONSOLE_SCREEN_BUFFER_INFOEX dwSize' dwCursorPosition'
+          wAttributes' srWindow' dwMaximumWindowSize' wPopupAttributes'
+          bFullscreenSupported' colorTable'
+    poke buf info = do
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, cbSize) buf cbSize
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, dwSize) buf (dwSizeEx info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, dwCursorPosition) buf (dwCursorPositionEx info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, wAttributes) buf (wAttributesEx info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, srWindow) buf (srWindowEx info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, dwMaximumWindowSize) buf (dwMaximumWindowSizeEx info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, wPopupAttributes) buf (wPopupAttributes info)
+        (#poke CONSOLE_SCREEN_BUFFER_INFOEX, bFullscreenSupported) buf (bFullscreenSupported info)
+        pokeArray ((#ptr CONSOLE_SCREEN_BUFFER_INFOEX, ColorTable) buf) colorTable'
+      where
+        cbSize :: ULONG
+        cbSize = #{size CONSOLE_SCREEN_BUFFER_INFOEX}
+        colorTable' = take 16 $ colorTable info ++ repeat 0
+
+data COORD = COORD
+    { xPos :: SHORT
+    , yPos :: SHORT
+    } deriving (Show, Eq)
+
+instance Storable COORD where
+    sizeOf = const #{size COORD}
+    alignment _ = #alignment COORD
+    peek buf = do
+        x' <- (#peek COORD, X) buf
+        y' <- (#peek COORD, Y) buf
+        return $ COORD x' y'
+    poke buf coord = do
+        (#poke COORD, X) buf (xPos coord)
+        (#poke COORD, Y) buf (yPos coord)
+
+data SMALL_RECT = SMALL_RECT
+    { leftPos   :: SHORT
+    , topPos    :: SHORT
+    , rightPos  :: SHORT
+    , bottomPos :: SHORT
+    } deriving (Show, Eq)
+
+instance Storable SMALL_RECT where
+    sizeOf _ = #{size SMALL_RECT}
+    alignment _ = #alignment SMALL_RECT
+    peek buf = do
+        left'   <- (#peek SMALL_RECT, Left) buf
+        top'    <- (#peek SMALL_RECT, Top) buf
+        right'  <- (#peek SMALL_RECT, Right) buf
+        bottom' <- (#peek SMALL_RECT, Bottom) buf
+        return $ SMALL_RECT left' top' right' bottom'
+    poke buf small_rect = do
+        (#poke SMALL_RECT, Left) buf (leftPos small_rect)
+        (#poke SMALL_RECT, Top) buf (topPos small_rect)
+        (#poke SMALL_RECT, Right) buf (rightPos small_rect)
+        (#poke SMALL_RECT, Bottom) buf (bottomPos small_rect)
+
+foreign import WINDOWS_CCONV safe "windows.h GetConsoleScreenBufferInfo"
+    c_GetConsoleScreenBufferInfo :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFO -> IO BOOL
+
+foreign import WINDOWS_CCONV safe "windows.h GetConsoleScreenBufferInfoEx"
+    c_GetConsoleScreenBufferInfoEx :: HANDLE -> Ptr CONSOLE_SCREEN_BUFFER_INFOEX -> IO BOOL
+
+-- | This type represents a keyboard input event. The structure is documented here:
+-- https://learn.microsoft.com/en-us/windows/console/key-event-record-str
+data KEY_EVENT_RECORD = KEY_EVENT_RECORD
+    { keyDown          :: BOOL
+    , repeatCount      :: WORD
+    , virtualKeyCode   :: WORD
+    , virtualScanCode  :: WORD
+    , uChar            :: CWchar
+    , controlKeyStateK :: DWORD
+    } deriving (Eq, Show)
+
+-- | This type represents a mouse event. The structure is documented here:
+-- https://learn.microsoft.com/en-us/windows/console/mouse-event-record-str
+data MOUSE_EVENT_RECORD = MOUSE_EVENT_RECORD
+  { mousePosition    :: COORD
+  , buttonState      :: DWORD
+  , controlKeyStateM :: DWORD
+  , eventFlags       :: DWORD
+  } deriving (Eq, Show)
+
+-- | This type represents a window size change event. The structure is documented here:
+-- https://learn.microsoft.com/en-us/windows/console/window-buffer-size-record-str
+newtype WINDOW_BUFFER_SIZE_RECORD = WINDOW_BUFFER_SIZE_RECORD
+  { windowSize :: COORD
+  } deriving (Eq, Show)
+
+-- | This type represents a window menu event. (Current ignored by VTY). The structure
+-- is documented here: https://learn.microsoft.com/en-us/windows/console/menu-event-record-str
+newtype MENU_EVENT_RECORD = MENU_EVENT_RECORD
+  { commandId :: UINT
+  } deriving (Eq, Show)
+
+-- | This type represents a window focus change event. The structure is documented here:
+-- https://learn.microsoft.com/en-us/windows/console/focus-event-record-str
+newtype FOCUS_EVENT_RECORD = FOCUS_EVENT_RECORD
+  { setFocus :: BOOL
+  } deriving (Eq, Show)
+
+-- | Description of a Windows console input event. Documented here:
+-- https://learn.microsoft.com/en-us/windows/console/input-record-str
+data INPUT_RECORD =
+    KeyEvent KEY_EVENT_RECORD
+  | MouseEvent MOUSE_EVENT_RECORD
+  | WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD
+  | MenuEvent MENU_EVENT_RECORD
+  | FocusEvent FOCUS_EVENT_RECORD
+  deriving (Eq, Show)
+
+instance Storable KEY_EVENT_RECORD where
+    sizeOf = const #{size KEY_EVENT_RECORD}
+    alignment _ = #alignment KEY_EVENT_RECORD
+    poke buf input = do
+        (#poke KEY_EVENT_RECORD, bKeyDown)          buf (keyDown input)
+        (#poke KEY_EVENT_RECORD, wRepeatCount)      buf (repeatCount input)
+        (#poke KEY_EVENT_RECORD, wVirtualKeyCode)   buf (virtualKeyCode input)
+        (#poke KEY_EVENT_RECORD, wVirtualScanCode)  buf (virtualScanCode input)
+        (#poke KEY_EVENT_RECORD, uChar)             buf (uChar input)
+        (#poke KEY_EVENT_RECORD, dwControlKeyState) buf (controlKeyStateK input)
+    peek buf = do
+        keyDown'          <- (#peek KEY_EVENT_RECORD, bKeyDown) buf
+        repeatCount'      <- (#peek KEY_EVENT_RECORD, wRepeatCount) buf
+        virtualKeyCode'   <- (#peek KEY_EVENT_RECORD, wVirtualKeyCode) buf
+        virtualScanCode'  <- (#peek KEY_EVENT_RECORD, wVirtualScanCode) buf
+        uChar'            <- (#peek KEY_EVENT_RECORD, uChar) buf
+        controlKeyStateK' <- (#peek KEY_EVENT_RECORD, dwControlKeyState) buf
+        return $ KEY_EVENT_RECORD keyDown' repeatCount' virtualKeyCode' virtualScanCode' uChar' controlKeyStateK'
+
+instance Storable MOUSE_EVENT_RECORD where
+    sizeOf = const #{size MOUSE_EVENT_RECORD}
+    alignment _ = #alignment MOUSE_EVENT_RECORD
+    poke buf input = do
+        (#poke MOUSE_EVENT_RECORD, dwMousePosition)   buf (mousePosition input)
+        (#poke MOUSE_EVENT_RECORD, dwButtonState)     buf (buttonState input)
+        (#poke MOUSE_EVENT_RECORD, dwControlKeyState) buf (controlKeyStateM input)
+        (#poke MOUSE_EVENT_RECORD, dwEventFlags)      buf (eventFlags input)
+    peek buf = do
+        mousePosition'    <- (#peek MOUSE_EVENT_RECORD, dwMousePosition) buf
+        buttonState'      <- (#peek MOUSE_EVENT_RECORD, dwButtonState) buf
+        controlKeyStateM' <- (#peek MOUSE_EVENT_RECORD, dwControlKeyState) buf
+        eventFlags'       <- (#peek MOUSE_EVENT_RECORD, dwEventFlags) buf
+        return $ MOUSE_EVENT_RECORD mousePosition' buttonState' controlKeyStateM' eventFlags'
+
+instance Storable WINDOW_BUFFER_SIZE_RECORD where
+    sizeOf = const #{size WINDOW_BUFFER_SIZE_RECORD}
+    alignment _ = #alignment WINDOW_BUFFER_SIZE_RECORD
+    poke buf input = do
+        (#poke WINDOW_BUFFER_SIZE_RECORD, dwSize) buf (windowSize input)
+    peek buf = do
+        size' <- (#peek WINDOW_BUFFER_SIZE_RECORD, dwSize) buf
+        return $ WINDOW_BUFFER_SIZE_RECORD size'
+
+instance Storable MENU_EVENT_RECORD where
+    sizeOf = const #{size MENU_EVENT_RECORD}
+    alignment _ = #alignment MENU_EVENT_RECORD
+    poke buf input = do
+        (#poke MENU_EVENT_RECORD, dwCommandId) buf (commandId input)
+    peek buf = do
+        commandId' <- (#peek MENU_EVENT_RECORD, dwCommandId) buf
+        return $ MENU_EVENT_RECORD commandId'
+
+instance Storable FOCUS_EVENT_RECORD where
+    sizeOf = const #{size FOCUS_EVENT_RECORD}
+    alignment _ = #alignment FOCUS_EVENT_RECORD
+    poke buf input = do
+        (#poke FOCUS_EVENT_RECORD, bSetFocus) buf (setFocus input)
+    peek buf = do
+        setFocus' <- (#peek FOCUS_EVENT_RECORD, bSetFocus) buf
+        return $ FOCUS_EVENT_RECORD setFocus'
+
+instance Storable INPUT_RECORD where
+    sizeOf = const #{size INPUT_RECORD}
+    alignment _ = #alignment INPUT_RECORD
+
+    poke buf (KeyEvent key) = do
+        (#poke INPUT_RECORD, EventType) buf (#{const KEY_EVENT} :: WORD)
+        (#poke INPUT_RECORD, Event) buf key
+    poke buf (MouseEvent mouse) = do
+        (#poke INPUT_RECORD, EventType) buf (#{const MOUSE_EVENT} :: WORD)
+        (#poke INPUT_RECORD, Event) buf mouse
+    poke buf (WindowBufferSizeEvent window) = do
+        (#poke INPUT_RECORD, EventType) buf (#{const WINDOW_BUFFER_SIZE_EVENT} :: WORD)
+        (#poke INPUT_RECORD, Event) buf window
+    poke buf (MenuEvent menu) = do
+        (#poke INPUT_RECORD, EventType) buf (#{const MENU_EVENT} :: WORD)
+        (#poke INPUT_RECORD, Event) buf menu
+    poke buf (FocusEvent focus) = do
+        (#poke INPUT_RECORD, EventType) buf (#{const FOCUS_EVENT} :: WORD)
+        (#poke INPUT_RECORD, Event) buf focus
+
+    peek buf = do
+        event <- (#peek INPUT_RECORD, EventType) buf :: IO WORD
+        case event of
+          #{const KEY_EVENT} ->
+              KeyEvent `fmap` (#peek INPUT_RECORD, Event) buf
+          #{const MOUSE_EVENT} ->
+              MouseEvent `fmap` (#peek INPUT_RECORD, Event) buf
+          #{const WINDOW_BUFFER_SIZE_EVENT} ->
+              WindowBufferSizeEvent `fmap` (#peek INPUT_RECORD, Event) buf
+          #{const MENU_EVENT} ->
+              MenuEvent `fmap` (#peek INPUT_RECORD, Event) buf
+          #{const FOCUS_EVENT} ->
+              FocusEvent `fmap` (#peek INPUT_RECORD, Event) buf
+          _ -> error $ "Unknown input event type " ++ show event
+
+foreign import WINDOWS_CCONV unsafe "windows.h ReadConsoleInputW"
+    c_ReadConsoleInput :: HANDLE -> Ptr INPUT_RECORD -> DWORD -> LPDWORD -> IO BOOL
diff --git a/System/Win32/Console/Title.hsc b/System/Win32/Console/Title.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Console/Title.hsc
@@ -0,0 +1,46 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Console.Title
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Get/Set the title for the current console window.
+-}
+module System.Win32.Console.Title
+    ( getConsoleTitle
+    , setConsoleTitle
+    ) where
+
+import System.Win32.String ( LPTSTR, LPCTSTR
+                           , withTStringBufferLen, withTString, peekTStringLen )
+import System.Win32.Types  ( BOOL, failIfFalse_, failIfZero )
+import System.Win32.Word   ( DWORD )
+
+#include <windows.h>
+##include "windows_cconv.h"
+
+getConsoleTitle :: IO String
+getConsoleTitle =
+  withTStringBufferLen maxLength $ \(buf, len) -> do
+      len' <- failIfZero "GetConsoleTitle"
+        $ c_GetConsoleTitle buf (fromIntegral len)
+      peekTStringLen (buf, (fromIntegral len'))
+  where
+    maxLength = #const MAX_PATH
+
+setConsoleTitle :: String -> IO ()
+setConsoleTitle title =
+  withTString title $ \buf ->
+      failIfFalse_ (unwords ["SetConsoleTitle", title])
+        $ c_SetConsoleTitle buf
+
+foreign import WINDOWS_CCONV "windows.h GetConsoleTitleW"
+  c_GetConsoleTitle :: LPTSTR -> DWORD -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h SetConsoleTitleW"
+  c_SetConsoleTitle :: LPCTSTR -> IO BOOL
+
diff --git a/System/Win32/DLL.hsc b/System/Win32/DLL.hsc
--- a/System/Win32/DLL.hsc
+++ b/System/Win32/DLL.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -15,70 +17,63 @@
 --
 -----------------------------------------------------------------------------
 
-module System.Win32.DLL where
+module System.Win32.DLL
+    ( disableThreadLibraryCalls
+    , freeLibrary
+    , getModuleFileName
+    , getModuleHandle
+    , getProcAddress
+    , loadLibrary
+    , loadLibraryEx
+    , setDllDirectory
+    , LoadLibraryFlags
+    , lOAD_LIBRARY_AS_DATAFILE
+    , lOAD_WITH_ALTERED_SEARCH_PATH
+    ) where
 
+import System.Win32.DLL.Internal
 import System.Win32.Types
 
 import Foreign
 import Foreign.C
-
-#include <windows.h>
+import Data.Maybe (fromMaybe)
 
 disableThreadLibraryCalls :: HMODULE -> IO ()
 disableThreadLibraryCalls hmod =
   failIfFalse_ "DisableThreadLibraryCalls" $ c_DisableThreadLibraryCalls hmod
-foreign import stdcall unsafe "windows.h DisableThreadLibraryCalls"
-  c_DisableThreadLibraryCalls :: HMODULE -> IO Bool
 
 freeLibrary :: HMODULE -> IO ()
 freeLibrary hmod =
   failIfFalse_ "FreeLibrary" $ c_FreeLibrary hmod
-foreign import stdcall unsafe "windows.h FreeLibrary"
-  c_FreeLibrary :: HMODULE -> IO Bool
 
-{-# CFILES cbits/HsWin32.c #-}
-foreign import ccall "HsWin32.h &FreeLibraryFinaliser"
-    c_FreeLibraryFinaliser :: FunPtr (HMODULE -> IO ())
-
 getModuleFileName :: HMODULE -> IO String
 getModuleFileName hmod =
   allocaArray 512 $ \ c_str -> do
   failIfFalse_ "GetModuleFileName" $ c_GetModuleFileName hmod c_str 512
   peekTString c_str
-foreign import stdcall unsafe "windows.h GetModuleFileNameW"
-  c_GetModuleFileName :: HMODULE -> LPTSTR -> Int -> IO Bool
 
 getModuleHandle :: Maybe String -> IO HMODULE
 getModuleHandle mb_name =
   maybeWith withTString mb_name $ \ c_name ->
   failIfNull "GetModuleHandle" $ c_GetModuleHandle c_name
-foreign import stdcall unsafe "windows.h GetModuleHandleW"
-  c_GetModuleHandle :: LPCTSTR -> IO HMODULE
 
 getProcAddress :: HMODULE -> String -> IO Addr
 getProcAddress hmod procname =
   withCAString procname $ \ c_procname ->
   failIfNull "GetProcAddress" $ c_GetProcAddress hmod c_procname
-foreign import stdcall unsafe "windows.h GetProcAddress"
-  c_GetProcAddress :: HMODULE -> LPCSTR -> IO Addr
 
-loadLibrary :: String -> IO HINSTANCE
+loadLibrary :: String -> IO HMODULE
 loadLibrary name =
   withTString name $ \ c_name ->
   failIfNull "LoadLibrary" $ c_LoadLibrary c_name
-foreign import stdcall unsafe "windows.h LoadLibraryW"
-  c_LoadLibrary :: LPCTSTR -> IO HINSTANCE
 
-type LoadLibraryFlags = DWORD
-
-#{enum LoadLibraryFlags,
- , lOAD_LIBRARY_AS_DATAFILE      = LOAD_LIBRARY_AS_DATAFILE
- , lOAD_WITH_ALTERED_SEARCH_PATH = LOAD_WITH_ALTERED_SEARCH_PATH
- }
-
-loadLibraryEx :: String -> HANDLE -> LoadLibraryFlags -> IO HINSTANCE
+loadLibraryEx :: String -> HANDLE -> LoadLibraryFlags -> IO HMODULE
 loadLibraryEx name h flags =
   withTString name $ \ c_name ->
   failIfNull "LoadLibraryEx" $ c_LoadLibraryEx c_name h flags
-foreign import stdcall unsafe "windows.h LoadLibraryExW"
-  c_LoadLibraryEx :: LPCTSTR -> HANDLE -> LoadLibraryFlags -> IO HINSTANCE
+
+setDllDirectory :: Maybe String -> IO ()
+setDllDirectory name =
+  maybeWith withTString name $ \ c_name ->
+  failIfFalse_ (unwords ["SetDllDirectory", fromMaybe "NULL" name]) $ c_SetDllDirectory c_name
+
diff --git a/System/Win32/DLL/Internal.hsc b/System/Win32/DLL/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/DLL/Internal.hsc
@@ -0,0 +1,57 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.DLL.Internal
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.DLL.Internal where
+
+import System.Win32.Types
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+
+foreign import WINDOWS_CCONV unsafe "windows.h DisableThreadLibraryCalls"
+  c_DisableThreadLibraryCalls :: HMODULE -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h FreeLibrary"
+  c_FreeLibrary :: HMODULE -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetModuleFileNameW"
+  c_GetModuleFileName :: HMODULE -> LPTSTR -> Int -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetModuleHandleW"
+  c_GetModuleHandle :: LPCTSTR -> IO HMODULE
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetProcAddress"
+  c_GetProcAddress :: HMODULE -> LPCSTR -> IO Addr
+
+foreign import WINDOWS_CCONV unsafe "windows.h LoadLibraryW"
+  c_LoadLibrary :: LPCTSTR -> IO HMODULE
+
+type LoadLibraryFlags = DWORD
+
+#{enum LoadLibraryFlags,
+ , lOAD_LIBRARY_AS_DATAFILE      = LOAD_LIBRARY_AS_DATAFILE
+ , lOAD_WITH_ALTERED_SEARCH_PATH = LOAD_WITH_ALTERED_SEARCH_PATH
+ }
+
+foreign import WINDOWS_CCONV unsafe "windows.h LoadLibraryExW"
+  c_LoadLibraryEx :: LPCTSTR -> HANDLE -> LoadLibraryFlags -> IO HMODULE
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetDllDirectoryW"
+  c_SetDllDirectory :: LPTSTR -> IO BOOL
diff --git a/System/Win32/DebugApi.hsc b/System/Win32/DebugApi.hsc
--- a/System/Win32/DebugApi.hsc
+++ b/System/Win32/DebugApi.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -14,32 +16,82 @@
 -- A collection of FFI declarations for using Windows DebugApi.
 --
 -----------------------------------------------------------------------------
-module System.Win32.DebugApi where
+module System.Win32.DebugApi
+    ( PID, TID, DebugEventId, ForeignAddress
+    , PHANDLE, THANDLE
+    , ThreadInfo
+    , ImageInfo
+    , ExceptionInfo
+    , Exception(..)
+    , DebugEventInfo(..)
+    , DebugEvent
 
+    , debugBreak
+    , isDebuggerPresent
+
+      -- * Debug events
+    , waitForDebugEvent
+    , getDebugEvents
+    , continueDebugEvent
+
+      -- * Debugging another process
+    , debugActiveProcess
+    , peekProcessMemory
+    , readProcessMemory
+    , pokeProcessMemory
+    , withProcessMemory
+    , peekP
+    , pokeP
+
+      -- * Thread control
+    , suspendThread
+    , resumeThread
+    , withSuspendedThread
+
+      -- * Thread register control
+    , getThreadContext
+    , setThreadContext
+    , useAllRegs
+    , withThreadContext
+
+#if defined(i386_HOST_ARCH)
+    , eax, ebx, ecx, edx, esi, edi, ebp, eip, esp
+#elif defined(x86_64_HOST_ARCH)
+    , rax, rbx, rcx, rdx, rsi, rdi, rbp, rip, rsp
+#endif
+#if defined(x86_64_HOST_ARCH) || defined(i386_HOST_ARCH)
+    , segCs, segDs, segEs, segFs, segGs
+    , eFlags
+    , dr
+#endif
+#if defined(aarch64_HOST_ARCH)
+    , x0,   x1,  x2,  x3,  x4,  x5,  x6,  x7,  x8
+    , x9,  x10, x11, x12, x13, x14, x15, x16, x17
+    , x18, x19, x20, x21, x22, x23, x24, x25, x26
+    , x27, x28,  fp,  lr,  sp,  pc
+#endif
+    , setReg, getReg, modReg
+    , makeModThreadContext
+    , modifyThreadContext
+
+      -- * Sending debug output to another process
+    , outputDebugString
+    ) where
+
+import System.Win32.DebugApi.Internal
 import Control.Exception( bracket_ )
-import Data.Word        ( Word8, Word32 )
 import Foreign          ( Ptr, nullPtr, ForeignPtr, mallocForeignPtrBytes
                         , peekByteOff, plusPtr, allocaBytes, castPtr, poke
                         , withForeignPtr, Storable, sizeOf, peek, pokeByteOff )
 import System.IO        ( fixIO )
-import System.Win32.Types   ( HANDLE, BOOL, WORD, DWORD, failIf_, failWith
-                            , getLastError, failIf, LPTSTR, withTString )
+import System.Win32.Types   ( WORD, DWORD, failIf_, failWith
+                            , getLastError, failIf, withTString )
 
+##include "windows_cconv.h"
 #include "windows.h"
 
-type PID = DWORD
-type TID = DWORD
-type DebugEventId = (PID, TID)
-type ForeignAddress = Word32
 
-type PHANDLE = Ptr ()
-type THANDLE = Ptr ()
 
-type ThreadInfo = (THANDLE, ForeignAddress, ForeignAddress)   -- handle to thread, thread local, thread start
-type ImageInfo = (HANDLE, ForeignAddress, DWORD, DWORD, ForeignAddress)
-type ExceptionInfo = (Bool, Bool, ForeignAddress) -- First chance, continuable, address
-
-
 data Exception
     = UnknownException
     | AccessViolation Bool ForeignAddress
@@ -63,7 +115,7 @@
     | SingleStep
     | StackOverflow
     deriving (Show)
-    
+
 data DebugEventInfo
     = UnknownDebugEvent
     | Exception         ExceptionInfo Exception
@@ -91,12 +143,12 @@
     where
         dwZero = 0 :: DWORD
         wZero = 0 :: WORD
-        
-        rest (#const EXCEPTION_DEBUG_EVENT) p = do
-            chance  <- (#peek EXCEPTION_DEBUG_INFO, dwFirstChance) p
-            flags   <- (#peek EXCEPTION_RECORD, ExceptionFlags) p
-            addr    <- (#peek EXCEPTION_RECORD, ExceptionAddress) p
-            code    <- (#peek EXCEPTION_RECORD, ExceptionCode) p
+
+        rest (#const EXCEPTION_DEBUG_EVENT) p' = do
+            chance  <- (#peek EXCEPTION_DEBUG_INFO, dwFirstChance) p'
+            flags   <- (#peek EXCEPTION_RECORD, ExceptionFlags) p'
+            addr    <- (#peek EXCEPTION_RECORD, ExceptionAddress) p'
+            code    <- (#peek EXCEPTION_RECORD, ExceptionCode) p'
             e <- case code::DWORD of
                 (#const EXCEPTION_ACCESS_VIOLATION)         -> return $ AccessViolation False 0
                 (#const EXCEPTION_ARRAY_BOUNDS_EXCEEDED)    -> return ArrayBoundsExceeded
@@ -118,55 +170,55 @@
                 (#const EXCEPTION_PRIV_INSTRUCTION)         -> return PrivilegedInstruction
                 (#const EXCEPTION_SINGLE_STEP)              -> return SingleStep
                 (#const EXCEPTION_STACK_OVERFLOW)           -> return StackOverflow
-                _                                           -> return UnknownException 
+                _                                           -> return UnknownException
             return $ Exception (chance/=dwZero, flags==dwZero, addr) e
 
-        rest (#const CREATE_THREAD_DEBUG_EVENT) p = do
-            handle <- (#peek CREATE_THREAD_DEBUG_INFO, hThread)             p
-            local <- (#peek CREATE_THREAD_DEBUG_INFO, lpThreadLocalBase)    p
-            start <- (#peek CREATE_THREAD_DEBUG_INFO, lpStartAddress)       p
+        rest (#const CREATE_THREAD_DEBUG_EVENT) p' = do
+            handle <- (#peek CREATE_THREAD_DEBUG_INFO, hThread)          p'
+            local <- (#peek CREATE_THREAD_DEBUG_INFO, lpThreadLocalBase) p'
+            start <- (#peek CREATE_THREAD_DEBUG_INFO, lpStartAddress)    p'
             return $ CreateThread (handle, local, start)
 
-        rest (#const CREATE_PROCESS_DEBUG_EVENT) p = do
-            file    <- (#peek CREATE_PROCESS_DEBUG_INFO, hFile) p
-            proc    <- (#peek CREATE_PROCESS_DEBUG_INFO, hProcess) p
-            thread  <- (#peek CREATE_PROCESS_DEBUG_INFO, hThread) p
-            imgbase <- (#peek CREATE_PROCESS_DEBUG_INFO, lpBaseOfImage) p
-            dbgoff  <- (#peek CREATE_PROCESS_DEBUG_INFO, dwDebugInfoFileOffset) p
-            dbgsize <- (#peek CREATE_PROCESS_DEBUG_INFO, nDebugInfoSize) p
-            local   <- (#peek CREATE_PROCESS_DEBUG_INFO, lpThreadLocalBase) p
-            start   <- (#peek CREATE_PROCESS_DEBUG_INFO, lpStartAddress) p
-            imgname <- (#peek CREATE_PROCESS_DEBUG_INFO, lpImageName) p
-            --unicode <- (#peek CREATE_PROCESS_DEBUG_INFO, fUnicode) p
-            return $ CreateProcess proc 
+        rest (#const CREATE_PROCESS_DEBUG_EVENT) p' = do
+            file    <- (#peek CREATE_PROCESS_DEBUG_INFO, hFile) p'
+            proc    <- (#peek CREATE_PROCESS_DEBUG_INFO, hProcess) p'
+            thread  <- (#peek CREATE_PROCESS_DEBUG_INFO, hThread) p'
+            imgbase <- (#peek CREATE_PROCESS_DEBUG_INFO, lpBaseOfImage) p'
+            dbgoff  <- (#peek CREATE_PROCESS_DEBUG_INFO, dwDebugInfoFileOffset) p'
+            dbgsize <- (#peek CREATE_PROCESS_DEBUG_INFO, nDebugInfoSize) p'
+            local   <- (#peek CREATE_PROCESS_DEBUG_INFO, lpThreadLocalBase) p'
+            start   <- (#peek CREATE_PROCESS_DEBUG_INFO, lpStartAddress) p'
+            imgname <- (#peek CREATE_PROCESS_DEBUG_INFO, lpImageName) p'
+            --unicode <- (#peek CREATE_PROCESS_DEBUG_INFO, fUnicode) p'
+            return $ CreateProcess proc
                         (file, imgbase, dbgoff, dbgsize, imgname) --, unicode/=wZero)
                         (thread, local, start)
-        
-        rest (#const EXIT_THREAD_DEBUG_EVENT) p =
-            (#peek EXIT_THREAD_DEBUG_INFO, dwExitCode) p >>= return.ExitThread
-        
-        rest (#const EXIT_PROCESS_DEBUG_EVENT) p =
-            (#peek EXIT_PROCESS_DEBUG_INFO, dwExitCode) p >>= return.ExitProcess
-        
-        rest (#const LOAD_DLL_DEBUG_EVENT) p = do
-            file    <- (#peek LOAD_DLL_DEBUG_INFO, hFile) p
-            imgbase <- (#peek LOAD_DLL_DEBUG_INFO, lpBaseOfDll) p
-            dbgoff  <- (#peek LOAD_DLL_DEBUG_INFO, dwDebugInfoFileOffset) p
-            dbgsize <- (#peek LOAD_DLL_DEBUG_INFO, nDebugInfoSize) p
-            imgname <- (#peek LOAD_DLL_DEBUG_INFO, lpImageName) p
-            --unicode <- (#peek LOAD_DLL_DEBUG_INFO, fUnicode) p
-            return $ 
+
+        rest (#const EXIT_THREAD_DEBUG_EVENT) p' =
+            (#peek EXIT_THREAD_DEBUG_INFO, dwExitCode) p' >>= return.ExitThread
+
+        rest (#const EXIT_PROCESS_DEBUG_EVENT) p' =
+            (#peek EXIT_PROCESS_DEBUG_INFO, dwExitCode) p' >>= return.ExitProcess
+
+        rest (#const LOAD_DLL_DEBUG_EVENT) p' = do
+            file    <- (#peek LOAD_DLL_DEBUG_INFO, hFile) p'
+            imgbase <- (#peek LOAD_DLL_DEBUG_INFO, lpBaseOfDll) p'
+            dbgoff  <- (#peek LOAD_DLL_DEBUG_INFO, dwDebugInfoFileOffset) p'
+            dbgsize <- (#peek LOAD_DLL_DEBUG_INFO, nDebugInfoSize) p'
+            imgname <- (#peek LOAD_DLL_DEBUG_INFO, lpImageName) p'
+            --unicode <- (#peek LOAD_DLL_DEBUG_INFO, fUnicode) p'
+            return $
                 LoadDll (file, imgbase, dbgoff, dbgsize, imgname)--, unicode/=wZero)
 
-        rest (#const OUTPUT_DEBUG_STRING_EVENT) p = do
-            dat     <- (#peek OUTPUT_DEBUG_STRING_INFO, lpDebugStringData) p
-            unicode <- (#peek OUTPUT_DEBUG_STRING_INFO, fUnicode) p
-            length  <- (#peek OUTPUT_DEBUG_STRING_INFO, nDebugStringLength) p
-            return $ DebugString dat (unicode/=wZero) length
-        
-        rest (#const UNLOAD_DLL_DEBUG_EVENT) p =
-            (#peek UNLOAD_DLL_DEBUG_INFO, lpBaseOfDll) p >>= return.UnloadDll
+        rest (#const OUTPUT_DEBUG_STRING_EVENT) p' = do
+            dat     <- (#peek OUTPUT_DEBUG_STRING_INFO, lpDebugStringData) p'
+            unicode <- (#peek OUTPUT_DEBUG_STRING_INFO, fUnicode) p'
+            len     <- (#peek OUTPUT_DEBUG_STRING_INFO, nDebugStringLength) p'
+            return $ DebugString dat (unicode/=wZero) len
 
+        rest (#const UNLOAD_DLL_DEBUG_EVENT) p' =
+            (#peek UNLOAD_DLL_DEBUG_INFO, lpBaseOfDll) p' >>= return.UnloadDll
+
         rest _ _ = return UnknownDebugEvent
 
 
@@ -188,9 +240,9 @@
     where
         getMore e = case e of
             Nothing -> return []
-            Just e  -> do
+            Just e'  -> do
                 rest <- waitForDebugEvent (Just 0) >>= getMore
-                return $ e:rest
+                return $ e':rest
 
 continueDebugEvent :: DebugEventId -> Bool -> IO ()
 continueDebugEvent (pid,tid) cont =
@@ -287,12 +339,10 @@
             (act buf)
 
 
+#if defined(i386_HOST_ARCH)
 eax, ebx, ecx, edx :: Int
 esi, edi :: Int
 ebp, eip, esp :: Int
-segCs, segDs, segEs, segFs, segGs :: Int
-eFlags :: Int
-
 eax = (#offset CONTEXT, Eax)
 ebx = (#offset CONTEXT, Ebx)
 ecx = (#offset CONTEXT, Ecx)
@@ -302,11 +352,70 @@
 ebp = (#offset CONTEXT, Ebp)
 eip = (#offset CONTEXT, Eip)
 esp = (#offset CONTEXT, Esp)
+#elif defined(x86_64_HOST_ARCH)
+rax, rbx, rcx, rdx :: Int
+rsi, rdi :: Int
+rbp, rip, rsp :: Int
+rax = (#offset CONTEXT, Rax)
+rbx = (#offset CONTEXT, Rbx)
+rcx = (#offset CONTEXT, Rcx)
+rdx = (#offset CONTEXT, Rdx)
+rsi = (#offset CONTEXT, Rsi)
+rdi = (#offset CONTEXT, Rdi)
+rbp = (#offset CONTEXT, Rbp)
+rip = (#offset CONTEXT, Rip)
+rsp = (#offset CONTEXT, Rsp)
+#elif defined(aarch64_HOST_ARCH)
+x0,   x1,  x2,  x3,  x4,  x5,  x6,  x7,  x8 :: Int
+x9,  x10, x11, x12, x13, x14, x15, x16, x17 :: Int
+x18, x19, x20, x21, x22, x23, x24, x25, x26 :: Int
+x27, x28,  fp,  lr,  sp,  pc                :: Int
+x0  = (#offset CONTEXT, X0 )
+x1  = (#offset CONTEXT, X1 )
+x2  = (#offset CONTEXT, X2 )
+x3  = (#offset CONTEXT, X3 )
+x4  = (#offset CONTEXT, X4 )
+x5  = (#offset CONTEXT, X5 )
+x6  = (#offset CONTEXT, X6 )
+x7  = (#offset CONTEXT, X7 )
+x8  = (#offset CONTEXT, X8 )
+x9  = (#offset CONTEXT, X9 )
+x10 = (#offset CONTEXT, X10)
+x11 = (#offset CONTEXT, X11)
+x12 = (#offset CONTEXT, X12)
+x13 = (#offset CONTEXT, X13)
+x14 = (#offset CONTEXT, X14)
+x15 = (#offset CONTEXT, X15)
+x16 = (#offset CONTEXT, X16)
+x17 = (#offset CONTEXT, X17)
+x18 = (#offset CONTEXT, X18)
+x19 = (#offset CONTEXT, X19)
+x20 = (#offset CONTEXT, X20)
+x21 = (#offset CONTEXT, X21)
+x22 = (#offset CONTEXT, X22)
+x23 = (#offset CONTEXT, X23)
+x24 = (#offset CONTEXT, X24)
+x25 = (#offset CONTEXT, X25)
+x26 = (#offset CONTEXT, X26)
+x27 = (#offset CONTEXT, X27)
+x28 = (#offset CONTEXT, X28)
+fp  = (#offset CONTEXT, Fp)
+lr  = (#offset CONTEXT, Lr)
+sp  = (#offset CONTEXT, Sp)
+pc  = (#offset CONTEXT, Pc)
+#else
+#error Unknown mingw32 arch
+#endif
+
+#if defined(x86_64_HOST_ARCH) || defined(i386_HOST_ARCH)
+segCs, segDs, segEs, segFs, segGs :: Int
 segCs = (#offset CONTEXT, SegCs)
 segDs = (#offset CONTEXT, SegDs)
 segEs = (#offset CONTEXT, SegEs)
 segFs = (#offset CONTEXT, SegFs)
 segGs = (#offset CONTEXT, SegGs)
+
+eFlags :: Int
 eFlags  = (#offset CONTEXT, EFlags)
 
 dr :: Int -> Int
@@ -318,7 +427,8 @@
     6 -> (#offset CONTEXT, Dr6)
     7 -> (#offset CONTEXT, Dr7)
     _ -> undefined
-    
+#endif
+
 setReg :: Ptr a -> Int -> DWORD -> IO ()
 setReg = pokeByteOff
 
@@ -341,50 +451,5 @@
 -- On process being debugged
 
 outputDebugString :: String -> IO ()
-outputDebugString s = withTString s $ \s -> c_OutputDebugString s
-
---------------------------------------------------------------------------
--- Raw imports
-
-foreign import stdcall "windows.h SuspendThread"
-    c_SuspendThread :: THANDLE -> IO DWORD
-
-foreign import stdcall "windows.h ResumeThread"
-    c_ResumeThread :: THANDLE -> IO DWORD
-
-foreign import stdcall "windows.h WaitForDebugEvent"
-    c_WaitForDebugEvent :: Ptr () -> DWORD -> IO BOOL
-
-foreign import stdcall "windows.h ContinueDebugEvent"
-    c_ContinueDebugEvent :: DWORD -> DWORD -> DWORD -> IO BOOL
-
-foreign import stdcall "windows.h DebugActiveProcess"
-    c_DebugActiveProcess :: DWORD -> IO Bool
-    
--- Windows XP
--- foreign import stdcall "windows.h DebugActiveProcessStop"
---     c_DebugActiveProcessStop :: DWORD -> IO Bool
-
-foreign import stdcall "windows.h ReadProcessMemory" c_ReadProcessMemory :: 
-    PHANDLE -> Ptr () -> Ptr Word8 -> DWORD -> Ptr DWORD -> IO BOOL
-
-foreign import stdcall "windows.h WriteProcessMemory" c_WriteProcessMemory ::
-    PHANDLE -> Ptr () -> Ptr Word8 -> DWORD -> Ptr DWORD -> IO BOOL
-
-foreign import stdcall "windows.h GetThreadContext"
-    c_GetThreadContext :: THANDLE -> Ptr () -> IO BOOL
-
-foreign import stdcall "windows.h SetThreadContext"
-    c_SetThreadContext :: THANDLE -> Ptr () -> IO BOOL
-
---foreign import stdcall "windows.h GetThreadId"
---    c_GetThreadId :: THANDLE -> IO TID
-
-foreign import stdcall "windows.h OutputDebugStringW"
-    c_OutputDebugString :: LPTSTR -> IO ()
-
-foreign import stdcall "windows.h IsDebuggerPresent"
-    isDebuggerPresent :: IO BOOL
+outputDebugString s = withTString s $ \c_s -> c_OutputDebugString c_s
 
-foreign import stdcall "windows.h  DebugBreak"
-    debugBreak :: IO ()
diff --git a/System/Win32/DebugApi/Internal.hsc b/System/Win32/DebugApi/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/DebugApi/Internal.hsc
@@ -0,0 +1,80 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.WindowsString.DebugApi.Internal
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for using Windows DebugApi.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.DebugApi.Internal where
+
+import Data.Word            ( Word8, Word32 )
+import Foreign              ( Ptr )
+import System.Win32.Types   ( BOOL, DWORD, HANDLE, LPTSTR )
+
+##include "windows_cconv.h"
+#include "windows.h"
+
+type PID = DWORD
+type TID = DWORD
+type DebugEventId = (PID, TID)
+type ForeignAddress = Word32
+
+type PHANDLE = Ptr ()
+type THANDLE = Ptr ()
+
+type ThreadInfo = (THANDLE, ForeignAddress, ForeignAddress)   -- handle to thread, thread local, thread start
+type ImageInfo = (HANDLE, ForeignAddress, DWORD, DWORD, ForeignAddress)
+type ExceptionInfo = (Bool, Bool, ForeignAddress) -- First chance, continuable, address
+
+--------------------------------------------------------------------------
+-- Raw imports
+
+foreign import WINDOWS_CCONV "windows.h SuspendThread"
+    c_SuspendThread :: THANDLE -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h ResumeThread"
+    c_ResumeThread :: THANDLE -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h WaitForDebugEvent"
+    c_WaitForDebugEvent :: Ptr () -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h ContinueDebugEvent"
+    c_ContinueDebugEvent :: DWORD -> DWORD -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h DebugActiveProcess"
+    c_DebugActiveProcess :: DWORD -> IO Bool
+
+-- Windows XP
+-- foreign import WINDOWS_CCONV "windows.h DebugActiveProcessStop"
+--     c_DebugActiveProcessStop :: DWORD -> IO Bool
+
+foreign import WINDOWS_CCONV "windows.h ReadProcessMemory" c_ReadProcessMemory ::
+    PHANDLE -> Ptr () -> Ptr Word8 -> DWORD -> Ptr DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h WriteProcessMemory" c_WriteProcessMemory ::
+    PHANDLE -> Ptr () -> Ptr Word8 -> DWORD -> Ptr DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h GetThreadContext"
+    c_GetThreadContext :: THANDLE -> Ptr () -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h SetThreadContext"
+    c_SetThreadContext :: THANDLE -> Ptr () -> IO BOOL
+
+--foreign import WINDOWS_CCONV "windows.h GetThreadId"
+--    c_GetThreadId :: THANDLE -> IO TID
+
+foreign import WINDOWS_CCONV "windows.h OutputDebugStringW"
+    c_OutputDebugString :: LPTSTR -> IO ()
+
+foreign import WINDOWS_CCONV "windows.h IsDebuggerPresent"
+    isDebuggerPresent :: IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h  DebugBreak"
+    debugBreak :: IO ()
diff --git a/System/Win32/Encoding.hs b/System/Win32/Encoding.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Encoding.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Encoding
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Enocode/Decode mutibyte character using Win32 API.
+-}
+
+module System.Win32.Encoding
+  ( getCurrentCodePage
+  , encodeMultiByte
+  , encodeMultiByteIO
+  , decodeMultiByte
+  , decodeMultiByteIO
+  , wideCharToMultiByte
+  , multiByteToWideChar
+  ) where
+
+import Foreign.C.Types        (CInt(..))
+import Foreign.C.String       (peekCAStringLen, withCWStringLen)
+import Foreign.Marshal.Array  (allocaArray)
+import Foreign.Marshal.Unsafe (unsafeLocalState)
+import System.Win32.Console
+import System.Win32.NLS
+import System.Win32.Types
+
+#include "windows_cconv.h"
+
+-- note CodePage = UInt which might not work on Win64.  But the Win32 package
+-- also has this issue.
+getCurrentCodePage :: IO DWORD
+getCurrentCodePage = do
+    conCP <- getConsoleCP
+    if conCP > 0
+        then return conCP
+        else getACP
+
+-- | The "System.IO" output functions (e.g., `putStr`) don't
+-- automatically convert to multibyte string on Windows, so this
+-- function is provided to make the conversion from a Unicode string
+-- in the given code page to a proper multibyte string.  To get the
+-- code page for the console, use `getCurrentCodePage`.
+--
+encodeMultiByte :: CodePage -> String -> String
+encodeMultiByte cp = unsafeLocalState . encodeMultiByteIO cp
+
+encodeMultiByteIO :: CodePage -> String -> IO String
+encodeMultiByteIO _ "" = return ""
+  -- WideCharToMultiByte doesn't handle empty strings
+encodeMultiByteIO cp wstr =
+  withCWStringLen wstr $ \(cwstr,len) -> do
+    mbchars' <- failIfZero "WideCharToMultiByte" $ wideCharToMultiByte 
+                cp
+                0
+                cwstr
+                (fromIntegral len)
+                nullPtr 0
+                nullPtr nullPtr
+    -- mbchar' is the length of buffer required
+    allocaArray (fromIntegral mbchars') $ \mbstr -> do
+      mbchars <- failIfZero "WideCharToMultiByte" $ wideCharToMultiByte 
+                 cp
+                 0
+                 cwstr
+                 (fromIntegral len)
+                 mbstr mbchars'
+                 nullPtr nullPtr
+      peekCAStringLen (mbstr,fromIntegral mbchars)  -- converts [Char] to UTF-16
+
+foreign import WINDOWS_CCONV "WideCharToMultiByte"
+  wideCharToMultiByte
+        :: CodePage
+        -> DWORD   -- dwFlags,
+        -> LPCWSTR -- lpWideCharStr
+        -> CInt    -- cchWideChar
+        -> LPSTR   -- lpMultiByteStr
+        -> CInt    -- cbMultiByte
+        -> LPCSTR  -- lpMultiByteStr
+        -> LPBOOL  -- lpbFlags
+        -> IO CInt
+
+-- | The "System.IO" input functions (e.g. `getLine`) don't
+-- automatically convert to Unicode, so this function is provided to
+-- make the conversion from a multibyte string in the given code page 
+-- to a proper Unicode string.  To get the code page for the console,
+-- use `getCurrentCodePage`.
+decodeMultiByte :: CodePage -> String -> String
+decodeMultiByte cp = unsafeLocalState . decodeMultiByteIO cp
+
+-- | Because of `stringToUnicode` is unclear name, we use `decodeMultiByteIO`
+-- for alias of `stringToUnicode`. 
+decodeMultiByteIO :: CodePage -> String -> IO String
+decodeMultiByteIO = stringToUnicode
+{-# INLINE decodeMultiByteIO #-}
diff --git a/System/Win32/Event.hsc b/System/Win32/Event.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Event.hsc
@@ -0,0 +1,163 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Event
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32 event system between
+-- processes.
+--
+-----------------------------------------------------------------------------
+module System.Win32.Event
+    ( -- * Duplicate options
+      DuplicateOption
+    , dUPLICATE_CLOSE_SOURCE
+    , dUPLICATE_SAME_ACCESS
+      -- * Access modes
+    , AccessMode
+    , eVENT_ALL_ACCESS
+    , eVENT_MODIFY_STATE
+      -- * Wait results
+    , WaitResult
+    , wAIT_ABANDONED
+    , wAIT_IO_COMPLETION
+    , wAIT_OBJECT_0
+    , wAIT_TIMEOUT
+    , wAIT_FAILED
+      -- * Managing events
+    , openEvent
+    , createEvent
+    , duplicateHandle
+    , setEvent
+    , resetEvent
+    , pulseEvent
+      -- * Signalling objects
+    , signalObjectAndWait
+      -- * Waiting on objects
+    , waitForSingleObject
+    , waitForSingleObjectEx
+    , waitForMultipleObjects
+    , waitForMultipleObjectsEx
+    ) where
+
+import Foreign.Marshal.Alloc     ( alloca )
+import Foreign.Marshal.Array     ( withArrayLen )
+import Foreign.Marshal.Utils     ( maybeWith, with )
+import Foreign.Ptr               ( Ptr, nullPtr )
+import Foreign.Storable          ( Storable(..) )
+import Graphics.Win32.Misc       ( MilliSeconds )
+import System.Win32.File         ( AccessMode, LPSECURITY_ATTRIBUTES, SECURITY_ATTRIBUTES )
+import System.Win32.Types        ( LPCTSTR, HANDLE, BOOL, withTString, failIf, failIfFalse_  )
+import System.Win32.Word         ( DWORD )
+
+##include "windows_cconv.h"
+
+#include "windows.h"
+#include "winuser_compat.h"
+#include "alignment.h"
+
+---------------------------------------------------------------------------
+-- Enums
+---------------------------------------------------------------------------
+type DuplicateOption = DWORD
+#{enum DuplicateOption,
+    , dUPLICATE_CLOSE_SOURCE = DUPLICATE_CLOSE_SOURCE
+    , dUPLICATE_SAME_ACCESS  = DUPLICATE_SAME_ACCESS
+    }
+
+#{enum AccessMode,
+    , eVENT_ALL_ACCESS   = EVENT_ALL_ACCESS
+    , eVENT_MODIFY_STATE = EVENT_MODIFY_STATE
+    }
+
+type WaitResult = DWORD
+#{enum WaitResult,
+    , wAIT_ABANDONED     = WAIT_ABANDONED
+    , wAIT_IO_COMPLETION = WAIT_IO_COMPLETION
+    , wAIT_OBJECT_0      = WAIT_OBJECT_0
+    , wAIT_TIMEOUT       = WAIT_TIMEOUT
+    , wAIT_FAILED        = WAIT_FAILED
+    }
+
+---------------------------------------------------------------------------
+-- API in Haskell
+---------------------------------------------------------------------------
+openEvent :: AccessMode -> Bool -> String -> IO HANDLE
+openEvent amode inherit name = withTString name $ \c_name ->
+    failIf (==nullPtr) "openEvent: OpenEvent" $ c_OpenEvent (fromIntegral amode) inherit c_name
+
+createEvent :: Maybe SECURITY_ATTRIBUTES -> Bool -> Bool -> String -> IO HANDLE
+createEvent msecurity manual initial name = withTString name $ \c_name ->
+    maybeWith with msecurity $ \c_sec ->
+        failIf (==nullPtr) "createEvent: CreateEvent" $ c_CreateEvent c_sec manual initial c_name
+
+duplicateHandle :: HANDLE -> HANDLE -> HANDLE -> AccessMode -> Bool -> DuplicateOption -> IO HANDLE
+duplicateHandle srcProccess srcHandler targetProcess access inherit opts = alloca $ \res -> do
+    failIfFalse_ "duplicateHandle: DuplicateHandle" $ c_DuplicateHandle srcProccess srcHandler targetProcess res (fromIntegral access) inherit opts
+    peek res
+
+setEvent :: HANDLE -> IO ()
+setEvent event = failIfFalse_ "setEvent: SetEvent" $ c_SetEvent event
+
+resetEvent :: HANDLE -> IO ()
+resetEvent event = failIfFalse_ "resetEvent: ResetEvent" $ c_ResetEvent event
+
+pulseEvent :: HANDLE -> IO ()
+pulseEvent event = failIfFalse_ "pulseEvent: PulseEvent" $ c_PulseEvent event
+
+signalObjectAndWait :: HANDLE -> HANDLE -> MilliSeconds -> Bool -> IO WaitResult
+signalObjectAndWait toSignal toWaitOn millis alterable = c_SignalObjectAndWait toSignal toWaitOn millis alterable
+
+waitForSingleObject :: HANDLE -> MilliSeconds -> IO WaitResult
+waitForSingleObject toWaitOn millis = c_WaitForSingleObject toWaitOn millis
+
+waitForSingleObjectEx :: HANDLE -> MilliSeconds -> Bool -> IO WaitResult
+waitForSingleObjectEx toWaitOn millis alterable = c_WaitForSingleObjectEx toWaitOn millis alterable
+
+waitForMultipleObjects :: [HANDLE] -> Bool -> MilliSeconds -> IO WaitResult
+waitForMultipleObjects hs waitAll millis = withArrayLen hs $ \n hsp ->
+  c_WaitForMultipleObjects (fromIntegral n) hsp waitAll millis
+
+waitForMultipleObjectsEx :: [HANDLE] -> Bool -> MilliSeconds -> Bool -> IO WaitResult
+waitForMultipleObjectsEx hs waitAll millis alterable = withArrayLen hs $ \n hsp ->
+  c_WaitForMultipleObjectsEx (fromIntegral n) hsp waitAll millis alterable
+
+---------------------------------------------------------------------------
+-- Imports
+---------------------------------------------------------------------------
+foreign import WINDOWS_CCONV "windows.h OpenEventW"
+    c_OpenEvent :: DWORD -> BOOL -> LPCTSTR -> IO HANDLE
+
+foreign import WINDOWS_CCONV "windows.h CreateEventW"
+    c_CreateEvent :: LPSECURITY_ATTRIBUTES -> BOOL -> BOOL -> LPCTSTR -> IO HANDLE
+
+foreign import WINDOWS_CCONV "windows.h DuplicateHandle"
+    c_DuplicateHandle :: HANDLE -> HANDLE -> HANDLE -> Ptr HANDLE -> DWORD -> BOOL -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h SetEvent"
+    c_SetEvent :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV "windows.h ResetEvent"
+    c_ResetEvent :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV "windows.h PulseEvent"
+    c_PulseEvent :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV "windows.h SignalObjectAndWait"
+    c_SignalObjectAndWait :: HANDLE -> HANDLE -> DWORD -> BOOL -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h WaitForSingleObject"
+    c_WaitForSingleObject :: HANDLE -> DWORD -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h WaitForSingleObjectEx"
+    c_WaitForSingleObjectEx :: HANDLE -> DWORD -> BOOL -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h WaitForMultipleObjects"
+    c_WaitForMultipleObjects :: DWORD -> Ptr HANDLE -> BOOL -> DWORD -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h WaitForMultipleObjectsEx"
+    c_WaitForMultipleObjectsEx :: DWORD -> Ptr HANDLE -> BOOL -> DWORD -> BOOL -> IO DWORD
diff --git a/System/Win32/Exception/Unsupported.hs b/System/Win32/Exception/Unsupported.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Exception/Unsupported.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{- |
+   Module      :  System.Win32.Exception.Unsupported
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Exception handling if using unsupported Win32 API.
+-}
+
+module System.Win32.Exception.Unsupported
+  ( module System.Win32.Exception.Unsupported
+  ) where
+
+import Control.Exception      ( Exception(..), throwIO )
+import Data.Typeable          ( Typeable )
+import Foreign.Ptr            ( Ptr, nullPtr )
+import Foreign.Marshal.Unsafe ( unsafeLocalState )
+
+----------------------------------------------------------------
+-- Exception type of Unsupported
+----------------------------------------------------------------
+data Unsupported = MissingLibrary  FilePath String
+                 | MissingFunction String   String
+                 | MissingValue    String   String
+                 deriving Typeable
+
+instance Show Unsupported where
+  show (MissingLibrary  name reason)
+    = "Can't load library \"" ++ name ++ "\". "  ++ reason
+  show (MissingFunction name reason)
+    = "Can't find \"" ++ name ++ "\" function. " ++ reason
+  show (MissingValue    name reason)
+    = "Can't use \""  ++ name ++ "\" value. "    ++ reason
+
+instance Exception Unsupported
+
+missingLibrary                          :: FilePath -> Unsupported
+missingFunction,      missingValue      :: String -> Unsupported
+missingLibrary  name = MissingLibrary  name ""
+missingFunction name = MissingFunction name ""
+missingValue    name = MissingValue    name ""
+
+missingWin32Function, missingWin32Value :: String -> String -> Unsupported
+missingWin32Function name reason = MissingFunction name $ doesn'tSupport ++ '\n':reason
+missingWin32Value    name reason = MissingValue    name $ doesn'tSupport ++ '\n':reason
+
+doesn'tSupport, upgradeVista, removed :: String
+doesn'tSupport = "Because it's not supported on this OS."
+upgradeVista   = upgradeWindowsOS "Windows Vista"
+removed = "It's removed. "
+
+upgradeWindowsOS :: String -> String
+upgradeWindowsOS ver
+  =  "If you want to use it, please upgrade your OS to "
+  ++ ver ++ " or higher."
+
+unsupportedIfNull :: Unsupported -> IO (Ptr a) -> IO (Ptr a)
+unsupportedIfNull wh act = do
+  v <- act
+  if v /= nullPtr then return v else throwIO wh
+
+unsupportedVal :: String -> IO Bool -> String -> a -> a
+unsupportedVal name checkVer reason val = unsafeLocalState $ do
+  cv <- checkVer
+  if cv then return val else throwIO $ MissingValue name reason
+
diff --git a/System/Win32/File.hsc b/System/Win32/File.hsc
--- a/System/Win32/File.hsc
+++ b/System/Win32/File.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,250 +18,249 @@
 -----------------------------------------------------------------------------
 
 module System.Win32.File
-{-
-	( AccessMode, ShareMode, CreateMode, FileAttributeOrFlag
-	, CreateFile, CloseHandle, DeleteFile, CopyFile
-	, MoveFileFlag, MoveFile, MoveFileEx,
-	)
--}
-where
-
-import System.Win32.Types
-import System.Win32.Time
-
-import Foreign hiding (void)
-import Control.Monad
-import Control.Concurrent
-
-#include <windows.h>
-
-----------------------------------------------------------------
--- Enumeration types
-----------------------------------------------------------------
-
-type AccessMode   = UINT
-
-gENERIC_NONE :: AccessMode
-gENERIC_NONE = 0
-
-#{enum AccessMode,
- , gENERIC_READ             = GENERIC_READ
- , gENERIC_WRITE            = GENERIC_WRITE
- , gENERIC_EXECUTE          = GENERIC_EXECUTE
- , gENERIC_ALL              = GENERIC_ALL
- , dELETE                   = DELETE
- , rEAD_CONTROL             = READ_CONTROL
- , wRITE_DAC                = WRITE_DAC
- , wRITE_OWNER              = WRITE_OWNER
- , sYNCHRONIZE              = SYNCHRONIZE
- , sTANDARD_RIGHTS_REQUIRED = STANDARD_RIGHTS_REQUIRED
- , sTANDARD_RIGHTS_READ     = STANDARD_RIGHTS_READ
- , sTANDARD_RIGHTS_WRITE    = STANDARD_RIGHTS_WRITE
- , sTANDARD_RIGHTS_EXECUTE  = STANDARD_RIGHTS_EXECUTE
- , sTANDARD_RIGHTS_ALL      = STANDARD_RIGHTS_ALL
- , sPECIFIC_RIGHTS_ALL      = SPECIFIC_RIGHTS_ALL
- , aCCESS_SYSTEM_SECURITY   = ACCESS_SYSTEM_SECURITY
- , mAXIMUM_ALLOWED          = MAXIMUM_ALLOWED
- }
-
-----------------------------------------------------------------
-
-type ShareMode   = UINT
-
-fILE_SHARE_NONE :: ShareMode
-fILE_SHARE_NONE = 0
-
-#{enum ShareMode,
- , fILE_SHARE_READ      = FILE_SHARE_READ
- , fILE_SHARE_WRITE     = FILE_SHARE_WRITE
- }
-
-----------------------------------------------------------------
-
-type CreateMode   = UINT
-
-#{enum CreateMode,
- , cREATE_NEW           = CREATE_NEW
- , cREATE_ALWAYS        = CREATE_ALWAYS
- , oPEN_EXISTING        = OPEN_EXISTING
- , oPEN_ALWAYS          = OPEN_ALWAYS
- , tRUNCATE_EXISTING    = TRUNCATE_EXISTING
- }
+    ( -- * Access modes
+      AccessMode
+    , gENERIC_NONE
+    , gENERIC_READ
+    , gENERIC_WRITE
+    , gENERIC_EXECUTE
+    , gENERIC_ALL
+    , dELETE
+    , rEAD_CONTROL
+    , wRITE_DAC
+    , wRITE_OWNER
+    , sYNCHRONIZE
+    , sTANDARD_RIGHTS_REQUIRED
+    , sTANDARD_RIGHTS_READ
+    , sTANDARD_RIGHTS_WRITE
+    , sTANDARD_RIGHTS_EXECUTE
+    , sTANDARD_RIGHTS_ALL
+    , sPECIFIC_RIGHTS_ALL
+    , aCCESS_SYSTEM_SECURITY
+    , mAXIMUM_ALLOWED
+    , fILE_ADD_FILE
+    , fILE_ADD_SUBDIRECTORY
+    , fILE_ALL_ACCESS
+    , fILE_APPEND_DATA
+    , fILE_CREATE_PIPE_INSTANCE
+    , fILE_DELETE_CHILD
+    , fILE_EXECUTE
+    , fILE_LIST_DIRECTORY
+    , fILE_READ_ATTRIBUTES
+    , fILE_READ_DATA
+    , fILE_READ_EA
+    , fILE_TRAVERSE
+    , fILE_WRITE_ATTRIBUTES
+    , fILE_WRITE_DATA
+    , fILE_WRITE_EA
 
-----------------------------------------------------------------
+      -- * Sharing modes
+    , ShareMode
+    , fILE_SHARE_NONE
+    , fILE_SHARE_READ
+    , fILE_SHARE_WRITE
+    , fILE_SHARE_DELETE
 
-type FileAttributeOrFlag   = UINT
+      -- * Creation modes
+    , CreateMode
+    , cREATE_NEW
+    , cREATE_ALWAYS
+    , oPEN_EXISTING
+    , oPEN_ALWAYS
+    , tRUNCATE_EXISTING
 
-#{enum FileAttributeOrFlag,
- , fILE_ATTRIBUTE_READONLY      = FILE_ATTRIBUTE_READONLY
- , fILE_ATTRIBUTE_HIDDEN        = FILE_ATTRIBUTE_HIDDEN
- , fILE_ATTRIBUTE_SYSTEM        = FILE_ATTRIBUTE_SYSTEM
- , fILE_ATTRIBUTE_DIRECTORY     = FILE_ATTRIBUTE_DIRECTORY
- , fILE_ATTRIBUTE_ARCHIVE       = FILE_ATTRIBUTE_ARCHIVE
- , fILE_ATTRIBUTE_NORMAL        = FILE_ATTRIBUTE_NORMAL
- , fILE_ATTRIBUTE_TEMPORARY     = FILE_ATTRIBUTE_TEMPORARY
- , fILE_ATTRIBUTE_COMPRESSED    = FILE_ATTRIBUTE_COMPRESSED
- , fILE_FLAG_WRITE_THROUGH      = FILE_FLAG_WRITE_THROUGH
- , fILE_FLAG_OVERLAPPED         = FILE_FLAG_OVERLAPPED
- , fILE_FLAG_NO_BUFFERING       = FILE_FLAG_NO_BUFFERING
- , fILE_FLAG_RANDOM_ACCESS      = FILE_FLAG_RANDOM_ACCESS
- , fILE_FLAG_SEQUENTIAL_SCAN    = FILE_FLAG_SEQUENTIAL_SCAN
- , fILE_FLAG_DELETE_ON_CLOSE    = FILE_FLAG_DELETE_ON_CLOSE
- , fILE_FLAG_BACKUP_SEMANTICS   = FILE_FLAG_BACKUP_SEMANTICS
- , fILE_FLAG_POSIX_SEMANTICS    = FILE_FLAG_POSIX_SEMANTICS
- }
+      -- * File attributes and flags
+    , FileAttributeOrFlag
+    , fILE_ATTRIBUTE_READONLY
+    , fILE_ATTRIBUTE_HIDDEN
+    , fILE_ATTRIBUTE_SYSTEM
+    , fILE_ATTRIBUTE_DIRECTORY
+    , fILE_ATTRIBUTE_ARCHIVE
+    , fILE_ATTRIBUTE_NORMAL
+    , fILE_ATTRIBUTE_TEMPORARY
+    , fILE_ATTRIBUTE_COMPRESSED
+    , fILE_ATTRIBUTE_REPARSE_POINT
+    , fILE_FLAG_WRITE_THROUGH
+    , fILE_FLAG_OVERLAPPED
+    , fILE_FLAG_NO_BUFFERING
+    , fILE_FLAG_RANDOM_ACCESS
+    , fILE_FLAG_SEQUENTIAL_SCAN
+    , fILE_FLAG_DELETE_ON_CLOSE
+    , fILE_FLAG_BACKUP_SEMANTICS
+    , fILE_FLAG_POSIX_SEMANTICS
 #ifndef __WINE_WINDOWS_H
-#{enum FileAttributeOrFlag,
- , sECURITY_ANONYMOUS           = SECURITY_ANONYMOUS
- , sECURITY_IDENTIFICATION      = SECURITY_IDENTIFICATION
- , sECURITY_IMPERSONATION       = SECURITY_IMPERSONATION
- , sECURITY_DELEGATION          = SECURITY_DELEGATION
- , sECURITY_CONTEXT_TRACKING    = SECURITY_CONTEXT_TRACKING
- , sECURITY_EFFECTIVE_ONLY      = SECURITY_EFFECTIVE_ONLY
- , sECURITY_SQOS_PRESENT        = SECURITY_SQOS_PRESENT
- , sECURITY_VALID_SQOS_FLAGS    = SECURITY_VALID_SQOS_FLAGS
- }
+    , sECURITY_ANONYMOUS
+    , sECURITY_IDENTIFICATION
+    , sECURITY_IMPERSONATION
+    , sECURITY_DELEGATION
+    , sECURITY_CONTEXT_TRACKING
+    , sECURITY_EFFECTIVE_ONLY
+    , sECURITY_SQOS_PRESENT
+    , sECURITY_VALID_SQOS_FLAGS
 #endif
 
-----------------------------------------------------------------
-
-type MoveFileFlag   = DWORD
-
-#{enum MoveFileFlag,
- , mOVEFILE_REPLACE_EXISTING	= MOVEFILE_REPLACE_EXISTING
- , mOVEFILE_COPY_ALLOWED	= MOVEFILE_COPY_ALLOWED
- , mOVEFILE_DELAY_UNTIL_REBOOT	= MOVEFILE_DELAY_UNTIL_REBOOT
- }
-
-----------------------------------------------------------------
-
-type FilePtrDirection   = DWORD
+      -- * Move file flags
+    , MoveFileFlag
+    , mOVEFILE_REPLACE_EXISTING
+    , mOVEFILE_COPY_ALLOWED
+    , mOVEFILE_DELAY_UNTIL_REBOOT
 
-#{enum FilePtrDirection,
- , fILE_BEGIN   = FILE_BEGIN
- , fILE_CURRENT = FILE_CURRENT
- , fILE_END     = FILE_END
- }
+      -- * File pointer directions
+    , FilePtrDirection
+    , fILE_BEGIN
+    , fILE_CURRENT
+    , fILE_END
 
-----------------------------------------------------------------
+      -- * Drive types
+    , DriveType
+    , dRIVE_UNKNOWN
+    , dRIVE_NO_ROOT_DIR
+    , dRIVE_REMOVABLE
+    , dRIVE_FIXED
+    , dRIVE_REMOTE
+    , dRIVE_CDROM
+    , dRIVE_RAMDISK
 
-type DriveType = UINT
+      -- * Define DOS device flags
+    , DefineDosDeviceFlags
+    , dDD_RAW_TARGET_PATH
+    , dDD_REMOVE_DEFINITION
+    , dDD_EXACT_MATCH_ON_REMOVE
 
-#{enum DriveType,
- , dRIVE_UNKNOWN        = DRIVE_UNKNOWN
- , dRIVE_NO_ROOT_DIR    = DRIVE_NO_ROOT_DIR
- , dRIVE_REMOVABLE      = DRIVE_REMOVABLE
- , dRIVE_FIXED          = DRIVE_FIXED
- , dRIVE_REMOTE         = DRIVE_REMOTE
- , dRIVE_CDROM          = DRIVE_CDROM
- , dRIVE_RAMDISK        = DRIVE_RAMDISK
- }
+      -- * Binary types
+    , BinaryType
+    , sCS_32BIT_BINARY
+    , sCS_DOS_BINARY
+    , sCS_WOW_BINARY
+    , sCS_PIF_BINARY
+    , sCS_POSIX_BINARY
+    , sCS_OS216_BINARY
 
-----------------------------------------------------------------
+      -- * File notification flags
+    , FileNotificationFlag
+    , fILE_NOTIFY_CHANGE_FILE_NAME
+    , fILE_NOTIFY_CHANGE_DIR_NAME
+    , fILE_NOTIFY_CHANGE_ATTRIBUTES
+    , fILE_NOTIFY_CHANGE_SIZE
+    , fILE_NOTIFY_CHANGE_LAST_WRITE
+    , fILE_NOTIFY_CHANGE_SECURITY
 
-type DefineDosDeviceFlags = DWORD
+      -- * File types
+    , FileType
+    , fILE_TYPE_UNKNOWN
+    , fILE_TYPE_DISK
+    , fILE_TYPE_CHAR
+    , fILE_TYPE_PIPE
+    , fILE_TYPE_REMOTE
 
-#{enum DefineDosDeviceFlags,
- , dDD_RAW_TARGET_PATH          = DDD_RAW_TARGET_PATH
- , dDD_REMOVE_DEFINITION        = DDD_REMOVE_DEFINITION
- , dDD_EXACT_MATCH_ON_REMOVE    = DDD_EXACT_MATCH_ON_REMOVE
- }
+      -- * Lock modes
+    , LockMode
+    , lOCKFILE_EXCLUSIVE_LOCK
+    , lOCKFILE_FAIL_IMMEDIATELY
 
-----------------------------------------------------------------
+      -- * GetFileEx information levels
+    , GET_FILEEX_INFO_LEVELS
+    , getFileExInfoStandard
+    , getFileExMaxInfoLevel
 
-type BinaryType = DWORD
+      -- * Security attributes
+    , SECURITY_ATTRIBUTES(..)
+    , PSECURITY_ATTRIBUTES
+    , LPSECURITY_ATTRIBUTES
+    , MbLPSECURITY_ATTRIBUTES
 
-#{enum BinaryType,
- , sCS_32BIT_BINARY     = SCS_32BIT_BINARY
- , sCS_DOS_BINARY       = SCS_DOS_BINARY
- , sCS_WOW_BINARY       = SCS_WOW_BINARY
- , sCS_PIF_BINARY       = SCS_PIF_BINARY
- , sCS_POSIX_BINARY     = SCS_POSIX_BINARY
- , sCS_OS216_BINARY     = SCS_OS216_BINARY
- }
+      -- * BY_HANDLE file information
+    , BY_HANDLE_FILE_INFORMATION(..)
 
-----------------------------------------------------------------
+      -- * Win32 file attribute data
+    , WIN32_FILE_ATTRIBUTE_DATA(..)
 
-type FileNotificationFlag = DWORD
+      -- * Helpers
+    , failIfWithRetry
+    , failIfWithRetry_
+    , failIfFalseWithRetry_
+      -- * File operations
+    , deleteFile
+    , copyFile
+    , moveFile
+    , moveFileEx
+    , setCurrentDirectory
+    , createDirectory
+    , createDirectoryEx
+    , removeDirectory
+    , getBinaryType
+    , getTempFileName
+    , replaceFile
 
-#{enum FileNotificationFlag,
- , fILE_NOTIFY_CHANGE_FILE_NAME  = FILE_NOTIFY_CHANGE_FILE_NAME
- , fILE_NOTIFY_CHANGE_DIR_NAME   = FILE_NOTIFY_CHANGE_DIR_NAME
- , fILE_NOTIFY_CHANGE_ATTRIBUTES = FILE_NOTIFY_CHANGE_ATTRIBUTES
- , fILE_NOTIFY_CHANGE_SIZE       = FILE_NOTIFY_CHANGE_SIZE
- , fILE_NOTIFY_CHANGE_LAST_WRITE = FILE_NOTIFY_CHANGE_LAST_WRITE
- , fILE_NOTIFY_CHANGE_SECURITY   = FILE_NOTIFY_CHANGE_SECURITY
- }
+      -- * HANDLE operations
+    , createFile
+    , createFile_NoRetry
+    , closeHandle
+    , getFileType
+    , flushFileBuffers
+    , setEndOfFile
+    , setFileAttributes
+    , getFileAttributes
+    , getFileAttributesExStandard
+    , getFileInformationByHandle
 
-----------------------------------------------------------------
+      -- ** Reading/writing
+      -- | Some operations below bear the @win32_@ prefix to avoid shadowing
+      -- operations from "Prelude".
+    , OVERLAPPED(..)
+    , LPOVERLAPPED
+    , MbLPOVERLAPPED
+    , win32_ReadFile
+    , win32_WriteFile
+    , setFilePointerEx
 
-type FileType = DWORD
+      -- * File notifications
+    , findFirstChangeNotification
+    , findNextChangeNotification
+    , findCloseChangeNotification
 
-#{enum FileType,
- , fILE_TYPE_UNKNOWN    = FILE_TYPE_UNKNOWN
- , fILE_TYPE_DISK       = FILE_TYPE_DISK
- , fILE_TYPE_CHAR       = FILE_TYPE_CHAR
- , fILE_TYPE_PIPE       = FILE_TYPE_PIPE
- , fILE_TYPE_REMOTE     = FILE_TYPE_REMOTE
- }
+      -- * Directories
+    , FindData
+    , getFindDataFileName
+    , findFirstFile
+    , findNextFile
+    , findClose
 
-----------------------------------------------------------------
+      -- * DOS device flags
+    , defineDosDevice
+    , areFileApisANSI
+    , setFileApisToOEM
+    , setFileApisToANSI
+    , setHandleCount
+    , getLogicalDrives
+    , getDiskFreeSpace
+    , setVolumeLabel
 
-type LPSECURITY_ATTRIBUTES = Ptr ()
-type MbLPSECURITY_ATTRIBUTES = Maybe LPSECURITY_ATTRIBUTES
+      -- * File locks
+    , lockFile
+    , unlockFile
+    ) where
 
-----------------------------------------------------------------
--- Other types
-----------------------------------------------------------------
+import System.Win32.File.Internal
+import System.Win32.Types
 
-data BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION
-    { bhfiFileAttributes :: FileAttributeOrFlag
-    , bhfiCreationTime, bhfiLastAccessTime, bhfiLastWriteTime :: FILETIME
-    , bhfiVolumeSerialNumber :: DWORD
-    , bhfiSize :: DDWORD
-    , bhfiNumberOfLinks :: DWORD
-    , bhfiFileIndex :: DDWORD
-    } deriving (Show)
+import Foreign hiding (void)
+import Control.Monad
+import Control.Concurrent
+import Data.Maybe (fromMaybe)
 
-instance Storable BY_HANDLE_FILE_INFORMATION where
-    sizeOf = const (#size BY_HANDLE_FILE_INFORMATION)
-    alignment = sizeOf
-    poke buf bhi = do
-        (#poke BY_HANDLE_FILE_INFORMATION, dwFileAttributes)     buf (bhfiFileAttributes bhi)
-        (#poke BY_HANDLE_FILE_INFORMATION, ftCreationTime)       buf (bhfiCreationTime bhi)
-        (#poke BY_HANDLE_FILE_INFORMATION, ftLastAccessTime)     buf (bhfiLastAccessTime bhi)
-        (#poke BY_HANDLE_FILE_INFORMATION, ftLastWriteTime)      buf (bhfiLastWriteTime bhi)
-        (#poke BY_HANDLE_FILE_INFORMATION, dwVolumeSerialNumber) buf (bhfiVolumeSerialNumber bhi)
-        (#poke BY_HANDLE_FILE_INFORMATION, nFileSizeHigh)        buf sizeHi
-        (#poke BY_HANDLE_FILE_INFORMATION, nFileSizeLow)         buf sizeLow
-        (#poke BY_HANDLE_FILE_INFORMATION, nNumberOfLinks)       buf (bhfiNumberOfLinks bhi)
-        (#poke BY_HANDLE_FILE_INFORMATION, nFileIndexHigh)       buf idxHi
-        (#poke BY_HANDLE_FILE_INFORMATION, nFileIndexLow)        buf idxLow
-        where
-            (sizeHi,sizeLow) = ddwordToDwords $ bhfiSize bhi
-            (idxHi,idxLow) = ddwordToDwords $ bhfiFileIndex bhi
+##include "windows_cconv.h"
 
-    peek buf = do
-        attr <- (#peek BY_HANDLE_FILE_INFORMATION, dwFileAttributes)     buf
-        ctim <- (#peek BY_HANDLE_FILE_INFORMATION, ftCreationTime)       buf
-        lati <- (#peek BY_HANDLE_FILE_INFORMATION, ftLastAccessTime)     buf
-        lwti <- (#peek BY_HANDLE_FILE_INFORMATION, ftLastWriteTime)      buf
-        vser <- (#peek BY_HANDLE_FILE_INFORMATION, dwVolumeSerialNumber) buf
-        fshi <- (#peek BY_HANDLE_FILE_INFORMATION, nFileSizeHigh)        buf
-        fslo <- (#peek BY_HANDLE_FILE_INFORMATION, nFileSizeLow)         buf
-        link <- (#peek BY_HANDLE_FILE_INFORMATION, nNumberOfLinks)       buf
-        idhi <- (#peek BY_HANDLE_FILE_INFORMATION, nFileIndexHigh)       buf
-        idlo <- (#peek BY_HANDLE_FILE_INFORMATION, nFileIndexLow)        buf
-        return $ BY_HANDLE_FILE_INFORMATION attr ctim lati lwti vser
-            (dwordsToDdword (fshi,fslo)) link (dwordsToDdword (idhi,idlo))
+#include <windows.h>
+#include "alignment.h"
 
 ----------------------------------------------------------------
 -- File operations
 ----------------------------------------------------------------
 
--- | like failIfFalse_, but retried on sharing violations.
+-- | like failIf, but retried on sharing violations.
 -- This is necessary for many file operations; see
---   http://support.microsoft.com/kb/316609
+--   https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/316609
 --
 failIfWithRetry :: (a -> Bool) -> String -> IO a -> IO a
 failIfWithRetry cond msg action = retryOrFail retries
@@ -289,157 +290,144 @@
 
 deleteFile :: String -> IO ()
 deleteFile name =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
     failIfFalseWithRetry_ (unwords ["DeleteFile",show name]) $
       c_DeleteFile c_name
-foreign import stdcall unsafe "windows.h DeleteFileW"
-  c_DeleteFile :: LPCTSTR -> IO Bool
 
 copyFile :: String -> String -> Bool -> IO ()
 copyFile src dest over =
-  withTString src $ \ c_src ->
-  withTString dest $ \ c_dest ->
+  withFilePath src $ \ c_src ->
+  withFilePath dest $ \ c_dest ->
   failIfFalseWithRetry_ (unwords ["CopyFile",show src,show dest]) $
     c_CopyFile c_src c_dest over
-foreign import stdcall unsafe "windows.h CopyFileW"
-  c_CopyFile :: LPCTSTR -> LPCTSTR -> Bool -> IO Bool
 
 moveFile :: String -> String -> IO ()
 moveFile src dest =
-  withTString src $ \ c_src ->
-  withTString dest $ \ c_dest ->
+  withFilePath src $ \ c_src ->
+  withFilePath dest $ \ c_dest ->
   failIfFalseWithRetry_ (unwords ["MoveFile",show src,show dest]) $
     c_MoveFile c_src c_dest
-foreign import stdcall unsafe "windows.h MoveFileW"
-  c_MoveFile :: LPCTSTR -> LPCTSTR -> IO Bool
 
-moveFileEx :: String -> String -> MoveFileFlag -> IO ()
+moveFileEx :: String -> Maybe String -> MoveFileFlag -> IO ()
 moveFileEx src dest flags =
-  withTString src $ \ c_src ->
-  withTString dest $ \ c_dest ->
+  withFilePath src $ \ c_src ->
+  maybeWith withFilePath dest $ \ c_dest ->
   failIfFalseWithRetry_ (unwords ["MoveFileEx",show src,show dest]) $
     c_MoveFileEx c_src c_dest flags
-foreign import stdcall unsafe "windows.h MoveFileExW"
-  c_MoveFileEx :: LPCTSTR -> LPCTSTR -> MoveFileFlag -> IO Bool
 
 setCurrentDirectory :: String -> IO ()
 setCurrentDirectory name =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
   failIfFalse_ (unwords ["SetCurrentDirectory",show name]) $
     c_SetCurrentDirectory c_name
-foreign import stdcall unsafe "windows.h SetCurrentDirectoryW"
-  c_SetCurrentDirectory :: LPCTSTR -> IO Bool
 
 createDirectory :: String -> Maybe LPSECURITY_ATTRIBUTES -> IO ()
 createDirectory name mb_attr =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
   failIfFalseWithRetry_ (unwords ["CreateDirectory",show name]) $
     c_CreateDirectory c_name (maybePtr mb_attr)
-foreign import stdcall unsafe "windows.h CreateDirectoryW"
-  c_CreateDirectory :: LPCTSTR -> LPSECURITY_ATTRIBUTES -> IO Bool
 
 createDirectoryEx :: String -> String -> Maybe LPSECURITY_ATTRIBUTES -> IO ()
 createDirectoryEx template name mb_attr =
-  withTString template $ \ c_template ->
-  withTString name $ \ c_name ->
+  withFilePath template $ \ c_template ->
+  withFilePath name $ \ c_name ->
   failIfFalseWithRetry_ (unwords ["CreateDirectoryEx",show template,show name]) $
     c_CreateDirectoryEx c_template c_name (maybePtr mb_attr)
-foreign import stdcall unsafe "windows.h CreateDirectoryExW"
-  c_CreateDirectoryEx :: LPCTSTR -> LPCTSTR -> LPSECURITY_ATTRIBUTES -> IO Bool
 
 removeDirectory :: String -> IO ()
 removeDirectory name =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
   failIfFalseWithRetry_ (unwords ["RemoveDirectory",show name]) $
     c_RemoveDirectory c_name
-foreign import stdcall unsafe "windows.h RemoveDirectoryW"
-  c_RemoveDirectory :: LPCTSTR -> IO Bool
 
 getBinaryType :: String -> IO BinaryType
 getBinaryType name =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
   alloca $ \ p_btype -> do
   failIfFalse_ (unwords ["GetBinaryType",show name]) $
     c_GetBinaryType c_name p_btype
   peek p_btype
-foreign import stdcall unsafe "windows.h GetBinaryTypeW"
-  c_GetBinaryType :: LPCTSTR -> Ptr DWORD -> IO Bool
 
+-- | Get a unique temporary filename.
+--
+-- Calls 'GetTempFileNameW'.
+getTempFileName :: String     -- ^ directory for the temporary file (must be at most MAX_PATH - 14 characters long)
+                -> String     -- ^ prefix for the temporary file name
+                -> Maybe UINT -- ^ if 'Nothing', a unique name is generated
+                              --   otherwise a non-zero value is used as the unique part
+                -> IO (String, UINT)
+getTempFileName dir prefix unique = allocaBytes ((#const MAX_PATH) * sizeOf (undefined :: TCHAR)) $ \c_buf -> do
+  uid <- withFilePath dir $ \c_dir ->
+    withFilePath prefix $ \ c_prefix -> do
+      failIfZero "getTempFileName" $
+        c_GetTempFileNameW c_dir c_prefix (fromMaybe 0 unique) c_buf
+  fname <- peekTString c_buf
+  return (fname, uid)
+
 ----------------------------------------------------------------
 -- HANDLE operations
 ----------------------------------------------------------------
 
 createFile :: String -> AccessMode -> ShareMode -> Maybe LPSECURITY_ATTRIBUTES -> CreateMode -> FileAttributeOrFlag -> Maybe HANDLE -> IO HANDLE
-createFile name access share mb_attr mode flag mb_h =
-  withTString name $ \ c_name ->
-  failIfWithRetry (==iNVALID_HANDLE_VALUE) (unwords ["CreateFile",show name]) $
+createFile = createFile' failIfWithRetry
+
+createFile' :: ((HANDLE -> Bool) -> String -> IO HANDLE -> IO HANDLE) -> String -> AccessMode -> ShareMode -> Maybe LPSECURITY_ATTRIBUTES -> CreateMode -> FileAttributeOrFlag -> Maybe HANDLE -> IO HANDLE
+createFile' f name access share mb_attr mode flag mb_h =
+  withFilePath name $ \ c_name ->
+  f (==iNVALID_HANDLE_VALUE) (unwords ["CreateFile",show name]) $
     c_CreateFile c_name access share (maybePtr mb_attr) mode flag (maybePtr mb_h)
-foreign import stdcall unsafe "windows.h CreateFileW"
-  c_CreateFile :: LPCTSTR -> AccessMode -> ShareMode -> LPSECURITY_ATTRIBUTES -> CreateMode -> FileAttributeOrFlag -> HANDLE -> IO HANDLE
 
+-- | Like createFile, but does not use failIfWithRetry. If another
+-- process has the same file open, this will fail.
+createFile_NoRetry :: String -> AccessMode -> ShareMode -> Maybe LPSECURITY_ATTRIBUTES -> CreateMode -> FileAttributeOrFlag -> Maybe HANDLE -> IO HANDLE
+createFile_NoRetry = createFile' failIf
+
 closeHandle :: HANDLE -> IO ()
 closeHandle h =
   failIfFalse_ "CloseHandle" $ c_CloseHandle h
-foreign import stdcall unsafe "windows.h CloseHandle"
-  c_CloseHandle :: HANDLE -> IO Bool
 
-{-# CFILES cbits/HsWin32.c #-}
-foreign import ccall "HsWin32.h &CloseHandleFinaliser"
-    c_CloseHandleFinaliser :: FunPtr (Ptr a -> IO ())
-
-foreign import stdcall unsafe "windows.h GetFileType"
-  getFileType :: HANDLE -> IO FileType
 --Apparently no error code
 
 flushFileBuffers :: HANDLE -> IO ()
 flushFileBuffers h =
   failIfFalse_ "FlushFileBuffers" $ c_FlushFileBuffers h
-foreign import stdcall unsafe "windows.h FlushFileBuffers"
-  c_FlushFileBuffers :: HANDLE -> IO Bool
 
 setEndOfFile :: HANDLE -> IO ()
 setEndOfFile h =
   failIfFalse_ "SetEndOfFile" $ c_SetEndOfFile h
-foreign import stdcall unsafe "windows.h SetEndOfFile"
-  c_SetEndOfFile :: HANDLE -> IO Bool
 
 setFileAttributes :: String -> FileAttributeOrFlag -> IO ()
 setFileAttributes name attr =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
   failIfFalseWithRetry_ (unwords ["SetFileAttributes",show name])
     $ c_SetFileAttributes c_name attr
-foreign import stdcall unsafe "windows.h SetFileAttributesW"
-  c_SetFileAttributes :: LPCTSTR -> FileAttributeOrFlag -> IO Bool
 
 getFileAttributes :: String -> IO FileAttributeOrFlag
 getFileAttributes name =
-  withTString name $ \ c_name ->
+  withFilePath name $ \ c_name ->
   failIfWithRetry (== 0xFFFFFFFF) (unwords ["GetFileAttributes",show name]) $
     c_GetFileAttributes c_name
-foreign import stdcall unsafe "windows.h GetFileAttributesW"
-  c_GetFileAttributes :: LPCTSTR -> IO FileAttributeOrFlag
 
+getFileAttributesExStandard :: String -> IO WIN32_FILE_ATTRIBUTE_DATA
+getFileAttributesExStandard name =  alloca $ \res -> do
+  withFilePath name $ \ c_name ->
+    failIfFalseWithRetry_ "getFileAttributesExStandard" $
+      c_GetFileAttributesEx c_name getFileExInfoStandard res
+  peek res
+
 getFileInformationByHandle :: HANDLE -> IO BY_HANDLE_FILE_INFORMATION
 getFileInformationByHandle h = alloca $ \res -> do
     failIfFalseWithRetry_ "GetFileInformationByHandle" $ c_GetFileInformationByHandle h res
     peek res
-foreign import stdcall unsafe "windows.h GetFileInformationByHandle"
-    c_GetFileInformationByHandle :: HANDLE -> Ptr BY_HANDLE_FILE_INFORMATION -> IO BOOL
 
+replaceFile :: LPCWSTR -> LPCWSTR -> LPCWSTR -> DWORD -> IO ()
+replaceFile replacedFile replacementFile backupFile replaceFlags =
+  failIfFalse_ "ReplaceFile" $ c_ReplaceFile replacedFile replacementFile backupFile replaceFlags nullPtr nullPtr
+
 ----------------------------------------------------------------
 -- Read/write files
 ----------------------------------------------------------------
 
--- No support for this yet
---type OVERLAPPED =
--- (DWORD,  -- Offset
---  DWORD,  -- OffsetHigh
---  HANDLE) -- hEvent
-
-type LPOVERLAPPED = Ptr ()
-
-type MbLPOVERLAPPED = Maybe LPOVERLAPPED
-
 --Sigh - I give up & prefix win32_ to the next two to avoid
 -- senseless Prelude name clashes. --sof.
 
@@ -448,20 +436,18 @@
   alloca $ \ p_n -> do
   failIfFalse_ "ReadFile" $ c_ReadFile h buf n p_n (maybePtr mb_over)
   peek p_n
-foreign import stdcall unsafe "windows.h ReadFile"
-  c_ReadFile :: HANDLE -> Ptr a -> DWORD -> Ptr DWORD -> LPOVERLAPPED -> IO Bool
 
 win32_WriteFile :: HANDLE -> Ptr a -> DWORD -> Maybe LPOVERLAPPED -> IO DWORD
 win32_WriteFile h buf n mb_over =
   alloca $ \ p_n -> do
   failIfFalse_ "WriteFile" $ c_WriteFile h buf n p_n (maybePtr mb_over)
   peek p_n
-foreign import stdcall unsafe "windows.h WriteFile"
-  c_WriteFile :: HANDLE -> Ptr a -> DWORD -> Ptr DWORD -> LPOVERLAPPED -> IO Bool
 
--- missing Seek functioinality; GSL ???
--- Dont have Word64; ADR
--- %fun SetFilePointer :: HANDLE -> Word64 -> FilePtrDirection -> IO Word64
+setFilePointerEx :: HANDLE -> LARGE_INTEGER -> FilePtrDirection -> IO LARGE_INTEGER
+setFilePointerEx h dist dir =
+  alloca $ \p_pos -> do
+  failIfFalse_ "SetFilePointerEx" $ c_SetFilePointerEx h dist p_pos dir
+  peek p_pos
 
 ----------------------------------------------------------------
 -- File Notifications
@@ -472,47 +458,35 @@
 
 findFirstChangeNotification :: String -> Bool -> FileNotificationFlag -> IO HANDLE
 findFirstChangeNotification path watch flag =
-  withTString path $ \ c_path ->
+  withFilePath path $ \ c_path ->
   failIfNull (unwords ["FindFirstChangeNotification",show path]) $
     c_FindFirstChangeNotification c_path watch flag
-foreign import stdcall unsafe "windows.h FindFirstChangeNotificationW"
-  c_FindFirstChangeNotification :: LPCTSTR -> Bool -> FileNotificationFlag -> IO HANDLE
 
 findNextChangeNotification :: HANDLE -> IO ()
 findNextChangeNotification h =
   failIfFalse_ "FindNextChangeNotification" $ c_FindNextChangeNotification h
-foreign import stdcall unsafe "windows.h FindNextChangeNotification"
-  c_FindNextChangeNotification :: HANDLE -> IO Bool
 
 findCloseChangeNotification :: HANDLE -> IO ()
 findCloseChangeNotification h =
   failIfFalse_ "FindCloseChangeNotification" $ c_FindCloseChangeNotification h
-foreign import stdcall unsafe "windows.h FindCloseChangeNotification"
-  c_FindCloseChangeNotification :: HANDLE -> IO Bool
 
 ----------------------------------------------------------------
 -- Directories
 ----------------------------------------------------------------
 
-type WIN32_FIND_DATA = ()
-
-newtype FindData = FindData (ForeignPtr WIN32_FIND_DATA)
-
 getFindDataFileName :: FindData -> IO FilePath
-getFindDataFileName (FindData fp) = 
-  withForeignPtr fp $ \p -> 
+getFindDataFileName (FindData fp) =
+  withForeignPtr fp $ \p ->
     peekTString ((# ptr WIN32_FIND_DATAW, cFileName ) p)
 
 findFirstFile :: String -> IO (HANDLE, FindData)
 findFirstFile str = do
   fp_finddata <- mallocForeignPtrBytes (# const sizeof(WIN32_FIND_DATAW) )
   withForeignPtr fp_finddata $ \p_finddata -> do
-    handle <- withTString str $ \tstr -> do
-                failIf (== iNVALID_HANDLE_VALUE) "findFirstFile" $ 
+    handle <- withFilePath str $ \tstr -> do
+                failIf (== iNVALID_HANDLE_VALUE) "findFirstFile" $
                   c_FindFirstFile tstr p_finddata
     return (handle, FindData fp_finddata)
-foreign import stdcall unsafe "windows.h FindFirstFileW"
-  c_FindFirstFile :: LPCTSTR -> Ptr WIN32_FIND_DATA -> IO HANDLE
 
 findNextFile :: HANDLE -> FindData -> IO Bool -- False -> no more files
 findNextFile h (FindData finddata) = do
@@ -525,56 +499,31 @@
              if err_code == (# const ERROR_NO_MORE_FILES )
                 then return False
                 else failWith "findNextFile" err_code
-foreign import stdcall unsafe "windows.h FindNextFileW"
-  c_FindNextFile :: HANDLE -> Ptr WIN32_FIND_DATA -> IO BOOL
 
 findClose :: HANDLE -> IO ()
 findClose h = failIfFalse_ "findClose" $ c_FindClose h
-foreign import stdcall unsafe "windows.h FindClose"
-  c_FindClose :: HANDLE -> IO BOOL
 
 ----------------------------------------------------------------
 -- DOS Device flags
 ----------------------------------------------------------------
 
-defineDosDevice :: DefineDosDeviceFlags -> String -> String -> IO ()
+defineDosDevice :: DefineDosDeviceFlags -> String -> Maybe String -> IO ()
 defineDosDevice flags name path =
-  withTString path $ \ c_path ->
-  withTString name $ \ c_name ->
+  maybeWith withFilePath path $ \ c_path ->
+  withFilePath name $ \ c_name ->
   failIfFalse_ "DefineDosDevice" $ c_DefineDosDevice flags c_name c_path
-foreign import stdcall unsafe "windows.h DefineDosDeviceW"
-  c_DefineDosDevice :: DefineDosDeviceFlags -> LPCTSTR -> LPCTSTR -> IO Bool
 
 ----------------------------------------------------------------
 
--- These functions are very unusual in the Win32 API:
--- They dont return error codes
-
-foreign import stdcall unsafe "windows.h AreFileApisANSI"
-  areFileApisANSI :: IO Bool
-
-foreign import stdcall unsafe "windows.h SetFileApisToOEM"
-  setFileApisToOEM :: IO ()
-
-foreign import stdcall unsafe "windows.h SetFileApisToANSI"
-  setFileApisToANSI :: IO ()
-
-foreign import stdcall unsafe "windows.h SetHandleCount"
-  setHandleCount :: UINT -> IO UINT
-
-----------------------------------------------------------------
-
 getLogicalDrives :: IO DWORD
 getLogicalDrives =
   failIfZero "GetLogicalDrives" $ c_GetLogicalDrives
-foreign import stdcall unsafe "windows.h GetLogicalDrives"
-  c_GetLogicalDrives :: IO DWORD
 
 -- %fun GetDriveType :: Maybe String -> IO DriveType
 
 getDiskFreeSpace :: Maybe String -> IO (DWORD,DWORD,DWORD,DWORD)
 getDiskFreeSpace path =
-  maybeWith withTString path $ \ c_path ->
+  maybeWith withFilePath path $ \ c_path ->
   alloca $ \ p_sectors ->
   alloca $ \ p_bytes ->
   alloca $ \ p_nfree ->
@@ -586,16 +535,47 @@
   nfree <- peek p_nfree
   nclusters <- peek p_nclusters
   return (sectors, bytes, nfree, nclusters)
-foreign import stdcall unsafe "windows.h GetDiskFreeSpaceW"
-  c_GetDiskFreeSpace :: LPCTSTR -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> IO Bool
 
-setVolumeLabel :: String -> String -> IO ()
+setVolumeLabel :: Maybe String -> Maybe String -> IO ()
 setVolumeLabel path name =
-  withTString path $ \ c_path ->
-  withTString name $ \ c_name ->
+  maybeWith withFilePath path $ \ c_path ->
+  maybeWith withFilePath name $ \ c_name ->
   failIfFalse_ "SetVolumeLabel" $ c_SetVolumeLabel c_path c_name
-foreign import stdcall unsafe "windows.h SetVolumeLabelW"
-  c_SetVolumeLabel :: LPCTSTR -> LPCTSTR -> IO Bool
+
+----------------------------------------------------------------
+-- File locks
+----------------------------------------------------------------
+
+-- | Locks a given range in a file handle, To lock an entire file
+--   use 0xFFFFFFFFFFFFFFFF for size and 0 for offset.
+lockFile :: HANDLE   -- ^ CreateFile handle
+         -> LockMode -- ^ Locking mode
+         -> DWORD64  -- ^ Size of region to lock
+         -> DWORD64  -- ^ Beginning offset of file to lock
+         -> IO BOOL  -- ^ Indicates if locking was successful, if not query
+                     --   getLastError.
+lockFile hwnd mode size f_offset =
+  do let s_low = fromIntegral (size .&. 0xFFFFFFFF)
+         s_hi  = fromIntegral (size `shiftR` 32)
+         o_low = fromIntegral (f_offset .&. 0xFFFFFFFF)
+         o_hi  = fromIntegral (f_offset `shiftR` 32)
+         ovlp  = OVERLAPPED 0 0 o_low o_hi nullPtr
+     with ovlp $ \ptr -> c_LockFileEx hwnd mode 0 s_low s_hi ptr
+
+-- | Unlocks a given range in a file handle, To unlock an entire file
+--   use 0xFFFFFFFFFFFFFFFF for size and 0 for offset.
+unlockFile :: HANDLE  -- ^ CreateFile handle
+           -> DWORD64 -- ^ Size of region to unlock
+           -> DWORD64 -- ^ Beginning offset of file to unlock
+           -> IO BOOL -- ^ Indicates if unlocking was successful, if not query
+                      --   getLastError.
+unlockFile hwnd size f_offset =
+  do let s_low = fromIntegral (size .&. 0xFFFFFFFF)
+         s_hi  = fromIntegral (size `shiftR` 32)
+         o_low = fromIntegral (f_offset .&. 0xFFFFFFFF)
+         o_hi  = fromIntegral (f_offset `shiftR` 32)
+         ovlp  = OVERLAPPED 0 0 o_low o_hi nullPtr
+     with ovlp $ \ptr -> c_UnlockFileEx hwnd 0 s_low s_hi ptr
 
 ----------------------------------------------------------------
 -- End
diff --git a/System/Win32/File/Internal.hsc b/System/Win32/File/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/File/Internal.hsc
@@ -0,0 +1,548 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.File.Internal
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.File.Internal where
+
+import System.Win32.Types
+import System.Win32.Time
+
+import Foreign hiding (void)
+
+##include "windows_cconv.h"
+
+-- For REPLACEFILE_IGNORE_ACL_ERRORS
+#define  _WIN32_WINNT 0x0600
+
+#include <windows.h>
+#include "alignment.h"
+
+----------------------------------------------------------------
+-- Enumeration types
+----------------------------------------------------------------
+
+type AccessMode   = UINT
+
+gENERIC_NONE :: AccessMode
+gENERIC_NONE = 0
+
+#{enum AccessMode,
+ , gENERIC_READ              = GENERIC_READ
+ , gENERIC_WRITE             = GENERIC_WRITE
+ , gENERIC_EXECUTE           = GENERIC_EXECUTE
+ , gENERIC_ALL               = GENERIC_ALL
+ , dELETE                    = DELETE
+ , rEAD_CONTROL              = READ_CONTROL
+ , wRITE_DAC                 = WRITE_DAC
+ , wRITE_OWNER               = WRITE_OWNER
+ , sYNCHRONIZE               = SYNCHRONIZE
+ , sTANDARD_RIGHTS_REQUIRED  = STANDARD_RIGHTS_REQUIRED
+ , sTANDARD_RIGHTS_READ      = STANDARD_RIGHTS_READ
+ , sTANDARD_RIGHTS_WRITE     = STANDARD_RIGHTS_WRITE
+ , sTANDARD_RIGHTS_EXECUTE   = STANDARD_RIGHTS_EXECUTE
+ , sTANDARD_RIGHTS_ALL       = STANDARD_RIGHTS_ALL
+ , sPECIFIC_RIGHTS_ALL       = SPECIFIC_RIGHTS_ALL
+ , aCCESS_SYSTEM_SECURITY    = ACCESS_SYSTEM_SECURITY
+ , mAXIMUM_ALLOWED           = MAXIMUM_ALLOWED
+ , fILE_ADD_FILE             = FILE_ADD_FILE
+ , fILE_ADD_SUBDIRECTORY     = FILE_ADD_SUBDIRECTORY
+ , fILE_ALL_ACCESS           = FILE_ALL_ACCESS
+ , fILE_APPEND_DATA          = FILE_APPEND_DATA
+ , fILE_CREATE_PIPE_INSTANCE = FILE_CREATE_PIPE_INSTANCE
+ , fILE_DELETE_CHILD         = FILE_DELETE_CHILD
+ , fILE_EXECUTE              = FILE_EXECUTE
+ , fILE_LIST_DIRECTORY       = FILE_LIST_DIRECTORY
+ , fILE_READ_ATTRIBUTES      = FILE_READ_ATTRIBUTES
+ , fILE_READ_DATA            = FILE_READ_DATA
+ , fILE_READ_EA              = FILE_READ_EA
+ , fILE_TRAVERSE             = FILE_TRAVERSE
+ , fILE_WRITE_ATTRIBUTES     = FILE_WRITE_ATTRIBUTES
+ , fILE_WRITE_DATA           = FILE_WRITE_DATA
+ , fILE_WRITE_EA             = FILE_WRITE_EA
+ }
+
+----------------------------------------------------------------
+
+type ShareMode   = UINT
+
+fILE_SHARE_NONE :: ShareMode
+fILE_SHARE_NONE = 0
+
+#{enum ShareMode,
+ , fILE_SHARE_READ      = FILE_SHARE_READ
+ , fILE_SHARE_WRITE     = FILE_SHARE_WRITE
+ , fILE_SHARE_DELETE    = FILE_SHARE_DELETE
+ }
+
+----------------------------------------------------------------
+
+type CreateMode   = UINT
+
+#{enum CreateMode,
+ , cREATE_NEW           = CREATE_NEW
+ , cREATE_ALWAYS        = CREATE_ALWAYS
+ , oPEN_EXISTING        = OPEN_EXISTING
+ , oPEN_ALWAYS          = OPEN_ALWAYS
+ , tRUNCATE_EXISTING    = TRUNCATE_EXISTING
+ }
+
+----------------------------------------------------------------
+
+type FileAttributeOrFlag   = UINT
+
+#{enum FileAttributeOrFlag,
+ , fILE_ATTRIBUTE_READONLY      = FILE_ATTRIBUTE_READONLY
+ , fILE_ATTRIBUTE_HIDDEN        = FILE_ATTRIBUTE_HIDDEN
+ , fILE_ATTRIBUTE_SYSTEM        = FILE_ATTRIBUTE_SYSTEM
+ , fILE_ATTRIBUTE_DIRECTORY     = FILE_ATTRIBUTE_DIRECTORY
+ , fILE_ATTRIBUTE_ARCHIVE       = FILE_ATTRIBUTE_ARCHIVE
+ , fILE_ATTRIBUTE_NORMAL        = FILE_ATTRIBUTE_NORMAL
+ , fILE_ATTRIBUTE_TEMPORARY     = FILE_ATTRIBUTE_TEMPORARY
+ , fILE_ATTRIBUTE_COMPRESSED    = FILE_ATTRIBUTE_COMPRESSED
+ , fILE_ATTRIBUTE_REPARSE_POINT = FILE_ATTRIBUTE_REPARSE_POINT
+ , fILE_FLAG_WRITE_THROUGH      = FILE_FLAG_WRITE_THROUGH
+ , fILE_FLAG_OVERLAPPED         = FILE_FLAG_OVERLAPPED
+ , fILE_FLAG_NO_BUFFERING       = FILE_FLAG_NO_BUFFERING
+ , fILE_FLAG_RANDOM_ACCESS      = FILE_FLAG_RANDOM_ACCESS
+ , fILE_FLAG_SEQUENTIAL_SCAN    = FILE_FLAG_SEQUENTIAL_SCAN
+ , fILE_FLAG_DELETE_ON_CLOSE    = FILE_FLAG_DELETE_ON_CLOSE
+ , fILE_FLAG_BACKUP_SEMANTICS   = FILE_FLAG_BACKUP_SEMANTICS
+ , fILE_FLAG_POSIX_SEMANTICS    = FILE_FLAG_POSIX_SEMANTICS
+ }
+#ifndef __WINE_WINDOWS_H
+#{enum FileAttributeOrFlag,
+ , sECURITY_ANONYMOUS           = SECURITY_ANONYMOUS
+ , sECURITY_IDENTIFICATION      = SECURITY_IDENTIFICATION
+ , sECURITY_IMPERSONATION       = SECURITY_IMPERSONATION
+ , sECURITY_DELEGATION          = SECURITY_DELEGATION
+ , sECURITY_CONTEXT_TRACKING    = SECURITY_CONTEXT_TRACKING
+ , sECURITY_EFFECTIVE_ONLY      = SECURITY_EFFECTIVE_ONLY
+ , sECURITY_SQOS_PRESENT        = SECURITY_SQOS_PRESENT
+ , sECURITY_VALID_SQOS_FLAGS    = SECURITY_VALID_SQOS_FLAGS
+ }
+#endif
+
+----------------------------------------------------------------
+
+type MoveFileFlag   = DWORD
+
+#{enum MoveFileFlag,
+ , mOVEFILE_REPLACE_EXISTING    = MOVEFILE_REPLACE_EXISTING
+ , mOVEFILE_COPY_ALLOWED        = MOVEFILE_COPY_ALLOWED
+ , mOVEFILE_DELAY_UNTIL_REBOOT  = MOVEFILE_DELAY_UNTIL_REBOOT
+ }
+
+----------------------------------------------------------------
+
+type FilePtrDirection   = DWORD
+
+#{enum FilePtrDirection,
+ , fILE_BEGIN   = FILE_BEGIN
+ , fILE_CURRENT = FILE_CURRENT
+ , fILE_END     = FILE_END
+ }
+
+----------------------------------------------------------------
+
+type DriveType = UINT
+
+#{enum DriveType,
+ , dRIVE_UNKNOWN        = DRIVE_UNKNOWN
+ , dRIVE_NO_ROOT_DIR    = DRIVE_NO_ROOT_DIR
+ , dRIVE_REMOVABLE      = DRIVE_REMOVABLE
+ , dRIVE_FIXED          = DRIVE_FIXED
+ , dRIVE_REMOTE         = DRIVE_REMOTE
+ , dRIVE_CDROM          = DRIVE_CDROM
+ , dRIVE_RAMDISK        = DRIVE_RAMDISK
+ }
+
+----------------------------------------------------------------
+
+type DefineDosDeviceFlags = DWORD
+
+#{enum DefineDosDeviceFlags,
+ , dDD_RAW_TARGET_PATH          = DDD_RAW_TARGET_PATH
+ , dDD_REMOVE_DEFINITION        = DDD_REMOVE_DEFINITION
+ , dDD_EXACT_MATCH_ON_REMOVE    = DDD_EXACT_MATCH_ON_REMOVE
+ }
+
+----------------------------------------------------------------
+
+type BinaryType = DWORD
+
+#{enum BinaryType,
+ , sCS_32BIT_BINARY     = SCS_32BIT_BINARY
+ , sCS_DOS_BINARY       = SCS_DOS_BINARY
+ , sCS_WOW_BINARY       = SCS_WOW_BINARY
+ , sCS_PIF_BINARY       = SCS_PIF_BINARY
+ , sCS_POSIX_BINARY     = SCS_POSIX_BINARY
+ , sCS_OS216_BINARY     = SCS_OS216_BINARY
+ }
+
+----------------------------------------------------------------
+
+type ReplaceType = DWORD
+
+#{enum ReplaceType,
+ , rEPLACEFILE_WRITE_THROUGH       = REPLACEFILE_WRITE_THROUGH
+ , rEPLACEFILE_IGNORE_MERGE_ERRORS = REPLACEFILE_IGNORE_MERGE_ERRORS
+ , rEPLACEFILE_IGNORE_ACL_ERRORS   = REPLACEFILE_IGNORE_ACL_ERRORS
+ }
+
+----------------------------------------------------------------
+
+type FileNotificationFlag = DWORD
+
+#{enum FileNotificationFlag,
+ , fILE_NOTIFY_CHANGE_FILE_NAME  = FILE_NOTIFY_CHANGE_FILE_NAME
+ , fILE_NOTIFY_CHANGE_DIR_NAME   = FILE_NOTIFY_CHANGE_DIR_NAME
+ , fILE_NOTIFY_CHANGE_ATTRIBUTES = FILE_NOTIFY_CHANGE_ATTRIBUTES
+ , fILE_NOTIFY_CHANGE_SIZE       = FILE_NOTIFY_CHANGE_SIZE
+ , fILE_NOTIFY_CHANGE_LAST_WRITE = FILE_NOTIFY_CHANGE_LAST_WRITE
+ , fILE_NOTIFY_CHANGE_SECURITY   = FILE_NOTIFY_CHANGE_SECURITY
+ }
+
+----------------------------------------------------------------
+
+type FileType = DWORD
+
+#{enum FileType,
+ , fILE_TYPE_UNKNOWN    = FILE_TYPE_UNKNOWN
+ , fILE_TYPE_DISK       = FILE_TYPE_DISK
+ , fILE_TYPE_CHAR       = FILE_TYPE_CHAR
+ , fILE_TYPE_PIPE       = FILE_TYPE_PIPE
+ , fILE_TYPE_REMOTE     = FILE_TYPE_REMOTE
+ }
+
+----------------------------------------------------------------
+
+type LockMode = DWORD
+
+#{enum LockMode,
+ , lOCKFILE_EXCLUSIVE_LOCK   = LOCKFILE_EXCLUSIVE_LOCK
+ , lOCKFILE_FAIL_IMMEDIATELY = LOCKFILE_FAIL_IMMEDIATELY
+ }
+
+----------------------------------------------------------------
+
+newtype GET_FILEEX_INFO_LEVELS = GET_FILEEX_INFO_LEVELS (#type GET_FILEEX_INFO_LEVELS)
+    deriving (Eq, Ord)
+
+#{enum GET_FILEEX_INFO_LEVELS, GET_FILEEX_INFO_LEVELS
+ , getFileExInfoStandard = GetFileExInfoStandard
+ , getFileExMaxInfoLevel = GetFileExMaxInfoLevel
+ }
+
+----------------------------------------------------------------
+
+data SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES
+    { nLength              :: !DWORD
+    , lpSecurityDescriptor :: !LPVOID
+    , bInheritHandle       :: !BOOL
+    } deriving Show
+
+type PSECURITY_ATTRIBUTES = Ptr SECURITY_ATTRIBUTES
+type LPSECURITY_ATTRIBUTES = Ptr SECURITY_ATTRIBUTES
+type MbLPSECURITY_ATTRIBUTES = Maybe LPSECURITY_ATTRIBUTES
+
+instance Storable SECURITY_ATTRIBUTES where
+    sizeOf = const #{size SECURITY_ATTRIBUTES}
+    alignment _ = #alignment SECURITY_ATTRIBUTES
+    poke buf input = do
+        (#poke SECURITY_ATTRIBUTES, nLength)              buf (nLength input)
+        (#poke SECURITY_ATTRIBUTES, lpSecurityDescriptor) buf (lpSecurityDescriptor input)
+        (#poke SECURITY_ATTRIBUTES, bInheritHandle)       buf (bInheritHandle input)
+    peek buf = do
+        nLength'              <- (#peek SECURITY_ATTRIBUTES, nLength)              buf
+        lpSecurityDescriptor' <- (#peek SECURITY_ATTRIBUTES, lpSecurityDescriptor) buf
+        bInheritHandle'       <- (#peek SECURITY_ATTRIBUTES, bInheritHandle)       buf
+        return $ SECURITY_ATTRIBUTES nLength' lpSecurityDescriptor' bInheritHandle'
+
+----------------------------------------------------------------
+-- Other types
+----------------------------------------------------------------
+
+data BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION
+    { bhfiFileAttributes :: FileAttributeOrFlag
+    , bhfiCreationTime, bhfiLastAccessTime, bhfiLastWriteTime :: FILETIME
+    , bhfiVolumeSerialNumber :: DWORD
+    , bhfiSize :: DDWORD
+    , bhfiNumberOfLinks :: DWORD
+    , bhfiFileIndex :: DDWORD
+    } deriving (Show)
+
+instance Storable BY_HANDLE_FILE_INFORMATION where
+    sizeOf = const (#size BY_HANDLE_FILE_INFORMATION)
+    alignment _ = #alignment BY_HANDLE_FILE_INFORMATION
+    poke buf bhi = do
+        (#poke BY_HANDLE_FILE_INFORMATION, dwFileAttributes)     buf (bhfiFileAttributes bhi)
+        (#poke BY_HANDLE_FILE_INFORMATION, ftCreationTime)       buf (bhfiCreationTime bhi)
+        (#poke BY_HANDLE_FILE_INFORMATION, ftLastAccessTime)     buf (bhfiLastAccessTime bhi)
+        (#poke BY_HANDLE_FILE_INFORMATION, ftLastWriteTime)      buf (bhfiLastWriteTime bhi)
+        (#poke BY_HANDLE_FILE_INFORMATION, dwVolumeSerialNumber) buf (bhfiVolumeSerialNumber bhi)
+        (#poke BY_HANDLE_FILE_INFORMATION, nFileSizeHigh)        buf sizeHi
+        (#poke BY_HANDLE_FILE_INFORMATION, nFileSizeLow)         buf sizeLow
+        (#poke BY_HANDLE_FILE_INFORMATION, nNumberOfLinks)       buf (bhfiNumberOfLinks bhi)
+        (#poke BY_HANDLE_FILE_INFORMATION, nFileIndexHigh)       buf idxHi
+        (#poke BY_HANDLE_FILE_INFORMATION, nFileIndexLow)        buf idxLow
+        where
+            (sizeHi,sizeLow) = ddwordToDwords $ bhfiSize bhi
+            (idxHi,idxLow) = ddwordToDwords $ bhfiFileIndex bhi
+
+    peek buf = do
+        attr <- (#peek BY_HANDLE_FILE_INFORMATION, dwFileAttributes)     buf
+        ctim <- (#peek BY_HANDLE_FILE_INFORMATION, ftCreationTime)       buf
+        lati <- (#peek BY_HANDLE_FILE_INFORMATION, ftLastAccessTime)     buf
+        lwti <- (#peek BY_HANDLE_FILE_INFORMATION, ftLastWriteTime)      buf
+        vser <- (#peek BY_HANDLE_FILE_INFORMATION, dwVolumeSerialNumber) buf
+        fshi <- (#peek BY_HANDLE_FILE_INFORMATION, nFileSizeHigh)        buf
+        fslo <- (#peek BY_HANDLE_FILE_INFORMATION, nFileSizeLow)         buf
+        link <- (#peek BY_HANDLE_FILE_INFORMATION, nNumberOfLinks)       buf
+        idhi <- (#peek BY_HANDLE_FILE_INFORMATION, nFileIndexHigh)       buf
+        idlo <- (#peek BY_HANDLE_FILE_INFORMATION, nFileIndexLow)        buf
+        return $ BY_HANDLE_FILE_INFORMATION attr ctim lati lwti vser
+            (dwordsToDdword (fshi,fslo)) link (dwordsToDdword (idhi,idlo))
+
+----------------------------------------------------------------
+
+data WIN32_FILE_ATTRIBUTE_DATA = WIN32_FILE_ATTRIBUTE_DATA
+    { fadFileAttributes :: DWORD
+    , fadCreationTime , fadLastAccessTime , fadLastWriteTime :: FILETIME
+    , fadFileSize :: DDWORD
+    } deriving (Show)
+
+instance Storable WIN32_FILE_ATTRIBUTE_DATA where
+    sizeOf = const (#size WIN32_FILE_ATTRIBUTE_DATA)
+    alignment _ = #alignment WIN32_FILE_ATTRIBUTE_DATA
+    poke buf ad = do
+        (#poke WIN32_FILE_ATTRIBUTE_DATA, dwFileAttributes) buf (fadFileAttributes ad)
+        (#poke WIN32_FILE_ATTRIBUTE_DATA, ftCreationTime)   buf (fadCreationTime ad)
+        (#poke WIN32_FILE_ATTRIBUTE_DATA, ftLastAccessTime) buf (fadLastAccessTime ad)
+        (#poke WIN32_FILE_ATTRIBUTE_DATA, ftLastWriteTime)  buf (fadLastWriteTime ad)
+        (#poke WIN32_FILE_ATTRIBUTE_DATA, nFileSizeHigh)    buf sizeHi
+        (#poke WIN32_FILE_ATTRIBUTE_DATA, nFileSizeLow)     buf sizeLo
+        where
+            (sizeHi,sizeLo) = ddwordToDwords $ fadFileSize ad
+
+    peek buf = do
+        attr <- (#peek WIN32_FILE_ATTRIBUTE_DATA, dwFileAttributes) buf
+        ctim <- (#peek WIN32_FILE_ATTRIBUTE_DATA, ftCreationTime)   buf
+        lati <- (#peek WIN32_FILE_ATTRIBUTE_DATA, ftLastAccessTime) buf
+        lwti <- (#peek WIN32_FILE_ATTRIBUTE_DATA, ftLastWriteTime)  buf
+        fshi <- (#peek WIN32_FILE_ATTRIBUTE_DATA, nFileSizeHigh)    buf
+        fslo <- (#peek WIN32_FILE_ATTRIBUTE_DATA, nFileSizeLow)     buf
+        return $ WIN32_FILE_ATTRIBUTE_DATA attr ctim lati lwti
+            (dwordsToDdword (fshi,fslo))
+
+----------------------------------------------------------------
+-- File operations
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h DeleteFileW"
+  c_DeleteFile :: LPCTSTR -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h CopyFileW"
+  c_CopyFile :: LPCTSTR -> LPCTSTR -> Bool -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h MoveFileW"
+  c_MoveFile :: LPCTSTR -> LPCTSTR -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h MoveFileExW"
+  c_MoveFileEx :: LPCTSTR -> LPCTSTR -> MoveFileFlag -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetCurrentDirectoryW"
+  c_SetCurrentDirectory :: LPCTSTR -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateDirectoryW"
+  c_CreateDirectory :: LPCTSTR -> LPSECURITY_ATTRIBUTES -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateDirectoryExW"
+  c_CreateDirectoryEx :: LPCTSTR -> LPCTSTR -> LPSECURITY_ATTRIBUTES -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h RemoveDirectoryW"
+  c_RemoveDirectory :: LPCTSTR -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetBinaryTypeW"
+  c_GetBinaryType :: LPCTSTR -> Ptr DWORD -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h ReplaceFileW"
+  c_ReplaceFile :: LPCWSTR -> LPCWSTR -> LPCWSTR -> DWORD -> LPVOID -> LPVOID -> IO Bool
+
+----------------------------------------------------------------
+-- HANDLE operations
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateFileW"
+  c_CreateFile :: LPCTSTR -> AccessMode -> ShareMode -> LPSECURITY_ATTRIBUTES -> CreateMode -> FileAttributeOrFlag -> HANDLE -> IO HANDLE
+
+foreign import WINDOWS_CCONV unsafe "windows.h CloseHandle"
+  c_CloseHandle :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetFileType"
+  getFileType :: HANDLE -> IO FileType
+--Apparently no error code
+
+foreign import WINDOWS_CCONV unsafe "windows.h FlushFileBuffers"
+  c_FlushFileBuffers :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetEndOfFile"
+  c_SetEndOfFile :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetFileAttributesW"
+  c_SetFileAttributes :: LPCTSTR -> FileAttributeOrFlag -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetFileAttributesW"
+  c_GetFileAttributes :: LPCTSTR -> IO FileAttributeOrFlag
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetFileAttributesExW"
+  c_GetFileAttributesEx :: LPCTSTR -> GET_FILEEX_INFO_LEVELS -> Ptr a -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetFileInformationByHandle"
+    c_GetFileInformationByHandle :: HANDLE -> Ptr BY_HANDLE_FILE_INFORMATION -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetTempFileNameW"
+    c_GetTempFileNameW :: LPCWSTR -> LPCWSTR -> UINT -> LPWSTR -> IO UINT
+
+----------------------------------------------------------------
+-- Read/write files
+----------------------------------------------------------------
+
+-- No support for this yet
+data OVERLAPPED
+  = OVERLAPPED { ovl_internal     :: ULONG_PTR
+               , ovl_internalHigh :: ULONG_PTR
+               , ovl_offset       :: DWORD
+               , ovl_offsetHigh   :: DWORD
+               , ovl_hEvent       :: HANDLE
+               } deriving (Show)
+
+instance Storable OVERLAPPED where
+  sizeOf = const (#size OVERLAPPED)
+  alignment _ = #alignment OVERLAPPED
+  poke buf ad = do
+      (#poke OVERLAPPED, Internal    ) buf (ovl_internal     ad)
+      (#poke OVERLAPPED, InternalHigh) buf (ovl_internalHigh ad)
+      (#poke OVERLAPPED, Offset      ) buf (ovl_offset       ad)
+      (#poke OVERLAPPED, OffsetHigh  ) buf (ovl_offsetHigh   ad)
+      (#poke OVERLAPPED, hEvent      ) buf (ovl_hEvent       ad)
+
+  peek buf = do
+      intnl      <- (#peek OVERLAPPED, Internal    ) buf
+      intnl_high <- (#peek OVERLAPPED, InternalHigh) buf
+      off        <- (#peek OVERLAPPED, Offset      ) buf
+      off_high   <- (#peek OVERLAPPED, OffsetHigh  ) buf
+      hevnt      <- (#peek OVERLAPPED, hEvent      ) buf
+      return $ OVERLAPPED intnl intnl_high off off_high hevnt
+
+type LPOVERLAPPED = Ptr OVERLAPPED
+
+type MbLPOVERLAPPED = Maybe LPOVERLAPPED
+
+foreign import WINDOWS_CCONV unsafe "windows.h ReadFile"
+  c_ReadFile :: HANDLE -> Ptr a -> DWORD -> Ptr DWORD -> LPOVERLAPPED -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h WriteFile"
+  c_WriteFile :: HANDLE -> Ptr a -> DWORD -> Ptr DWORD -> LPOVERLAPPED -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetFilePointerEx"
+  c_SetFilePointerEx :: HANDLE -> LARGE_INTEGER -> Ptr LARGE_INTEGER -> FilePtrDirection -> IO Bool
+
+----------------------------------------------------------------
+-- File Notifications
+--
+-- Use these to initialise, "increment" and close a HANDLE you can wait
+-- on.
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindFirstChangeNotificationW"
+  c_FindFirstChangeNotification :: LPCTSTR -> Bool -> FileNotificationFlag -> IO HANDLE
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindNextChangeNotification"
+  c_FindNextChangeNotification :: HANDLE -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindCloseChangeNotification"
+  c_FindCloseChangeNotification :: HANDLE -> IO Bool
+
+----------------------------------------------------------------
+-- Directories
+----------------------------------------------------------------
+
+type WIN32_FIND_DATA = ()
+
+newtype FindData = FindData (ForeignPtr WIN32_FIND_DATA)
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindFirstFileW"
+  c_FindFirstFile :: LPCTSTR -> Ptr WIN32_FIND_DATA -> IO HANDLE
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindNextFileW"
+  c_FindNextFile :: HANDLE -> Ptr WIN32_FIND_DATA -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "windows.h FindClose"
+  c_FindClose :: HANDLE -> IO BOOL
+
+----------------------------------------------------------------
+-- DOS Device flags
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h DefineDosDeviceW"
+  c_DefineDosDevice :: DefineDosDeviceFlags -> LPCTSTR -> LPCTSTR -> IO Bool
+
+----------------------------------------------------------------
+
+-- These functions are very unusual in the Win32 API:
+-- They don't return error codes
+
+foreign import WINDOWS_CCONV unsafe "windows.h AreFileApisANSI"
+  areFileApisANSI :: IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetFileApisToOEM"
+  setFileApisToOEM :: IO ()
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetFileApisToANSI"
+  setFileApisToANSI :: IO ()
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetHandleCount"
+  setHandleCount :: UINT -> IO UINT
+
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetLogicalDrives"
+  c_GetLogicalDrives :: IO DWORD
+
+-- %fun GetDriveType :: Maybe String -> IO DriveType
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetDiskFreeSpaceW"
+  c_GetDiskFreeSpace :: LPCTSTR -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetVolumeLabelW"
+  c_SetVolumeLabel :: LPCTSTR -> LPCTSTR -> IO Bool
+
+----------------------------------------------------------------
+-- File locks
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "LockFileEx"
+  c_LockFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> DWORD -> LPOVERLAPPED
+               -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "UnlockFileEx"
+  c_UnlockFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> LPOVERLAPPED -> IO BOOL
+
+----------------------------------------------------------------
+-- End
+----------------------------------------------------------------
diff --git a/System/Win32/FileMapping.hsc b/System/Win32/FileMapping.hsc
--- a/System/Win32/FileMapping.hsc
+++ b/System/Win32/FileMapping.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -14,9 +16,37 @@
 -- A collection of FFI declarations for interfacing with Win32 mapped files.
 --
 -----------------------------------------------------------------------------
-module System.Win32.FileMapping where
+module System.Win32.FileMapping
+    ( mapFile
+    , MappedObject(..)
+    , withMappedFile
+    , withMappedArea
+      -- * Enums
+      -- ** Section protection flags
+    , ProtectSectionFlags
+    , sEC_COMMIT
+    , sEC_IMAGE
+    , sEC_NOCACHE
+    , sEC_RESERVE
+      -- ** Access falgs
+    , FileMapAccess
+    , fILE_MAP_ALL_ACCESS
+    , fILE_MAP_COPY
+    , fILE_MAP_READ
+    , fILE_MAP_WRITE
+    , fILE_SHARE_WRITE
 
-import System.Win32.Types   ( HANDLE, DWORD, BOOL, SIZE_T, LPCTSTR, withTString
+      -- * Mapping files
+    , createFileMapping
+    , openFileMapping
+    , mapViewOfFileEx
+    , mapViewOfFile
+    , unmapViewOfFile
+    ) where
+
+
+import System.Win32.FileMapping.Internal
+import System.Win32.Types   ( HANDLE, BOOL, SIZE_T, withTString
                             , failIf, failIfNull, DDWORD, ddwordToDwords
                             , iNVALID_HANDLE_VALUE )
 import System.Win32.Mem
@@ -24,11 +54,11 @@
 import System.Win32.Info
 
 import Control.Exception        ( mask_, bracket )
-import Data.ByteString          ( ByteString )
-import Data.ByteString.Internal ( fromForeignPtr )
-import Foreign                  ( Ptr, nullPtr, plusPtr, maybeWith, FunPtr
+import Foreign                  ( Ptr, nullPtr, plusPtr, maybeWith
                                 , ForeignPtr, newForeignPtr )
 
+##include "windows_cconv.h"
+
 #include "windows.h"
 
 ---------------------------------------------------------------------------
@@ -52,14 +82,6 @@
                     newForeignPtr c_UnmapViewOfFileFinaliser ptr
                 return (fp, fromIntegral $ bhfiSize fi)
 
--- | As mapFile, but returns ByteString
-mapFileBs :: FilePath -> IO ByteString
-mapFileBs p = do
-    (fp,i) <- mapFile p
-    return $ fromForeignPtr fp 0 i
-
-data MappedObject = MappedObject HANDLE HANDLE FileMapAccess
-
 -- | Opens an existing file and creates mapping object to it.
 withMappedFile
     :: FilePath             -- ^ Path
@@ -105,42 +127,24 @@
         (act . flip plusPtr (fromIntegral offset))
 
 ---------------------------------------------------------------------------
--- Enums
----------------------------------------------------------------------------
-type ProtectSectionFlags = DWORD
-#{enum ProtectSectionFlags,
-    , sEC_COMMIT    = SEC_COMMIT
-    , sEC_IMAGE     = SEC_IMAGE
-    , sEC_NOCACHE   = SEC_NOCACHE
-    , sEC_RESERVE   = SEC_RESERVE
-    }
-type FileMapAccess = DWORD
-#{enum FileMapAccess,
-    , fILE_MAP_ALL_ACCESS   = FILE_MAP_ALL_ACCESS
-    , fILE_MAP_COPY         = FILE_MAP_COPY
-    , fILE_MAP_READ         = FILE_MAP_READ
-    , fILE_MAP_WRITE        = FILE_MAP_WRITE
-    }
-
----------------------------------------------------------------------------
 -- API in Haskell
 ---------------------------------------------------------------------------
 createFileMapping :: Maybe HANDLE -> ProtectFlags -> DDWORD -> Maybe String -> IO HANDLE
 createFileMapping mh flags mosize name =
-    maybeWith withTString name $ \name ->
-        failIf (==nullPtr) "createFileMapping: CreateFileMapping" $ c_CreateFileMapping handle nullPtr flags moshi moslow name
+    maybeWith withTString name $ \c_name ->
+        failIf (==nullPtr) "createFileMapping: CreateFileMapping" $ c_CreateFileMapping handle nullPtr flags moshi moslow c_name
     where
         (moshi,moslow) = ddwordToDwords mosize
         handle = maybe iNVALID_HANDLE_VALUE id mh
 
 openFileMapping :: FileMapAccess -> BOOL -> Maybe String -> IO HANDLE
 openFileMapping access inherit name =
-    maybeWith withTString name $ \name ->
+    maybeWith withTString name $ \c_name ->
         failIf (==nullPtr) "openFileMapping: OpenFileMapping" $
-            c_OpenFileMapping access inherit name
+            c_OpenFileMapping access inherit c_name
 
 mapViewOfFileEx :: HANDLE -> FileMapAccess -> DDWORD -> SIZE_T -> Ptr a -> IO (Ptr b)
-mapViewOfFileEx h access offset size base = 
+mapViewOfFileEx h access offset size base =
     failIfNull "mapViewOfFile(Ex): c_MapViewOfFileEx" $
         c_MapViewOfFileEx h access ohi olow size base
     where
@@ -152,21 +156,3 @@
 unmapViewOfFile :: Ptr a -> IO ()
 unmapViewOfFile v = c_UnmapViewOfFile v >> return ()
 
----------------------------------------------------------------------------
--- Imports
----------------------------------------------------------------------------
-foreign import stdcall "windows.h OpenFileMappingW"
-    c_OpenFileMapping :: DWORD -> BOOL -> LPCTSTR -> IO HANDLE
-
-foreign import stdcall "windows.h CreateFileMappingW"
-    c_CreateFileMapping :: HANDLE -> Ptr () -> DWORD -> DWORD -> DWORD -> LPCTSTR -> IO HANDLE 
-
-foreign import stdcall "windows.h MapViewOfFileEx"
-    c_MapViewOfFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> SIZE_T -> Ptr a -> IO (Ptr b)
-
-foreign import stdcall "windows.h UnmapViewOfFile"
-    c_UnmapViewOfFile :: Ptr a -> IO BOOL
-
-{-# CFILES cbits/HsWin32.c #-}
-foreign import ccall "HsWin32.h &UnmapViewOfFileFinaliser"
-    c_UnmapViewOfFileFinaliser :: FunPtr (Ptr a -> IO ())
diff --git a/System/Win32/FileMapping/Internal.hsc b/System/Win32/FileMapping/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/FileMapping/Internal.hsc
@@ -0,0 +1,72 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.FileMapping.Internal
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32 mapped files.
+--
+-----------------------------------------------------------------------------
+module System.Win32.FileMapping.Internal where
+
+import System.Win32.Types   ( HANDLE, DWORD, BOOL, SIZE_T, LPCTSTR )
+
+import Foreign                  ( Ptr, FunPtr )
+import Foreign.C.Types (CUIntPtr(..))
+
+##include "windows_cconv.h"
+
+#include "windows.h"
+
+---------------------------------------------------------------------------
+-- Derived functions
+---------------------------------------------------------------------------
+
+data MappedObject = MappedObject HANDLE HANDLE FileMapAccess
+
+
+---------------------------------------------------------------------------
+-- Enums
+---------------------------------------------------------------------------
+type ProtectSectionFlags = DWORD
+#{enum ProtectSectionFlags,
+    , sEC_COMMIT    = SEC_COMMIT
+    , sEC_IMAGE     = SEC_IMAGE
+    , sEC_NOCACHE   = SEC_NOCACHE
+    , sEC_RESERVE   = SEC_RESERVE
+    }
+type FileMapAccess = DWORD
+#{enum FileMapAccess,
+    , fILE_MAP_ALL_ACCESS   = FILE_MAP_ALL_ACCESS
+    , fILE_MAP_COPY         = FILE_MAP_COPY
+    , fILE_MAP_READ         = FILE_MAP_READ
+    , fILE_MAP_WRITE        = FILE_MAP_WRITE
+    }
+
+---------------------------------------------------------------------------
+-- Imports
+---------------------------------------------------------------------------
+foreign import WINDOWS_CCONV "windows.h OpenFileMappingW"
+    c_OpenFileMapping :: DWORD -> BOOL -> LPCTSTR -> IO HANDLE
+
+foreign import WINDOWS_CCONV "windows.h CreateFileMappingW"
+    c_CreateFileMapping :: HANDLE -> Ptr () -> DWORD -> DWORD -> DWORD -> LPCTSTR -> IO HANDLE
+
+foreign import WINDOWS_CCONV "windows.h MapViewOfFileEx"
+    c_MapViewOfFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> SIZE_T -> Ptr a -> IO (Ptr b)
+
+foreign import WINDOWS_CCONV "windows.h UnmapViewOfFile"
+    c_UnmapViewOfFile :: Ptr a -> IO BOOL
+
+{-# CFILES cbits/HsWin32.c #-}
+foreign import ccall "HsWin32.h &UnmapViewOfFileFinaliser"
+    c_UnmapViewOfFileFinaliser :: FunPtr (Ptr a -> IO ())
diff --git a/System/Win32/HardLink.hs b/System/Win32/HardLink.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/HardLink.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.HardLink
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Handling hard link using Win32 API. [NTFS only]
+
+   Note: You should worry about file system type when use this module's function in your application:
+
+     * NTFS only supports this functionality.
+
+     * ReFS doesn't support hard link currently.
+-}
+module System.Win32.HardLink
+  ( createHardLink
+  , createHardLink'
+  ) where
+
+import System.Win32.HardLink.Internal
+import System.Win32.File   ( failIfFalseWithRetry_ )
+import System.Win32.String ( withTString )
+import System.Win32.Types  ( nullPtr )
+
+#include "windows_cconv.h"
+
+-- | NOTE: createHardLink is /flipped arguments/ to provide compatibility for Unix.
+-- 
+-- If you want to create hard link by Windows way, use 'createHardLink'' instead.
+createHardLink :: FilePath -- ^ Target file path
+               -> FilePath -- ^ Hard link name
+               -> IO ()
+createHardLink = flip createHardLink'
+
+createHardLink' :: FilePath -- ^ Hard link name
+                -> FilePath -- ^ Target file path
+                -> IO ()
+createHardLink' link target =
+   withTString target $ \c_target ->
+   withTString link   $ \c_link ->
+        failIfFalseWithRetry_ (unwords ["CreateHardLinkW",show link,show target]) $
+          c_CreateHardLink c_link c_target nullPtr
+
+
+{-
+-- We plan to check file system type internally.
+
+-- We are thinking about API design, currently...
+data VolumeInformation = VolumeInformation
+      { volumeName         :: String
+      , volumeSerialNumber :: DWORD
+      , maximumComponentLength :: DWORD
+      , fileSystemFlags    :: DWORD
+      , fileSystemName     :: String
+      } deriving Show
+
+getVolumeInformation :: Maybe String -> IO VolumeInformation
+getVolumeInformation drive =
+   maybeWith withTString drive $ \c_drive ->
+   withTStringBufferLen 256    $ \(vnBuf, vnLen) ->
+   alloca $ \serialNum ->
+   alloca $ \maxLen ->
+   alloca $ \fsFlags ->
+   withTStringBufferLen 256 $ \(fsBuf, fsLen) -> do
+       failIfFalse_ (unwords ["GetVolumeInformationW", drive]) $
+         c_GetVolumeInformation c_drive vnBuf (fromIntegral vnLen)
+                                serialNum maxLen fsFlags
+                                fsBuf (fromIntegral fsLen)
+       return VolumeInformation
+         <*> peekTString vnBuf
+         <*> peek serialNum
+         <*> peek maxLen
+         <*> peek fsFlags
+         <*> peekTString fsBuf
+
+-- Which is better?
+getVolumeFileType :: String -> IO String
+getVolumeFileType drive = fileSystemName <$> getVolumeInformation drive
+
+getVolumeFileType :: String -> IO String
+getVolumeFileType drive =
+   withTString drive        $ \c_drive ->
+   withTStringBufferLen 256 $ \(buf, len) -> do
+       failIfFalse_ (unwords ["GetVolumeInformationW", drive]) $
+         c_GetVolumeInformation c_drive nullPtr 0 nullPtr nullPtr nullPtr buf (fromIntegral len)
+       peekTString buf
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetVolumeInformationW"
+  c_GetVolumeInformation :: LPCTSTR -> LPTSTR -> DWORD -> LPDWORD -> LPDWORD -> LPDWORD -> LPTSTR -> DWORD -> IO BOOL
+-}
diff --git a/System/Win32/HardLink/Internal.hs b/System/Win32/HardLink/Internal.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/HardLink/Internal.hs
@@ -0,0 +1,31 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.HardLink.Internal
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Handling hard link using Win32 API. [NTFS only]
+
+   Note: You should worry about file system type when use this module's function in your application:
+
+     * NTFS only supprts this functionality.
+
+     * ReFS doesn't support hard link currently.
+-}
+module System.Win32.HardLink.Internal where
+
+import System.Win32.File   ( LPSECURITY_ATTRIBUTES )
+import System.Win32.String ( LPCTSTR )
+import System.Win32.Types  ( BOOL )
+
+#include "windows_cconv.h"
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateHardLinkW"
+  c_CreateHardLink :: LPCTSTR -- ^ Hard link name
+                   -> LPCTSTR -- ^ Target file path
+                   -> LPSECURITY_ATTRIBUTES -- ^ This parameter is reserved. You should pass just /nullPtr/.
+                   -> IO BOOL
diff --git a/System/Win32/Info.hsc b/System/Win32/Info.hsc
--- a/System/Win32/Info.hsc
+++ b/System/Win32/Info.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -15,18 +17,136 @@
 --
 -----------------------------------------------------------------------------
 
-module System.Win32.Info where
+module System.Win32.Info
+    ( SystemColor
+    , cOLOR_SCROLLBAR
+    , cOLOR_BACKGROUND
+    , cOLOR_ACTIVECAPTION
+    , cOLOR_INACTIVECAPTION
+    , cOLOR_MENU
+    , cOLOR_WINDOW
+    , cOLOR_WINDOWFRAME
+    , cOLOR_MENUTEXT
+    , cOLOR_WINDOWTEXT
+    , cOLOR_CAPTIONTEXT
+    , cOLOR_ACTIVEBORDER
+    , cOLOR_INACTIVEBORDER
+    , cOLOR_APPWORKSPACE
+    , cOLOR_HIGHLIGHT
+    , cOLOR_HIGHLIGHTTEXT
+    , cOLOR_BTNFACE
+    , cOLOR_BTNSHADOW
+    , cOLOR_GRAYTEXT
+    , cOLOR_BTNTEXT
+    , cOLOR_INACTIVECAPTIONTEXT
+    , cOLOR_BTNHIGHLIGHT
 
-import System.Win32.Types
+      -- * Standard directories
+    , getSystemDirectory
+    , getWindowsDirectory
+    , getCurrentDirectory
+    , getTemporaryDirectory
+    , getFullPathName
+    , getLongPathName
+    , getShortPathName
+    , searchPath
 
-import Prelude hiding (catch)
+      -- * System information
+    , ProcessorArchitecture(..)
+    , SYSTEM_INFO(..)
+    , getSystemInfo
+
+      -- * System metrics
+    , SMSetting
+    , sM_ARRANGE
+    , sM_CLEANBOOT
+    , sM_CMETRICS
+    , sM_CMOUSEBUTTONS
+    , sM_CXBORDER
+    , sM_CYBORDER
+    , sM_CXCURSOR
+    , sM_CYCURSOR
+    , sM_CXDLGFRAME
+    , sM_CYDLGFRAME
+    , sM_CXDOUBLECLK
+    , sM_CYDOUBLECLK
+    , sM_CXDRAG
+    , sM_CYDRAG
+    , sM_CXEDGE
+    , sM_CYEDGE
+    , sM_CXFRAME
+    , sM_CYFRAME
+    , sM_CXFULLSCREEN
+    , sM_CYFULLSCREEN
+    , sM_CXHSCROLL
+    , sM_CYVSCROLL
+    , sM_CXICON
+    , sM_CYICON
+    , sM_CXICONSPACING
+    , sM_CYICONSPACING
+    , sM_CXMAXIMIZED
+    , sM_CYMAXIMIZED
+    , sM_CXMENUCHECK
+    , sM_CYMENUCHECK
+    , sM_CXMENUSIZE
+    , sM_CYMENUSIZE
+    , sM_CXMIN
+    , sM_CYMIN
+    , sM_CXMINIMIZED
+    , sM_CYMINIMIZED
+    , sM_CXMINTRACK
+    , sM_CYMINTRACK
+    , sM_CXSCREEN
+    , sM_CYSCREEN
+    , sM_CXSIZE
+    , sM_CYSIZE
+    , sM_CXSIZEFRAME
+    , sM_CYSIZEFRAME
+    , sM_CXSMICON
+    , sM_CYSMICON
+    , sM_CXSMSIZE
+    , sM_CYSMSIZE
+    , sM_CXVSCROLL
+    , sM_CYHSCROLL
+    , sM_CYVTHUMB
+    , sM_CYCAPTION
+    , sM_CYKANJIWINDOW
+    , sM_CYMENU
+    , sM_CYSMCAPTION
+    , sM_DBCSENABLED
+    , sM_DEBUG
+    , sM_MENUDROPALIGNMENT
+    , sM_MIDEASTENABLED
+    , sM_MOUSEPRESENT
+    , sM_NETWORK
+    , sM_PENWINDOWS
+    , sM_SECURE
+    , sM_SHOWSOUNDS
+    , sM_SLOWMACHINE
+    , sM_SWAPBUTTON
+
+      -- * User name
+    , getUserName
+    ) where
+
+import System.Win32.Info.Internal
 import Control.Exception (catch)
-import System.IO.Error hiding (catch, try)
-import Foreign      ( Storable(sizeOf, alignment, peekByteOff, pokeByteOff,
-                               peek, poke)
-                    , Ptr, alloca, allocaArray )
+import Foreign.Marshal.Alloc (alloca)
+import Foreign.Marshal.Utils (with, maybeWith)
+import Foreign.Marshal.Array (allocaArray)
+import Foreign.Ptr (nullPtr)
+import Foreign.Storable (Storable(..))
+import System.IO.Error (isDoesNotExistError)
+import System.Win32.Types (failIfFalse_, peekTStringLen, withTString, try)
 
+#if !MIN_VERSION_base(4,6,0)
+import Prelude hiding (catch)
+#endif
+
+##include "windows_cconv.h"
+
 #include <windows.h>
+#include "alignment.h"
 
 ----------------------------------------------------------------
 -- Environment Strings
@@ -55,41 +175,6 @@
 -- %fun GetKeyboardType :: KeyboardTypeKind -> IO KeyboardType
 
 ----------------------------------------------------------------
--- System Color
-----------------------------------------------------------------
-
-type SystemColor   = UINT
-
--- ToDo: This list is out of date.
-
-#{enum SystemColor,
- , cOLOR_SCROLLBAR      = COLOR_SCROLLBAR
- , cOLOR_BACKGROUND     = COLOR_BACKGROUND
- , cOLOR_ACTIVECAPTION  = COLOR_ACTIVECAPTION
- , cOLOR_INACTIVECAPTION = COLOR_INACTIVECAPTION
- , cOLOR_MENU           = COLOR_MENU
- , cOLOR_WINDOW         = COLOR_WINDOW
- , cOLOR_WINDOWFRAME    = COLOR_WINDOWFRAME
- , cOLOR_MENUTEXT       = COLOR_MENUTEXT
- , cOLOR_WINDOWTEXT     = COLOR_WINDOWTEXT
- , cOLOR_CAPTIONTEXT    = COLOR_CAPTIONTEXT
- , cOLOR_ACTIVEBORDER   = COLOR_ACTIVEBORDER
- , cOLOR_INACTIVEBORDER = COLOR_INACTIVEBORDER
- , cOLOR_APPWORKSPACE   = COLOR_APPWORKSPACE
- , cOLOR_HIGHLIGHT      = COLOR_HIGHLIGHT
- , cOLOR_HIGHLIGHTTEXT  = COLOR_HIGHLIGHTTEXT
- , cOLOR_BTNFACE        = COLOR_BTNFACE
- , cOLOR_BTNSHADOW      = COLOR_BTNSHADOW
- , cOLOR_GRAYTEXT       = COLOR_GRAYTEXT
- , cOLOR_BTNTEXT        = COLOR_BTNTEXT
- , cOLOR_INACTIVECAPTIONTEXT = COLOR_INACTIVECAPTIONTEXT
- , cOLOR_BTNHIGHLIGHT   = COLOR_BTNHIGHLIGHT
- }
-
--- %fun GetSysColor :: SystemColor -> IO COLORREF
--- %fun SetSysColors :: [(SystemColor,COLORREF)] -> IO ()
-
-----------------------------------------------------------------
 -- Standard Directories
 ----------------------------------------------------------------
 
@@ -101,6 +186,7 @@
 
 getCurrentDirectory :: IO String
 getCurrentDirectory = try "GetCurrentDirectory" (flip c_getCurrentDirectory) 512
+
 getTemporaryDirectory :: IO String
 getTemporaryDirectory = try "GetTempPath" (flip c_getTempPath) 512
 
@@ -110,11 +196,23 @@
     try "getFullPathName"
       (\buf len -> c_GetFullPathName c_name len buf nullPtr) 512
 
-searchPath :: Maybe String -> FilePath -> String -> IO (Maybe FilePath)
+getLongPathName :: FilePath -> IO FilePath
+getLongPathName name = do
+  withTString name $ \ c_name ->
+    try "getLongPathName"
+      (c_GetLongPathName c_name) 512
+
+getShortPathName :: FilePath -> IO FilePath
+getShortPathName name = do
+  withTString name $ \ c_name ->
+    try "getShortPathName"
+      (c_GetShortPathName c_name) 512
+
+searchPath :: Maybe String -> FilePath -> Maybe String -> IO (Maybe FilePath)
 searchPath path filename ext =
   maybe ($ nullPtr) withTString path $ \p_path ->
   withTString filename $ \p_filename ->
-  withTString ext      $ \p_ext ->
+  maybeWith withTString ext      $ \p_ext ->
   alloca $ \ppFilePart -> (do
     s <- try "searchPath" (\buf len -> c_SearchPath p_path p_filename p_ext
                           len buf ppFilePart) 512
@@ -123,212 +221,15 @@
                        then return Nothing
                        else ioError e
 
--- Support for API calls that are passed a fixed-size buffer and tell
--- you via the return value if the buffer was too small.  In that
--- case, we double the buffer size and try again.
-try :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO String
-try loc f n = do
-   e <- allocaArray (fromIntegral n) $ \lptstr -> do
-	  r <- failIfZero loc $ f lptstr n
-	  if (r > n) then return (Left r) else do
-	    str <- peekTStringLen (lptstr, fromIntegral r)
-	    return (Right str)
-   case e of
-	Left n    -> try loc f n   
-	Right str -> return str
-
-foreign import stdcall unsafe "GetWindowsDirectoryW"
-  c_getWindowsDirectory :: LPTSTR -> UINT -> IO UINT
-
-foreign import stdcall unsafe "GetSystemDirectoryW"
-  c_getSystemDirectory :: LPTSTR -> UINT -> IO UINT
-
-foreign import stdcall unsafe "GetCurrentDirectoryW"
-  c_getCurrentDirectory :: DWORD -> LPTSTR -> IO UINT
-
-foreign import stdcall unsafe "GetTempPathW"
-  c_getTempPath :: DWORD -> LPTSTR -> IO UINT
-
-foreign import stdcall unsafe "GetFullPathNameW"
-  c_GetFullPathName :: LPCTSTR -> DWORD -> LPTSTR -> Ptr LPTSTR -> IO DWORD
-
-foreign import stdcall unsafe "SearchPathW"
-  c_SearchPath :: LPCTSTR -> LPCTSTR -> LPCTSTR -> DWORD -> LPTSTR -> Ptr LPTSTR
-               -> IO DWORD
-
 ----------------------------------------------------------------
 -- System Info (Info about processor and memory subsystem)
 ----------------------------------------------------------------
 
-data ProcessorArchitecture = PaUnknown WORD | PaIntel | PaMips | PaAlpha | PaPpc | PaIa64 | PaIa32OnIa64 | PaAmd64
-    deriving (Show,Eq)
-
-instance Storable ProcessorArchitecture where
-    sizeOf _ = sizeOf (undefined::WORD)
-    alignment _ = alignment (undefined::WORD)
-    poke buf pa = pokeByteOff buf 0 $ case pa of
-        PaUnknown w -> w
-        PaIntel     -> #const PROCESSOR_ARCHITECTURE_INTEL
-        PaMips      -> #const PROCESSOR_ARCHITECTURE_MIPS
-        PaAlpha     -> #const PROCESSOR_ARCHITECTURE_ALPHA
-        PaPpc       -> #const PROCESSOR_ARCHITECTURE_PPC
-        PaIa64      -> #const PROCESSOR_ARCHITECTURE_IA64
-#ifndef __WINE_WINDOWS_H
-        PaIa32OnIa64 -> #const PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
-#endif
-        PaAmd64     -> #const PROCESSOR_ARCHITECTURE_AMD64
-    peek buf = do
-        v <- (peekByteOff buf 0:: IO WORD)
-        return $ case v of
-            (#const PROCESSOR_ARCHITECTURE_INTEL) -> PaIntel
-            (#const PROCESSOR_ARCHITECTURE_MIPS)  -> PaMips
-            (#const PROCESSOR_ARCHITECTURE_ALPHA) -> PaAlpha
-            (#const PROCESSOR_ARCHITECTURE_PPC)   -> PaPpc
-            (#const PROCESSOR_ARCHITECTURE_IA64)  -> PaIa64
-#ifndef __WINE_WINDOWS_H
-            (#const PROCESSOR_ARCHITECTURE_IA32_ON_WIN64) -> PaIa32OnIa64
-#endif
-            (#const PROCESSOR_ARCHITECTURE_AMD64) -> PaAmd64
-            w                                   -> PaUnknown w
-
-data SYSTEM_INFO = SYSTEM_INFO
-    { siProcessorArchitecture :: ProcessorArchitecture
-    , siPageSize :: DWORD
-    , siMinimumApplicationAddress, siMaximumApplicationAddress :: LPVOID
-    , siActiveProcessorMask :: DWORD
-    , siNumberOfProcessors :: DWORD
-    , siProcessorType :: DWORD
-    , siAllocationGranularity :: DWORD
-    , siProcessorLevel :: WORD
-    , siProcessorRevision :: WORD
-    } deriving (Show)
-
-instance Storable SYSTEM_INFO where
-    sizeOf = const #size SYSTEM_INFO
-    alignment = sizeOf
-    poke buf si = do
-        (#poke SYSTEM_INFO, wProcessorArchitecture) buf (siProcessorArchitecture si)
-        (#poke SYSTEM_INFO, dwPageSize)             buf (siPageSize si)
-        (#poke SYSTEM_INFO, lpMinimumApplicationAddress) buf (siMinimumApplicationAddress si)
-        (#poke SYSTEM_INFO, lpMaximumApplicationAddress) buf (siMaximumApplicationAddress si)
-        (#poke SYSTEM_INFO, dwActiveProcessorMask)  buf (siActiveProcessorMask si)
-        (#poke SYSTEM_INFO, dwNumberOfProcessors)   buf (siNumberOfProcessors si)
-        (#poke SYSTEM_INFO, dwProcessorType)        buf (siProcessorType si)
-        (#poke SYSTEM_INFO, dwAllocationGranularity) buf (siAllocationGranularity si)
-        (#poke SYSTEM_INFO, wProcessorLevel)        buf (siProcessorLevel si)
-        (#poke SYSTEM_INFO, wProcessorRevision)     buf (siProcessorRevision si)
-
-    peek buf = do
-        processorArchitecture <-
-            (#peek SYSTEM_INFO, wProcessorArchitecture) buf
-        pageSize            <- (#peek SYSTEM_INFO, dwPageSize) buf
-        minimumApplicationAddress <-
-            (#peek SYSTEM_INFO, lpMinimumApplicationAddress) buf
-        maximumApplicationAddress <-
-            (#peek SYSTEM_INFO, lpMaximumApplicationAddress) buf
-        activeProcessorMask <- (#peek SYSTEM_INFO, dwActiveProcessorMask) buf
-        numberOfProcessors  <- (#peek SYSTEM_INFO, dwNumberOfProcessors) buf
-        processorType       <- (#peek SYSTEM_INFO, dwProcessorType) buf
-        allocationGranularity <-
-            (#peek SYSTEM_INFO, dwAllocationGranularity) buf
-        processorLevel      <- (#peek SYSTEM_INFO, wProcessorLevel) buf
-        processorRevision   <- (#peek SYSTEM_INFO, wProcessorRevision) buf
-        return $ SYSTEM_INFO {
-            siProcessorArchitecture     = processorArchitecture,
-            siPageSize                  = pageSize,
-            siMinimumApplicationAddress = minimumApplicationAddress,
-            siMaximumApplicationAddress = maximumApplicationAddress,
-            siActiveProcessorMask       = activeProcessorMask,
-            siNumberOfProcessors        = numberOfProcessors,
-            siProcessorType             = processorType,
-            siAllocationGranularity     = allocationGranularity,
-            siProcessorLevel            = processorLevel,
-            siProcessorRevision         = processorRevision
-            }
-
-foreign import stdcall unsafe "windows.h GetSystemInfo"
-    c_GetSystemInfo :: Ptr SYSTEM_INFO -> IO ()
-
 getSystemInfo :: IO SYSTEM_INFO
 getSystemInfo = alloca $ \ret -> do
     c_GetSystemInfo ret
     peek ret
 
-----------------------------------------------------------------
--- System metrics
-----------------------------------------------------------------
-
-type SMSetting = UINT
-
-#{enum SMSetting,
- , sM_ARRANGE           = SM_ARRANGE
- , sM_CLEANBOOT         = SM_CLEANBOOT
- , sM_CMETRICS          = SM_CMETRICS
- , sM_CMOUSEBUTTONS     = SM_CMOUSEBUTTONS
- , sM_CXBORDER          = SM_CXBORDER
- , sM_CYBORDER          = SM_CYBORDER
- , sM_CXCURSOR          = SM_CXCURSOR
- , sM_CYCURSOR          = SM_CYCURSOR
- , sM_CXDLGFRAME        = SM_CXDLGFRAME
- , sM_CYDLGFRAME        = SM_CYDLGFRAME
- , sM_CXDOUBLECLK       = SM_CXDOUBLECLK
- , sM_CYDOUBLECLK       = SM_CYDOUBLECLK
- , sM_CXDRAG            = SM_CXDRAG
- , sM_CYDRAG            = SM_CYDRAG
- , sM_CXEDGE            = SM_CXEDGE
- , sM_CYEDGE            = SM_CYEDGE
- , sM_CXFRAME           = SM_CXFRAME
- , sM_CYFRAME           = SM_CYFRAME
- , sM_CXFULLSCREEN      = SM_CXFULLSCREEN
- , sM_CYFULLSCREEN      = SM_CYFULLSCREEN
- , sM_CXHSCROLL         = SM_CXHSCROLL
- , sM_CYVSCROLL         = SM_CYVSCROLL
- , sM_CXICON            = SM_CXICON
- , sM_CYICON            = SM_CYICON
- , sM_CXICONSPACING     = SM_CXICONSPACING
- , sM_CYICONSPACING     = SM_CYICONSPACING
- , sM_CXMAXIMIZED       = SM_CXMAXIMIZED
- , sM_CYMAXIMIZED       = SM_CYMAXIMIZED
- , sM_CXMENUCHECK       = SM_CXMENUCHECK
- , sM_CYMENUCHECK       = SM_CYMENUCHECK
- , sM_CXMENUSIZE        = SM_CXMENUSIZE
- , sM_CYMENUSIZE        = SM_CYMENUSIZE
- , sM_CXMIN             = SM_CXMIN
- , sM_CYMIN             = SM_CYMIN
- , sM_CXMINIMIZED       = SM_CXMINIMIZED
- , sM_CYMINIMIZED       = SM_CYMINIMIZED
- , sM_CXMINTRACK        = SM_CXMINTRACK
- , sM_CYMINTRACK        = SM_CYMINTRACK
- , sM_CXSCREEN          = SM_CXSCREEN
- , sM_CYSCREEN          = SM_CYSCREEN
- , sM_CXSIZE            = SM_CXSIZE
- , sM_CYSIZE            = SM_CYSIZE
- , sM_CXSIZEFRAME       = SM_CXSIZEFRAME
- , sM_CYSIZEFRAME       = SM_CYSIZEFRAME
- , sM_CXSMICON          = SM_CXSMICON
- , sM_CYSMICON          = SM_CYSMICON
- , sM_CXSMSIZE          = SM_CXSMSIZE
- , sM_CYSMSIZE          = SM_CYSMSIZE
- , sM_CXVSCROLL         = SM_CXVSCROLL
- , sM_CYHSCROLL         = SM_CYHSCROLL
- , sM_CYVTHUMB          = SM_CYVTHUMB
- , sM_CYCAPTION         = SM_CYCAPTION
- , sM_CYKANJIWINDOW     = SM_CYKANJIWINDOW
- , sM_CYMENU            = SM_CYMENU
- , sM_CYSMCAPTION       = SM_CYSMCAPTION
- , sM_DBCSENABLED       = SM_DBCSENABLED
- , sM_DEBUG             = SM_DEBUG
- , sM_MENUDROPALIGNMENT = SM_MENUDROPALIGNMENT
- , sM_MIDEASTENABLED    = SM_MIDEASTENABLED
- , sM_MOUSEPRESENT      = SM_MOUSEPRESENT
- , sM_NETWORK           = SM_NETWORK
- , sM_PENWINDOWS        = SM_PENWINDOWS
- , sM_SECURE            = SM_SECURE
- , sM_SHOWSOUNDS        = SM_SHOWSOUNDS
- , sM_SLOWMACHINE       = SM_SLOWMACHINE
- , sM_SWAPBUTTON        = SM_SWAPBUTTON
- }
-
 -- %fun GetSystemMetrics :: SMSetting -> IO Int
 
 ----------------------------------------------------------------
@@ -344,44 +245,13 @@
 
 -- %fun GetUserName :: IO String
 
-----------------------------------------------------------------
--- Version Info
-----------------------------------------------------------------
-
--- %fun GetVersionEx :: IO VersionInfo
---
--- typedef struct _OSVERSIONINFO{
---     DWORD dwOSVersionInfoSize;
---     DWORD dwMajorVersion;
---     DWORD dwMinorVersion;
---     DWORD dwBuildNumber;
---     DWORD dwPlatformId;
---     TCHAR szCSDVersion[ 128 ];
--- } OSVERSIONINFO;
-
-----------------------------------------------------------------
--- Processor features
-----------------------------------------------------------------
-
---
--- Including these lines causes problems on Win95
--- %fun IsProcessorFeaturePresent :: ProcessorFeature -> Bool
---
--- type ProcessorFeature   = DWORD
--- %dis processorFeature x = dWORD x
---
--- %const ProcessorFeature
--- % [ PF_FLOATING_POINT_PRECISION_ERRATA
--- % , PF_FLOATING_POINT_EMULATED
--- % , PF_COMPARE_EXCHANGE_DOUBLE
--- % , PF_MMX_INSTRUCTIONS_AVAILABLE
--- % ]
-
-----------------------------------------------------------------
--- System Parameter Information
-----------------------------------------------------------------
-
--- %fun SystemParametersInfo :: ?? -> Bool -> IO ??
+getUserName :: IO String
+getUserName =
+  allocaArray 512 $ \ c_str ->
+    with 512 $ \ c_len -> do
+        failIfFalse_ "GetUserName" $ c_GetUserName c_str c_len
+        len <- peek c_len
+        peekTStringLen (c_str, fromIntegral len - 1)
 
 ----------------------------------------------------------------
 -- End
diff --git a/System/Win32/Info/Computer.hsc b/System/Win32/Info/Computer.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Info/Computer.hsc
@@ -0,0 +1,237 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Info.Computer
+   Copyright   :  2012-2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Information about your computer.
+-}
+module System.Win32.Info.Computer
+  ( -- * Environment Strings
+    expandEnvironmentStrings, c_ExpandEnvironmentStrings
+
+    -- * Computer Name
+  , getComputerName, setComputerName
+  , c_GetComputerName, c_SetComputerName
+
+    -- * System metrics
+  , getSystemMetrics
+  , sM_CMONITORS
+  , sM_IMMENABLED
+  , sM_MOUSEWHEELPRESENT
+  , sM_REMOTESESSION
+  , sM_SAMEDISPLAYFORMAT
+  , sM_XVIRTUALSCREEN
+  , sM_YVIRTUALSCREEN
+  , sM_SERVERR2
+  , sM_MEDIACENTER
+  , sM_STARTER
+  , sM_TABLETPC
+
+  -- * User name
+  , getUserName, c_GetUserName
+
+  -- * Version Info
+  , OSVERSIONINFOEX(..), POSVERSIONINFOEX, LPOSVERSIONINFOEX
+  , ProductType(..)
+  , getVersionEx, c_GetVersionEx
+
+  -- * Processor features
+  , ProcessorFeature
+  , isProcessorFeaturePresent
+  , pF_3DNOW_INSTRUCTIONS_AVAILABLE
+  , pF_COMPARE_EXCHANGE_DOUBLE
+  , pF_FLOATING_POINT_EMULATED
+  , pF_FLOATING_POINT_PRECISION_ERRATA
+  , pF_MMX_INSTRUCTIONS_AVAILABLE
+  , pF_PAE_ENABLED
+  , pF_RDTSC_INSTRUCTION_AVAILABLE
+  , pF_XMMI_INSTRUCTIONS_AVAILABLE
+  , pF_XMMI64_INSTRUCTIONS_AVAILABLE
+  ) where
+
+import Foreign.Marshal.Utils ( with )
+import Foreign.Storable      ( Storable(..) )
+import System.Win32.Info     ( SMSetting )
+import System.Win32.Info.Version
+import System.Win32.String   ( LPCTSTR, LPTSTR, withTString, withTStringBuffer
+                             , peekTString, peekTStringLen )
+import System.Win32.Types    ( BOOL, failIfFalse_ )
+import System.Win32.Utils    ( tryWithoutNull )
+import System.Win32.Word     ( DWORD, LPDWORD )
+
+#include <windows.h>
+#include <lmcons.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+
+----------------------------------------------------------------
+-- Environment Strings
+----------------------------------------------------------------
+expandEnvironmentStrings :: String -> IO String
+expandEnvironmentStrings name =
+  withTString name $ \ c_name ->
+    tryWithoutNull (unwords ["ExpandEnvironmentStrings", name])
+      (\buf len -> c_ExpandEnvironmentStrings c_name buf len) 512
+
+foreign import WINDOWS_CCONV unsafe "windows.h ExpandEnvironmentStringsW"
+  c_ExpandEnvironmentStrings :: LPCTSTR -> LPTSTR -> DWORD -> IO DWORD
+
+----------------------------------------------------------------
+-- Computer Name
+----------------------------------------------------------------
+
+getComputerName :: IO String
+getComputerName =
+  withTStringBuffer  maxLength  $ \buf ->
+  with (fromIntegral maxLength) $ \len -> do
+      failIfFalse_ "GetComputerName"
+        $ c_GetComputerName buf len
+      len' <- peek len
+      peekTStringLen (buf, (fromIntegral len'))
+  where
+    maxLength = #const MAX_PATH
+
+foreign import WINDOWS_CCONV unsafe "GetComputerNameW"
+  c_GetComputerName :: LPTSTR -> LPDWORD -> IO Bool
+
+setComputerName :: String -> IO ()
+setComputerName name =
+  withTString name $ \buf ->
+      failIfFalse_ (unwords ["SetComputerName", name])
+        $ c_SetComputerName buf
+
+foreign import WINDOWS_CCONV unsafe "SetComputerNameW"
+  c_SetComputerName :: LPTSTR -> IO Bool
+{-
+type COMPUTER_NAME_FORMAT = UINT
+{enum COMPUTER_NAME_FORMAT,
+ , computerNameNetBIOS                   = ComputerNameNetBIOS
+ , computerNameDnsHostname               = ComputerNameDnsHostname
+ , computerNameDnsDomain                 = ComputerNameDnsDomain
+ , computerNameDnsFullyQualified         = ComputerNameDnsFullyQualified
+ , computerNamePhysicalNetBIOS           = ComputerNamePhysicalNetBIOS
+ , computerNamePhysicalDnsHostname       = ComputerNamePhysicalDnsHostname
+ , computerNamePhysicalDnsDomain         = ComputerNamePhysicalDnsFullyQualified
+ , computerNamePhysicalDnsFullyQualified = ComputerNamePhysicalDnsFullyQualified
+ , computerNameMax                       = ComputerNameMax
+ }
+-}
+
+----------------------------------------------------------------
+-- Hardware Profiles
+----------------------------------------------------------------
+{-
+-- TODO: Decide HW_PROFILE_INFO type design
+
+type LPHW_PROFILE_INFO = Ptr HW_PROFILE_INFO
+
+data HW_PROFILE_INFO = HW_PROFILE_INFO
+     { dwDockInfo       :: DWORD
+     , szHwProfileGuid  :: String -- Should we use GUID type instead of String?
+     , szHwProfileName  :: String
+     } deriving Show
+
+instance Storable HW_PROFILE_INFO where
+    sizeOf = const #{size HW_PROFILE_INFOW}
+    alignment _ = #alignment HW_PROFILE_INFOW
+    poke buf info = do
+        (#poke HW_PROFILE_INFOW, dwDockInfo) buf (dwDockInfo info)
+        withTString (szHwProfileGuid info) $ \szHwProfileGuid' ->
+          (#poke HW_PROFILE_INFOW, szHwProfileGuid) buf szHwProfileGuid'
+        withTString (szHwProfileName info) $ \szHwProfileName' ->
+          (#poke HW_PROFILE_INFOW, szHwProfileName) buf szHwProfileName'
+
+    peek buf = do
+        dockInfo       <- (#peek HW_PROFILE_INFOW, dwDockInfo) buf
+        hwProfileGuid  <- peekTString $ (#ptr HW_PROFILE_INFOW, szHwProfileGuid) buf
+        hwProfileName  <- peekTString $ (#ptr HW_PROFILE_INFOW, szHwProfileName) buf
+        return $ HW_PROFILE_INFO dockInfo hwProfileGuid hwProfileName
+
+getCurrentHwProfile :: IO HW_PROFILE_INFO
+getCurrentHwProfile =
+  alloca $ \buf -> do
+    failIfFalse_ "GetCurrentHwProfile"
+      $ c_GetCurrentHwProfile buf
+    peek buf
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentHwProfileW"
+  c_GetCurrentHwProfile :: LPHW_PROFILE_INFO -> IO Bool
+-}
+
+----------------------------------------------------------------
+-- System metrics
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetSystemMetrics"
+  getSystemMetrics :: SMSetting -> IO Int
+
+#{enum SMSetting,
+ , sM_CMONITORS           = SM_CMONITORS
+ , sM_IMMENABLED          = SM_IMMENABLED
+ , sM_MOUSEWHEELPRESENT   = SM_MOUSEWHEELPRESENT
+ , sM_REMOTESESSION       = SM_REMOTESESSION
+ , sM_SAMEDISPLAYFORMAT   = SM_SAMEDISPLAYFORMAT
+ , sM_XVIRTUALSCREEN      = SM_XVIRTUALSCREEN
+ , sM_YVIRTUALSCREEN      = SM_YVIRTUALSCREEN
+ , sM_SERVERR2            = SM_SERVERR2
+ , sM_MEDIACENTER         = SM_MEDIACENTER
+ , sM_STARTER             = SM_STARTER
+ , sM_TABLETPC            = SM_TABLETPC
+ }
+
+----------------------------------------------------------------
+-- User name
+----------------------------------------------------------------
+
+-- | Get user name. See: <https://github.com/haskell/win32/issues/8>, <http://lpaste.net/41521>
+getUserName :: IO String
+getUserName =
+  withTStringBuffer  maxLength  $ \buf ->
+  with (fromIntegral maxLength) $ \len -> do
+      failIfFalse_ "GetComputerName"
+        $ c_GetUserName buf len
+      -- GetUserNameW includes NUL character.
+      peekTString buf
+  where
+    -- This requires Lmcons.h
+    maxLength = #const UNLEN
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetUserNameW"
+  c_GetUserName :: LPTSTR -> LPDWORD -> IO Bool
+
+----------------------------------------------------------------
+-- Processor features
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "windows.h IsProcessorFeaturePresent"
+  isProcessorFeaturePresent :: ProcessorFeature -> IO BOOL
+
+type ProcessorFeature   = DWORD
+
+#{enum ProcessorFeature,
+ , pF_3DNOW_INSTRUCTIONS_AVAILABLE     = PF_3DNOW_INSTRUCTIONS_AVAILABLE
+ , pF_COMPARE_EXCHANGE_DOUBLE          = PF_COMPARE_EXCHANGE_DOUBLE
+ , pF_FLOATING_POINT_EMULATED          = PF_FLOATING_POINT_EMULATED
+ , pF_FLOATING_POINT_PRECISION_ERRATA  = PF_FLOATING_POINT_PRECISION_ERRATA
+ , pF_MMX_INSTRUCTIONS_AVAILABLE       = PF_MMX_INSTRUCTIONS_AVAILABLE
+ , pF_PAE_ENABLED                      = PF_PAE_ENABLED
+ , pF_RDTSC_INSTRUCTION_AVAILABLE      = PF_RDTSC_INSTRUCTION_AVAILABLE
+ , pF_XMMI_INSTRUCTIONS_AVAILABLE      = PF_XMMI_INSTRUCTIONS_AVAILABLE
+ , pF_XMMI64_INSTRUCTIONS_AVAILABLE    = PF_XMMI64_INSTRUCTIONS_AVAILABLE
+ }
+
+{-
+ , pF_CHANNELS_ENABLED                 = PF_CHANNELS_ENABLED
+ , pF_NX_ENABLED                       = PF_NX_ENABLED
+ , pF_COMPARE_EXCHANGE128              = PF_COMPARE_EXCHANGE128
+ , pF_COMPARE64_EXCHANGE128            = PF_COMPARE64_EXCHANGE128
+ , pF_SECOND_LEVEL_ADDRESS_TRANSLATION = PF_SECOND_LEVEL_ADDRESS_TRANSLATION
+ , pF_SSE3_INSTRUCTIONS_AVAILABLE      = PF_SSE3_INSTRUCTIONS_AVAILABLE
+ , pF_VIRT_FIRMWARE_ENABLED            = PF_VIRT_FIRMWARE_ENABLED
+ , pF_XSAVE_ENABLED                    = PF_XSAVE_ENABLED
+-}
diff --git a/System/Win32/Info/Internal.hsc b/System/Win32/Info/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Info/Internal.hsc
@@ -0,0 +1,313 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Info.Internal
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Info.Internal where
+
+import Foreign.Ptr (Ptr)
+import Foreign.Storable (Storable(..))
+import System.Win32.Types (DWORD, LPDWORD, LPCTSTR, LPTSTR, LPVOID, UINT, WORD)
+
+#if !MIN_VERSION_base(4,6,0)
+import Prelude hiding (catch)
+#endif
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+#include "alignment.h"
+
+----------------------------------------------------------------
+-- Environment Strings
+----------------------------------------------------------------
+
+-- %fun ExpandEnvironmentStrings :: String -> IO String
+
+----------------------------------------------------------------
+-- Computer Name
+----------------------------------------------------------------
+
+-- %fun GetComputerName :: IO String
+-- %fun SetComputerName :: String -> IO ()
+-- %end free(arg1)
+
+----------------------------------------------------------------
+-- Hardware Profiles
+----------------------------------------------------------------
+
+-- %fun GetCurrentHwProfile :: IO HW_PROFILE_INFO
+
+----------------------------------------------------------------
+-- Keyboard Type
+----------------------------------------------------------------
+
+-- %fun GetKeyboardType :: KeyboardTypeKind -> IO KeyboardType
+
+----------------------------------------------------------------
+-- System Color
+----------------------------------------------------------------
+
+type SystemColor   = UINT
+
+-- ToDo: This list is out of date.
+
+#{enum SystemColor,
+ , cOLOR_SCROLLBAR      = COLOR_SCROLLBAR
+ , cOLOR_BACKGROUND     = COLOR_BACKGROUND
+ , cOLOR_ACTIVECAPTION  = COLOR_ACTIVECAPTION
+ , cOLOR_INACTIVECAPTION = COLOR_INACTIVECAPTION
+ , cOLOR_MENU           = COLOR_MENU
+ , cOLOR_WINDOW         = COLOR_WINDOW
+ , cOLOR_WINDOWFRAME    = COLOR_WINDOWFRAME
+ , cOLOR_MENUTEXT       = COLOR_MENUTEXT
+ , cOLOR_WINDOWTEXT     = COLOR_WINDOWTEXT
+ , cOLOR_CAPTIONTEXT    = COLOR_CAPTIONTEXT
+ , cOLOR_ACTIVEBORDER   = COLOR_ACTIVEBORDER
+ , cOLOR_INACTIVEBORDER = COLOR_INACTIVEBORDER
+ , cOLOR_APPWORKSPACE   = COLOR_APPWORKSPACE
+ , cOLOR_HIGHLIGHT      = COLOR_HIGHLIGHT
+ , cOLOR_HIGHLIGHTTEXT  = COLOR_HIGHLIGHTTEXT
+ , cOLOR_BTNFACE        = COLOR_BTNFACE
+ , cOLOR_BTNSHADOW      = COLOR_BTNSHADOW
+ , cOLOR_GRAYTEXT       = COLOR_GRAYTEXT
+ , cOLOR_BTNTEXT        = COLOR_BTNTEXT
+ , cOLOR_INACTIVECAPTIONTEXT = COLOR_INACTIVECAPTIONTEXT
+ , cOLOR_BTNHIGHLIGHT   = COLOR_BTNHIGHLIGHT
+ }
+
+-- %fun GetSysColor :: SystemColor -> IO COLORREF
+-- %fun SetSysColors :: [(SystemColor,COLORREF)] -> IO ()
+
+----------------------------------------------------------------
+-- Standard Directories
+----------------------------------------------------------------
+
+foreign import WINDOWS_CCONV unsafe "GetWindowsDirectoryW"
+  c_getWindowsDirectory :: LPTSTR -> UINT -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "GetSystemDirectoryW"
+  c_getSystemDirectory :: LPTSTR -> UINT -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "GetCurrentDirectoryW"
+  c_getCurrentDirectory :: DWORD -> LPTSTR -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "GetTempPathW"
+  c_getTempPath :: DWORD -> LPTSTR -> IO UINT
+
+foreign import WINDOWS_CCONV unsafe "GetFullPathNameW"
+  c_GetFullPathName :: LPCTSTR -> DWORD -> LPTSTR -> Ptr LPTSTR -> IO DWORD
+
+foreign import WINDOWS_CCONV unsafe "GetLongPathNameW"
+  c_GetLongPathName :: LPCTSTR -> LPTSTR -> DWORD -> IO DWORD
+
+foreign import WINDOWS_CCONV unsafe "GetShortPathNameW"
+  c_GetShortPathName :: LPCTSTR -> LPTSTR -> DWORD -> IO DWORD
+
+foreign import WINDOWS_CCONV unsafe "SearchPathW"
+  c_SearchPath :: LPCTSTR -> LPCTSTR -> LPCTSTR -> DWORD -> LPTSTR -> Ptr LPTSTR
+               -> IO DWORD
+
+----------------------------------------------------------------
+-- System Info (Info about processor and memory subsystem)
+----------------------------------------------------------------
+
+data ProcessorArchitecture = PaUnknown WORD | PaIntel | PaMips | PaAlpha | PaPpc | PaIa64 | PaIa32OnIa64 | PaAmd64
+    deriving (Show,Eq)
+
+instance Storable ProcessorArchitecture where
+    sizeOf _ = sizeOf (undefined::WORD)
+    alignment _ = alignment (undefined::WORD)
+    poke buf pa = pokeByteOff buf 0 $ case pa of
+        PaUnknown w -> w
+        PaIntel     -> #const PROCESSOR_ARCHITECTURE_INTEL
+        PaMips      -> #const PROCESSOR_ARCHITECTURE_MIPS
+        PaAlpha     -> #const PROCESSOR_ARCHITECTURE_ALPHA
+        PaPpc       -> #const PROCESSOR_ARCHITECTURE_PPC
+        PaIa64      -> #const PROCESSOR_ARCHITECTURE_IA64
+#ifndef __WINE_WINDOWS_H
+        PaIa32OnIa64 -> #const PROCESSOR_ARCHITECTURE_IA32_ON_WIN64
+#endif
+        PaAmd64     -> #const PROCESSOR_ARCHITECTURE_AMD64
+    peek buf = do
+        v <- (peekByteOff buf 0:: IO WORD)
+        return $ case v of
+            (#const PROCESSOR_ARCHITECTURE_INTEL) -> PaIntel
+            (#const PROCESSOR_ARCHITECTURE_MIPS)  -> PaMips
+            (#const PROCESSOR_ARCHITECTURE_ALPHA) -> PaAlpha
+            (#const PROCESSOR_ARCHITECTURE_PPC)   -> PaPpc
+            (#const PROCESSOR_ARCHITECTURE_IA64)  -> PaIa64
+#ifndef __WINE_WINDOWS_H
+            (#const PROCESSOR_ARCHITECTURE_IA32_ON_WIN64) -> PaIa32OnIa64
+#endif
+            (#const PROCESSOR_ARCHITECTURE_AMD64) -> PaAmd64
+            w                                   -> PaUnknown w
+
+data SYSTEM_INFO = SYSTEM_INFO
+    { siProcessorArchitecture :: ProcessorArchitecture
+    , siPageSize :: DWORD
+    , siMinimumApplicationAddress, siMaximumApplicationAddress :: LPVOID
+    , siActiveProcessorMask :: DWORD
+    , siNumberOfProcessors :: DWORD
+    , siProcessorType :: DWORD
+    , siAllocationGranularity :: DWORD
+    , siProcessorLevel :: WORD
+    , siProcessorRevision :: WORD
+    } deriving (Show)
+
+instance Storable SYSTEM_INFO where
+    sizeOf = const #size SYSTEM_INFO
+    alignment _ = #alignment SYSTEM_INFO
+    poke buf si = do
+        (#poke SYSTEM_INFO, wProcessorArchitecture) buf (siProcessorArchitecture si)
+        (#poke SYSTEM_INFO, dwPageSize)             buf (siPageSize si)
+        (#poke SYSTEM_INFO, lpMinimumApplicationAddress) buf (siMinimumApplicationAddress si)
+        (#poke SYSTEM_INFO, lpMaximumApplicationAddress) buf (siMaximumApplicationAddress si)
+        (#poke SYSTEM_INFO, dwActiveProcessorMask)  buf (siActiveProcessorMask si)
+        (#poke SYSTEM_INFO, dwNumberOfProcessors)   buf (siNumberOfProcessors si)
+        (#poke SYSTEM_INFO, dwProcessorType)        buf (siProcessorType si)
+        (#poke SYSTEM_INFO, dwAllocationGranularity) buf (siAllocationGranularity si)
+        (#poke SYSTEM_INFO, wProcessorLevel)        buf (siProcessorLevel si)
+        (#poke SYSTEM_INFO, wProcessorRevision)     buf (siProcessorRevision si)
+
+    peek buf = do
+        processorArchitecture <-
+            (#peek SYSTEM_INFO, wProcessorArchitecture) buf
+        pageSize            <- (#peek SYSTEM_INFO, dwPageSize) buf
+        minimumApplicationAddress <-
+            (#peek SYSTEM_INFO, lpMinimumApplicationAddress) buf
+        maximumApplicationAddress <-
+            (#peek SYSTEM_INFO, lpMaximumApplicationAddress) buf
+        activeProcessorMask <- (#peek SYSTEM_INFO, dwActiveProcessorMask) buf
+        numberOfProcessors  <- (#peek SYSTEM_INFO, dwNumberOfProcessors) buf
+        processorType       <- (#peek SYSTEM_INFO, dwProcessorType) buf
+        allocationGranularity <-
+            (#peek SYSTEM_INFO, dwAllocationGranularity) buf
+        processorLevel      <- (#peek SYSTEM_INFO, wProcessorLevel) buf
+        processorRevision   <- (#peek SYSTEM_INFO, wProcessorRevision) buf
+        return $ SYSTEM_INFO {
+            siProcessorArchitecture     = processorArchitecture,
+            siPageSize                  = pageSize,
+            siMinimumApplicationAddress = minimumApplicationAddress,
+            siMaximumApplicationAddress = maximumApplicationAddress,
+            siActiveProcessorMask       = activeProcessorMask,
+            siNumberOfProcessors        = numberOfProcessors,
+            siProcessorType             = processorType,
+            siAllocationGranularity     = allocationGranularity,
+            siProcessorLevel            = processorLevel,
+            siProcessorRevision         = processorRevision
+            }
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetSystemInfo"
+    c_GetSystemInfo :: Ptr SYSTEM_INFO -> IO ()
+
+----------------------------------------------------------------
+-- System metrics
+----------------------------------------------------------------
+
+type SMSetting = UINT
+
+#{enum SMSetting,
+ , sM_ARRANGE           = SM_ARRANGE
+ , sM_CLEANBOOT         = SM_CLEANBOOT
+ , sM_CMETRICS          = SM_CMETRICS
+ , sM_CMOUSEBUTTONS     = SM_CMOUSEBUTTONS
+ , sM_CXBORDER          = SM_CXBORDER
+ , sM_CYBORDER          = SM_CYBORDER
+ , sM_CXCURSOR          = SM_CXCURSOR
+ , sM_CYCURSOR          = SM_CYCURSOR
+ , sM_CXDLGFRAME        = SM_CXDLGFRAME
+ , sM_CYDLGFRAME        = SM_CYDLGFRAME
+ , sM_CXDOUBLECLK       = SM_CXDOUBLECLK
+ , sM_CYDOUBLECLK       = SM_CYDOUBLECLK
+ , sM_CXDRAG            = SM_CXDRAG
+ , sM_CYDRAG            = SM_CYDRAG
+ , sM_CXEDGE            = SM_CXEDGE
+ , sM_CYEDGE            = SM_CYEDGE
+ , sM_CXFRAME           = SM_CXFRAME
+ , sM_CYFRAME           = SM_CYFRAME
+ , sM_CXFULLSCREEN      = SM_CXFULLSCREEN
+ , sM_CYFULLSCREEN      = SM_CYFULLSCREEN
+ , sM_CXHSCROLL         = SM_CXHSCROLL
+ , sM_CYVSCROLL         = SM_CYVSCROLL
+ , sM_CXICON            = SM_CXICON
+ , sM_CYICON            = SM_CYICON
+ , sM_CXICONSPACING     = SM_CXICONSPACING
+ , sM_CYICONSPACING     = SM_CYICONSPACING
+ , sM_CXMAXIMIZED       = SM_CXMAXIMIZED
+ , sM_CYMAXIMIZED       = SM_CYMAXIMIZED
+ , sM_CXMENUCHECK       = SM_CXMENUCHECK
+ , sM_CYMENUCHECK       = SM_CYMENUCHECK
+ , sM_CXMENUSIZE        = SM_CXMENUSIZE
+ , sM_CYMENUSIZE        = SM_CYMENUSIZE
+ , sM_CXMIN             = SM_CXMIN
+ , sM_CYMIN             = SM_CYMIN
+ , sM_CXMINIMIZED       = SM_CXMINIMIZED
+ , sM_CYMINIMIZED       = SM_CYMINIMIZED
+ , sM_CXMINTRACK        = SM_CXMINTRACK
+ , sM_CYMINTRACK        = SM_CYMINTRACK
+ , sM_CXSCREEN          = SM_CXSCREEN
+ , sM_CYSCREEN          = SM_CYSCREEN
+ , sM_CXSIZE            = SM_CXSIZE
+ , sM_CYSIZE            = SM_CYSIZE
+ , sM_CXSIZEFRAME       = SM_CXSIZEFRAME
+ , sM_CYSIZEFRAME       = SM_CYSIZEFRAME
+ , sM_CXSMICON          = SM_CXSMICON
+ , sM_CYSMICON          = SM_CYSMICON
+ , sM_CXSMSIZE          = SM_CXSMSIZE
+ , sM_CYSMSIZE          = SM_CYSMSIZE
+ , sM_CXVSCROLL         = SM_CXVSCROLL
+ , sM_CYHSCROLL         = SM_CYHSCROLL
+ , sM_CYVTHUMB          = SM_CYVTHUMB
+ , sM_CYCAPTION         = SM_CYCAPTION
+ , sM_CYKANJIWINDOW     = SM_CYKANJIWINDOW
+ , sM_CYMENU            = SM_CYMENU
+ , sM_CYSMCAPTION       = SM_CYSMCAPTION
+ , sM_DBCSENABLED       = SM_DBCSENABLED
+ , sM_DEBUG             = SM_DEBUG
+ , sM_MENUDROPALIGNMENT = SM_MENUDROPALIGNMENT
+ , sM_MIDEASTENABLED    = SM_MIDEASTENABLED
+ , sM_MOUSEPRESENT      = SM_MOUSEPRESENT
+ , sM_NETWORK           = SM_NETWORK
+ , sM_PENWINDOWS        = SM_PENWINDOWS
+ , sM_SECURE            = SM_SECURE
+ , sM_SHOWSOUNDS        = SM_SHOWSOUNDS
+ , sM_SLOWMACHINE       = SM_SLOWMACHINE
+ , sM_SWAPBUTTON        = SM_SWAPBUTTON
+ }
+
+-- %fun GetSystemMetrics :: SMSetting -> IO Int
+
+----------------------------------------------------------------
+-- Thread Desktops
+----------------------------------------------------------------
+
+-- %fun GetThreadDesktop :: ThreadId -> IO HDESK
+-- %fun SetThreadDesktop :: ThreadId -> HDESK -> IO ()
+
+----------------------------------------------------------------
+-- User name
+----------------------------------------------------------------
+
+-- %fun GetUserName :: IO String
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetUserNameW"
+  c_GetUserName :: LPTSTR -> LPDWORD -> IO Bool
+
+----------------------------------------------------------------
+-- End
+----------------------------------------------------------------
diff --git a/System/Win32/Info/Version.hsc b/System/Win32/Info/Version.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Info/Version.hsc
@@ -0,0 +1,165 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Info.Version
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Version information about your computer.
+-}
+module System.Win32.Info.Version
+  ( -- * Version Info
+    OSVERSIONINFOEX(..), POSVERSIONINFOEX, LPOSVERSIONINFOEX
+  , ProductType(..)
+  , getVersionEx, c_GetVersionEx
+  
+    -- * Verify OS version
+  , isVistaOrLater, is7OrLater
+  ) where
+
+import Foreign.Ptr           ( Ptr, plusPtr )
+import Foreign.Marshal.Alloc ( alloca )
+import Foreign.Storable      ( Storable(..) )
+import System.Win32.String   ( withTString, peekTString )
+import System.Win32.Types    ( BOOL, BYTE, failIfFalse_ )
+import System.Win32.Word     ( WORD, DWORD )
+
+#include <windows.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+
+----------------------------------------------------------------
+-- Version Info
+----------------------------------------------------------------
+getVersionEx :: IO OSVERSIONINFOEX
+getVersionEx =
+  alloca $ \buf -> do
+    (#poke OSVERSIONINFOEXW, dwOSVersionInfoSize) buf
+      $ sizeOf (undefined::OSVERSIONINFOEX)
+    failIfFalse_ "GetVersionEx"
+      $ c_GetVersionEx buf
+    peek buf
+
+data ProductType = VerUnknow BYTE | VerNTWorkStation | VerNTDomainControler | VerNTServer
+    deriving (Show,Eq)
+
+instance Storable ProductType where
+    sizeOf    _ = sizeOf    (undefined::BYTE)
+    alignment _ = alignment (undefined::BYTE)
+    poke buf v = pokeByteOff buf 0 $ case v of
+        VerUnknow w          -> w
+        VerNTWorkStation     -> #const VER_NT_WORKSTATION
+        VerNTDomainControler -> #const VER_NT_DOMAIN_CONTROLLER
+        VerNTServer          -> #const VER_NT_SERVER
+    peek buf = do
+        v <- peekByteOff buf 0
+        return $ case v of
+            (#const VER_NT_WORKSTATION)       -> VerNTWorkStation
+            (#const VER_NT_DOMAIN_CONTROLLER) -> VerNTDomainControler
+            (#const VER_NT_SERVER)            -> VerNTServer
+            w                                 -> VerUnknow w
+
+type POSVERSIONINFOEX = Ptr OSVERSIONINFOEX
+type LPOSVERSIONINFOEX = Ptr OSVERSIONINFOEX
+
+data OSVERSIONINFOEX = OSVERSIONINFOEX
+     { dwMajorVersion    :: DWORD
+     , dwMinorVersion    :: DWORD
+     , dwBuildNumber     :: DWORD
+     , dwPlatformId      :: DWORD
+     , szCSDVersion      :: String
+     , wServicePackMajor :: WORD
+     , wServicePackMinor :: WORD
+     , wSuiteMask        :: WORD
+     , wProductType      :: ProductType
+     } deriving Show
+
+instance Storable OSVERSIONINFOEX where
+    sizeOf = const #{size struct _OSVERSIONINFOEXW}
+    alignment _ = #alignment OSVERSIONINFOEX
+    poke buf info = do
+        (#poke OSVERSIONINFOEXW, dwOSVersionInfoSize) buf (sizeOf info)
+        (#poke OSVERSIONINFOEXW, dwMajorVersion) buf (dwMajorVersion info)
+        (#poke OSVERSIONINFOEXW, dwMinorVersion) buf (dwMinorVersion info)
+        (#poke OSVERSIONINFOEXW, dwBuildNumber)  buf (dwBuildNumber info)
+        (#poke OSVERSIONINFOEXW, dwPlatformId) buf (dwPlatformId info)
+        withTString (szCSDVersion info) $ \szCSDVersion' ->
+          (#poke OSVERSIONINFOEXW, szCSDVersion) buf szCSDVersion'
+        (#poke OSVERSIONINFOEXW, wServicePackMajor) buf (wServicePackMajor info)
+        (#poke OSVERSIONINFOEXW, wServicePackMinor) buf (wServicePackMinor info)
+        (#poke OSVERSIONINFOEXW, wSuiteMask)   buf (wSuiteMask info)
+        (#poke OSVERSIONINFOEXW, wProductType) buf (wProductType info)
+        (#poke OSVERSIONINFOEXW, wReserved)    buf (0::BYTE)
+
+    peek buf = do
+        majorVersion     <- (#peek OSVERSIONINFOEXW, dwMajorVersion) buf
+        minorVersion     <- (#peek OSVERSIONINFOEXW, dwMinorVersion) buf
+        buildNumber      <- (#peek OSVERSIONINFOEXW, dwBuildNumber) buf
+        platformId       <- (#peek OSVERSIONINFOEXW, dwPlatformId) buf
+        cSDVersion       <- peekTString $ (#ptr OSVERSIONINFOEXW, szCSDVersion) buf
+        servicePackMajor <- (#peek OSVERSIONINFOEXW, wServicePackMajor) buf
+        servicePackMinor <- (#peek OSVERSIONINFOEXW, wServicePackMinor) buf
+        suiteMask        <- (#peek OSVERSIONINFOEXW, wSuiteMask) buf
+        productType      <- (#peek OSVERSIONINFOEXW, wProductType) buf
+        return $ OSVERSIONINFOEX majorVersion minorVersion
+                                 buildNumber platformId cSDVersion
+                                 servicePackMajor servicePackMinor
+                                 suiteMask productType
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetVersionExW"
+  c_GetVersionEx :: LPOSVERSIONINFOEX -> IO BOOL
+
+----------------------------------------------------------------
+-- Verify OS version
+----------------------------------------------------------------
+-- See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
+
+isVistaOrLater, is7OrLater :: IO Bool
+isVistaOrLater = do
+  ver <- getVersionEx
+  return $ 6 <= dwMajorVersion ver
+
+is7OrLater = do
+  ver <- getVersionEx
+  return $  6 <= dwMajorVersion ver
+         && 1 <= dwMinorVersion ver
+
+{-
+We don't use VerifyVersionInfo function to above functions.
+
+Because VerifyVersionInfo is more difficult than GetVersionEx and accessing field in Haskell.
+
+-- | See: http://support.microsoft.com/kb/225013/
+-- http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491(v=vs.85).aspx
+
+bIsWindowsVersionOK :: DWORD -> DWORD -> WORD -> IO BOOL
+bIsWindowsVersionOK dwMajor dwMinor dwSPMajor =
+  alloca $ \buf -> do
+    zeroMemory buf
+      (#{size OSVERSIONINFOEXW}::DWORD)
+    (#poke OSVERSIONINFOEXW, dwOSVersionInfoSize) buf
+      (#{size OSVERSIONINFOEXW}::DWORD)
+    (#poke OSVERSIONINFOEXW, dwMajorVersion)    buf dwMajor
+    (#poke OSVERSIONINFOEXW, dwMinorVersion)    buf dwMinor
+    (#poke OSVERSIONINFOEXW, wServicePackMajor) buf dwSPMajor
+    --  Set up the condition mask.
+    let dwlConditionMask = 0
+        flag =    #const VER_MAJORVERSION
+             .|.  #const VER_MINORVERSION
+             .|.  #const VER_SERVICEPACKMAJOR
+    dwlConditionMask'   <- vER_SET_CONDITION dwlConditionMask   #{const VER_MAJORVERSION} #{const VER_GREATER_EQUAL}
+    dwlConditionMask''  <- vER_SET_CONDITION dwlConditionMask'  #{const VER_MINORVERSION} #{const VER_MINORVERSION}
+    dwlConditionMask''' <- vER_SET_CONDITION dwlConditionMask'' #{const VER_SERVICEPACKMAJOR} #{const VER_SERVICEPACKMAJOR}
+    verifyVersionInfo buf flag dwlConditionMask'''
+
+type ULONGLONG = DWORDLONG
+
+foreign import capi unsafe "windows.h VER_SET_CONDITION"
+  vER_SET_CONDITION :: ULONGLONG -> DWORD -> BYTE -> IO ULONGLONG
+
+foreign import WINDOWS_CCONV unsafe "windows.h VerifyVersionInfoW"
+  verifyVersionInfo :: LPOSVERSIONINFOEX -> DWORD -> DWORDLONG -> IO BOOL
+-}
diff --git a/System/Win32/Mem.hsc b/System/Win32/Mem.hsc
--- a/System/Win32/Mem.hsc
+++ b/System/Win32/Mem.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -15,15 +17,142 @@
 --
 -----------------------------------------------------------------------------
 
-module System.Win32.Mem where
+module System.Win32.Mem
+    ( MEMORY_BASIC_INFORMATION(..)
+    , copyMemory
+    , moveMemory
+    , fillMemory
+    , zeroMemory
+    , memset
+    , getProcessHeap
+#ifndef __WINE_WINDOWS_H
+    , getProcessHeaps
+#endif
+    , HGLOBAL
+      -- * Global allocation
+    , GlobalAllocFlags
+    , gMEM_INVALID_HANDLE
+    , gMEM_FIXED
+    , gMEM_MOVEABLE
+    , gPTR
+    , gHND
+    , gMEM_DDESHARE
+    , gMEM_SHARE
+    , gMEM_LOWER
+    , gMEM_NOCOMPACT
+    , gMEM_NODISCARD
+    , gMEM_NOT_BANKED
+    , gMEM_NOTIFY
+    , gMEM_ZEROINIT
+    , globalAlloc
+    , globalFlags
+    , globalFree
+    , globalHandle
+    , globalLock
+    , globalReAlloc
+    , globalSize
+    , globalUnlock
 
+      -- * Heap allocation
+    , HeapAllocFlags
+    , hEAP_GENERATE_EXCEPTIONS
+    , hEAP_NO_SERIALIZE
+    , hEAP_ZERO_MEMORY
+    , heapAlloc
+    , heapCompact
+    , heapCreate
+    , heapDestroy
+    , heapFree
+    , heapLock
+    , heapReAlloc
+    , heapSize
+    , heapUnlock
+    , heapValidate
+
+      -- * Virtual allocation
+      -- ** Allocation
+    , virtualAlloc
+    , virtualAllocEx
+    , VirtualAllocFlags
+    , mEM_COMMIT
+    , mEM_RESERVE
+      -- ** Locking
+    , virtualLock
+    , virtualUnlock
+      -- ** Protection
+    , virtualProtect
+    , virtualProtectEx
+    , virtualQueryEx
+    , ProtectFlags
+    , pAGE_READONLY
+    , pAGE_READWRITE
+    , pAGE_EXECUTE
+    , pAGE_EXECUTE_READ
+    , pAGE_EXECUTE_READWRITE
+    , pAGE_GUARD
+    , pAGE_NOACCESS
+    , pAGE_NOCACHE
+      -- ** Freeing
+    , virtualFree
+    , virtualFreeEx
+    , FreeFlags
+    , mEM_DECOMMIT
+    , mEM_RELEASE
+
+
+    ) where
+
 import System.Win32.Types
 
 import Foreign
 import Foreign.C.Types
 
+##include "windows_cconv.h"
+
 #include <windows.h>
+#include "alignment.h"
 
+----------------------------------------------------------------
+-- Data types
+----------------------------------------------------------------
+
+data MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION
+    { mbiBaseAddress       :: Addr
+    , mbiAllocationBase    :: Addr
+    , mbiAllocationProtect :: DWORD
+    , mbiRegionSize        :: SIZE_T
+    , mbiState             :: DWORD
+    , mbiProtect           :: DWORD
+    , mbiType              :: DWORD
+    } deriving (Show)
+
+----------------------------------------------------------------
+-- Instances
+----------------------------------------------------------------
+
+instance Storable MEMORY_BASIC_INFORMATION where
+    sizeOf _ = #size MEMORY_BASIC_INFORMATION
+    alignment _ = #alignment MEMORY_BASIC_INFORMATION
+    poke buf mbi = do
+        (#poke MEMORY_BASIC_INFORMATION, BaseAddress)       buf (mbiBaseAddress mbi)
+        (#poke MEMORY_BASIC_INFORMATION, AllocationBase)    buf (mbiAllocationBase mbi)
+        (#poke MEMORY_BASIC_INFORMATION, AllocationProtect) buf (mbiAllocationProtect mbi)
+        (#poke MEMORY_BASIC_INFORMATION, RegionSize)        buf (mbiRegionSize mbi)
+        (#poke MEMORY_BASIC_INFORMATION, State)             buf (mbiState mbi)
+        (#poke MEMORY_BASIC_INFORMATION, Protect)           buf (mbiProtect mbi)
+        (#poke MEMORY_BASIC_INFORMATION, Type)              buf (mbiType mbi)
+    peek buf = do
+        baseAddress       <- (#peek MEMORY_BASIC_INFORMATION, BaseAddress)       buf
+        allocationBase    <- (#peek MEMORY_BASIC_INFORMATION, AllocationBase)    buf
+        allocationProtect <- (#peek MEMORY_BASIC_INFORMATION, AllocationProtect) buf
+        regionSize        <- (#peek MEMORY_BASIC_INFORMATION, RegionSize)        buf
+        state             <- (#peek MEMORY_BASIC_INFORMATION, State)             buf
+        protect           <- (#peek MEMORY_BASIC_INFORMATION, Protect)           buf
+        ty                <- (#peek MEMORY_BASIC_INFORMATION, Type)              buf
+        return $ MEMORY_BASIC_INFORMATION baseAddress allocationBase allocationProtect regionSize state protect ty
+
+----------------------------------------------------------------
+
 copyMemory :: Ptr a -> Ptr a -> DWORD -> IO ()
 copyMemory dest src nbytes = copyBytes dest src (fromIntegral nbytes)
 
@@ -39,11 +168,11 @@
 
 foreign import ccall unsafe "string.h" memset :: Ptr a -> CInt -> CSize -> IO ()
 
-foreign import stdcall unsafe "windows.h GetProcessHeap"
+foreign import WINDOWS_CCONV unsafe "windows.h GetProcessHeap"
   getProcessHeap :: IO HANDLE
 
 #ifndef __WINE_WINDOWS_H
-foreign import stdcall unsafe "windows.h GetProcessHeaps"
+foreign import WINDOWS_CCONV unsafe "windows.h GetProcessHeaps"
   getProcessHeaps :: DWORD -> Addr -> IO DWORD
 #endif
 
@@ -72,7 +201,7 @@
 globalAlloc :: GlobalAllocFlags -> DWORD -> IO HGLOBAL
 globalAlloc flags size =
   failIfNull "GlobalAlloc" $ c_GlobalAlloc flags size
-foreign import stdcall unsafe "windows.h GlobalAlloc"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalAlloc"
   c_GlobalAlloc :: GlobalAllocFlags -> DWORD -> IO HGLOBAL
 
 -- %fun GlobalDiscard :: HGLOBAL -> IO HGLOBAL
@@ -81,25 +210,25 @@
 globalFlags :: HGLOBAL -> IO GlobalAllocFlags
 globalFlags mem =
   failIf (== gMEM_INVALID_HANDLE) "GlobalFlags" $ c_GlobalFlags mem
-foreign import stdcall unsafe "windows.h GlobalFlags"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalFlags"
   c_GlobalFlags :: HGLOBAL -> IO GlobalAllocFlags
 
 globalFree :: HGLOBAL -> IO HGLOBAL
 globalFree mem =
   failIfNull "GlobalFree" $ c_GlobalFree mem
-foreign import stdcall unsafe "windows.h GlobalFree"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalFree"
   c_GlobalFree :: HGLOBAL -> IO HGLOBAL
 
 globalHandle :: Addr -> IO HGLOBAL
 globalHandle addr =
   failIfNull "GlobalHandle" $ c_GlobalHandle addr
-foreign import stdcall unsafe "windows.h GlobalHandle"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalHandle"
   c_GlobalHandle :: Addr -> IO HGLOBAL
 
 globalLock :: HGLOBAL -> IO Addr
 globalLock mem =
   failIfNull "GlobalLock" $ c_GlobalLock mem
-foreign import stdcall unsafe "windows.h GlobalLock"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalLock"
   c_GlobalLock :: HGLOBAL -> IO Addr
 
 -- %fun GlobalMemoryStatus :: IO MEMORYSTATUS
@@ -107,84 +236,84 @@
 globalReAlloc :: HGLOBAL -> DWORD -> GlobalAllocFlags -> IO HGLOBAL
 globalReAlloc mem size flags =
   failIfNull "GlobalReAlloc" $ c_GlobalReAlloc mem size flags
-foreign import stdcall unsafe "windows.h GlobalReAlloc"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalReAlloc"
   c_GlobalReAlloc :: HGLOBAL -> DWORD -> GlobalAllocFlags -> IO HGLOBAL
 
 globalSize :: HGLOBAL -> IO DWORD
 globalSize mem =
   failIfZero "GlobalSize" $ c_GlobalSize mem
-foreign import stdcall unsafe "windows.h GlobalSize"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalSize"
   c_GlobalSize :: HGLOBAL -> IO DWORD
 
 globalUnlock :: HGLOBAL -> IO ()
 globalUnlock mem =
   failIfFalse_ "GlobalUnlock" $ c_GlobalUnlock mem
-foreign import stdcall unsafe "windows.h GlobalUnlock"
+foreign import WINDOWS_CCONV unsafe "windows.h GlobalUnlock"
   c_GlobalUnlock :: HGLOBAL -> IO Bool
 
 type HeapAllocFlags = DWORD
 
 #{enum HeapAllocFlags,
- , hEAP_GENERATE_EXCEPTIONS	= HEAP_GENERATE_EXCEPTIONS
- , hEAP_NO_SERIALIZE		= HEAP_NO_SERIALIZE
- , hEAP_ZERO_MEMORY		= HEAP_ZERO_MEMORY
+ , hEAP_GENERATE_EXCEPTIONS     = HEAP_GENERATE_EXCEPTIONS
+ , hEAP_NO_SERIALIZE            = HEAP_NO_SERIALIZE
+ , hEAP_ZERO_MEMORY             = HEAP_ZERO_MEMORY
  }
 
 heapAlloc :: HANDLE -> HeapAllocFlags -> DWORD -> IO Addr
 heapAlloc heap flags size =
   failIfNull "HeapAlloc" $ c_HeapAlloc heap flags size
-foreign import stdcall unsafe "windows.h HeapAlloc"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapAlloc"
   c_HeapAlloc :: HANDLE -> HeapAllocFlags -> DWORD -> IO Addr
 
 heapCompact :: HANDLE -> HeapAllocFlags -> IO UINT
 heapCompact heap flags =
   failIfZero "HeapCompact" $ c_HeapCompact heap flags
-foreign import stdcall unsafe "windows.h HeapCompact"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapCompact"
   c_HeapCompact :: HANDLE -> HeapAllocFlags -> IO UINT
 
 heapCreate :: HeapAllocFlags -> DWORD -> DWORD -> IO HANDLE
 heapCreate flags initSize maxSize =
   failIfNull "HeapCreate" $ c_HeapCreate flags initSize maxSize
-foreign import stdcall unsafe "windows.h HeapCreate"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapCreate"
   c_HeapCreate :: HeapAllocFlags -> DWORD -> DWORD -> IO HANDLE
 
 heapDestroy :: HANDLE -> IO ()
 heapDestroy heap =
   failIfFalse_ "HeapDestroy" $ c_HeapDestroy heap
-foreign import stdcall unsafe "windows.h HeapDestroy"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapDestroy"
   c_HeapDestroy :: HANDLE -> IO Bool
 
 heapFree :: HANDLE -> HeapAllocFlags -> Addr -> IO ()
 heapFree heap flags addr =
   failIfFalse_ "HeapFree" $ c_HeapFree heap flags addr
-foreign import stdcall unsafe "windows.h HeapFree"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapFree"
   c_HeapFree :: HANDLE -> HeapAllocFlags -> Addr -> IO Bool
 
 heapLock :: HANDLE -> IO ()
 heapLock heap =
   failIfFalse_ "HeapLock" $ c_HeapLock heap
-foreign import stdcall unsafe "windows.h HeapLock"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapLock"
   c_HeapLock :: HANDLE -> IO Bool
 
 heapReAlloc :: HANDLE -> HeapAllocFlags -> Addr -> DWORD -> IO Addr
 heapReAlloc heap flags addr size =
   failIfNull "HeapReAlloc" $ c_HeapReAlloc heap flags addr size
-foreign import stdcall unsafe "windows.h HeapReAlloc"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapReAlloc"
   c_HeapReAlloc :: HANDLE -> HeapAllocFlags -> Addr -> DWORD -> IO Addr
 
 heapSize :: HANDLE -> HeapAllocFlags -> Addr -> IO DWORD
 heapSize heap flags addr =
   failIfZero "HeapSize" $ c_HeapSize heap flags addr
-foreign import stdcall unsafe "windows.h HeapSize"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapSize"
   c_HeapSize :: HANDLE -> HeapAllocFlags -> Addr -> IO DWORD
 
 heapUnlock :: HANDLE -> IO ()
 heapUnlock heap =
   failIfFalse_ "HeapUnlock" $ c_HeapUnlock heap
-foreign import stdcall unsafe "windows.h HeapUnlock"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapUnlock"
   c_HeapUnlock :: HANDLE -> IO Bool
 
-foreign import stdcall unsafe "windows.h HeapValidate"
+foreign import WINDOWS_CCONV unsafe "windows.h HeapValidate"
   heapValidate :: HANDLE -> HeapAllocFlags -> Addr -> IO Bool
 
 type VirtualAllocFlags = DWORD
@@ -219,29 +348,31 @@
 virtualAlloc :: Addr -> DWORD -> VirtualAllocFlags -> ProtectFlags -> IO Addr
 virtualAlloc addt size ty flags =
   failIfNull "VirtualAlloc" $ c_VirtualAlloc addt size ty flags
-foreign import stdcall unsafe "windows.h VirtualAlloc"
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualAlloc"
   c_VirtualAlloc :: Addr -> DWORD -> DWORD -> DWORD -> IO Addr
 
--- %fun VirtualAllocEx :: HANDLE -> Addr -> DWORD -> VirtualAllocFlags -> ProtectFlags ->IO Addr
--- %code extern LPVOID WINAPI VirtualAllocEx(HANDLE,LPVOID,DWORD,DWORD,DWORD);
--- %     LPVOID res1=VirtualAllocEx(arg1,arg2,arg3,arg4,arg5);
--- %fail {res1==NULL}{ErrorWin("VirtualAllocEx")}
+virtualAllocEx :: HANDLE -> Addr -> DWORD -> VirtualAllocFlags -> ProtectFlags -> IO Addr
+virtualAllocEx proc addt size ty flags =
+  failIfNull "VirtualAllocEx" $ c_VirtualAllocEx proc addt size ty flags
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualAllocEx"
+  c_VirtualAllocEx :: HANDLE -> Addr -> DWORD -> DWORD -> DWORD -> IO Addr
 
 virtualFree :: Addr -> DWORD -> FreeFlags -> IO ()
 virtualFree addr size flags =
   failIfFalse_ "VirtualFree" $ c_VirtualFree addr size flags
-foreign import stdcall unsafe "windows.h VirtualFree"
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualFree"
   c_VirtualFree :: Addr -> DWORD -> FreeFlags -> IO Bool
 
--- %fun VirtualFreeEx :: HANDLE -> Addr -> DWORD -> FreeFlags -> IO ()
--- %code extern BOOL WINAPI VirtualFreeEx(HANDLE,LPVOID,DWORD,DWORD);
--- %     BOOL res1=VirtualFreeEx(arg1,arg2,arg3,arg4);
--- %fail {res1=0}{ErrorWin("VirtualFreeEx")}
+virtualFreeEx :: HANDLE -> Addr -> DWORD -> FreeFlags -> IO ()
+virtualFreeEx proc addr size flags =
+  failIfFalse_ "VirtualFreeEx" $ c_VirtualFreeEx proc addr size flags
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualFreeEx"
+  c_VirtualFreeEx :: HANDLE -> Addr -> DWORD -> FreeFlags -> IO Bool
 
 virtualLock :: Addr -> DWORD -> IO ()
 virtualLock addr size =
   failIfFalse_ "VirtualLock" $ c_VirtualLock addr size
-foreign import stdcall unsafe "windows.h VirtualLock"
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualLock"
   c_VirtualLock :: Addr -> DWORD -> IO Bool
 
 virtualProtect :: Addr -> DWORD -> ProtectFlags -> IO ProtectFlags
@@ -249,7 +380,7 @@
   alloca $ \ p_old -> do
   failIfFalse_ "VirtualProtect" $ c_VirtualProtect addr size new_prot p_old
   peek p_old
-foreign import stdcall unsafe "windows.h VirtualProtect"
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualProtect"
   c_VirtualProtect :: Addr -> DWORD -> DWORD -> Ptr DWORD -> IO Bool
 
 virtualProtectEx :: HANDLE -> Addr -> DWORD -> ProtectFlags -> IO ProtectFlags
@@ -258,13 +389,17 @@
   failIfFalse_ "VirtualProtectEx" $
     c_VirtualProtectEx proc addr size new_prot p_old
   peek p_old
-foreign import stdcall unsafe "windows.h VirtualProtectEx"
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualProtectEx"
   c_VirtualProtectEx :: HANDLE -> Addr -> DWORD -> DWORD -> Ptr DWORD -> IO Bool
 
--- No VirtualQuery..()
+virtualQueryEx :: HANDLE -> LPVOID -> Ptr MEMORY_BASIC_INFORMATION -> SIZE_T -> IO DWORD
+virtualQueryEx hProcess lpAddress lpBuffer dwLength =
+  failIfZero "VirtualQueryEx" $ c_VirtualQueryEx hProcess lpAddress lpBuffer dwLength
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualQueryEx"
+  c_VirtualQueryEx :: HANDLE -> LPVOID -> Ptr MEMORY_BASIC_INFORMATION -> SIZE_T -> IO DWORD
 
 virtualUnlock :: Addr -> DWORD -> IO ()
 virtualUnlock addr size =
   failIfFalse_ "VirtualUnlock" $ c_VirtualUnlock addr size
-foreign import stdcall unsafe "windows.h VirtualUnlock"
+foreign import WINDOWS_CCONV unsafe "windows.h VirtualUnlock"
   c_VirtualUnlock :: Addr -> DWORD -> IO Bool
diff --git a/System/Win32/MinTTY.hsc b/System/Win32/MinTTY.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/MinTTY.hsc
@@ -0,0 +1,245 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.MinTTY
+-- Copyright   :  (c) University of Glasgow 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A function to check if the current terminal uses an old version of MinTTY
+-- that emulates a TTY. Note, however, that this does not check for more recent
+-- versions of MinTTY that use the native Windows console PTY directly. The old
+-- approach (where MinTTY emulates a TTY) sometimes requires different
+-- approaches to handling keyboard inputs.
+--
+-- Much of this code was originally authored by Phil Ruffwind and the
+-- git-for-windows project.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.MinTTY (isMinTTY, isMinTTYHandle) where
+
+import Graphics.Win32.Misc
+import System.Win32.DLL
+import System.Win32.File
+import System.Win32.Types
+
+#if MIN_VERSION_base(4,6,0)
+import Control.Exception (catch)
+#endif
+import Data.List (isInfixOf)
+import Foreign
+import Foreign.C.Types
+
+#if __GLASGOW_HASKELL__ < 711
+#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__)
+#endif
+
+-- The headers that are shipped with GHC's copy of MinGW-w64 assume Windows XP.
+-- Since we need some structs that are only available with Vista or later,
+-- we must manually set WINVER/_WIN32_WINNT accordingly.
+#undef WINVER
+#define WINVER 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+##include "windows_cconv.h"
+#include <windows.h>
+#include "winternl_compat.h"
+
+-- | Returns 'True' if the current process's standard error is attached to an
+-- emulated MinTTY console (e.g., Cygwin or MSYS that use an old version of
+-- MinTTY). Returns 'False' otherwise.
+isMinTTY :: IO Bool
+isMinTTY = do
+    h <- getStdHandle sTD_ERROR_HANDLE
+           `catch` \(_ :: IOError) ->
+             return nullHANDLE
+    if h == nullHANDLE
+       then return False
+       else isMinTTYHandle h
+
+-- | Returns 'True' is the given handle is attached to an emulated MinTTY
+-- console (e.g., Cygwin or MSYS that use an old version of MinTTY). Returns
+-- 'False' otherwise.
+isMinTTYHandle :: HANDLE -> IO Bool
+isMinTTYHandle h = do
+    fileType <- getFileType h
+    if fileType /= fILE_TYPE_PIPE
+      then return False
+      else isMinTTYVista h `catch` \(_ :: IOError) -> isMinTTYCompat h
+      -- GetFileNameByHandleEx is only available on Vista and later (hence
+      -- the name isMinTTYVista). If we're on an older version of Windows,
+      -- getProcAddress will throw an IOException when it fails to find
+      -- GetFileNameByHandleEx, and thus we will default to using
+      -- NtQueryObject (isMinTTYCompat).
+
+isMinTTYVista :: HANDLE -> IO Bool
+isMinTTYVista h = do
+    fn <- getFileNameByHandle h
+    return $ cygwinMSYSCheck fn
+  `catch` \(_ :: IOError) ->
+    return False
+
+isMinTTYCompat :: HANDLE -> IO Bool
+isMinTTYCompat h = do
+    fn <- ntQueryObjectNameInformation h
+    return $ cygwinMSYSCheck fn
+  `catch` \(_ :: IOError) ->
+    return False
+
+cygwinMSYSCheck :: String -> Bool
+cygwinMSYSCheck fn = ("cygwin-" `isInfixOf` fn || "msys-" `isInfixOf` fn) &&
+            "-pty" `isInfixOf` fn
+-- Note that GetFileInformationByHandleEx might return a filepath like:
+--
+--    \msys-dd50a72ab4668b33-pty1-to-master
+--
+-- But NtQueryObject might return something like:
+--
+--    \Device\NamedPipe\msys-dd50a72ab4668b33-pty1-to-master
+--
+-- This means we can't rely on "\cygwin-" or "\msys-" being at the very start
+-- of the filepath. As a result, we use `isPrefixOf` to check for "cygwin" and
+-- "msys".
+--
+-- It's unclear if "-master" will always appear in the filepath name. Recent
+-- versions of MinTTY have been known to give filepaths like this (#186):
+--
+--    \msys-dd50a72ab4668b33-pty0-to-master-nat
+--
+-- Just in case MinTTY ever changes this convention, we don't bother checking
+-- for the presence of "-master" in the filepath name at all.
+
+getFileNameByHandle :: HANDLE -> IO String
+getFileNameByHandle h = do
+  let sizeOfDWORD = sizeOf (undefined :: DWORD)
+      -- note: implicitly assuming that DWORD has stronger alignment than wchar_t
+      bufSize     = sizeOfDWORD + mAX_PATH * sizeOfTCHAR
+  allocaBytes bufSize $ \buf -> do
+    getFileInformationByHandleEx h fileNameInfo buf (fromIntegral bufSize)
+    fni <- peek buf
+    return $ fniFileName fni
+
+getFileInformationByHandleEx
+  :: HANDLE -> CInt -> Ptr FILE_NAME_INFO -> DWORD -> IO ()
+getFileInformationByHandleEx h cls buf bufSize = do
+  lib <- getModuleHandle (Just "kernel32.dll")
+  ptr <- getProcAddress lib "GetFileInformationByHandleEx"
+  let c_GetFileInformationByHandleEx =
+        mk_GetFileInformationByHandleEx (castPtrToFunPtr ptr)
+  failIfFalse_ "getFileInformationByHandleEx"
+    (c_GetFileInformationByHandleEx h cls buf bufSize)
+
+ntQueryObjectNameInformation :: HANDLE -> IO String
+ntQueryObjectNameInformation h = do
+  let sizeOfONI = sizeOf (undefined :: OBJECT_NAME_INFORMATION)
+      bufSize   = sizeOfONI + mAX_PATH * sizeOfTCHAR
+  allocaBytes bufSize $ \buf ->
+    alloca $ \p_len -> do
+      hwnd <- getModuleHandle (Just "ntdll.exe")
+      addr <- getProcAddress hwnd "NtQueryObject"
+      let c_NtQueryObject = mk_NtQueryObject (castPtrToFunPtr addr)
+      _ <- failIfNeg "NtQueryObject" $ c_NtQueryObject
+             h objectNameInformation buf (fromIntegral bufSize) p_len
+      oni <- peek buf
+      return $ usBuffer $ oniName oni
+
+fileNameInfo :: CInt
+fileNameInfo = #const FileNameInfo
+
+mAX_PATH :: Num a => a
+mAX_PATH = #const MAX_PATH
+
+objectNameInformation :: CInt
+objectNameInformation = #const ObjectNameInformation
+
+type F_NtQueryObject = HANDLE -> CInt -> Ptr OBJECT_NAME_INFORMATION
+                     -> ULONG -> Ptr ULONG -> IO NTSTATUS
+
+foreign import WINDOWS_CCONV "dynamic"
+  mk_NtQueryObject :: FunPtr F_NtQueryObject -> F_NtQueryObject
+
+type F_GetFileInformationByHandleEx =
+  HANDLE -> CInt -> Ptr FILE_NAME_INFO -> DWORD -> IO BOOL
+
+foreign import WINDOWS_CCONV "dynamic"
+  mk_GetFileInformationByHandleEx
+    :: FunPtr F_GetFileInformationByHandleEx -> F_GetFileInformationByHandleEx
+
+data FILE_NAME_INFO = FILE_NAME_INFO
+  { fniFileNameLength :: DWORD
+  , fniFileName       :: String
+  } deriving Show
+
+instance Storable FILE_NAME_INFO where
+    sizeOf    _ = #size      FILE_NAME_INFO
+    alignment _ = #alignment FILE_NAME_INFO
+    poke buf fni = withTStringLen (fniFileName fni) $ \(str, len) -> do
+        let len'  = (min mAX_PATH len) * sizeOfTCHAR
+            start = advancePtr (castPtr buf) (#offset FILE_NAME_INFO, FileName)
+            end   = advancePtr start len'
+        (#poke FILE_NAME_INFO, FileNameLength) buf len'
+        copyArray start (castPtr str :: Ptr Word8) len'
+        poke (castPtr end) (0 :: TCHAR)
+    peek buf = do
+        vfniFileNameLength <- (#peek FILE_NAME_INFO, FileNameLength) buf
+        let len = fromIntegral vfniFileNameLength `div` sizeOfTCHAR
+        vfniFileName <- peekTStringLen (plusPtr buf (#offset FILE_NAME_INFO, FileName), len)
+        return $ FILE_NAME_INFO
+          { fniFileNameLength = vfniFileNameLength
+          , fniFileName       = vfniFileName
+          }
+
+type NTSTATUS = #type NTSTATUS
+
+newtype OBJECT_NAME_INFORMATION = OBJECT_NAME_INFORMATION
+  { oniName :: UNICODE_STRING
+  } deriving Show
+
+instance Storable OBJECT_NAME_INFORMATION where
+    sizeOf    _ = #size      OBJECT_NAME_INFORMATION
+    alignment _ = #alignment OBJECT_NAME_INFORMATION
+    poke buf oni = (#poke OBJECT_NAME_INFORMATION, Name) buf (oniName oni)
+    peek buf = fmap OBJECT_NAME_INFORMATION $ (#peek OBJECT_NAME_INFORMATION, Name) buf
+
+data UNICODE_STRING = UNICODE_STRING
+  { usLength        :: USHORT
+  , usMaximumLength :: USHORT
+  , usBuffer        :: String
+  } deriving Show
+
+instance Storable UNICODE_STRING where
+    sizeOf    _ = #size      UNICODE_STRING
+    alignment _ = #alignment UNICODE_STRING
+    poke buf us = withTStringLen (usBuffer us) $ \(str, len) -> do
+        let len'  = (min mAX_PATH len) * sizeOfTCHAR
+            start = advancePtr (castPtr buf) (#size UNICODE_STRING)
+            end   = advancePtr start len'
+        (#poke UNICODE_STRING, Length)        buf len'
+        (#poke UNICODE_STRING, MaximumLength) buf (len' + sizeOfTCHAR)
+        (#poke UNICODE_STRING, Buffer)        buf start
+        copyArray start (castPtr str :: Ptr Word8) len'
+        poke (castPtr end) (0 :: TCHAR)
+    peek buf = do
+        vusLength        <- (#peek UNICODE_STRING, Length)        buf
+        vusMaximumLength <- (#peek UNICODE_STRING, MaximumLength) buf
+        vusBufferPtr     <- (#peek UNICODE_STRING, Buffer)        buf
+        let len          =  fromIntegral vusLength `div` sizeOfTCHAR
+        vusBuffer        <- peekTStringLen (vusBufferPtr, len)
+        return $ UNICODE_STRING
+          { usLength        = vusLength
+          , usMaximumLength = vusMaximumLength
+          , usBuffer        = vusBuffer
+          }
+
+sizeOfTCHAR :: Int
+sizeOfTCHAR = sizeOf (undefined :: TCHAR)
diff --git a/System/Win32/NLS.hsc b/System/Win32/NLS.hsc
--- a/System/Win32/NLS.hsc
+++ b/System/Win32/NLS.hsc
@@ -1,6 +1,5 @@
-#if __GLASGOW_HASKELL__ >= 701
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  System.Win32.NLS
@@ -16,35 +15,70 @@
 -----------------------------------------------------------------------------
 
 module System.Win32.NLS  (
-	module System.Win32.NLS,
+        module System.Win32.NLS,
+#if MIN_VERSION_base(4,15,0)
+        CodePage,
+#endif
 
-	-- defined in System.Win32.Types
-	LCID, LANGID, SortID, SubLANGID, PrimaryLANGID,
-	mAKELCID, lANGIDFROMLCID, sORTIDFROMLCID,
-	mAKELANGID, pRIMARYLANGID, sUBLANGID
-	) where
+        -- defined in System.Win32.Types
+        LCID, LANGID, SortID, SubLANGID, PrimaryLANGID,
+        mAKELCID, lANGIDFROMLCID, sORTIDFROMLCID,
+        mAKELANGID, pRIMARYLANGID, sUBLANGID
+        ) where
 
+import System.Win32.String (withTStringBufferLen)
 import System.Win32.Types
+import System.Win32.Utils (trySized)
 
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative ((<$>), (<*>))
+#endif
+import Control.Monad (when)
+import Data.IORef (modifyIORef, newIORef, readIORef)
 import Foreign
 import Foreign.C
+#if MIN_VERSION_base(4,15,0)
+import GHC.IO.Encoding.CodePage (CodePage)
+#endif
+import Text.Printf (printf)
 
+##include "windows_cconv.h"
+
+-- Somewhere, WINVER and _WIN32_WINNT are being defined as less than 0x0600 -
+-- that is, before Windows Vista. Support for Windows XP was dropped in
+-- GHC 8.0.1 of May 2016. This forces them to be at least 0x0600.
+#if WINVER < 0x0600
+#undef WINVER
+#define WINVER 0x0600
+#endif
+#if _WIN32_WINNT < 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
 #include <windows.h>
+#include "alignment.h"
 #include "errors.h"
 #include "win32debug.h"
+#include "winnls_compat.h"
+#include "winnt_compat.h"
 
+type NLS_FUNCTION = DWORD
+
 #{enum LCID,
  , lOCALE_SYSTEM_DEFAULT = LOCALE_SYSTEM_DEFAULT
  , lOCALE_USER_DEFAULT   = LOCALE_USER_DEFAULT
  , lOCALE_NEUTRAL        = LOCALE_NEUTRAL
  }
 
-foreign import stdcall unsafe "windows.h ConvertDefaultLocale"
+foreign import WINDOWS_CCONV unsafe "windows.h ConvertDefaultLocale"
   convertDefaultLocale :: LCID -> IO LCID
 
--- ToDo: various enum functions.
+-- TODO: various enum functions.
 
+#if !MIN_VERSION_base(4,15,0)
 type CodePage = UINT
+#endif
 
 #{enum CodePage,
  , cP_ACP       = CP_ACP
@@ -52,74 +86,402 @@
  , cP_OEMCP     = CP_OEMCP
  }
 
-foreign import stdcall unsafe "windows.h GetACP"
+foreign import WINDOWS_CCONV unsafe "windows.h GetACP"
   getACP :: IO CodePage
 
-foreign import stdcall unsafe "windows.h SetThreadLocale"
+foreign import WINDOWS_CCONV unsafe "windows.h SetThreadLocale"
   setThreadLocale :: LCID -> IO ()
 
 type LCTYPE = UINT
 
+-- The following locale information constants are excluded from the `enum` list
+-- below, for the reason indicated:
+-- LOCALE_IDIALINGCODE -- Introduced in Windows 10 but not supported. Synonym
+                       -- for LOCALE_ICOUNTRY.
+-- LOCALE_INEGATIVEPERCENT -- Introduced in Windows 7 but not supported.
+-- LOCALE_IPOSITIVEPERCENT -- Introduced in Windows 7 but not supported.
+-- LOCALE_IREADINGLAYOUT -- Introduced in Windows 7 but not supported.
+-- LOCALE_SAM -- Introduced by Windows 10 but not supported. Synonym for
+              -- LOCALE_S1159.
+-- LOCALE_SENGLISHDISPLAYNAME -- Introduced in Windows 7 but not supported.
+-- LOCALE_SIETFLANGUAGE -- Not supported (deprecated from Windows Vista).
+-- LOCALE_SNATIVEDISPLAYNAME -- Introduced in Windows 7 but not supported.
+-- LOCALE_SNATIVELANGUAGENAME -- Introduced in Windows 7 but not supported.
+-- LOCALE_SPERCENT -- Introduced in Windows 7 but not supported.
+-- LOCALE_SPM -- Introduced in Windows 10 but not supported. Synonym for
+              -- LOCALE_S2359.
+-- LOCALE_SSHORTESTAM -- Not supported.
+-- LOCALE_SSHORTESTPM -- Not supported.
+-- LOCALE_SSHORTTIME -- Introduced in Windows 7 but not supported.
+
+-- The following locale information constant is included in the list below, but
+-- note:
+-- LOCALE_IINTLCURRDIGITS -- Not supported by Windows 10, use
+                          -- LOCALE_ICURRDIGITS.
+
 #{enum LCTYPE,
+ , lOCALE_FONTSIGNATURE = LOCALE_FONTSIGNATURE
  , lOCALE_ICALENDARTYPE = LOCALE_ICALENDARTYPE
- , lOCALE_SDATE         = LOCALE_SDATE
+ , lOCALE_ICENTURY      = LOCALE_ICENTURY
+ , lOCALE_ICOUNTRY      = LOCALE_ICOUNTRY
  , lOCALE_ICURRDIGITS   = LOCALE_ICURRDIGITS
- , lOCALE_SDECIMAL      = LOCALE_SDECIMAL
  , lOCALE_ICURRENCY     = LOCALE_ICURRENCY
- , lOCALE_SGROUPING     = LOCALE_SGROUPING
+ , lOCALE_IDATE         = LOCALE_IDATE
+ , lOCALE_IDAYLZERO     = LOCALE_IDAYLZERO
+ , lOCALE_IDEFAULTANSICODEPAGE = LOCALE_IDEFAULTANSICODEPAGE
+ , lOCALE_IDEFAULTCODEPAGE = LOCALE_IDEFAULTCODEPAGE
+ , lOCALE_IDEFAULTCOUNTRY = LOCALE_IDEFAULTCOUNTRY
+ , lOCALE_IDEFAULTEBCDICCODEPAGE = LOCALE_IDEFAULTEBCDICCODEPAGE
+ , lOCALE_IDEFAULTLANGUAGE = LOCALE_IDEFAULTLANGUAGE
+ , lOCALE_IDEFAULTMACCODEPAGE = LOCALE_IDEFAULTMACCODEPAGE
  , lOCALE_IDIGITS       = LOCALE_IDIGITS
- , lOCALE_SLIST         = LOCALE_SLIST
+ , lOCALE_IDIGITSUBSTITUTION = LOCALE_IDIGITSUBSTITUTION
  , lOCALE_IFIRSTDAYOFWEEK = LOCALE_IFIRSTDAYOFWEEK
- , lOCALE_SLONGDATE     = LOCALE_SLONGDATE
  , lOCALE_IFIRSTWEEKOFYEAR = LOCALE_IFIRSTWEEKOFYEAR
- , lOCALE_SMONDECIMALSEP = LOCALE_SMONDECIMALSEP
+ , lOCALE_IGEOID        = LOCALE_IGEOID
+ , lOCALE_IINTLCURRDIGITS = LOCALE_IINTLCURRDIGITS
+ , lOCALE_ILANGUAGE     = LOCALE_ILANGUAGE
+ , lOCALE_ILDATE        = LOCALE_ILDATE
  , lOCALE_ILZERO        = LOCALE_ILZERO
- , lOCALE_SMONGROUPING  = LOCALE_SMONGROUPING
  , lOCALE_IMEASURE      = LOCALE_IMEASURE
- , lOCALE_SMONTHOUSANDSEP = LOCALE_SMONTHOUSANDSEP
+ , lOCALE_IMONLZERO     = LOCALE_IMONLZERO
  , lOCALE_INEGCURR      = LOCALE_INEGCURR
- , lOCALE_SNEGATIVESIGN = LOCALE_SNEGATIVESIGN
  , lOCALE_INEGNUMBER    = LOCALE_INEGNUMBER
+ , lOCALE_INEGSEPBYSPACE = LOCALE_INEGSEPBYSPACE
+ , lOCALE_INEGSIGNPOSN   = LOCALE_INEGSIGNPOSN
+ , lOCALE_INEGSYMPRECEDES = LOCALE_INEGSYMPRECEDES
+ , lOCALE_IOPTIONALCALENDAR = LOCALE_IOPTIONALCALENDAR
+ , lOCALE_PAPERSIZE     = LOCALE_IPAPERSIZE
+ , lOCALE_IPOSSEPBYSPACE = LOCALE_IPOSSEPBYSPACE
+ , lOCALE_IPOSSIGNPOSN  = LOCALE_IPOSSIGNPOSN
+ , lOCALE_IPSSYMPRECEDES = LOCALE_IPOSSYMPRECEDES
+ , lOCALE_ITIME         = LOCALE_ITIME
+ , lOCALE_ITIMEMARKPOSN = LOCALE_ITIMEMARKPOSN
+ , lOCALE_ITLZERO       = LOCALE_ITLZERO
+ , lOCALE_RETURN_NUMBER = LOCALE_RETURN_NUMBER
+ , lOCALE_S1159         = LOCALE_S1159
+ , lOCALE_S2359         = LOCALE_S2359
+ , lOCALE_SABBREVCTRYNAME = LOCALE_SABBREVCTRYNAME
+ , lOCALE_SABBREVDAYNAME1 = LOCALE_SABBREVDAYNAME1
+ , lOCALE_SABBREVDAYNAME2 = LOCALE_SABBREVDAYNAME2
+ , lOCALE_SABBREVDAYNAME3 = LOCALE_SABBREVDAYNAME3
+ , lOCALE_SABBREVDAYNAME4 = LOCALE_SABBREVDAYNAME4
+ , lOCALE_SABBREVDAYNAME5 = LOCALE_SABBREVDAYNAME5
+ , lOCALE_SABBREVDAYNAME6 = LOCALE_SABBREVDAYNAME6
+ , lOCALE_SABBREVDAYNAME7 = LOCALE_SABBREVDAYNAME7
+ , lOCALE_SABBREVLANGNAME = LOCALE_SABBREVLANGNAME
+ , lOCALE_SABBREVMONTHNAME1 = LOCALE_SABBREVMONTHNAME1
+ , lOCALE_SABBREVMONTHNAME2 = LOCALE_SABBREVMONTHNAME2
+ , lOCALE_SABBREVMONTHNAME3 = LOCALE_SABBREVMONTHNAME3
+ , lOCALE_SABBREVMONTHNAME4 = LOCALE_SABBREVMONTHNAME4
+ , lOCALE_SABBREVMONTHNAME5 = LOCALE_SABBREVMONTHNAME5
+ , lOCALE_SABBREVMONTHNAME6 = LOCALE_SABBREVMONTHNAME6
+ , lOCALE_SABBREVMONTHNAME7 = LOCALE_SABBREVMONTHNAME7
+ , lOCALE_SABBREVMONTHNAME8 = LOCALE_SABBREVMONTHNAME8
+ , lOCALE_SABBREVMONTHNAME9 = LOCALE_SABBREVMONTHNAME9
+ , lOCALE_SABBREVMONTHNAME10 = LOCALE_SABBREVMONTHNAME10
+ , lOCALE_SABBREVMONTHNAME11 = LOCALE_SABBREVMONTHNAME11
+ , lOCALE_SABBREVMONTHNAME12 = LOCALE_SABBREVMONTHNAME12
+ , lOCALE_SABBREVMONTHNAME13 = LOCALE_SABBREVMONTHNAME13
+ , lOCALE_SCONSOLEFALLBACKNAME = LOCALE_SCONSOLEFALLBACKNAME
+ , lOCALE_SCURRENCY     = LOCALE_SCURRENCY
+ , lOCALE_SDATE         = LOCALE_SDATE
+ , lOCALE_SDAYNAME1     = LOCALE_SDAYNAME1
+ , lOCALE_SDAYNAME2     = LOCALE_SDAYNAME2
+ , lOCALE_SDAYNAME3     = LOCALE_SDAYNAME3
+ , lOCALE_SDAYNAME4     = LOCALE_SDAYNAME4
+ , lOCALE_SDAYNAME5     = LOCALE_SDAYNAME5
+ , lOCALE_SDAYNAME6     = LOCALE_SDAYNAME6
+ , lOCALE_SDAYNAME7     = LOCALE_SDAYNAME7
+ , lOCALE_SDECIMAL      = LOCALE_SDECIMAL
+ , lOCALE_SDURATION     = LOCALE_SDURATION
+ , lOCALE_SENGCURRNAME  = LOCALE_SENGCURRNAME
+ , lOCALE_SENGLISHCOUNTRYNAME = LOCALE_SENGLISHCOUNTRYNAME
+ , lOCALE_SENGLISHLANGUAGENAME = LOCALE_SENGLISHLANGUAGENAME
+ , lOCALE_SGROUPING     = LOCALE_SGROUPING
+ , lOCALE_SINTLSYMBOL   = LOCALE_SINTLSYMBOL
+ , lOCALE_SISO3166CTRYNAME = LOCALE_SISO3166CTRYNAME
+ , lOCALE_SISO3166CTRYNAME2 = LOCALE_SISO3166CTRYNAME2
+ , lOCALE_SISO639LANGNAME = LOCALE_SISO639LANGNAME
+ , lOCALE_SISO639LANGNAME2 = LOCALE_SISO639LANGNAME2
+ , lOCALE_SKEYBOARDSTOINSTALL = LOCALE_SKEYBOARDSTOINSTALL
+ , lOCALE_SLIST         = LOCALE_SLIST
+ , lOCALE_SLONGDATE     = LOCALE_SLONGDATE
+ , lOCALE_SMONDECIMALSEP = LOCALE_SMONDECIMALSEP
+ , lOCALE_SMONGROUPING  = LOCALE_SMONGROUPING
+ , lOCALE_SMONTHNAME1   = LOCALE_SMONTHNAME1
+ , lOCALE_SMONTHNAME2   = LOCALE_SMONTHNAME2
+ , lOCALE_SMONTHNAME3   = LOCALE_SMONTHNAME3
+ , lOCALE_SMONTHNAME4   = LOCALE_SMONTHNAME4
+ , lOCALE_SMONTHNAME5   = LOCALE_SMONTHNAME5
+ , lOCALE_SMONTHNAME6   = LOCALE_SMONTHNAME6
+ , lOCALE_SMONTHNAME7   = LOCALE_SMONTHNAME7
+ , lOCALE_SMONTHNAME8   = LOCALE_SMONTHNAME8
+ , lOCALE_SMONTHNAME9   = LOCALE_SMONTHNAME9
+ , lOCALE_SMONTHNAME10  = LOCALE_SMONTHNAME10
+ , lOCALE_SMONTHNAME11  = LOCALE_SMONTHNAME11
+ , lOCALE_SMONTHNAME12  = LOCALE_SMONTHNAME12
+ , lOCALE_SMONTHNAME13  = LOCALE_SMONTHNAME13
+ , lOCALE_SMONTHOUSANDSEP = LOCALE_SMONTHOUSANDSEP
+ , lOCALE_SNAME         = LOCALE_SNAME
+ , lOCALE_SNAN          = LOCALE_SNAN
+ , lOCALE_SNATIVECOUNTRYNAME = LOCALE_SNATIVECOUNTRYNAME
+ , lOCALE_SNATIVECURRNAME = LOCALE_SNATIVECURRNAME
+ , lOCALE_SNATIVEDIGITS = LOCALE_SNATIVEDIGITS
+ , lOCALE_SNEGATIVESIGN = LOCALE_SNEGATIVESIGN
+ , lOCALE_SNEGINFINITY  = LOCALE_SNEGINFINITY
+ , lOCALE_SPARENT       = LOCALE_SPARENT
+ , lOCALE_SPOSINFINITY  = LOCALE_SPOSINFINITY
  , lOCALE_SPOSITIVESIGN = LOCALE_SPOSITIVESIGN
+ , lOCALE_SSCRIPTS      = LOCALE_SSCRIPTS
  , lOCALE_SSHORTDATE    = LOCALE_SSHORTDATE
- , lOCALE_ITIME         = LOCALE_ITIME
+ , lOCALE_SSHORTESTDAYNAME1 = LOCALE_SSHORTESTDAYNAME1
+ , lOCALE_SSHORTESTDAYNAME2 = LOCALE_SSHORTESTDAYNAME2
+ , lOCALE_SSHORTESTDAYNAME3 = LOCALE_SSHORTESTDAYNAME3
+ , lOCALE_SSHORTESTDAYNAME4 = LOCALE_SSHORTESTDAYNAME4
+ , lOCALE_SSHORTESTDAYNAME5 = LOCALE_SSHORTESTDAYNAME5
+ , lOCALE_SSHORTESTDAYNAME6 = LOCALE_SSHORTESTDAYNAME6
+ , lOCALE_SSHORTESTDAYNAME7 = LOCALE_SSHORTESTDAYNAME7
+ , lOCALE_SSORTNAME     = LOCALE_SSORTNAME
  , lOCALE_STHOUSAND     = LOCALE_STHOUSAND
- , lOCALE_S1159         = LOCALE_S1159
  , lOCALE_STIME         = LOCALE_STIME
- , lOCALE_S2359         = LOCALE_S2359
  , lOCALE_STIMEFORMAT   = LOCALE_STIMEFORMAT
- , lOCALE_SCURRENCY     = LOCALE_SCURRENCY
+ , lOCALE_SYEARMONTH    = LOCALE_SYEARMONTH
  }
 
+-- |Type representing locale data
+data LCData
+  -- | Data in the form of a Unicode string.
+  = LCTextualData !String
+  -- | Data in the form of a number. See 'lOCAL_RETURN_NUMBER' and @LOCAL_I*@
+  -- locate information constants.
+  | LCNumericData !DWORD
+  -- | Data in the fomr of a 'LOCALESIGNATURE'. See 'lOCAL_FONTSIGNATURE' locale
+  -- information constant.
+  | LCSignatureData !LOCALESIGNATURE
+  deriving (Eq, Show)
+
+data LOCALESIGNATURE = LOCALESIGNATURE
+  { lsUsb :: !UnicodeSubsetBitfield
+  , lsCsbDefault :: !DDWORD
+  , lsCsbSupported :: !DDWORD
+  } deriving (Eq, Show)
+
+instance Storable LOCALESIGNATURE where
+  sizeOf = const #{size LOCALESIGNATURE}
+  alignment _ = #alignment LOCALESIGNATURE
+  peek buf = do
+    lsUsb'          <- (#peek LOCALESIGNATURE, lsUsb) buf
+    lsCsbDefault'   <- (#peek LOCALESIGNATURE, lsCsbDefault) buf
+    lsCsbSupported' <- (#peek LOCALESIGNATURE, lsCsbSupported) buf
+    return $ LOCALESIGNATURE lsUsb' lsCsbDefault' lsCsbSupported'
+  poke buf info = do
+    (#poke LOCALESIGNATURE, lsUsb) buf (lsUsb info)
+    (#poke LOCALESIGNATURE, lsCsbDefault) buf (lsCsbDefault info)
+    (#poke LOCALESIGNATURE, lsCsbSupported) buf (lsCsbSupported info)
+
+-- | Type representing 128-bit Unicode subset bitfields, as the @base@ package
+-- does include a module exporting a 128-bit unsigned integer type.
+data UnicodeSubsetBitfield = UnicodeSubsetBitfield
+  { usbLow :: !DDWORD
+  , usbHigh :: !DDWORD
+  } deriving (Eq, Show)
+
+instance Storable UnicodeSubsetBitfield where
+  sizeOf _ = 2 * sizeOf (undefined :: DDWORD)
+  alignment _ = alignment (undefined :: DWORD)
+  peek buf = do
+    usbLow'  <- (peekByteOff buf 0 :: IO DDWORD)
+    usbHigh' <- (peekByteOff buf (sizeOf (undefined :: DDWORD)) :: IO DDWORD)
+    return $ UnicodeSubsetBitfield usbLow' usbHigh'
+  poke buf info = do
+    pokeByteOff buf 0 (usbLow info)
+    pokeByteOff buf (sizeOf (undefined :: DDWORD)) (usbHigh info)
+
+getLocaleInfoEx :: Maybe String -> LCTYPE -> IO LCData
+getLocaleInfoEx locale ty
+  | ty == lOCALE_FONTSIGNATURE =
+      getLocaleInfoEx' LCSignatureData localSigCharCount
+
+  | ty .&. lOCALE_RETURN_NUMBER /= 0 =
+      getLocaleInfoEx' LCNumericData dWORDCharCount
+
+  | otherwise = maybeWith withTString locale $ \c_locale ->
+      LCTextualData <$> trySized cFuncName (c_GetLocaleInfoEx c_locale ty)
+ where
+  cFuncName = "GetLocaleInfoEx"
+  -- See https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getlocaleinfoex
+  localSigCharCount = (#size LOCALESIGNATURE) `div` (#size WCHAR)
+  dWORDCharCount = (#size DWORD) `div` (#size WCHAR)
+
+  getLocaleInfoEx' constructor bufSize = maybeWith withTString locale $
+    \c_locale -> do
+      value <- alloca $ \buf -> do
+        _ <- failIfZero cFuncName
+          $ c_GetLocaleInfoEx c_locale ty (castPtr buf) bufSize
+        peek buf
+      return $ constructor value
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetLocaleInfoEx"
+  c_GetLocaleInfoEx :: LPCWSTR -> LCTYPE -> LPWSTR -> CInt -> IO CInt
+
 setLocaleInfo :: LCID -> LCTYPE -> String -> IO ()
 setLocaleInfo locale ty info =
   withTString info $ \ c_info ->
   failIfFalse_ "SetLocaleInfo" $ c_SetLocaleInfo locale ty c_info
-foreign import stdcall unsafe "windows.h SetLocaleInfoW"
+foreign import WINDOWS_CCONV unsafe "windows.h SetLocaleInfoW"
   c_SetLocaleInfo :: LCID -> LCTYPE -> LPCTSTR -> IO Bool
 
 type LCMapFlags = DWORD
 
 #{enum LCMapFlags,
- , lCMAP_BYTEREV        = LCMAP_BYTEREV
- , lCMAP_FULLWIDTH      = LCMAP_FULLWIDTH
- , lCMAP_HALFWIDTH      = LCMAP_HALFWIDTH
- , lCMAP_HIRAGANA       = LCMAP_HIRAGANA
- , lCMAP_KATAKANA       = LCMAP_KATAKANA
- , lCMAP_LOWERCASE      = LCMAP_LOWERCASE
- , lCMAP_SORTKEY        = LCMAP_SORTKEY
- , lCMAP_UPPERCASE      = LCMAP_UPPERCASE
- , nORM_IGNORECASE      = NORM_IGNORECASE
- , nORM_IGNORENONSPACE  = NORM_IGNORENONSPACE
- , nORM_IGNOREKANATYPE  = NORM_IGNOREKANATYPE
- , nORM_IGNORESYMBOLS   = NORM_IGNORESYMBOLS
- , nORM_IGNOREWIDTH     = NORM_IGNOREWIDTH
- , sORT_STRINGSORT      = SORT_STRINGSORT
- , lCMAP_LINGUISTIC_CASING      = LCMAP_LINGUISTIC_CASING
- , lCMAP_SIMPLIFIED_CHINESE     = LCMAP_SIMPLIFIED_CHINESE
- , lCMAP_TRADITIONAL_CHINESE    = LCMAP_TRADITIONAL_CHINESE
+ , lCMAP_BYTEREV              = LCMAP_BYTEREV
+ , lCMAP_FULLWIDTH            = LCMAP_FULLWIDTH
+ , lCMAP_HALFWIDTH            = LCMAP_HALFWIDTH
+ , lCMAP_HIRAGANA             = LCMAP_HIRAGANA
+ , lCMAP_KATAKANA             = LCMAP_KATAKANA
+ , lCMAP_LINGUISTIC_CASING    = LCMAP_LINGUISTIC_CASING
+ , lCMAP_LOWERCASE            = LCMAP_LOWERCASE
+ , lCMAP_SIMPLIFIED_CHINESE   = LCMAP_SIMPLIFIED_CHINESE
+ , lCMAP_SORTKEY              = LCMAP_SORTKEY
+ , lCMAP_TRADITIONAL_CHINESE  = LCMAP_TRADITIONAL_CHINESE
+ , lCMAP_UPPERCASE            = LCMAP_UPPERCASE
+ , lINGUISTIC_IGNORECASE      = LINGUISTIC_IGNORECASE
+ , lINGUISTIC_IGNOREDIACRITIC = LINGUISTIC_IGNOREDIACRITIC
+ , nORM_IGNORECASE            = NORM_IGNORECASE
+ , nORM_IGNORENONSPACE        = NORM_IGNORENONSPACE
+ , nORM_IGNOREKANATYPE        = NORM_IGNOREKANATYPE
+ , nORM_IGNORESYMBOLS         = NORM_IGNORESYMBOLS
+ , nORM_IGNOREWIDTH           = NORM_IGNOREWIDTH
+ , nORM_LINGUISTIC_CASING     = NORM_LINGUISTIC_CASING
+ , sORT_STRINGSORT            = SORT_STRINGSORT
  }
 
+data NLSVERSIONINFOEX = NLSVERSIONINFOEX
+  { dwNLSVersionInfoSize :: DWORD
+  , dwNLSVersion :: DWORD
+  , dwDefinedVersion :: DWORD
+  , dwEffectiveId :: DWORD
+  , guidCustomVersion :: GUID
+  } deriving (Eq, Show)
+
+instance Storable NLSVERSIONINFOEX where
+  sizeOf = const #{size NLSVERSIONINFOEX}
+  alignment _ = #alignment NLSVERSIONINFOEX
+  peek buf = do
+    dwNLSVersionInfoSize' <- (#peek NLSVERSIONINFOEX, dwNLSVersionInfoSize) buf
+    dwNLSVersion' <- (#peek NLSVERSIONINFOEX, dwNLSVersion) buf
+    dwDefinedVersion' <- (#peek NLSVERSIONINFOEX, dwDefinedVersion) buf
+    dwEffectiveId' <- (#peek NLSVERSIONINFOEX, dwEffectiveId) buf
+    guidCustomVersion' <- (#peek NLSVERSIONINFOEX, guidCustomVersion) buf
+    return $ NLSVERSIONINFOEX dwNLSVersionInfoSize' dwNLSVersion'
+               dwDefinedVersion' dwEffectiveId' guidCustomVersion'
+  poke buf info = do
+    (#poke NLSVERSIONINFOEX, dwNLSVersionInfoSize) buf
+      (dwNLSVersionInfoSize info)
+    (#poke NLSVERSIONINFOEX, dwNLSVersion) buf (dwNLSVersion info)
+    (#poke NLSVERSIONINFOEX, dwDefinedVersion) buf (dwDefinedVersion info)
+    (#poke NLSVERSIONINFOEX, dwEffectiveId) buf (dwEffectiveId info)
+    (#poke NLSVERSIONINFOEX, guidCustomVersion) buf (guidCustomVersion info)
+
+-- Based on the `UnpackedUUID` type of package `uuid-types`.
+data GUID = GUID
+  !Word32
+  !Word16
+  !Word16
+  !Word8
+  !Word8
+  !Word8
+  !Word8
+  !Word8
+  !Word8
+  !Word8
+  !Word8
+  deriving (Eq)
+
+instance Show GUID where
+  show (GUID data1 data2 data3 b1 b2 b3 b4 b5 b6 b7 b8) =
+    printf "{%.8x-%.4x-%.4x-%.2x%2x-%.2x%.2x%.2x%.2x%.2x%.2x}" data1 data2 data3 b1 b2 b3 b4 b5 b6 b7 b8
+
+instance Storable GUID where
+  sizeOf _ = 16
+  alignment _ = 4
+  peekByteOff p off = GUID
+    <$> peekByteOff p off
+    <*> peekByteOff p (off + 4)
+    <*> peekByteOff p (off + 6)
+    <*> peekByteOff p (off + 8)
+    <*> peekByteOff p (off + 9)
+    <*> peekByteOff p (off + 10)
+    <*> peekByteOff p (off + 11)
+    <*> peekByteOff p (off + 12)
+    <*> peekByteOff p (off + 13)
+    <*> peekByteOff p (off + 14)
+    <*> peekByteOff p (off + 15)
+  pokeByteOff p off (GUID data1 data2 data3 b1 b2 b3 b4 b5 b6 b7 b8) = do
+    pokeByteOff p off data1
+    pokeByteOff p (off + 4) data2
+    pokeByteOff p (off + 6) data3
+    pokeByteOff p (off + 8) b1
+    pokeByteOff p (off + 9) b2
+    pokeByteOff p (off + 10) b3
+    pokeByteOff p (off + 11) b4
+    pokeByteOff p (off + 12) b5
+    pokeByteOff p (off + 13) b6
+    pokeByteOff p (off + 14) b7
+    pokeByteOff p (off + 15) b8
+
+getNLSVersionEx :: Maybe String -> IO NLSVERSIONINFOEX
+getNLSVersionEx locale = maybeWith withTString locale $ \c_locale ->
+  with defaultVersionInfo $ \c_versionInfo -> do
+    failIfFalse_ "GetNLSVersionEx" $
+      c_GetNLSVersionEx function c_locale c_versionInfo
+    peek c_versionInfo
+ where
+  function = #{const COMPARE_STRING}
+  defaultVersionInfo = NLSVERSIONINFOEX
+    { dwNLSVersionInfoSize = #{size NLSVERSIONINFOEX}
+    , dwNLSVersion = 0
+    , dwDefinedVersion = 0
+    , dwEffectiveId = 0
+    , guidCustomVersion = GUID 0 0 0 0 0 0 0 0 0 0 0
+    }
+foreign import WINDOWS_CCONV unsafe "windows.h GetNLSVersionEx"
+  c_GetNLSVersionEx :: NLS_FUNCTION
+                    -> LPCWSTR
+                    -> Ptr NLSVERSIONINFOEX
+                    -> IO Bool
+
+lCMapStringEx :: Maybe String
+              -> LCMapFlags
+              -> String
+              -> NLSVERSIONINFOEX
+              -> IO String
+lCMapStringEx locale flags src versionInfo =
+  maybeWith withTString locale $ \c_locale ->
+    withTStringLen src $ \(c_src, src_len) ->
+      with versionInfo $ \c_versionInfo -> do
+        let c_src_len = fromIntegral src_len
+            c_func s l = c_LCMapStringEx c_locale
+                                         flags
+                                         c_src c_src_len
+                                         s l
+                                         c_versionInfo
+                                         nullPtr -- Reserved, must be NULL
+                                         0 -- Reserved, must be 0
+        trySized "LCMapStringEx" c_func
+foreign import WINDOWS_CCONV unsafe "windows.h LCMapStringEx"
+  c_LCMapStringEx :: LPCWSTR
+                  -> LCMapFlags
+                  -> LPCWSTR
+                  -> CInt
+                  -> LPWSTR
+                  -> CInt
+                  -> Ptr NLSVERSIONINFOEX
+                  -> LPVOID
+                  -> LPARAM
+                  -> IO CInt
+
 lCMapString :: LCID -> LCMapFlags -> String -> Int -> IO String
 lCMapString locale flags src dest_size =
   withTStringLen src $ \ (c_src, src_len) ->
@@ -127,7 +489,7 @@
   _ <- failIfZero "LCMapString" $
     c_LCMapString locale flags c_src src_len c_dest dest_size
   peekTString c_dest
-foreign import stdcall unsafe "windows.h LCMapStringW"
+foreign import WINDOWS_CCONV unsafe "windows.h LCMapStringW"
   c_LCMapString :: LCID -> LCMapFlags -> LPCTSTR -> Int -> LPCTSTR -> Int -> IO Int
 
 type LocaleTestFlags = DWORD
@@ -137,28 +499,124 @@
  , lCID_SUPPORTED       = LCID_SUPPORTED
  }
 
-foreign import stdcall unsafe "windows.h IsValidLocale"
+isValidLocaleName :: Maybe String -> IO Bool
+isValidLocaleName lpLocaleName =
+  maybeWith withTString lpLocaleName c_IsValidLocaleName
+foreign import WINDOWS_CCONV unsafe "windows.h IsValidLocaleName"
+  c_IsValidLocaleName :: LPCWSTR -> IO Bool
+
+foreign import WINDOWS_CCONV unsafe "windows.h IsValidLocale"
   isValidLocale :: LCID -> LocaleTestFlags -> IO Bool
 
-foreign import stdcall unsafe "windows.h IsValidCodePage"
+type EnumLocalesFlag = DWORD
+
+-- The following locale enumeration flag constants are excluded from the `enum`
+-- list below, for the reason indicated:
+-- LOCALE_NEUTRALDATA -- Introduced in Windows 7 but not supported.
+
+#{enum EnumLocalesFlag,
+ , lOCALE_ALL             = LOCALE_ALL
+ , lOCALE_ALTERNATE_SORTS = LOCALE_ALTERNATE_SORTS
+ , lOCALE_REPLACEMENT     = LOCALE_REPLACEMENT
+ , lOCALE_SUPPLEMENTAL    = LOCALE_SUPPLEMENTAL
+ , lOCALE_WINDOWS         = LOCALE_WINDOWS
+ }
+
+type LOCALE_ENUMPROCEX = LPWSTR -> EnumLocalesFlag -> LPARAM -> IO BOOL
+foreign import WINDOWS_CCONV "wrapper"
+  mkLOCALE_ENUMPROCEX :: LOCALE_ENUMPROCEX -> IO (FunPtr LOCALE_ENUMPROCEX)
+
+enumSystemLocalesEx :: LOCALE_ENUMPROCEX -> EnumLocalesFlag -> LPARAM -> IO ()
+enumSystemLocalesEx callback dwFlags lParam = do
+  c_callback <- mkLOCALE_ENUMPROCEX callback
+  failIfFalse_ "EnumSystemLocalesEx" $
+    c_EnumSystemLocalesEx c_callback dwFlags lParam nullPtr
+  freeHaskellFunPtr c_callback
+foreign import WINDOWS_CCONV safe "windows.h EnumSystemLocalesEx"
+  c_EnumSystemLocalesEx :: (FunPtr LOCALE_ENUMPROCEX)
+                        -> DWORD
+                        -> LPARAM
+                        -> LPVOID
+                        -> IO Bool
+
+enumSystemLocalesEx' :: EnumLocalesFlag
+                     -> Maybe Bool
+                     -- ^ Maybe include (or exclude) replacement locales?
+                     -> IO [String]
+enumSystemLocalesEx' dwFlags mIsReplacement = do
+  store <- newIORef []
+  let localeEnumProcEx c_locale arg2 _ = do
+        locale <- peekTString c_locale
+        case mIsReplacement of
+          Nothing -> modifyIORef store (locale:)
+          Just isReplacement ->
+            when (isReplacement == (arg2 .&. lOCALE_REPLACEMENT /= 0)) $
+              modifyIORef store (locale:)
+        return True
+  enumSystemLocalesEx localeEnumProcEx dwFlags 0
+  reverse <$> readIORef store
+
+foreign import WINDOWS_CCONV unsafe "windows.h IsValidCodePage"
   isValidCodePage :: CodePage -> IO Bool
 
-foreign import stdcall unsafe "windows.h GetUserDefaultLCID"
+foreign import WINDOWS_CCONV unsafe "windows.h GetUserDefaultLCID"
   getUserDefaultLCID :: LCID
 
-foreign import stdcall unsafe "windows.h GetUserDefaultLangID"
+foreign import WINDOWS_CCONV unsafe "windows.h GetUserDefaultLangID"
   getUserDefaultLangID :: LANGID
 
-foreign import stdcall unsafe "windows.h GetThreadLocale"
+-- #define LOCALE_NAME_INVARIANT L""
+lOCALE_NAME_INVARIANT :: Maybe String
+lOCALE_NAME_INVARIANT = Just ""
+
+ -- #define LOCALE_NAME_SYSTEM_DEFAULT L"!x-sys-default-locale"
+lOCALE_NAME_SYSTEM_DEFAULT :: Maybe String
+lOCALE_NAME_SYSTEM_DEFAULT = Just "!x-sys-default-locale"
+
+ -- #define LOCALE_NAME_USER_DEFAULT NULL
+lOCALE_NAME_USER_DEFAULT :: Maybe String
+lOCALE_NAME_USER_DEFAULT = Nothing
+
+getUserDefaultLocaleName :: IO String
+getUserDefaultLocaleName =
+  getDefaultLocaleName "GetUserDefaultLocaleName" c_GetUserDefaultLocaleName
+foreign import WINDOWS_CCONV unsafe "windows.h GetUserDefaultLocaleName"
+  c_GetUserDefaultLocaleName :: LPWSTR -> CInt -> IO CInt
+
+#{enum CInt,
+ , lOCALE_NAME_MAX_LENGTH = LOCALE_NAME_MAX_LENGTH
+ }
+
+-- |Helper function for use with 'c_GetUserDefaultLocaleName' or
+-- 'c_GetSystemDefaultLocaleName'. See 'getUserDefaultLocaleName' and
+-- 'getSystemUserDefaultLocaleName'.
+getDefaultLocaleName :: String -> (LPWSTR -> CInt -> IO CInt) -> IO String
+getDefaultLocaleName cDefaultLocaleFuncName cDefaultLocaleFunc =
+  withTStringBufferLen maxLength $ \(buf, len) -> do
+    let c_len = fromIntegral len
+    c_len' <- failIfZero cDefaultLocaleFuncName $
+      cDefaultLocaleFunc buf c_len
+    let len' = fromIntegral c_len'
+    peekTStringLen (buf, len' - 1) -- Drop final null character
+ where
+  maxLength = fromIntegral lOCALE_NAME_MAX_LENGTH
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetThreadLocale"
   getThreadLocale :: IO LCID
 
-foreign import stdcall unsafe "windows.h GetSystemDefaultLCID"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSystemDefaultLCID"
   getSystemDefaultLCID :: LCID
 
-foreign import stdcall unsafe "windows.h GetSystemDefaultLangID"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSystemDefaultLangID"
   getSystemDefaultLangID :: LANGID
 
-foreign import stdcall unsafe "windows.h GetOEMCP"
+getSystemDefaultLocaleName :: IO String
+getSystemDefaultLocaleName =
+  getDefaultLocaleName "GetSystemDefaultLocaleName" c_GetSystemDefaultLocaleName
+foreign import WINDOWS_CCONV unsafe "windows.h GetSystemDefaultLocaleName"
+  c_GetSystemDefaultLocaleName :: LPWSTR -> CInt -> IO CInt
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetOEMCP"
   getOEMCP :: CodePage
 
 #{enum PrimaryLANGID,
@@ -343,9 +801,9 @@
 
 -- ----------------------------------------------------------------------------
 
--- | The `System.IO` input functions (e.g. `getLine`) don't
+-- | The `System.IO` input functions (e.g., `getLine`) don't
 -- automatically convert to Unicode, so this function is provided to
--- make the conversion from a multibyte string in the given code page 
+-- make the conversion from a multibyte string in the given code page
 -- to a proper Unicode string.  To get the code page for the console,
 -- use `getConsoleCP`.
 
@@ -354,7 +812,7 @@
      -- MultiByteToWideChar doesn't handle empty strings (#1929)
 stringToUnicode cp mbstr =
   withCAStringLen mbstr $ \(cstr,len) -> do
-    wchars <- failIfZero "MultiByteToWideChar" $ multiByteToWideChar 
+    wchars <- failIfZero "MultiByteToWideChar" $ multiByteToWideChar
                 cp
                 0
                 cstr
@@ -362,15 +820,15 @@
                 nullPtr 0
     -- wchars is the length of buffer required
     allocaArray (fromIntegral wchars) $ \cwstr -> do
-      wchars <- failIfZero "MultiByteToWideChar" $ multiByteToWideChar 
+      wchars' <- failIfZero "MultiByteToWideChar" $ multiByteToWideChar
                 cp
                 0
                 cstr
                 (fromIntegral len)
                 cwstr wchars
-      peekCWStringLen (cwstr,fromIntegral wchars)  -- converts UTF-16 to [Char]
+      peekCWStringLen (cwstr,fromIntegral wchars')  -- converts UTF-16 to [Char]
 
-foreign import stdcall unsafe "MultiByteToWideChar"
+foreign import WINDOWS_CCONV unsafe "MultiByteToWideChar"
   multiByteToWideChar
         :: CodePage
         -> DWORD   -- dwFlags,
diff --git a/System/Win32/NamedPipes.hsc b/System/Win32/NamedPipes.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/NamedPipes.hsc
@@ -0,0 +1,276 @@
+#include <fcntl.h>
+#include <windows.h>
+
+#include "namedpipeapi_compat.h"
+
+{-# LANGUAGE CPP                #-}
+{-# LANGUAGE BangPatterns       #-}
+{-# LANGUAGE MultiWayIf         #-}
+
+-- | For full details on the Windows named pipes API see
+-- <https://docs.microsoft.com/en-us/windows/desktop/ipc/named-pipes>
+--
+module System.Win32.NamedPipes (
+
+    -- * Named pipe server APIs
+    createNamedPipe,
+    pIPE_UNLIMITED_INSTANCES,
+
+    -- ** Parameter types
+    LPSECURITY_ATTRIBUTES,
+    OpenMode,
+    pIPE_ACCESS_DUPLEX,
+    pIPE_ACCESS_INBOUND,
+    pIPE_ACCESS_OUTBOUND,
+    fILE_FLAG_OVERLAPPED,
+    PipeMode,
+    pIPE_TYPE_BYTE,
+    pIPE_TYPE_MESSAGE,
+    pIPE_READMODE_BYTE,
+    pIPE_READMODE_MESSAGE,
+    pIPE_WAIT,
+    pIPE_NOWAIT,
+    pIPE_ACCEPT_REMOTE_CLIENTS,
+    pIPE_REJECT_REMOTE_CLIENTS,
+
+    -- * Named pipe client APIs
+    -- ** connect to a named pipe
+    connect,
+
+    -- ** waiting for named pipe instances
+    waitNamedPipe,
+
+    TimeOut,
+    nMPWAIT_USE_DEFAULT_WAIT,
+    nMPWAIT_WAIT_FOREVER,
+  ) where
+
+
+import Control.Exception
+import Control.Monad (when)
+import Foreign.C.String (withCString)
+
+import System.Win32.Types hiding (try)
+import System.Win32.File
+
+-- | The named pipe open mode.
+--
+-- This must specify one of:
+--
+-- * 'pIPE_ACCESS_DUPLEX'
+-- * 'pIPE_ACCESS_INBOUND'
+-- * 'pIPE_ACCESS_OUTBOUND'
+--
+-- It may also specify:
+--
+-- * 'fILE_FLAG_WRITE_THROUGH'
+-- * 'fILE_FLAG_OVERLAPPED'
+--
+-- It may also specify any combination of:
+--
+-- * 'wRITE_DAC'
+-- * 'wRITE_OWNER'
+-- * 'aCCESS_SYSTEM_SECURITY'
+--
+type OpenMode = UINT
+
+#{enum OpenMode,
+ , pIPE_ACCESS_DUPLEX            = PIPE_ACCESS_DUPLEX
+ , pIPE_ACCESS_INBOUND           = PIPE_ACCESS_INBOUND
+ , pIPE_ACCESS_OUTBOUND          = PIPE_ACCESS_OUTBOUND
+ }
+
+-- | The pipe mode.
+--
+-- One of the following type modes can be specified. The same type mode must be
+-- specified for each instance of the pipe.
+--
+-- * 'pIPE_TYPE_BYTE'
+-- * 'pIPE_TYPE_MESSAGE'
+--
+-- One of the following read modes can be specified. Different instances of the
+-- same pipe can specify different read modes.
+--
+-- * 'pIPE_READMODE_BYTE'
+-- * 'pIPE_READMODE_MESSAGE'
+--
+-- One of the following wait modes can be specified. Different instances of the
+-- same pipe can specify different wait modes.
+--
+-- * 'pIPE_WAIT'
+-- * 'pIPE_NOWAIT'
+--
+-- One of the following remote-client modes can be specified.  Different
+-- instances of the same pipe can specify different remote-client modes.
+--
+-- * 'pIPE_ACCEPT_REMOTE_CLIENT'
+-- * 'pIPE_REJECT_REMOTE_CLIENT'
+--
+type PipeMode = UINT
+
+#{enum PipeMode,
+ , pIPE_TYPE_BYTE                = PIPE_TYPE_BYTE
+ , pIPE_TYPE_MESSAGE             = PIPE_TYPE_MESSAGE
+ , pIPE_READMODE_BYTE            = PIPE_READMODE_BYTE
+ , pIPE_READMODE_MESSAGE         = PIPE_READMODE_MESSAGE
+ , pIPE_WAIT                     = PIPE_WAIT
+ , pIPE_NOWAIT                   = PIPE_NOWAIT
+ , pIPE_ACCEPT_REMOTE_CLIENTS    = PIPE_ACCEPT_REMOTE_CLIENTS
+ , pIPE_REJECT_REMOTE_CLIENTS    = PIPE_REJECT_REMOTE_CLIENTS
+ }
+
+-- | If the 'createNamedPipe' @nMaxInstances@ parameter is
+-- 'pIPE_UNLIMITED_INSTANCES', the number of pipe instances that can be created
+-- is limited only by the availability of system resources.
+pIPE_UNLIMITED_INSTANCES :: DWORD
+pIPE_UNLIMITED_INSTANCES = #const PIPE_UNLIMITED_INSTANCES
+
+-- | Creates an instance of a named pipe and returns a handle for subsequent
+-- pipe operations. A named pipe server process uses this function either to
+-- create the first instance of a specific named pipe and establish its basic
+-- attributes or to create a new instance of an existing named pipe.
+--
+-- For full details see
+-- <https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createnamedpipea>
+--
+-- To create a named pipe which can be associate with IO completion port on
+-- needs to pass 'fILE_FLAG_OVERLAPPED' to 'OpenMode' argument,
+-- e.g.
+--
+-- >  Win32.createNamedPipe pipeName
+-- >                        (pIPE_ACCESS_DUPLEX .|. fILE_FLAG_OVERLAPPED)
+-- >                        (pIPE_TYPE_BYTE .|. pIPE_READMODE_BYTE)
+-- >                        pIPE_UNLIMITED_INSTANCES
+-- >                        512
+-- >                        512
+-- >                        0
+-- >                        NothinROR
+--
+--
+createNamedPipe :: String   -- ^ A unique pipe name of the form @\\\\.\\pipe\\{pipename}@
+                            -- The `pipename` part of the name can include any
+                            -- character other than a backslash, including
+                            -- numbers and special characters. The entire pipe
+                            -- name string can be up to 256 characters long.
+                            -- Pipe names are not case sensitive.
+                -> OpenMode
+                -> PipeMode
+                -> DWORD    -- ^ nMaxInstances
+                -> DWORD    -- ^ nOutBufferSize
+                -> DWORD    -- ^ nInBufferSize
+                -> DWORD    -- ^ nDefaultTimeOut
+                -> Maybe LPSECURITY_ATTRIBUTES
+                -> IO HANDLE
+createNamedPipe name openMode pipeMode
+                nMaxInstances nOutBufferSize nInBufferSize
+                nDefaultTimeOut mb_attr =
+  withTString name $ \ c_name ->
+    failIf (==iNVALID_HANDLE_VALUE) ("CreateNamedPipe ('" ++ name ++ "')") $
+      c_CreateNamedPipe c_name openMode pipeMode
+                        nMaxInstances nOutBufferSize nInBufferSize
+                        nDefaultTimeOut (maybePtr mb_attr)
+
+foreign import ccall unsafe "windows.h CreateNamedPipeW"
+  c_CreateNamedPipe :: LPCTSTR
+                    -> DWORD
+                    -> DWORD
+                    -> DWORD
+                    -> DWORD
+                    -> DWORD
+                    -> DWORD
+                    -> LPSECURITY_ATTRIBUTES
+                    -> IO HANDLE
+
+
+-- | Timeout in milliseconds.
+--
+-- * 'nMPWAIT_USE_DEFAULT_WAIT' indicates that the timeout value passed to
+--   'createNamedPipe' should be used.
+-- * 'nMPWAIT_WAIT_FOREVER' - 'waitNamedPipe' will block forever, until a named
+--   pipe instance is available.
+--
+type TimeOut = DWORD
+#{enum TimeOut,
+ , nMPWAIT_USE_DEFAULT_WAIT = NMPWAIT_USE_DEFAULT_WAIT
+ , nMPWAIT_WAIT_FOREVER     = NMPWAIT_WAIT_FOREVER
+ }
+
+
+-- | Wait until a named pipe instance is available.  If there is no instance at
+-- hand before the timeout, it will error with 'ERROR_SEM_TIMEOUT', i.e.
+-- @invalid argument (The semaphore timeout period has expired)@
+--
+-- It returns 'True' if there is an available instance, subsequent 'createFile'
+-- might still fail, if another thread will take turn and connect before, or if
+-- the other end shuts down the name pipe.
+--
+-- It returns 'False' if timeout fired.
+--
+waitNamedPipe :: String  -- ^ pipe name
+              -> TimeOut -- ^ nTimeOut
+              -> IO Bool
+waitNamedPipe name timeout =
+    withCString name $ \ c_name -> do
+      r <- c_WaitNamedPipe c_name timeout
+      e <- getLastError
+      if | r                      -> pure r
+         | e == eRROR_SEM_TIMEOUT -> pure False
+         | otherwise              -> failWith "waitNamedPipe" e
+
+
+-- 'c_WaitNamedPipe' is a blocking call, hence the _safe_ import.
+foreign import ccall safe "windows.h WaitNamedPipeA"
+  c_WaitNamedPipe :: LPCSTR -- lpNamedPipeName
+                  -> DWORD  -- nTimeOut
+                  -> IO BOOL
+
+-- | A reliable connect call, as designed in
+-- <https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-client>
+--
+-- The arguments are passed directly to 'createFile'.
+--
+-- Note we pick the more familiar posix naming convention, do not confuse this
+-- function with 'connectNamedPipe' (which corresponds to posix 'accept')
+--
+connect :: String                      -- ^ file name
+        -> AccessMode                  -- ^ dwDesiredAccess
+        -> ShareMode                   -- ^ dwSharedMode
+        -> Maybe LPSECURITY_ATTRIBUTES -- ^ lpSecurityAttributes
+        -> CreateMode                  -- ^ dwCreationDisposition
+        -> FileAttributeOrFlag         -- ^ dwFlagsAndAttributes
+        -> Maybe HANDLE                -- ^ hTemplateFile
+        -> IO HANDLE
+connect fileName dwDesiredAccess dwSharedMode lpSecurityAttributes dwCreationDisposition dwFlagsAndAttributes hTemplateFile = connectLoop
+  where
+    connectLoop = do
+      -- `createFile` checks for `INVALID_HANDLE_VALUE` and retries if this is
+      -- caused by `ERROR_SHARING_VIOLATION`.
+      mh <- try $
+              createFile fileName
+                         dwDesiredAccess
+                         dwSharedMode
+                         lpSecurityAttributes
+                         dwCreationDisposition
+                         dwFlagsAndAttributes
+                         hTemplateFile
+      case mh :: Either IOException HANDLE of
+        Left e -> do
+          errorCode <- getLastError
+          when (errorCode /= eRROR_PIPE_BUSY)
+            $ throwIO e
+          -- all pipe instance were busy, wait 20s and retry; we ignore the
+          -- result
+          _ <- waitNamedPipe fileName 5000
+          connectLoop
+
+        Right h -> pure h
+
+
+-- | [ERROR_PIPE_BUSY](https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-#ERROR_PIPE_BUSY):
+-- all pipe instances are busy.
+--
+eRROR_PIPE_BUSY :: ErrCode
+eRROR_PIPE_BUSY = #const ERROR_PIPE_BUSY
+
+eRROR_SEM_TIMEOUT :: ErrCode
+eRROR_SEM_TIMEOUT = #const ERROR_SEM_TIMEOUT
diff --git a/System/Win32/Path.hsc b/System/Win32/Path.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Path.hsc
@@ -0,0 +1,56 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Path
+-- Copyright   :  (c) Tamar Christina, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Tamar Christina <tamar@zhox.com>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Path (
+   filepathRelativePathTo
+ , pathRelativePathTo
+ ) where
+
+import System.Win32.Path.Internal
+import System.Win32.Types
+import System.Win32.File
+
+import Foreign
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+
+filepathRelativePathTo :: FilePath -> FilePath -> IO FilePath
+filepathRelativePathTo from to =
+  withTString from $ \p_from ->
+  withTString to   $ \p_to   ->
+  allocaArray ((#const MAX_PATH) * (#size TCHAR)) $ \p_AbsPath -> do
+    _ <- failIfZero "PathRelativePathTo" (c_pathRelativePathTo p_AbsPath p_from fILE_ATTRIBUTE_DIRECTORY
+                                                                         p_to   fILE_ATTRIBUTE_NORMAL)
+    path <- peekTString p_AbsPath
+    _ <- localFree p_AbsPath
+    return path
+
+pathRelativePathTo :: FilePath -> FileAttributeOrFlag -> FilePath -> FileAttributeOrFlag -> IO FilePath
+pathRelativePathTo from from_attr to to_attr =
+  withTString from $ \p_from ->
+  withTString to   $ \p_to   ->
+  allocaArray ((#const MAX_PATH) * (#size TCHAR)) $ \p_AbsPath -> do
+    _ <- failIfZero "PathRelativePathTo" (c_pathRelativePathTo p_AbsPath p_from from_attr
+                                                                         p_to   to_attr)
+    path <- peekTString p_AbsPath
+    _ <- localFree p_AbsPath
+    return path
+
diff --git a/System/Win32/Path/Internal.hsc b/System/Win32/Path/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Path/Internal.hsc
@@ -0,0 +1,29 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Path.Internal
+-- Copyright   :  (c) Tamar Christina, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Tamar Christina <tamar@zhox.com>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Path.Internal where
+
+import System.Win32.Types
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+
+foreign import WINDOWS_CCONV unsafe "Shlwapi.h PathRelativePathToW"
+         c_pathRelativePathTo :: LPTSTR -> LPCTSTR -> DWORD -> LPCTSTR -> DWORD -> IO UINT
diff --git a/System/Win32/Process.hsc b/System/Win32/Process.hsc
--- a/System/Win32/Process.hsc
+++ b/System/Win32/Process.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -15,25 +17,71 @@
 --
 -----------------------------------------------------------------------------
 
-module System.Win32.Process where
-import Control.Exception    ( bracket )
-import Control.Monad        ( liftM5 )
-import Foreign              ( Ptr, peekByteOff, allocaBytes, pokeByteOff
-                            , plusPtr )
-import System.Win32.File    ( closeHandle )
+module System.Win32.Process
+    ( -- * Sleeping
+      iNFINITE
+    , sleep
+      -- * Processes pperations
+    , ProcessId
+    , ProcessHandle
+    , ProcessAccessRights
+    , pROCESS_ALL_ACCESS
+    , pROCESS_CREATE_PROCESS
+    , pROCESS_CREATE_THREAD
+    , pROCESS_DUP_HANDLE
+    , pROCESS_QUERY_INFORMATION
+    , pROCESS_SET_QUOTA
+    , pROCESS_SET_INFORMATION
+    , pROCESS_TERMINATE
+    , pROCESS_VM_OPERATION
+    , pROCESS_VM_READ
+    , pROCESS_VM_WRITE
+    , openProcess
+    , getProcessId
+    , getCurrentProcessId
+    , getCurrentProcess
+      -- * Terminating
+    , terminateProcessById
+      -- * Toolhelp32
+    , Th32SnapHandle
+    , Th32SnapFlags
+    , tH32CS_SNAPALL
+    , tH32CS_SNAPHEAPLIST
+    , tH32CS_SNAPMODULE
+    , tH32CS_SNAPMODULE32
+    , tH32CS_SNAPMODULE64
+    , tH32CS_SNAPPROCESS
+    , tH32CS_SNAPTHREAD
+    , ProcessEntry32
+    , ModuleEntry32
+    , createToolhelp32Snapshot
+    , withTh32Snap
+    , th32SnapEnumProcesses
+    , th32SnapEnumModules
+    ) where
+
+import Control.Exception     ( bracket )
+import Control.Monad         ( liftM5 )
+import Foreign               ( Ptr, peekByteOff, allocaBytes, pokeByteOff
+                             , plusPtr )
+import Foreign.C.Types       ( CUInt(..) )
+import System.Win32.File     ( closeHandle )
+import System.Win32.DebugApi ( ForeignAddress )
 import System.Win32.Types
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 #include <tlhelp32.h>
+#include "tlhelp32_compat.h"
 
 -- constant to wait for a very long time.
 iNFINITE :: DWORD
 iNFINITE = #{const INFINITE}
 
-foreign import stdcall unsafe "windows.h Sleep"
+foreign import WINDOWS_CCONV unsafe "windows.h Sleep"
   sleep :: DWORD -> IO ()
 
-
 type ProcessId = DWORD
 type ProcessHandle = HANDLE
 type ProcessAccessRights = DWORD
@@ -49,48 +97,76 @@
     , pROCESS_VM_OPERATION          = PROCESS_VM_OPERATION
     , pROCESS_VM_READ               = PROCESS_VM_READ
     , pROCESS_VM_WRITE              = PROCESS_VM_WRITE
-    , sYNCHORNIZE                   = SYNCHRONIZE 
     }
 
-foreign import stdcall unsafe "windows.h OpenProcess"
+foreign import WINDOWS_CCONV unsafe "windows.h OpenProcess"
     c_OpenProcess :: ProcessAccessRights -> BOOL -> ProcessId -> IO ProcessHandle
 
-
 openProcess :: ProcessAccessRights -> BOOL -> ProcessId -> IO ProcessHandle
 openProcess r inh i = failIfNull "OpenProcess" $ c_OpenProcess r inh i
 
-foreign import stdcall unsafe "windows.h GetProcessId"
+foreign import WINDOWS_CCONV unsafe "windows.h GetProcessId"
     c_GetProcessId :: ProcessHandle -> IO ProcessId
 
 getProcessId :: ProcessHandle -> IO ProcessId
 getProcessId h = failIfZero "GetProcessId" $ c_GetProcessId h
 
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentProcess"
+    c_GetCurrentProcess :: IO ProcessHandle
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetCurrentProcessId"
+    c_GetCurrentProcessId :: IO ProcessId
+
+getCurrentProcessId :: IO ProcessId
+getCurrentProcessId = c_GetCurrentProcessId
+
+getCurrentProcess :: IO ProcessHandle
+getCurrentProcess = c_GetCurrentProcess
+
+foreign import WINDOWS_CCONV unsafe "windows.h TerminateProcess"
+    c_TerminateProcess :: ProcessHandle -> CUInt -> IO Bool
+
+terminateProcessById :: ProcessId -> IO ()
+terminateProcessById p = bracket
+    (openProcess pROCESS_TERMINATE False p)
+    closeHandle
+    (\h -> failIfFalse_ "TerminateProcess" $ c_TerminateProcess h 1)
+
 type Th32SnapHandle = HANDLE
 type Th32SnapFlags = DWORD
 -- | ProcessId, number of threads, parent ProcessId, process base priority, path of executable file
 type ProcessEntry32 = (ProcessId, Int, ProcessId, LONG, String)
+type ModuleEntry32 = (ForeignAddress, Int, HMODULE, String, String)
 
 #{enum Th32SnapFlags,
     , tH32CS_SNAPALL        = TH32CS_SNAPALL
     , tH32CS_SNAPHEAPLIST   = TH32CS_SNAPHEAPLIST
     , tH32CS_SNAPMODULE     = TH32CS_SNAPMODULE
+    , tH32CS_SNAPMODULE32   = TH32CS_SNAPMODULE32
+    , tH32CS_SNAPMODULE64   = (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32)
     , tH32CS_SNAPPROCESS    = TH32CS_SNAPPROCESS
     , tH32CS_SNAPTHREAD     = TH32CS_SNAPTHREAD
     }
 {-
     , tH32CS_SNAPGETALLMODS = TH32CS_GETALLMODS
-    , tH32CS_SNAPNOHEAPS    = TH32CS_SNAPNOHEAPS 
+    , tH32CS_SNAPNOHEAPS    = TH32CS_SNAPNOHEAPS
 -}
 
-foreign import stdcall unsafe "tlhelp32.h CreateToolhelp32Snapshot"
+foreign import WINDOWS_CCONV unsafe "tlhelp32.h CreateToolhelp32Snapshot"
     c_CreateToolhelp32Snapshot :: Th32SnapFlags -> ProcessId -> IO Th32SnapHandle
 
-foreign import stdcall unsafe "tlhelp32.h Process32FirstW"
+foreign import WINDOWS_CCONV unsafe "tlhelp32.h Process32FirstW"
     c_Process32First :: Th32SnapHandle -> Ptr ProcessEntry32 -> IO BOOL
 
-foreign import stdcall unsafe "tlhelp32.h Process32NextW"
+foreign import WINDOWS_CCONV unsafe "tlhelp32.h Process32NextW"
     c_Process32Next :: Th32SnapHandle -> Ptr ProcessEntry32 -> IO BOOL
 
+foreign import WINDOWS_CCONV unsafe "tlhelp32.h Module32FirstW"
+    c_Module32First :: Th32SnapHandle -> Ptr ModuleEntry32 -> IO BOOL
+
+foreign import WINDOWS_CCONV unsafe "tlhelp32.h Module32NextW"
+    c_Module32Next :: Th32SnapHandle -> Ptr ModuleEntry32 -> IO BOOL
+
 -- | Create a snapshot of specified resources.  Call closeHandle to close snapshot.
 createToolhelp32Snapshot :: Th32SnapFlags -> Maybe ProcessId -> IO Th32SnapHandle
 createToolhelp32Snapshot f p
@@ -100,19 +176,26 @@
 withTh32Snap :: Th32SnapFlags -> Maybe ProcessId -> (Th32SnapHandle -> IO a) -> IO a
 withTh32Snap f p = bracket (createToolhelp32Snapshot f p) (closeHandle)
 
-
 peekProcessEntry32 :: Ptr ProcessEntry32 -> IO ProcessEntry32
 peekProcessEntry32 buf = liftM5 (,,,,)
-    ((#peek PROCESSENTRY32, th32ProcessID) buf)
-    ((#peek PROCESSENTRY32, cntThreads) buf)
-    ((#peek PROCESSENTRY32, th32ParentProcessID) buf)
-    ((#peek PROCESSENTRY32, pcPriClassBase) buf)
-    (peekTString $ (#ptr PROCESSENTRY32, szExeFile) buf)
+    ((#peek PROCESSENTRY32W, th32ProcessID) buf)
+    ((#peek PROCESSENTRY32W, cntThreads) buf)
+    ((#peek PROCESSENTRY32W, th32ParentProcessID) buf)
+    ((#peek PROCESSENTRY32W, pcPriClassBase) buf)
+    (peekTString $ (#ptr PROCESSENTRY32W, szExeFile) buf)
 
+peekModuleEntry32 :: Ptr ModuleEntry32 -> IO ModuleEntry32
+peekModuleEntry32 buf = liftM5 (,,,,)
+    ((#peek MODULEENTRY32W, modBaseAddr) buf)
+    ((#peek MODULEENTRY32W, modBaseSize) buf)
+    ((#peek MODULEENTRY32W, hModule) buf)
+    (peekTString $ (#ptr MODULEENTRY32W, szModule) buf)
+    (peekTString $ (#ptr MODULEENTRY32W, szExePath) buf)
+
 -- | Enumerate processes using Process32First and Process32Next
 th32SnapEnumProcesses :: Th32SnapHandle -> IO [ProcessEntry32]
-th32SnapEnumProcesses h = allocaBytes (#size PROCESSENTRY32) $ \pe -> do
-    (#poke PROCESSENTRY32, dwSize) pe ((#size PROCESSENTRY32)::DWORD)
+th32SnapEnumProcesses h = allocaBytes (#size PROCESSENTRY32W) $ \pe -> do
+    (#poke PROCESSENTRY32W, dwSize) pe ((#size PROCESSENTRY32W)::DWORD)
     ok <- c_Process32First h pe
     readAndNext ok pe []
     where
@@ -125,4 +208,22 @@
             | otherwise = do
                 entry <- peekProcessEntry32 pe
                 ok' <- c_Process32Next h pe
+                readAndNext ok' pe (entry:res)
+
+-- | Enumerate modules using Module32First and Module32Next
+th32SnapEnumModules :: Th32SnapHandle -> IO [ModuleEntry32]
+th32SnapEnumModules h = allocaBytes (#size MODULEENTRY32W) $ \pe -> do
+    (#poke MODULEENTRY32W, dwSize) pe ((#size MODULEENTRY32W)::DWORD)
+    ok <- c_Module32First h pe
+    readAndNext ok pe []
+    where
+        readAndNext ok pe res
+            | not ok    = do
+                err <- getLastError
+                if err == (#const ERROR_NO_MORE_FILES)
+                    then return $ reverse res
+                    else failWith "th32SnapEnumModules: Module32First/Module32Next" err
+            | otherwise = do
+                entry <- peekModuleEntry32 pe
+                ok' <- c_Module32Next h pe
                 readAndNext ok' pe (entry:res)
diff --git a/System/Win32/Registry.hsc b/System/Win32/Registry.hsc
--- a/System/Win32/Registry.hsc
+++ b/System/Win32/Registry.hsc
@@ -1,6 +1,4 @@
-#if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  System.Win32.Registry
@@ -16,42 +14,103 @@
 -----------------------------------------------------------------------------
 
 module System.Win32.Registry
-		( module System.Win32.Registry
-		) where
-{- What's really on offer:
-	(
-	  regCloseKey        -- :: HKEY -> IO ()
-	, regConnectRegistry -- :: Maybe String -> HKEY -> IO HKEY
-	, regCreateKey       -- :: HKEY -> String -> IO HKEY
-	, regCreateKeyEx     -- :: HKEY -> String -> String
-	                     -- -> RegCreateOptions -> REGSAM
-			     -- -> Maybe LPSECURITY_ATTRIBUTES
-			     -- -> IO (HKEY, Bool)
-        , regDeleteKey       -- :: HKEY -> String -> IO ()
-	, regDeleteValue     -- :: HKEY -> String -> IO ()
-	, regEnumKeys	     -- :: HKEY -> IO [String]
-	, regEnumKey 	     -- :: HKEY -> DWORD -> Addr -> DWORD -> IO String
-	, regEnumKeyValue    -- :: HKEY -> DWORD -> Addr -> DWORD -> Addr -> DWORD -> IO String
-	, regFlushKey        -- :: HKEY -> IO ()
-	, regLoadKey         -- :: HKEY -> String -> String -> IO ()
-	, regNotifyChangeKeyValue -- :: HKEY -> Bool -> RegNotifyOptions
-				  -- -> HANDLE -> Bool -> IO ()
-	, regOpenKey         -- :: HKEY -> String -> IO HKEY
-	, regOpenKeyEx 	     -- :: HKEY -> String -> REGSAM -> IO HKEY
-	, regQueryInfoKey    -- :: HKEY -> IO RegInfoKey
-	, regQueryValue      -- :: HKEY -> Maybe String -> IO String
-	, regQueryValueKey   -- :: HKEY -> Maybe String -> IO String
-	, regQueryValueEx    -- :: HKEY -> String -> Addr -> Int -> IO RegValueType
-	, regReplaceKey      -- :: HKEY -> String -> String -> String -> IO ()
-	, regRestoreKey      -- :: HKEY -> String -> RegRestoreFlags -> IO ()
-	, regSaveKey         -- :: HKEY -> String -> Maybe LPSECURITY_ATTRIBUTES -> IO ()
-	, regSetValue        -- :: HKEY -> String -> String -> IO ()
-	, regSetValueEx      -- :: HKEY -> String -> RegValueType -> LPTSTR -> Int -> IO ()
-	, regSetStringValue  -- :: HKEY -> String -> String -> IO ()
-	, regUnloadKey       -- :: HKEY -> String -> IO ()
-	) where
--}
+    (
+      -- * HKEY
+      HKEY
+    , hKEY_CLASSES_ROOT
+    , hKEY_CURRENT_CONFIG
+    , hKEY_CURRENT_USER
+    , hKEY_LOCAL_MACHINE
+    , hKEY_USERS
 
+      -- * Creation options
+    , RegCreateOptions
+    , rEG_OPTION_NON_VOLATILE
+    , rEG_OPTION_VOLATILE
+
+      -- * REGSAM
+    , REGSAM
+    , kEY_ALL_ACCESS
+    , kEY_CREATE_LINK
+    , kEY_CREATE_SUB_KEY
+    , kEY_ENUMERATE_SUB_KEYS
+    , kEY_EXECUTE
+    , kEY_NOTIFY
+    , kEY_QUERY_VALUE
+    , kEY_READ
+    , kEY_SET_VALUE
+    , kEY_WRITE
+
+      -- * Registry operations
+    , regCloseKey
+    , regConnectRegistry
+    , regCreateKey
+    , regCreateKeyEx
+    , regDeleteKey
+    , regDeleteValue
+    , regEnumKeys
+    , regEnumKeyVals
+    , regEnumKey
+    , regEnumValue
+    , regFlushKey
+    , regLoadKey
+    , regUnLoadKey
+    , regNotifyChangeKeyValue
+    , RegNotifyOptions
+    , rEG_NOTIFY_CHANGE_NAME
+    , rEG_NOTIFY_CHANGE_ATTRIBUTES
+    , rEG_NOTIFY_CHANGE_LAST_SET
+    , rEG_NOTIFY_CHANGE_SECURITY
+
+    , regOpenKey
+    , regOpenKeyEx
+    , regQueryInfoKey
+    , RegInfoKey(..)
+    , regQueryValue
+    , regQueryValueKey
+    , regQueryDefaultValue
+    , regQueryValueEx
+    , regReplaceKey
+    , RegRestoreFlags
+    , rEG_WHOLE_HIVE_VOLATILE
+    , rEG_REFRESH_HIVE
+    , rEG_NO_LAZY_FLUSH
+    , regRestoreKey
+    , regSaveKey
+    , regGetValue
+    , RegTypeRestriction
+    , rRF_RT_ANY
+    , rRF_RT_DWORD
+    , rRF_RT_QWORD
+    , rRF_RT_REG_BINARY
+    , rRF_RT_REG_DWORD
+    , rRF_RT_REG_EXPAND_SZ
+    , rRF_RT_REG_MULTI_SZ
+    , rRF_RT_REG_NONE
+    , rRF_RT_REG_QWORD
+    , rRF_RT_REG_SZ
+    , rRF_NOEXPAND
+    , rRF_ZEROONFAILURE
+    , rRF_SUBKEY_WOW6464KEY
+    , rRF_SUBKEY_WOW6432KEY
+
+    , regSetValue
+    , regSetValueEx
+    , RegValueType
+    , rEG_BINARY
+    , rEG_DWORD
+    , rEG_DWORD_LITTLE_ENDIAN
+    , rEG_DWORD_BIG_ENDIAN
+    , rEG_EXPAND_SZ
+    , rEG_LINK
+    , rEG_MULTI_SZ
+    , rEG_NONE
+    , rEG_RESOURCE_LIST
+    , rEG_SZ
+
+    , regSetStringValue  -- :: HKEY -> String -> String -> IO ()
+    ) where
+
 {-
  Registry API omissions:
 
@@ -60,21 +119,32 @@
 
 -}
 
-import System.Win32.Time
-import System.Win32.Types
-import System.Win32.File
+import Data.Word (Word32)
+import Foreign.ForeignPtr (withForeignPtr)
+import Foreign.Marshal.Alloc (alloca, allocaBytes, free, mallocBytes)
+import Foreign.Marshal.Array (allocaArray0)
+import Foreign.Marshal.Utils (maybeWith, with)
+import Foreign.Ptr (Ptr, castPtr, nullPtr)
+import Foreign.Storable (peek, peekByteOff, peekElemOff, sizeOf)
+import System.IO.Unsafe (unsafePerformIO)
+import System.Win32.File (LPSECURITY_ATTRIBUTES)
+import System.Win32.Time (FILETIME)
+import System.Win32.Types (DWORD, ErrCode, HKEY, LPCTSTR, PKEY, withTString)
+import System.Win32.Types (HANDLE, LONG, LPBYTE, newForeignHANDLE, peekTString)
+import System.Win32.Types (LPTSTR, TCHAR, LPDWORD, LPVOID, failUnlessSuccess)
+import System.Win32.Types (castUINTPtrToPtr, failUnlessSuccessOr, maybePtr)
 
-import System.IO.Unsafe
-import Foreign hiding (unsafePerformIO)
+##include "windows_cconv.h"
 
 #include <windows.h>
+#include "winreg_compat.h"
 
-#{enum HKEY, (unsafePerformIO . newForeignHANDLE . castUINTToPtr)
- , hKEY_CLASSES_ROOT    = (UINT)HKEY_CLASSES_ROOT
- , hKEY_CURRENT_CONFIG  = (UINT)HKEY_CURRENT_CONFIG
- , hKEY_CURRENT_USER    = (UINT)HKEY_CURRENT_USER
- , hKEY_LOCAL_MACHINE   = (UINT)HKEY_LOCAL_MACHINE
- , hKEY_USERS           = (UINT)HKEY_USERS
+#{enum HKEY, (unsafePerformIO . newForeignHANDLE . castUINTPtrToPtr)
+ , hKEY_CLASSES_ROOT    = (UINT_PTR)HKEY_CLASSES_ROOT
+ , hKEY_CURRENT_CONFIG  = (UINT_PTR)HKEY_CURRENT_CONFIG
+ , hKEY_CURRENT_USER    = (UINT_PTR)HKEY_CURRENT_USER
+ , hKEY_LOCAL_MACHINE   = (UINT_PTR)HKEY_LOCAL_MACHINE
+ , hKEY_USERS           = (UINT_PTR)HKEY_USERS
  }
 -- , PKEYERFORMANCE_DATA  NT only
 -- , HKEY_DYN_DATA     95/98 only
@@ -83,7 +153,7 @@
 regCloseKey key =
   withForeignPtr key $ \ p_key ->
   failUnlessSuccess "RegCloseKey" $ c_RegCloseKey p_key
-foreign import stdcall unsafe "windows.h RegCloseKey"
+foreign import WINDOWS_CCONV unsafe "windows.h RegCloseKey"
   c_RegCloseKey :: PKEY -> IO ErrCode
 
 -- Connects to a predefined registry handle on another computer.
@@ -97,7 +167,7 @@
     c_RegConnectRegistry c_machine p_key p_out_key
   p_new_key <- peek p_out_key
   newForeignHANDLE p_new_key
-foreign import stdcall unsafe "windows.h RegConnectRegistryW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegConnectRegistryW"
   c_RegConnectRegistry :: LPCTSTR -> PKEY -> Ptr PKEY -> IO ErrCode
 
 regCreateKey :: HKEY -> String -> IO HKEY
@@ -109,7 +179,7 @@
     c_RegCreateKey p_key c_subkey p_out_key
   p_new_key <- peek p_out_key
   newForeignHANDLE p_new_key
-foreign import stdcall unsafe "windows.h RegCreateKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegCreateKeyW"
   c_RegCreateKey :: PKEY -> LPCTSTR -> Ptr PKEY -> IO ErrCode
 
 type RegCreateOptions = DWORD
@@ -134,11 +204,11 @@
  , kEY_WRITE            = KEY_WRITE
  }
 
-regCreateKeyEx :: HKEY -> String -> String -> RegCreateOptions -> REGSAM -> Maybe LPSECURITY_ATTRIBUTES -> IO (HKEY, Bool)
+regCreateKeyEx :: HKEY -> String -> Maybe String -> RegCreateOptions -> REGSAM -> Maybe LPSECURITY_ATTRIBUTES -> IO (HKEY, Bool)
 regCreateKeyEx key subkey cls opts sam mb_attr =
   withForeignPtr key $ \ p_key ->
   withTString subkey $ \ c_subkey ->
-  withTString cls $ \ c_cls ->
+  maybeWith withTString cls $ \ c_cls ->
   alloca $ \ p_res ->
   alloca $ \ p_disp -> do
   failUnlessSuccess "RegCreateKeyEx" $
@@ -147,7 +217,7 @@
   out_key <- newForeignHANDLE p_out_key
   disp <- peek p_disp
   return (out_key, disp == #{const REG_CREATED_NEW_KEY})
-foreign import stdcall unsafe "windows.h RegCreateKeyExW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegCreateKeyExW"
   c_RegCreateKeyEx :: PKEY -> LPCTSTR -> DWORD -> LPCTSTR -> RegCreateOptions -> REGSAM -> LPSECURITY_ATTRIBUTES -> Ptr PKEY -> Ptr DWORD -> IO ErrCode
 
 regDeleteKey :: HKEY -> String -> IO ()
@@ -155,7 +225,7 @@
   withForeignPtr key $ \ p_key ->
   withTString subkey $ \ c_subkey ->
   failUnlessSuccess "RegDeleteKey" $ c_RegDeleteKey p_key c_subkey
-foreign import stdcall unsafe "windows.h RegDeleteKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegDeleteKeyW"
   c_RegDeleteKey :: PKEY -> LPCTSTR -> IO ErrCode
 
 regDeleteValue :: HKEY -> String -> IO ()
@@ -163,7 +233,7 @@
   withForeignPtr key $ \ p_key ->
   withTString name $ \ c_name ->
   failUnlessSuccess "RegDeleteValue" $ c_RegDeleteValue p_key c_name
-foreign import stdcall unsafe "windows.h RegDeleteValueW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegDeleteValueW"
   c_RegDeleteValue :: PKEY -> LPCTSTR -> IO ErrCode
 
 -- XXX Not 100% sure this is right, but I think it is.
@@ -207,10 +277,10 @@
        else do
 
         val <-
-	   case ty of
-	     x | x == rEG_SZ    -> peekTString (castPtr valbuf)
-	       | x == rEG_DWORD -> peekElemOff (castPtr valbuf) 0 >>= \ v -> return (show (v :: DWORD))
-	       | otherwise      -> return "<<unknown>>"
+           case ty of
+             x | x == rEG_SZ    -> peekTString (castPtr valbuf)
+               | x == rEG_DWORD -> peekElemOff (castPtr valbuf) 0 >>= \ v -> return (show (v :: DWORD))
+               | otherwise      -> return "<<unknown>>"
 
         vs <- go (n+1) nmbuf nmlen valbuf vallen
         return ((nm,val,ty):vs)
@@ -225,7 +295,7 @@
     c_RegEnumKey p_key index c_name len
   str <- peekTString c_name
   return (str, fromEnum no_more)
-foreign import stdcall unsafe "windows.h RegEnumKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegEnumKeyW"
   c_RegEnumKey :: PKEY -> DWORD -> LPTSTR -> DWORD -> IO ErrCode
 
 regEnumValue :: HKEY -> DWORD -> LPTSTR -> DWORD -> LPBYTE -> DWORD -> IO (RegValueType, String, Int)
@@ -239,7 +309,7 @@
   reg_ty <- peek p_reg_ty
   str <- peekTString name
   return (reg_ty, str, fromEnum no_more)
-foreign import stdcall unsafe "windows.h RegEnumValueW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegEnumValueW"
   c_RegEnumValue :: PKEY -> DWORD -> LPTSTR -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> LPBYTE -> Ptr DWORD -> IO ErrCode
 
 eRROR_NO_MORE_ITEMS :: ErrCode
@@ -249,7 +319,7 @@
 regFlushKey key =
   withForeignPtr key $ \ p_key ->
   failUnlessSuccess "RegFlushKey" $ c_RegFlushKey p_key
-foreign import stdcall unsafe "windows.h RegFlushKey"
+foreign import WINDOWS_CCONV unsafe "windows.h RegFlushKey"
   c_RegFlushKey :: PKEY -> IO ErrCode
 
 -- ifdef FOR_WINDOWS_NT
@@ -263,7 +333,7 @@
   withTString subkey $ \ c_subkey ->
   withTString file $ \ c_file ->
   failUnlessSuccess "RegLoadKey" $ c_RegLoadKey p_key c_subkey c_file
-foreign import stdcall unsafe "windows.h RegLoadKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegLoadKeyW"
   c_RegLoadKey :: PKEY -> LPCTSTR -> LPCTSTR -> IO ErrCode
 
 -- ifdef FOR_WINDOWS_NT
@@ -282,7 +352,7 @@
   withForeignPtr key $ \ p_key ->
   failUnlessSuccess "RegNotifyChangeKeyValue" $
     c_RegNotifyChangeKeyValue p_key watch notifyFilter event async
-foreign import stdcall unsafe "windows.h RegNotifyChangeKeyValue"
+foreign import WINDOWS_CCONV unsafe "windows.h RegNotifyChangeKeyValue"
   c_RegNotifyChangeKeyValue :: PKEY -> Bool -> RegNotifyOptions -> HANDLE -> Bool -> IO ErrCode
 
 -- endif
@@ -297,7 +367,7 @@
   failUnlessSuccess "RegOpenKey" $ c_RegOpenKey p_key c_subkey p_res
   p_res_key <- peek p_res
   newForeignHANDLE p_res_key
-foreign import stdcall unsafe "windows.h RegOpenKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegOpenKeyW"
   c_RegOpenKey :: PKEY -> LPCTSTR -> Ptr PKEY -> IO ErrCode
 
 regOpenKeyEx :: HKEY -> String -> REGSAM -> IO HKEY
@@ -308,7 +378,7 @@
   failUnlessSuccess "RegOpenKeyEx" $ c_RegOpenKeyEx p_key c_subkey 0 sam p_res
   p_res_key <- peek p_res
   newForeignHANDLE p_res_key
-foreign import stdcall unsafe "windows.h RegOpenKeyExW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegOpenKeyExW"
   c_RegOpenKeyEx :: PKEY -> LPCTSTR -> DWORD -> REGSAM -> Ptr PKEY -> IO ErrCode
 
 data RegInfoKey =
@@ -343,68 +413,79 @@
     c_RegQueryInfoKey p_key c_class_string p_class_id nullPtr p_subkeys
         p_max_subkey_len p_max_class_len p_values p_max_value_name_len
         p_max_value_len p_sec_len p_lastWrite
-  class_string <- peekTString c_class_string
-  class_id <- peek p_class_id
-  subkeys <- peek p_subkeys
-  max_subkey_len <- peek p_max_subkey_len
-  max_class_len <- peek p_max_class_len
-  values <- peek p_values
-  max_value_name_len <- peek p_max_value_name_len
-  max_value_len <- peek p_max_value_len
-  sec_len <- peek p_sec_len
-  lastWrite_lo <- #{peek FILETIME,dwLowDateTime} p_lastWrite
-  lastWrite_hi <- #{peek FILETIME,dwHighDateTime} p_lastWrite
+  class_string' <- peekTString c_class_string
+  class_id' <- peek p_class_id
+  subkeys' <- peek p_subkeys
+  max_subkey_len' <- peek p_max_subkey_len
+  max_class_len' <- peek p_max_class_len
+  values' <- peek p_values
+  max_value_name_len' <- peek p_max_value_name_len
+  max_value_len' <- peek p_max_value_len
+  sec_len' <- peek p_sec_len
+  lastWrite_lo' <- #{peek FILETIME,dwLowDateTime} p_lastWrite
+  lastWrite_hi' <- #{peek FILETIME,dwHighDateTime} p_lastWrite
   return $ RegInfoKey
-    { class_string = class_string
-    , class_id = fromIntegral class_id
-    , subkeys = subkeys
-    , max_subkey_len = max_subkey_len
-    , max_class_len = max_class_len
-    , values = values
-    , max_value_name_len = max_value_name_len
-    , max_value_len = max_value_len
-    , sec_len = fromIntegral sec_len
-    , lastWrite_lo = lastWrite_lo
-    , lastWrite_hi = lastWrite_hi
+    { class_string = class_string'
+    , class_id = fromIntegral class_id'
+    , subkeys = subkeys'
+    , max_subkey_len = max_subkey_len'
+    , max_class_len = max_class_len'
+    , values = values'
+    , max_value_name_len = max_value_name_len'
+    , max_value_len = max_value_len'
+    , sec_len = fromIntegral sec_len'
+    , lastWrite_lo = lastWrite_lo'
+    , lastWrite_hi = lastWrite_hi'
     }
-foreign import stdcall unsafe "windows.h RegQueryInfoKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegQueryInfoKeyW"
   c_RegQueryInfoKey :: PKEY -> LPTSTR -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr DWORD -> Ptr FILETIME -> IO ErrCode
 
 -- RegQueryMultipleValues :: HKEY -> IO ([VALENT],String)
 
--- RegQueryValue() isn't really that, it just allows you to
--- get at the default values of keys, so we provide our own
--- (and better!) version of it. If you want RegQueryValue()s
--- behaviour, use regQueryValueKey.
-
+{-# DEPRECATED regQueryValueKey "Use regQueryValue instead." #-}
 regQueryValueKey :: HKEY -> Maybe String -> IO String
 regQueryValueKey key mb_subkey =
   withForeignPtr key $ \ p_key ->
   maybeWith withTString mb_subkey $ \ c_subkey ->
   alloca $ \ p_value_len -> do
-  failUnlessSuccess "RegQueryValue" $
+  failUnlessSuccess "RegQueryValueKey" $
     c_RegQueryValue p_key c_subkey nullPtr p_value_len
   value_len <- peek p_value_len
   allocaArray0 (fromIntegral value_len) $ \ c_value -> do
-    failUnlessSuccess "RegQueryValue" $
+    failUnlessSuccess "RegQueryValueKey" $
       c_RegQueryValue p_key c_subkey c_value p_value_len
     peekTString c_value
-foreign import stdcall unsafe "windows.h RegQueryValueW"
-  c_RegQueryValue :: PKEY -> LPCTSTR -> LPTSTR -> Ptr LONG -> IO ErrCode
 
 regQueryValue :: HKEY -> Maybe String -> IO String
 regQueryValue key mb_subkey =
   withForeignPtr key $ \ p_key ->
   maybeWith withTString mb_subkey $ \ c_subkey ->
-  alloca $ \ p_ty ->
   alloca $ \ p_value_len -> do
   failUnlessSuccess "RegQueryValue" $
+    c_RegQueryValue p_key c_subkey nullPtr p_value_len
+  value_len <- peek p_value_len
+  allocaArray0 (fromIntegral value_len) $ \ c_value -> do
+    failUnlessSuccess "RegQueryValue" $
+      c_RegQueryValue p_key c_subkey c_value p_value_len
+    peekTString c_value
+foreign import WINDOWS_CCONV unsafe "windows.h RegQueryValueW"
+  c_RegQueryValue :: PKEY -> LPCTSTR -> LPTSTR -> Ptr LONG -> IO ErrCode
+
+-- Gets the data associated with the default value of a key (assumed to be of
+-- type REG_SZ) using RegQeryValueEx.
+regQueryDefaultValue :: HKEY -> String -> IO String
+regQueryDefaultValue key mb_subkey =
+  withForeignPtr key $ \ p_key ->
+  withTString mb_subkey $ \ c_subkey ->
+  alloca $ \ p_ty ->
+  alloca $ \ p_value_len -> do
+  failUnlessSuccess "regQueryDefaultValue" $
     c_RegQueryValueEx p_key c_subkey nullPtr p_ty nullPtr p_value_len
   ty <- peek p_ty
-  failUnlessSuccess "RegQueryValue" $ return (if ty == rEG_SZ then 0 else 1)
+  failUnlessSuccess "regQueryDefaultValue" $ return (if ty == rEG_SZ then 0 else 1)
   value_len <- peek p_value_len
   allocaArray0 (fromIntegral value_len) $ \ c_value -> do
-    failUnlessSuccess "RegQueryValue" $
+    failUnlessSuccess "regQueryDefaultValue" $
       c_RegQueryValueEx p_key c_subkey nullPtr p_ty c_value p_value_len
     peekTString (castPtr c_value)
 
@@ -417,18 +498,18 @@
   failUnlessSuccess "RegQueryValueEx" $
     c_RegQueryValueEx p_key c_name nullPtr p_ty value p_value_len
   peek p_ty
-foreign import stdcall unsafe "windows.h RegQueryValueExW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegQueryValueExW"
   c_RegQueryValueEx :: PKEY -> LPCTSTR -> Ptr DWORD -> Ptr DWORD -> LPBYTE -> Ptr DWORD -> IO ErrCode
 
-regReplaceKey :: HKEY -> String -> String -> String -> IO ()
+regReplaceKey :: HKEY -> Maybe String -> String -> String -> IO ()
 regReplaceKey key subkey newfile oldfile =
   withForeignPtr key $ \ p_key ->
-  withTString subkey $ \ c_subkey ->
+  maybeWith withTString subkey $ \ c_subkey ->
   withTString newfile $ \ c_newfile ->
   withTString oldfile $ \ c_oldfile ->
   failUnlessSuccess "RegReplaceKey" $
     c_RegReplaceKey p_key c_subkey c_newfile c_oldfile
-foreign import stdcall unsafe "windows.h RegReplaceKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegReplaceKeyW"
   c_RegReplaceKey :: PKEY -> LPCTSTR -> LPCTSTR -> LPCTSTR -> IO ErrCode
 
 type RegRestoreFlags = DWORD
@@ -444,7 +525,7 @@
   withForeignPtr key $ \ p_key ->
   withTString file $ \ c_file ->
   failUnlessSuccess "RegRestoreKey" $ c_RegRestoreKey p_key c_file flags
-foreign import stdcall unsafe "windows.h RegRestoreKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegRestoreKeyW"
   c_RegRestoreKey :: PKEY -> LPCTSTR -> RegRestoreFlags -> IO ErrCode
 
 regSaveKey :: HKEY -> String -> Maybe LPSECURITY_ATTRIBUTES -> IO ()
@@ -452,7 +533,7 @@
   withForeignPtr key $ \ p_key ->
   withTString file $ \ c_file ->
   failUnlessSuccess "RegSaveKey" $ c_RegSaveKey p_key c_file (maybePtr mb_attr)
-foreign import stdcall unsafe "windows.h RegSaveKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegSaveKeyW"
   c_RegSaveKey :: PKEY -> LPCTSTR -> LPSECURITY_ATTRIBUTES -> IO ErrCode
 
 -- ifdef FOR_WINDOWS_NT
@@ -463,14 +544,43 @@
 
 -- 3.1 compat. - only allows storage of REG_SZ values.
 
+regGetValue :: HKEY -> Maybe String -> Maybe String -> RegTypeRestriction -> Maybe LPDWORD -> Maybe LPVOID -> Maybe LPDWORD -> IO ()
+regGetValue key m_subkey m_valuename flags m_type m_value m_size =
+  withForeignPtr key $ \ p_key ->
+  maybeWith withTString m_subkey $ \ c_subkey ->
+  maybeWith withTString m_valuename $ \ c_valuename ->
+  failUnlessSuccess "RegGetValue" $
+    c_RegGetValue p_key c_subkey c_valuename flags (maybePtr m_type) (maybePtr m_value) (maybePtr m_size)
+foreign import WINDOWS_CCONV unsafe "windows.h RegGetValueW"
+  c_RegGetValue :: PKEY -> LPCTSTR -> LPCTSTR -> DWORD -> LPDWORD -> LPVOID -> LPDWORD -> IO ErrCode
+
+type RegTypeRestriction = DWORD
+
+#{enum RegTypeRestriction,
+  , rRF_RT_ANY            = RRF_RT_ANY
+  , rRF_RT_DWORD          = RRF_RT_DWORD
+  , rRF_RT_QWORD          = RRF_RT_QWORD
+  , rRF_RT_REG_BINARY     = RRF_RT_REG_BINARY
+  , rRF_RT_REG_DWORD      = RRF_RT_REG_DWORD
+  , rRF_RT_REG_EXPAND_SZ  = RRF_RT_REG_EXPAND_SZ
+  , rRF_RT_REG_MULTI_SZ   = RRF_RT_REG_MULTI_SZ
+  , rRF_RT_REG_NONE       = RRF_RT_REG_NONE
+  , rRF_RT_REG_QWORD      = RRF_RT_REG_QWORD
+  , rRF_RT_REG_SZ         = RRF_RT_REG_SZ
+  , rRF_NOEXPAND          = RRF_NOEXPAND
+  , rRF_ZEROONFAILURE     = RRF_ZEROONFAILURE
+  , rRF_SUBKEY_WOW6464KEY = RRF_SUBKEY_WOW6464KEY
+  , rRF_SUBKEY_WOW6432KEY = RRF_SUBKEY_WOW6432KEY
+  }
+
 regSetValue :: HKEY -> String -> String -> IO ()
 regSetValue key subkey value =
   withForeignPtr key $ \ p_key ->
   withTString subkey $ \ c_subkey ->
-  withTStringLen value $ \ (c_value, value_len) ->
+  withTString value $ \ c_value ->
   failUnlessSuccess "RegSetValue" $
-    c_RegSetValue p_key c_subkey rEG_SZ c_value value_len
-foreign import stdcall unsafe "windows.h RegSetValueW"
+    c_RegSetValue p_key c_subkey rEG_SZ c_value 0 -- cbData is ignored, value needs to be null terminated.
+foreign import WINDOWS_CCONV unsafe "windows.h RegSetValueW"
   c_RegSetValue :: PKEY -> LPCTSTR -> DWORD -> LPCTSTR -> Int -> IO ErrCode
 
 
@@ -495,7 +605,7 @@
 regSetStringValue :: HKEY -> String -> String -> IO ()
 regSetStringValue hk key val =
   withTString val $ \ v ->
-  regSetValueEx hk key rEG_SZ v (length val * sizeOf (undefined::TCHAR))
+  regSetValueEx hk key rEG_SZ v ((1+length val) * sizeOf (undefined::TCHAR))
 
 regSetValueEx :: HKEY -> String -> RegValueType -> LPTSTR -> Int -> IO ()
 regSetValueEx key subkey ty value value_len =
@@ -503,7 +613,7 @@
   withTString subkey $ \ c_subkey ->
   failUnlessSuccess "RegSetValueEx" $
     c_RegSetValueEx p_key c_subkey 0 ty value value_len
-foreign import stdcall unsafe "windows.h RegSetValueExW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegSetValueExW"
   c_RegSetValueEx :: PKEY -> LPCTSTR -> DWORD -> RegValueType -> LPTSTR -> Int -> IO ErrCode
 
 regUnLoadKey :: HKEY -> String -> IO ()
@@ -511,5 +621,5 @@
   withForeignPtr key $ \ p_key ->
   withTString subkey $ \ c_subkey ->
   failUnlessSuccess "RegUnLoadKey" $ c_RegUnLoadKey p_key c_subkey
-foreign import stdcall unsafe "windows.h RegUnLoadKeyW"
+foreign import WINDOWS_CCONV unsafe "windows.h RegUnLoadKeyW"
   c_RegUnLoadKey :: PKEY -> LPCTSTR -> IO ErrCode
diff --git a/System/Win32/Security.hsc b/System/Win32/Security.hsc
--- a/System/Win32/Security.hsc
+++ b/System/Win32/Security.hsc
@@ -1,13 +1,11 @@
 {-# OPTIONS_GHC -w #-}
--- The above warning supression flag is a temporary kludge.
+-- The above warning suppression flag is a temporary kludge.
 -- While working on this module you are encouraged to remove it and fix
 -- any warnings in the module. See
---     http://hackage.haskell.org/trac/ghc/wiki/WorkingConventions#Warnings
+--     https://ghc.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
 -- for details
 
-#if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
-#endif
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  System.Win32.Security
@@ -22,7 +20,7 @@
 --
 -----------------------------------------------------------------------------
 
-module System.Win32.Security ( 
+module System.Win32.Security (
         -- * Types
         SID, PSID,
         ACL, PACL,
@@ -57,6 +55,8 @@
 -- import Foreign.C
 import System.Win32.Types
 
+##include "windows_cconv.h"
+
 #include <windows.h>
 
 -- --------------------------------------------------------------------------
@@ -89,88 +89,88 @@
 newtype SID = SID SID
 type PSID = Ptr SID
 
-foreign import stdcall unsafe "windows.h GetSecurityDescriptorControl"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSecurityDescriptorControl"
   c_getSecurityDescriptorControl
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> Ptr SECURITY_DESCRIPTOR_CONTROL -- pControl
     -> LPDWORD -- lpdwRevision
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h GetSecurityDescriptorDacl"
-  c_getSecurityDescriptorDacl 
+foreign import WINDOWS_CCONV unsafe "windows.h GetSecurityDescriptorDacl"
+  c_getSecurityDescriptorDacl
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> LPBOOL -- lpbDaclPresent
     -> Ptr PACL -- pDacl
     -> LPBOOL -- lpbDaclDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h GetSecurityDescriptorGroup"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSecurityDescriptorGroup"
   c_getSecurityDescriptorGroup
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> Ptr PSID -- pGroup
     -> LPBOOL -- lpbGroupDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h GetSecurityDescriptorLength"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSecurityDescriptorLength"
   c_getSecurityDescriptorLength
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
-    -> IO DWORD 
+    -> IO DWORD
 
-foreign import stdcall unsafe "windows.h GetSecurityDescriptorOwner"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSecurityDescriptorOwner"
   c_getSecurityDescriptorOwner
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> Ptr PSID -- pOwner
     -> LPBOOL -- lpbOwnerDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h GetSecurityDescriptorSacl"
+foreign import WINDOWS_CCONV unsafe "windows.h GetSecurityDescriptorSacl"
   c_getSecurityDescriptorSacl
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> LPBOOL -- lpbSaclPresent
     -> Ptr PACL -- pSacl
     -> LPBOOL -- lpbSaclDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h InitializeSecurityDescriptor"
+foreign import WINDOWS_CCONV unsafe "windows.h InitializeSecurityDescriptor"
   c_initializeSecurityDescriptor
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> DWORD -- dwRevision
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h IsValidSecurityDescriptor"
+foreign import WINDOWS_CCONV unsafe "windows.h IsValidSecurityDescriptor"
   c_isValidSecurityDescriptor
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h SetSecurityDescriptorDacl"
+foreign import WINDOWS_CCONV unsafe "windows.h SetSecurityDescriptorDacl"
   c_setSecurityDescriptorDacl
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> BOOL -- bDaclPresent
     -> PACL -- pDacl
     -> BOOL -- bDaclDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h SetSecurityDescriptorGroup"
+foreign import WINDOWS_CCONV unsafe "windows.h SetSecurityDescriptorGroup"
   c_setSecurityDescriptorGroup
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> PSID -- pGroup
     -> BOOL -- bGroupDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h SetSecurityDescriptorOwner"
+foreign import WINDOWS_CCONV unsafe "windows.h SetSecurityDescriptorOwner"
   c_setSecurityDescriptorOwner
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> PSID -- pOwner
     -> BOOL -- bOwnerDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
-foreign import stdcall unsafe "windows.h SetSecurityDescriptorSacl"
+foreign import WINDOWS_CCONV unsafe "windows.h SetSecurityDescriptorSacl"
   c_setSecurityDescriptorSacl
     :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> BOOL -- bSaclPresent
     -> PACL -- pSacl
     -> BOOL -- bSaclDefaulted
-    -> IO BOOL 
+    -> IO BOOL
 
 -- ---------------------------------------------------------------------------
 
@@ -188,7 +188,7 @@
 -- , pROTECTED_DACL_SECURITY_INFORMATION = PROTECTED_DACL_SECURITY_INFORMATION
 -- , pROTECTED_SACL_SECURITY_INFORMATION = PROTECTED_SACL_SECURITY_INFORMATION
 -- , uNPROTECTED_DACL_SECURITY_INFORMATION = UNPROTECTED_DACL_SECURITY_INFORMATION
--- , uNPROTECTED_SACL_SECURITY_INFORMATION = UNPROTECTED_SACL_SECURITY_INFORMATION 
+-- , uNPROTECTED_SACL_SECURITY_INFORMATION = UNPROTECTED_SACL_SECURITY_INFORMATION
 
 getFileSecurity
     :: String
@@ -201,20 +201,20 @@
   needed <- peek lpnLengthNeeded
   fpSd <- mallocForeignPtrBytes (fromIntegral needed)
   withForeignPtr fpSd $ \pSd -> do
-  failIfFalse_ "getFileSecurity" $ 
+  failIfFalse_ "getFileSecurity" $
     c_GetFileSecurity lpFileName si pSd needed lpnLengthNeeded
   return (SecurityDescriptor fpSd)
 
-foreign import stdcall unsafe "windows.h GetFileSecurityW"
+foreign import WINDOWS_CCONV unsafe "windows.h GetFileSecurityW"
   c_GetFileSecurity
     :: LPCWSTR -- lpFileName
     -> SECURITY_INFORMATION -- RequestedInformation
     -> PSECURITY_DESCRIPTOR -- pSecurityDescriptor
     -> DWORD -- nLength
     -> LPDWORD -- lpnLengthNeeded
-    -> IO BOOL 
+    -> IO BOOL
 
---foreign import stdcall unsafe "windows.h AccessCheck"
+--foreign import WINDOWS_CCONV unsafe "windows.h AccessCheck"
 --  c_AccessCheck
 --    :: PSECURITY_DESCRIPTOR -- pSecurityDescriptor
 --    -> HANDLE -- ClientToken
@@ -226,7 +226,7 @@
 --    -> LPBOOL -- AccessStatus
 --    -> IO BOOL
 
--- foreign import stdcall unsafe "windows.h OpenThreadToken"
+-- foreign import WINDOWS_CCONV unsafe "windows.h OpenThreadToken"
 --    OpenThreadToken
 --      :: HANDLE -- ThreadHandle
 --      -> DWORD -- DesiredAccess
diff --git a/System/Win32/Semaphore.hsc b/System/Win32/Semaphore.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Semaphore.hsc
@@ -0,0 +1,146 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Semaphore
+-- Copyright   :  (c) Sam Derbyshire, 2022
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Sam Derbyshire
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- Windows Semaphore objects and operations
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Semaphore
+    ( -- * Semaphores
+      Semaphore(..)
+
+      -- * Access modes
+    , AccessMode
+    , sEMAPHORE_ALL_ACCESS
+    , sEMAPHORE_MODIFY_STATE
+
+      -- * Managing semaphores
+    , createSemaphore
+    , openSemaphore
+    , releaseSemaphore
+    ) where
+
+import System.Win32.File
+import System.Win32.Types
+
+import Data.Maybe (fromMaybe)
+import Foreign hiding (void)
+import Foreign.C (withCAString)
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+
+----------------------------------------------------------------
+-- Semaphore access modes
+----------------------------------------------------------------
+
+#{enum AccessMode,
+    , sEMAPHORE_ALL_ACCESS   = SEMAPHORE_ALL_ACCESS
+    , sEMAPHORE_MODIFY_STATE = SEMAPHORE_MODIFY_STATE
+    }
+
+----------------------------------------------------------------
+-- Semaphores
+----------------------------------------------------------------
+
+-- | A Windows semaphore.
+--
+-- To obtain a 'Semaphore', use 'createSemaphore' to create a new one,
+-- or 'openSemaphore' to open an existing one.
+--
+-- To wait on a semaphore, use 'System.Win32.Event.waitForSingleObject'.
+--
+-- To release resources on a semaphore, use 'releaseSemaphore'.
+--
+-- To free a semaphore, use 'System.Win32.File.closeHandle'.
+-- The semaphore object is destroyed when its last handle has been closed.
+-- Closing the handle does not affect the semaphore count; therefore, be sure to call
+-- 'releaseSemaphore' before closing the handle or before the process terminates.
+-- Otherwise, pending wait operations will either time out or continue indefinitely,
+-- depending on whether a time-out value has been specified.
+newtype Semaphore = Semaphore { semaphoreHandle :: HANDLE }
+
+-- | Open a 'Semaphore' with the given name, or create a new semaphore
+-- if no such semaphore exists, with initial count @i@ and maximum count @m@.
+--
+-- The counts must satisfy @i >= 0@, @m > 0@ and @i <= m@.
+--
+-- The returned 'Bool' is 'True' if the function found an existing semaphore
+-- with the given name, in which case a handle to that semaphore is returned
+-- and the counts are ignored.
+--
+-- Use 'openSemaphore' if you don't want to create a new semaphore.
+createSemaphore :: Maybe SECURITY_ATTRIBUTES
+                -> LONG         -- ^ initial count @i@ with @0 <= i <= m@
+                -> LONG         -- ^ maximum count @m > 0@
+                -> Maybe String -- ^ (optional) semaphore name
+                                -- (case-sensitive, limited to MAX_PATH characters)
+                -> IO (Semaphore, Bool)
+createSemaphore mb_sec initial_count max_count mb_name =
+  maybeWith with mb_sec $ \ c_sec -> do
+  maybeWith withCAString mb_name $ \ c_name -> do
+  handle <- c_CreateSemaphore c_sec initial_count max_count c_name
+  err_code <- getLastError
+  already_exists <-
+    case err_code of
+      (# const ERROR_INVALID_HANDLE) ->
+        errorWin $ "createSemaphore: semaphore name '"
+                ++ fromMaybe "" mb_name
+                ++ "' matches non-semaphore"
+      (# const ERROR_ALREADY_EXISTS) ->
+        return True
+      _                              ->
+        return False
+  if handle == nullPtr
+  then errorWin "createSemaphore"
+  else return (Semaphore handle, already_exists)
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateSemaphoreA"
+  c_CreateSemaphore :: LPSECURITY_ATTRIBUTES -> LONG -> LONG -> LPCSTR -> IO HANDLE
+
+-- | Open an existing 'Semaphore'.
+openSemaphore :: AccessMode -- ^ desired access mode
+              -> Bool       -- ^ should child processes inherit the handle?
+              -> String     -- ^ name of the semaphore to open (case-sensitive)
+              -> IO Semaphore
+openSemaphore amode inherit name =
+  withTString name $ \c_name -> do
+    handle <- failIfNull ("openSemaphore: '" ++ name ++ "'") $
+              c_OpenSemaphore (fromIntegral amode) inherit c_name
+    return (Semaphore handle)
+
+foreign import WINDOWS_CCONV unsafe "windows.h OpenSemaphoreW"
+  c_OpenSemaphore :: DWORD -> BOOL -> LPCWSTR -> IO HANDLE
+
+-- | Increase the count of the 'Semaphore' by the specified amount.
+--
+-- Returns the count of the semaphore before the increase.
+--
+-- Throws an error if the count would exceeded the maximum count
+-- of the semaphore.
+releaseSemaphore :: Semaphore -> LONG -> IO LONG
+releaseSemaphore (Semaphore handle) count =
+  with 0 $ \ ptr_prevCount -> do
+  failIfFalse_ "releaseSemaphore" $ c_ReleaseSemaphore handle count ptr_prevCount
+  peek ptr_prevCount
+
+foreign import WINDOWS_CCONV unsafe "windows.h ReleaseSemaphore"
+  c_ReleaseSemaphore :: HANDLE -> LONG -> Ptr LONG -> IO BOOL
+
+----------------------------------------------------------------
+-- End
+----------------------------------------------------------------
diff --git a/System/Win32/Shell.hsc b/System/Win32/Shell.hsc
--- a/System/Win32/Shell.hsc
+++ b/System/Win32/Shell.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -22,20 +24,24 @@
   cSIDL_APPDATA,
   cSIDL_WINDOWS,
   cSIDL_PERSONAL,
+  cSIDL_LOCAL_APPDATA,
+  cSIDL_DESKTOPDIRECTORY,
   cSIDL_PROGRAM_FILES,
   SHGetFolderPathFlags,
   sHGFP_TYPE_CURRENT,
   sHGFP_TYPE_DEFAULT
  ) where
 
+import System.Win32.Shell.Internal
 import System.Win32.Types
 import Graphics.Win32.GDI.Types (HWND)
 
 import Foreign
 import Foreign.C
 import Control.Monad
-import System.IO.Error
 
+##include "windows_cconv.h"
+
 -- for SHGetFolderPath stuff
 #define _WIN32_IE 0x500
 #include <windows.h>
@@ -54,6 +60,8 @@
  , cSIDL_APPDATA  = CSIDL_APPDATA
  , cSIDL_WINDOWS  = CSIDL_WINDOWS
  , cSIDL_PERSONAL = CSIDL_PERSONAL
+ , cSIDL_LOCAL_APPDATA = CSIDL_LOCAL_APPDATA
+ , cSIDL_DESKTOPDIRECTORY = CSIDL_DESKTOPDIRECTORY
  , cSIDL_PROGRAM_FILES = CSIDL_PROGRAM_FILES
  }
 -- XXX there are lots more of these
@@ -71,11 +79,3 @@
     r <- c_SHGetFolderPath hwnd csidl hdl flags pstr
     when (r < 0) $ raiseUnsupported "sHGetFolderPath"
     peekTString pstr
-
-raiseUnsupported :: String -> IO ()
-raiseUnsupported loc = 
-   ioError (ioeSetErrorString (mkIOError illegalOperationErrorType loc Nothing Nothing) "unsupported operation")
-
-foreign import stdcall unsafe "SHGetFolderPathW"
-  c_SHGetFolderPath :: HWND -> CInt -> HANDLE -> DWORD -> LPTSTR
-                    -> IO HRESULT
diff --git a/System/Win32/Shell/Internal.hsc b/System/Win32/Shell/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Shell/Internal.hsc
@@ -0,0 +1,50 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Shell.Internal
+-- Copyright   :  (c) The University of Glasgow 2009
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- Win32 stuff from shell32.dll
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Shell.Internal (
+   c_SHGetFolderPath
+ , raiseUnsupported
+ ) where
+
+import System.Win32.Types
+import Graphics.Win32.GDI.Types (HWND)
+
+import Foreign.C
+import System.IO.Error
+
+##include "windows_cconv.h"
+
+-- for SHGetFolderPath stuff
+#define _WIN32_IE 0x500
+#include <windows.h>
+#include <shlobj.h>
+
+----------------------------------------------------------------
+-- SHGetFolderPath
+--
+-- XXX: this is deprecated in Vista and later
+----------------------------------------------------------------
+
+raiseUnsupported :: String -> IO ()
+raiseUnsupported loc =
+   ioError (ioeSetErrorString (mkIOError illegalOperationErrorType loc Nothing Nothing) "unsupported operation")
+
+foreign import WINDOWS_CCONV unsafe "SHGetFolderPathW"
+  c_SHGetFolderPath :: HWND -> CInt -> HANDLE -> DWORD -> LPTSTR
+                    -> IO HRESULT
diff --git a/System/Win32/SimpleMAPI.hsc b/System/Win32/SimpleMAPI.hsc
--- a/System/Win32/SimpleMAPI.hsc
+++ b/System/Win32/SimpleMAPI.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -16,6 +18,12 @@
 -----------------------------------------------------------------------------
 module System.Win32.SimpleMAPI
 where
+
+-- I am not sure why exactly, but with mingw64 mapi.h does not define
+-- some of the values we use, e.g. MAPI_LOGOFF_SHARED.
+-- mapix.h does define MAPI_LOGOFF_SHARED, but the various flags
+-- clash with each other.
+
 import Control.Exception    ( bracket, handle, finally, onException
                             , IOException )
 import Control.Monad        ( liftM5 )
@@ -27,10 +35,11 @@
   -- Apparently, simple MAPI does not support unicode and probably never will,
   -- so this module will just mangle any Unicode in your strings
 import Graphics.Win32.GDI.Types     ( HWND)
-import System.Win32.DLL     ( loadLibrary, c_GetProcAddress, freeLibrary
-                            , c_FreeLibraryFinaliser )
+import System.Win32.DLL     ( loadLibrary, freeLibrary, getProcAddress )
 import System.Win32.Types   ( DWORD, LPSTR, HMODULE, failIfNull )
 
+##include "windows_cconv.h"
+
 #include "windows.h"
 #include "mapi.h"
 
@@ -43,8 +52,6 @@
     , mAPI_LOGON_UI         = MAPI_LOGON_UI
     , mAPI_NEW_SESSION      = MAPI_NEW_SESSION
     , mAPI_FORCE_DOWNLOAD   = MAPI_FORCE_DOWNLOAD
-    , mAPI_LOGOFF_SHARED    = MAPI_LOGOFF_SHARED
-    , mAPI_LOGOFF_UI        = MAPI_LOGOFF_UI
     , mAPI_DIALOG           = MAPI_DIALOG
     , mAPI_UNREAD_ONLY      = MAPI_UNREAD_ONLY
     , mAPI_LONG_MSGID       = MAPI_LONG_MSGID
@@ -60,6 +67,19 @@
     , mAPI_RECEIPT_REQUESTED = MAPI_RECEIPT_REQUESTED
     , mAPI_SENT             = MAPI_SENT
     }
+-- Have to define enum values outside previous declaration due to
+-- hsc2hs bug in --cross-compile mode:
+--    https://ghc.haskell.org/trac/ghc/ticket/13620
+#ifdef MAPI_LOGOFF_SHARED
+#{enum MapiFlag,
+    , mAPI_LOGOFF_SHARED    = MAPI_LOGOFF_SHARED
+}
+#endif
+#ifdef MAPI_LOGOFF_UI
+#{enum MapiFlag,
+    , mAPI_LOGOFF_UI        = MAPI_LOGOFF_UI
+}
+#endif
 
 mapiErrors :: [(ULONG,String)]
 mapiErrors =
@@ -71,11 +91,13 @@
     , ((#const MAPI_E_DISK_FULL)        , "Disk full")
     , ((#const MAPI_E_INSUFFICIENT_MEMORY)      , "Not enough memory")
     , ((#const MAPI_E_ACCESS_DENIED)    , "Access denied")
+#ifdef MAPI_E_BLK_TOO_SMALL
     , ((#const MAPI_E_BLK_TOO_SMALL)    , "BLK_TOO_SMALL")
+#endif
     , ((#const MAPI_E_TOO_MANY_SESSIONS), "Too many open sessions")
     , ((#const MAPI_E_TOO_MANY_FILES)   , "Too many open files")
     , ((#const MAPI_E_TOO_MANY_RECIPIENTS)      , "Too many recipients")
-    , ((#const MAPI_E_ATTACHMENT_NOT_FOUND)     , "Attachemnt not found")
+    , ((#const MAPI_E_ATTACHMENT_NOT_FOUND)     , "Attachment not found")
     , ((#const MAPI_E_ATTACHMENT_OPEN_FAILURE)  , "Couldn't open attachment")
     , ((#const MAPI_E_ATTACHMENT_WRITE_FAILURE) , "Couldn't write attachment")
     , ((#const MAPI_E_UNKNOWN_RECIPIENT)        , "Unknown recipient")
@@ -85,8 +107,10 @@
     , ((#const MAPI_E_TEXT_TOO_LARGE)           , "Text too large")
     , ((#const MAPI_E_INVALID_SESSION)          , "Invalid session")
     , ((#const MAPI_E_TYPE_NOT_SUPPORTED)       , "Type not supported")
-    , ((#const MAPI_E_AMBIGUOUS_RECIPIENT)      , "Ambigious recipient")
-    , ((#const MAPI_E_AMBIGUOUS_RECIP)          , "Ambigious recipient")
+    , ((#const MAPI_E_AMBIGUOUS_RECIPIENT)      , "Ambiguous recipient")
+#ifdef MAPI_E_AMBIGUOUS_RECIP
+    , ((#const MAPI_E_AMBIGUOUS_RECIP)          , "Ambiguous recipient")
+#endif
     , ((#const MAPI_E_MESSAGE_IN_USE)           , "Message in use")
     , ((#const MAPI_E_NETWORK_FAILURE)          , "Network failure")
     , ((#const MAPI_E_INVALID_EDITFIELDS)       , "Invalid editfields")
@@ -109,21 +133,21 @@
 mapiFail_ n a = mapiFail n a >> return ()
 
 type MapiLogonType = ULONG -> LPSTR -> LPSTR -> MapiFlag -> ULONG -> Ptr LHANDLE -> IO ULONG
-foreign import stdcall "dynamic" mkMapiLogon :: FunPtr MapiLogonType -> MapiLogonType
+foreign import WINDOWS_CCONV "dynamic" mkMapiLogon :: FunPtr MapiLogonType -> MapiLogonType
 
 type MapiLogoffType = LHANDLE -> ULONG -> MapiFlag -> ULONG -> IO ULONG
-foreign import stdcall "dynamic" mkMapiLogoff :: FunPtr MapiLogoffType -> MapiLogoffType
+foreign import WINDOWS_CCONV "dynamic" mkMapiLogoff :: FunPtr MapiLogoffType -> MapiLogoffType
 
 type MapiResolveNameType =
     LHANDLE -> ULONG -> LPSTR -> MapiFlag -> ULONG
     -> Ptr (Ptr MapiRecipDesc) -> IO ULONG
-foreign import stdcall "dynamic" mkMapiResolveName :: FunPtr MapiResolveNameType -> MapiResolveNameType
+foreign import WINDOWS_CCONV "dynamic" mkMapiResolveName :: FunPtr MapiResolveNameType -> MapiResolveNameType
 
 type MapiFreeBufferType = Ptr () -> IO ULONG
-foreign import stdcall "dynamic" mkMapiFreeBuffer :: FunPtr MapiFreeBufferType -> MapiFreeBufferType
+foreign import WINDOWS_CCONV "dynamic" mkMapiFreeBuffer :: FunPtr MapiFreeBufferType -> MapiFreeBufferType
 
 type MapiSendMailType = LHANDLE -> ULONG -> Ptr Message -> MapiFlag -> ULONG -> IO ULONG
-foreign import stdcall "dynamic" mkMapiSendMail :: FunPtr MapiSendMailType -> MapiSendMailType
+foreign import WINDOWS_CCONV "dynamic" mkMapiSendMail :: FunPtr MapiSendMailType -> MapiSendMailType
 
 data MapiFuncs = MapiFuncs
     { mapifLogon    :: MapiLogonType
@@ -146,9 +170,9 @@
     (loadProc "MAPISendMail"    dll mkMapiSendMail)
     where
        loadProc :: String -> HMODULE -> (FunPtr a -> a) -> IO a
-       loadProc name dll conv = withCAString name $ \name' -> do
+       loadProc name dll' conv = do
             proc <- failIfNull ("loadMapiDll: " ++ dllname ++ ": " ++ name)
-                        $ c_GetProcAddress dll name'
+                        $ getProcAddress dll' name
             return $ conv $ castPtrToFunPtr proc
 -- |
 loadMapiDll :: String -> IO (MapiFuncs, HMODULE)
@@ -179,6 +203,11 @@
             []  -> fail $ "loadMapi: Failed to load any of DLLs: " ++ show dlls
             x:y -> handleIOException (const $ loadOne y) (loadMapiDll x)
 
+
+{-# CFILES cbits/HsWin32.c #-}
+foreign import ccall "HsWin32.h &FreeLibraryFinaliser"
+    c_FreeLibraryFinaliser :: FunPtr (HMODULE -> IO ())
+
 -- |
 withMapiLoaded :: MapiLoaded -> (MapiFuncs -> IO a) -> IO a
 withMapiLoaded (f,m) act = finally (act f) (touchForeignPtr m)
@@ -195,12 +224,12 @@
     -> MapiFlag     -- ^ None, one or many flags: FORCE_DOWNLOAD, NEW_SESSION, LOGON_UI, PASSWORD_UI
     -> IO LHANDLE
 mapiLogon f hwnd ses pw flags =
-    maybeWith withCAString ses  $ \ses  ->
-    maybeWith withCAString pw   $ \pw   ->
-    alloca                      $ \out  -> do
+    maybeWith withCAString ses  $ \c_ses ->
+    maybeWith withCAString pw   $ \c_pw  ->
+    alloca                      $ \out   -> do
         mapiFail_ "MAPILogon: " $ mapifLogon
-            f (maybeHWND hwnd) 
-            ses pw flags 0 out
+            f (maybeHWND hwnd)
+            c_ses c_pw flags 0 out
         peek out
 
 -- | End Simple MAPI-session
@@ -247,12 +276,12 @@
             act buf
         resolve err rc = case rc of
             Recip name addr ->
-                withCAString name $ \name ->
-                withCAString addr $ \addr ->
+                withCAString name $ \c_name ->
+                withCAString addr $ \c_addr ->
                 allocaBytes (#size MapiRecipDesc) $ \buf -> do
                     (#poke MapiRecipDesc, ulReserved)   buf (0::ULONG)
-                    (#poke MapiRecipDesc, lpszName)     buf name
-                    (#poke MapiRecipDesc, lpszAddress)  buf addr
+                    (#poke MapiRecipDesc, lpszName)     buf c_name
+                    (#poke MapiRecipDesc, lpszAddress)  buf c_addr
                     (#poke MapiRecipDesc, ulEIDSize)    buf (0::ULONG)
                     (#poke MapiRecipDesc, lpEntryID)    buf nullPtr
                     a buf
@@ -333,18 +362,18 @@
     where
         as = (#size MapiFileDesc)
         len = length att
-        write act _ [] = act
-        write act buf (att:y) =
-            withCAString (attPath att) $ \path ->
-            maybeWith withFileTag (attTag att) $ \tag ->
-            withCAString (maybe (attPath att) id (attName att)) $ \name -> do
+        write act' _ [] = act'
+        write act' buf (att':y) =
+            withCAString (attPath att') $ \path ->
+            maybeWith withFileTag (attTag att') $ \tag ->
+            withCAString (maybe (attPath att') id (attName att')) $ \name -> do
                 (#poke MapiFileDesc, ulReserved)    buf (0::ULONG)
-                (#poke MapiFileDesc, flFlags)       buf (attFlag att)
-                (#poke MapiFileDesc, nPosition)     buf (maybe 0xffffffff id $ attPosition att)
+                (#poke MapiFileDesc, flFlags)       buf (attFlag att')
+                (#poke MapiFileDesc, nPosition)     buf (maybe 0xffffffff id $ attPosition att')
                 (#poke MapiFileDesc, lpszPathName)  buf path
                 (#poke MapiFileDesc, lpszFileName)  buf name
                 (#poke MapiFileDesc, lpFileType)    buf tag
-                write act (plusPtr buf as) y
+                write act' (plusPtr buf as) y
 
 data Message = Message
     { msgSubject    :: String
@@ -392,9 +421,8 @@
         act buf
 
 mapiSendMail :: MapiFuncs -> LHANDLE -> Maybe HWND -> Message -> MapiFlag -> IO ()
-mapiSendMail f ses hwnd msg flag = withMessage f ses msg $ \msg ->
-    mapiFail_ "MAPISendMail" $ mapifSendMail f ses (maybeHWND hwnd) msg flag 0
+mapiSendMail f ses hwnd msg flag = withMessage f ses msg $ \c_msg ->
+    mapiFail_ "MAPISendMail" $ mapifSendMail f ses (maybeHWND hwnd) c_msg flag 0
 
 handleIOException :: (IOException -> IO a) -> IO a -> IO a
 handleIOException = handle
-
diff --git a/System/Win32/String.hs b/System/Win32/String.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/String.hs
@@ -0,0 +1,51 @@
+{- |
+   Module      :  System.Win32.String
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Utilities for primitive marshalling of Windows' C strings.
+-}
+module System.Win32.String
+  ( LPSTR, LPCSTR, LPWSTR, LPCWSTR
+  , TCHAR, LPTSTR, LPCTSTR, LPCTSTR_
+  , withTString, withTStringLen, peekTString, peekTStringLen
+  , newTString
+  , withTStringBuffer, withTStringBufferLen
+  ) where
+import System.Win32.Types
+
+-- | Marshal a dummy Haskell string into a NUL terminated C wide string
+-- using temporary storage.
+--
+-- * the Haskell string is created by length parameter. And the Haskell
+--   string contains /only/ NUL characters.
+--
+-- * the memory is freed when the subcomputation terminates (either
+--   normally or via an exception), so the pointer to the temporary
+--   storage must /not/ be used after this.
+--
+withTStringBuffer :: Int -> (LPTSTR -> IO a) -> IO a
+withTStringBuffer maxLength
+  = let dummyBuffer = replicate maxLength '\0'
+    in  withTString dummyBuffer
+
+-- | Marshal a dummy Haskell string into a C wide string (i.e. wide
+-- character array) in temporary storage, with explicit length
+-- information.
+--
+-- * the Haskell string is created by length parameter. And the Haskell
+--   string contains /only/ NUL characters.
+--
+-- * the memory is freed when the subcomputation terminates (either
+--   normally or via an exception), so the pointer to the temporary
+--   storage must /not/ be used after this.
+--
+withTStringBufferLen :: Int -> ((LPTSTR, Int) -> IO a) -> IO a
+withTStringBufferLen maxLength
+  = let dummyBuffer = replicate maxLength '\0'
+    in  withTStringLen dummyBuffer
+
diff --git a/System/Win32/SymbolicLink.hsc b/System/Win32/SymbolicLink.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/SymbolicLink.hsc
@@ -0,0 +1,93 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.SymbolicLink
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Handling symbolic link using Win32 API. [Vista of later and desktop app only]
+
+   Note: When using the createSymbolicLink* functions without the
+   SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag, you should worry about UAC
+   (User Account Control) when use this module's function in your application:
+
+     * require to use 'Run As Administrator' to run your application.
+
+     * or modify your application's manifest file to add
+       \<requestedExecutionLevel level='requireAdministrator' uiAccess='false'/\>.
+
+   Starting from Windows 10 version 1703 (Creators Update), after enabling
+   Developer Mode, users can create symbolic links without requiring the
+   Administrator privilege in the current process. Supply a 'True' flag in
+   addition to the target and link name to enable this behavior.
+-}
+module System.Win32.SymbolicLink
+  ( SymbolicLinkFlags
+  , sYMBOLIC_LINK_FLAG_FILE
+  , sYMBOLIC_LINK_FLAG_DIRECTORY
+  , sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+  , createSymbolicLink
+  , createSymbolicLink'
+  , createSymbolicLinkFile
+  , createSymbolicLinkDirectory
+  ) where
+
+import System.Win32.SymbolicLink.Internal
+import Data.Bits ((.|.))
+import System.Win32.Types
+import System.Win32.File ( failIfFalseWithRetry_ )
+
+##include "windows_cconv.h"
+
+
+-- | createSymbolicLink* functions don't check that file is exist or not.
+--
+-- NOTE: createSymbolicLink* functions are /flipped arguments/ to provide compatibility for Unix,
+-- except 'createSymbolicLink''.
+--
+-- If you want to create symbolic link by Windows way, use 'createSymbolicLink'' instead.
+createSymbolicLink :: FilePath -- ^ Target file path
+                   -> FilePath -- ^ Symbolic link name
+                   -> SymbolicLinkFlags -> IO ()
+createSymbolicLink = flip createSymbolicLink'
+
+createSymbolicLinkFile :: FilePath -- ^ Target file path
+                       -> FilePath -- ^ Symbolic link name
+                       -> Bool -- ^ Create the symbolic link with the unprivileged mode
+                       -> IO ()
+createSymbolicLinkFile target link unprivileged =
+  createSymbolicLink'
+    link
+    target
+    ( if unprivileged
+        then sYMBOLIC_LINK_FLAG_FILE .|. sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+        else sYMBOLIC_LINK_FLAG_FILE
+    )
+
+createSymbolicLinkDirectory :: FilePath -- ^ Target file path
+                            -> FilePath -- ^ Symbolic link name
+                            -> Bool -- ^ Create the symbolic link with the unprivileged mode
+                            -> IO ()
+createSymbolicLinkDirectory target link unprivileged =
+  createSymbolicLink'
+    link
+    target
+    ( if unprivileged
+        then
+          sYMBOLIC_LINK_FLAG_DIRECTORY
+            .|. sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+        else sYMBOLIC_LINK_FLAG_DIRECTORY
+    )
+
+createSymbolicLink' :: FilePath -- ^ Symbolic link name
+                    -> FilePath -- ^ Target file path
+                    -> SymbolicLinkFlags -> IO ()
+createSymbolicLink' link target flag = do
+    withTString link $ \c_link ->
+      withTString target $ \c_target ->
+        failIfFalseWithRetry_ (unwords ["CreateSymbolicLink",show link,show target]) $
+          c_CreateSymbolicLink c_link c_target flag
+
diff --git a/System/Win32/SymbolicLink/Internal.hsc b/System/Win32/SymbolicLink/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/SymbolicLink/Internal.hsc
@@ -0,0 +1,26 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.SymbolicLink.Internal
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+-}
+module System.Win32.SymbolicLink.Internal where
+
+import System.Win32.Types
+
+##include "windows_cconv.h"
+
+type SymbolicLinkFlags = DWORD
+
+#{enum SymbolicLinkFlags,
+ , sYMBOLIC_LINK_FLAG_FILE      = 0x0
+ , sYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
+ , sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2
+}
+
+foreign import WINDOWS_CCONV unsafe "windows.h CreateSymbolicLinkW"
+  c_CreateSymbolicLink :: LPTSTR -> LPTSTR -> SymbolicLinkFlags -> IO BOOL
diff --git a/System/Win32/Thread.hs b/System/Win32/Thread.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Thread.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.Thread
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   This modules provides just thread control APIs.
+   This modules doesn't provide thread register control APIs. Because these APIs are used for Debugging.
+-}
+module System.Win32.Thread
+  ( THANDLE, TID
+  , getCurrentThread
+  , suspendThread
+  , resumeThread
+  , withSuspendedThread
+  , getThreadId
+  , getCurrentThreadId
+  ) where
+
+import System.Win32.DebugApi
+import System.Win32.Types ( failIfZero )
+
+#include "windows_cconv.h"
+
+getThreadId :: THANDLE -> IO TID
+getThreadId = failIfZero "GetThreadId" . c_GetThreadId
+
+getCurrentThreadId :: IO TID
+getCurrentThreadId = failIfZero "GetThreadId" c_GetCurrentThreadId
+
+foreign import WINDOWS_CCONV "windows.h GetCurrentThread"
+    getCurrentThread :: IO THANDLE
+
+foreign import WINDOWS_CCONV "windows.h GetThreadId"
+    c_GetThreadId :: THANDLE -> IO TID
+
+foreign import WINDOWS_CCONV "windows.h GetCurrentThreadId"
+    c_GetCurrentThreadId :: IO TID
diff --git a/System/Win32/Time.hsc b/System/Win32/Time.hsc
--- a/System/Win32/Time.hsc
+++ b/System/Win32/Time.hsc
@@ -1,4 +1,6 @@
-#if __GLASGOW_HASKELL__ >= 701
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
 {-# LANGUAGE Trustworthy #-}
 #endif
 -----------------------------------------------------------------------------
@@ -14,164 +16,111 @@
 -- A collection of FFI declarations for interfacing with Win32 Time API.
 --
 -----------------------------------------------------------------------------
-module System.Win32.Time where
+module System.Win32.Time
+    ( FILETIME(..)
+    , SYSTEMTIME(..)
+    , TIME_ZONE_INFORMATION(..)
+    , TimeZoneId(..)
+    , getSystemTime
+    , setSystemTime
+    , getSystemTimeAsFileTime
+    , getLocalTime
+    , setLocalTime
+    , getSystemTimeAdjustment
+    , getTickCount
+    , getLastInputInfo
+    , getIdleTime
+    , setSystemTimeAdjustment
+    , getTimeZoneInformation
+    , systemTimeToFileTime
+    , fileTimeToSystemTime
+    , getFileTime
+    , setFileTime
+    , invalidFileTime
+    , fileTimeToLocalFileTime
+    , localFileTimeToFileTime
+    , queryPerformanceFrequency
+    , queryPerformanceCounter
+    , GetTimeFormatFlags
+    , lOCALE_NOUSEROVERRIDE
+    , lOCALE_USE_CP_ACP
+    , tIME_NOMINUTESORSECONDS
+    , tIME_NOSECONDS
+    , tIME_NOTIMEMARKER
+    , tIME_FORCE24HOURFORMAT
+    , getTimeFormatEx
+    , getTimeFormat
+    ) where
 
-import System.Win32.Types   ( DWORD, WORD, LONG, BOOL, failIf, failIf_, HANDLE
-                            , peekTStringLen, LCID, LPTSTR, LPCTSTR, DDWORD
-                            , LARGE_INTEGER, ddwordToDwords, dwordsToDdword )
+import System.Win32.Time.Internal
+import System.Win32.String  ( peekTStringLen, withTString )
+import System.Win32.Types   ( DWORD, HANDLE, LCID
+                            , failIf
+                            , failIfFalse_, failIf_ )
+import System.Win32.Utils   ( trySized )
 
-import Control.Monad    ( when, liftM3, liftM )
-import Data.Word        ( Word8 )
-import Foreign          ( Storable(sizeOf, alignment, peekByteOff, peek,
-                                   pokeByteOff, poke)
-                        , Ptr, nullPtr, castPtr, plusPtr, advancePtr
-                        , with, alloca, allocaBytes, copyArray )
-import Foreign.C        ( CInt(..), CWchar(..)
-                        , peekCWString, withCWStringLen, withCWString )
+import Control.Monad    ( liftM3, liftM )
+import Foreign          ( Storable(sizeOf, peek)
+                        , Ptr, nullPtr, castPtr
+                        , with, alloca, allocaBytes )
+import Foreign.C        ( CWchar(..)
+                        , withCWString )
+import Foreign.Marshal.Utils (maybeWith)
 
-#include "windows.h"
+##include "windows_cconv.h"
+#include <windows.h>
+#include "alignment.h"
+#include "winnls_compat.h"
 
-----------------------------------------------------------------
--- data types
-----------------------------------------------------------------
 
-newtype FILETIME = FILETIME DDWORD deriving (Show, Eq, Ord)
-
-data SYSTEMTIME = SYSTEMTIME {
-    wYear, wMonth, wDayOfWeek, wDay, wHour, wMinute, wSecond, wMilliseconds :: WORD }
-    deriving (Show, Eq, Ord)
-
-data TIME_ZONE_INFORMATION = TIME_ZONE_INFORMATION
-    { tziBias :: LONG
-    , tziStandardName :: String
-    , tziStandardDate :: SYSTEMTIME
-    , tziStandardBias :: LONG
-    , tziDaylightName :: String
-    , tziDaylightDate :: SYSTEMTIME
-    , tziDaylightBias :: LONG
-    } deriving (Show,Eq,Ord)
-
-data TimeZoneId = TzIdUnknown | TzIdStandard | TzIdDaylight
-    deriving (Show, Eq, Ord)
-
-----------------------------------------------------------------
--- Instances
-----------------------------------------------------------------
-
-instance Storable FILETIME where
-    sizeOf = const (#size FILETIME)
-    alignment = sizeOf
-    poke buf (FILETIME n) = do
-        (#poke FILETIME, dwLowDateTime) buf low
-        (#poke FILETIME, dwHighDateTime) buf hi
-        where (hi,low) = ddwordToDwords n
-    peek buf = do
-        low <- (#peek FILETIME, dwLowDateTime) buf
-        hi <- (#peek FILETIME, dwHighDateTime) buf
-        return $ FILETIME $ dwordsToDdword (hi,low)
-
-instance Storable SYSTEMTIME where
-    sizeOf _ = #size SYSTEMTIME
-    alignment = sizeOf
-    poke buf st = do
-         (#poke SYSTEMTIME, wYear)          buf (wYear st)
-         (#poke SYSTEMTIME, wMonth)         buf (wMonth st)
-         (#poke SYSTEMTIME, wDayOfWeek)     buf (wDayOfWeek st)
-         (#poke SYSTEMTIME, wDay)           buf (wDay st)
-         (#poke SYSTEMTIME, wHour)          buf (wHour st)
-         (#poke SYSTEMTIME, wMinute)        buf (wMinute st)
-         (#poke SYSTEMTIME, wSecond)        buf (wSecond st)
-         (#poke SYSTEMTIME, wMilliseconds)  buf (wMilliseconds st)
-    peek buf = do
-        year    <- (#peek SYSTEMTIME, wYear)        buf
-        month   <- (#peek SYSTEMTIME, wMonth)       buf
-        dow     <- (#peek SYSTEMTIME, wDayOfWeek)   buf
-        day     <- (#peek SYSTEMTIME, wDay)         buf
-        hour    <- (#peek SYSTEMTIME, wHour)        buf
-        min     <- (#peek SYSTEMTIME, wMinute)      buf
-        sec     <- (#peek SYSTEMTIME, wSecond)      buf
-        ms      <- (#peek SYSTEMTIME, wMilliseconds) buf
-        return $ SYSTEMTIME year month dow day hour min sec ms
-
-instance Storable TIME_ZONE_INFORMATION where
-    sizeOf _ = (#size TIME_ZONE_INFORMATION)
-    alignment = sizeOf
-    poke buf tzi = do
-        (#poke TIME_ZONE_INFORMATION, Bias) buf (tziBias tzi)
-        (#poke TIME_ZONE_INFORMATION, StandardDate) buf (tziStandardDate tzi)
-        (#poke TIME_ZONE_INFORMATION, StandardBias) buf (tziStandardBias tzi)
-        (#poke TIME_ZONE_INFORMATION, DaylightDate) buf (tziDaylightDate tzi)
-        (#poke TIME_ZONE_INFORMATION, DaylightBias) buf (tziDaylightBias tzi)
-        write buf (#offset TIME_ZONE_INFORMATION, StandardName) (tziStandardName tzi)
-        write buf (#offset TIME_ZONE_INFORMATION, DaylightName) (tziDaylightName tzi)
-        where
-            write buf offset str = withCWStringLen str $ \(str,len) -> do
-                when (len>31) $ fail "Storable TIME_ZONE_INFORMATION.poke: Too long string."
-                let start = (advancePtr (castPtr buf) offset)
-                    end = advancePtr start len
-                copyArray (castPtr str :: Ptr Word8) start len
-                poke end 0
-    peek buf = do
-        bias <- (#peek TIME_ZONE_INFORMATION, Bias)         buf
-        sdat <- (#peek TIME_ZONE_INFORMATION, StandardDate) buf
-        sbia <- (#peek TIME_ZONE_INFORMATION, StandardBias) buf
-        ddat <- (#peek TIME_ZONE_INFORMATION, DaylightDate) buf
-        dbia <- (#peek TIME_ZONE_INFORMATION, DaylightBias) buf
-        snam <- peekCWString (plusPtr buf (#offset TIME_ZONE_INFORMATION, StandardName))
-        dnam <- peekCWString (plusPtr buf (#offset TIME_ZONE_INFORMATION, DaylightName))
-        return $ TIME_ZONE_INFORMATION bias snam sdat sbia dnam ddat dbia
-
-foreign import stdcall "windows.h GetSystemTime"
-    c_GetSystemTime :: Ptr SYSTEMTIME -> IO ()
 getSystemTime :: IO SYSTEMTIME
 getSystemTime = alloca $ \res -> do
     c_GetSystemTime res
     peek res
 
-foreign import stdcall "windows.h SetSystemTime"
-    c_SetSystemTime :: Ptr SYSTEMTIME -> IO BOOL
 setSystemTime :: SYSTEMTIME -> IO ()
-setSystemTime st = with st $ \st -> failIf_ not "setSystemTime: SetSystemTime" $
-    c_SetSystemTime st
+setSystemTime st = with st $ \c_st -> failIf_ not "setSystemTime: SetSystemTime" $
+    c_SetSystemTime c_st
 
-foreign import stdcall "windows.h GetSystemTimeAsFileTime"
-    c_GetSystemTimeAsFileTime :: Ptr FILETIME -> IO ()
 getSystemTimeAsFileTime :: IO FILETIME
 getSystemTimeAsFileTime = alloca $ \ret -> do
     c_GetSystemTimeAsFileTime ret
     peek ret
 
-foreign import stdcall "windows.h GetLocalTime"
-    c_GetLocalTime :: Ptr SYSTEMTIME -> IO ()
 getLocalTime :: IO SYSTEMTIME
 getLocalTime = alloca $ \res -> do
     c_GetLocalTime res
     peek res
 
-foreign import stdcall "windows.h SetLocalTime"
-    c_SetLocalTime :: Ptr SYSTEMTIME -> IO BOOL
 setLocalTime :: SYSTEMTIME -> IO ()
-setLocalTime st = with st $ \st -> failIf_ not "setLocalTime: SetLocalTime" $
-    c_SetLocalTime st
+setLocalTime st = with st $ \c_st -> failIf_ not "setLocalTime: SetLocalTime" $
+    c_SetLocalTime c_st
 
-foreign import stdcall "windows.h GetSystemTimeAdjustment"
-    c_GetSystemTimeAdjustment :: Ptr DWORD -> Ptr DWORD -> Ptr BOOL -> IO BOOL
 getSystemTimeAdjustment :: IO (Maybe (Int, Int))
 getSystemTimeAdjustment = alloca $ \ta -> alloca $ \ti -> alloca $ \enabled -> do
     failIf_ not "getSystemTimeAdjustment: GetSystemTimeAdjustment" $
         c_GetSystemTimeAdjustment ta ti enabled
-    enabled <- peek enabled
-    if enabled
+    enabled' <- peek enabled
+    if enabled'
         then do
-            ta <- peek ta
-            ti <- peek ti
-            return $ Just (fromIntegral ta, fromIntegral ti)
+            ta' <- peek ta
+            ti' <- peek ti
+            return $ Just (fromIntegral ta', fromIntegral ti')
         else return Nothing
 
-foreign import stdcall "windows.h GetTickCount" getTickCount :: IO DWORD
+getLastInputInfo :: IO DWORD
+getLastInputInfo =
+  with (LASTINPUTINFO 0) $ \lii_p -> do
+  failIfFalse_ "GetLastInputInfo" $ c_GetLastInputInfo lii_p
+  LASTINPUTINFO lii <- peek lii_p
+  return lii
 
-foreign import stdcall "windows.h SetSystemTimeAdjustment"
-    c_SetSystemTimeAdjustment :: DWORD -> BOOL -> IO BOOL
+getIdleTime :: IO Integer
+getIdleTime = do
+  lii <- getLastInputInfo
+  now <- getTickCount
+  return $ fromIntegral $ now - lii
+
 setSystemTimeAdjustment :: Maybe Int -> IO ()
 setSystemTimeAdjustment ta =
     failIf_ not "setSystemTimeAjustment: SetSystemTimeAdjustment" $
@@ -181,67 +130,63 @@
             Nothing -> (0,True)
             Just x  -> (fromIntegral x,False)
 
-foreign import stdcall "windows.h GetTimeZoneInformation"
-    c_GetTimeZoneInformation :: Ptr TIME_ZONE_INFORMATION -> IO DWORD
 getTimeZoneInformation :: IO (TimeZoneId, TIME_ZONE_INFORMATION)
 getTimeZoneInformation = alloca $ \tzi -> do
     tz <- failIf (==(#const TIME_ZONE_ID_INVALID)) "getTimeZoneInformation: GetTimeZoneInformation" $
         c_GetTimeZoneInformation tzi
-    tzi <- peek tzi
-    return . flip (,) tzi $ case tz of
+    tzi' <- peek tzi
+    return . flip (,) tzi' $ case tz of
         (#const TIME_ZONE_ID_UNKNOWN)   -> TzIdUnknown
         (#const TIME_ZONE_ID_STANDARD)  -> TzIdStandard
         (#const TIME_ZONE_ID_DAYLIGHT)  -> TzIdDaylight
         _                               -> TzIdUnknown   -- to remove warning
 
-foreign import stdcall "windows.h SystemTimeToFileTime"
-    c_SystemTimeToFileTime :: Ptr SYSTEMTIME -> Ptr FILETIME -> IO BOOL
 systemTimeToFileTime :: SYSTEMTIME -> IO FILETIME
-systemTimeToFileTime s = with s $ \s -> alloca $ \ret -> do
+systemTimeToFileTime s = with s $ \c_s -> alloca $ \ret -> do
     failIf_ not "systemTimeToFileTime: SystemTimeToFileTime" $
-        c_SystemTimeToFileTime s ret
+        c_SystemTimeToFileTime c_s ret
     peek ret
 
-foreign import stdcall "windows.h FileTimeToSystemTime"
-    c_FileTimeToSystemTime :: Ptr FILETIME -> Ptr SYSTEMTIME -> IO BOOL
 fileTimeToSystemTime :: FILETIME -> IO SYSTEMTIME
-fileTimeToSystemTime s = with s $ \s -> alloca $ \ret -> do
+fileTimeToSystemTime s = with s $ \c_s -> alloca $ \ret -> do
     failIf_ not "fileTimeToSystemTime: FileTimeToSystemTime" $
-        c_FileTimeToSystemTime s ret
+        c_FileTimeToSystemTime c_s ret
     peek ret
 
-foreign import stdcall "windows.h GetFileTime"
-    c_GetFileTime :: HANDLE -> Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO BOOL
 getFileTime :: HANDLE -> IO (FILETIME,FILETIME,FILETIME)
 getFileTime h = alloca $ \crt -> alloca $ \acc -> alloca $ \wrt -> do
     failIf_ not "getFileTime: GetFileTime" $ c_GetFileTime h crt acc wrt
     liftM3 (,,) (peek crt) (peek acc) (peek wrt)
 
-foreign import stdcall "windows.h SetFileTime"
-    c_SetFileTime :: HANDLE -> Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO BOOL
-setFileTime :: HANDLE -> FILETIME -> FILETIME -> FILETIME -> IO ()
-setFileTime h crt acc wrt = with crt $ \crt -> with acc $ \acc -> with wrt $ \wrt -> do
-    failIf_ not "setFileTime: SetFileTime" $ c_SetFileTime h crt acc wrt
+invalidFileTime :: FILETIME
+invalidFileTime = FILETIME 0
 
-foreign import stdcall "windows.h FileTimeToLocalFileTime"
-    c_FileTimeToLocalFileTime :: Ptr FILETIME -> Ptr FILETIME -> IO BOOL
+setFileTime :: HANDLE -> Maybe FILETIME -> Maybe FILETIME -> Maybe FILETIME -> IO ()
+setFileTime h crt acc wrt = withTime crt $
+    \c_crt -> withTime acc $
+    \c_acc -> withTime wrt $
+    \c_wrt -> do
+      failIf_ not "setFileTime: SetFileTime" $ c_SetFileTime h c_crt c_acc c_wrt
+  where
+    withTime :: Maybe FILETIME -> (Ptr FILETIME -> IO a) -> IO a
+    withTime Nothing k  = k nullPtr
+    withTime (Just t) k = with t k
+
 fileTimeToLocalFileTime :: FILETIME -> IO FILETIME
-fileTimeToLocalFileTime ft = with ft $ \ft -> alloca $ \res -> do
+fileTimeToLocalFileTime ft = with ft $ \c_ft -> alloca $ \res -> do
     failIf_ not "fileTimeToLocalFileTime: FileTimeToLocalFileTime"
-        $ c_FileTimeToLocalFileTime ft res
+        $ c_FileTimeToLocalFileTime c_ft res
     peek res
 
-foreign import stdcall "windows.h LocalFileTimeToFileTime"
-    c_LocalFileTimeToFileTime :: Ptr FILETIME -> Ptr FILETIME -> IO BOOL
 localFileTimeToFileTime :: FILETIME -> IO FILETIME
-localFileTimeToFileTime ft = with ft $ \ft -> alloca $ \res -> do
+localFileTimeToFileTime ft = with ft $ \c_ft -> alloca $ \res -> do
     failIf_ not "localFileTimeToFileTime: LocalFileTimeToFileTime"
-        $ c_LocalFileTimeToFileTime ft res
+        $ c_LocalFileTimeToFileTime c_ft res
     peek res
 
 {-
 -- Windows XP SP1
-foreign import stdcall "windows.h GetSystemTimes"
+foreign import WINDOWS_CCONV "windows.h GetSystemTimes"
     c_GetSystemTimes :: Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO BOOL
 getSystemTimes :: IO (FILETIME,FILETIME,FILETIME)
 getSystemTimes = alloca $ \idle -> alloca $ \kernel -> alloca $ \user -> do
@@ -251,7 +196,7 @@
 
 {-
 -- Windows XP
-foreign import stdcall "windows.h SystemTimeToTzSpecificLocalTime"
+foreign import WINDOWS_CCONV "windows.h SystemTimeToTzSpecificLocalTime"
     c_SystemTimeToTzSpecificLocalTime :: Ptr TIME_ZONE_INFORMATION -> Ptr SYSTEMTIME -> Ptr SYSTEMTIME -> IO BOOL
 systemTimeToTzSpecificLocalTime :: TIME_ZONE_INFORMATION -> SYSTEMTIME -> IO SYSTEMTIME
 systemTimeToTzSpecificLocalTime tzi st = with tzi $ \tzi -> with st $ \st -> alloca $ \res -> do
@@ -259,7 +204,7 @@
         c_SystemTimeToTzSpecificLocalTime tzi st res
     peek res
 
-foreign import stdcall "windows.h TzSpecificLocalTimeToSystemTime"
+foreign import WINDOWS_CCONV "windows.h TzSpecificLocalTimeToSystemTime"
     c_TzSpecificLocalTimeToSystemTime :: Ptr TIME_ZONE_INFORMATION -> Ptr SYSTEMTIME -> Ptr SYSTEMTIME -> IO BOOL
 tzSpecificLocalTimeToSystemTime :: TIME_ZONE_INFORMATION -> SYSTEMTIME -> IO SYSTEMTIME
 tzSpecificLocalTimeToSystemTime tzi st = with tzi $ \tzi -> with st $ \st -> alloca $ \res -> do
@@ -268,40 +213,37 @@
     peek res
 -}
 
-foreign import stdcall "windows.h QueryPerformanceFrequency"
-    c_QueryPerformanceFrequency :: Ptr LARGE_INTEGER -> IO BOOL
 queryPerformanceFrequency :: IO Integer
 queryPerformanceFrequency = alloca $ \res -> do
     failIf_ not "queryPerformanceFrequency: QueryPerformanceFrequency" $
         c_QueryPerformanceFrequency res
     liftM fromIntegral $ peek res
 
-foreign import stdcall "windows.h QueryPerformanceCounter"
-    c_QueryPerformanceCounter:: Ptr LARGE_INTEGER -> IO BOOL
 queryPerformanceCounter:: IO Integer
 queryPerformanceCounter= alloca $ \res -> do
     failIf_ not "queryPerformanceCounter: QueryPerformanceCounter" $
         c_QueryPerformanceCounter res
     liftM fromIntegral $ peek res
 
-type GetTimeFormatFlags = DWORD
-#{enum GetTimeFormatFlags,
-    , lOCALE_NOUSEROVERRIDE = LOCALE_NOUSEROVERRIDE
-    , lOCALE_USE_CP_ACP     = LOCALE_USE_CP_ACP
-    , tIME_NOMINUTESORSECONDS = TIME_NOMINUTESORSECONDS
-    , tIME_NOSECONDS        = TIME_NOSECONDS
-    , tIME_NOTIMEMARKER     = TIME_NOTIMEMARKER
-    , tIME_FORCE24HOURFORMAT= TIME_FORCE24HOURFORMAT
-    }
 
-foreign import stdcall "windows.h GetTimeFormatW"
-    c_GetTimeFormat :: LCID -> GetTimeFormatFlags -> Ptr SYSTEMTIME -> LPCTSTR -> LPTSTR -> CInt -> IO CInt
-getTimeFormat :: LCID -> GetTimeFormatFlags -> SYSTEMTIME -> String -> IO String
+getTimeFormatEx :: Maybe String
+                -> GetTimeFormatFlags
+                -> Maybe SYSTEMTIME
+                -> Maybe String
+                -> IO String
+getTimeFormatEx locale flags st fmt =
+    maybeWith withTString locale $ \c_locale ->
+        maybeWith with st $ \c_st ->
+            maybeWith withTString fmt $ \c_fmt -> do
+                let c_func = c_GetTimeFormatEx c_locale flags c_st c_fmt
+                trySized "GetTimeFormatEx" c_func
+
+getTimeFormat :: LCID -> GetTimeFormatFlags -> Maybe SYSTEMTIME -> Maybe String -> IO String
 getTimeFormat locale flags st fmt =
-    with st $ \st ->
-    withCWString fmt $ \fmt -> do
-        size <- c_GetTimeFormat locale flags st fmt nullPtr 0
+    maybeWith with st $ \c_st ->
+    maybeWith withCWString fmt $ \c_fmt -> do
+        size <- c_GetTimeFormat locale flags c_st c_fmt nullPtr 0
         allocaBytes ((fromIntegral size) * (sizeOf (undefined::CWchar))) $ \out -> do
-            size <- failIf (==0) "getTimeFormat: GetTimeFormat" $
-                c_GetTimeFormat locale flags st fmt (castPtr out) size
-            peekTStringLen (out,fromIntegral size)
+            size' <- failIf (==0) "getTimeFormat: GetTimeFormat" $
+                c_GetTimeFormat locale flags c_st c_fmt (castPtr out) size
+            peekTStringLen (out,fromIntegral size')
diff --git a/System/Win32/Time/Internal.hsc b/System/Win32/Time/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Time/Internal.hsc
@@ -0,0 +1,245 @@
+#if __GLASGOW_HASKELL__ >= 709
+{-# LANGUAGE Safe #-}
+#else
+{-# LANGUAGE Trustworthy #-}
+#endif
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Time.Internal
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32 Time API.
+--
+-----------------------------------------------------------------------------
+module System.Win32.Time.Internal where
+
+import System.Win32.Types   ( BOOL, DDWORD, DWORD, HANDLE, LARGE_INTEGER, LCID
+                            , LONG, LPCTSTR, LPCWSTR, LPTSTR, LPWSTR, UINT, WORD
+                            , dwordsToDdword, ddwordToDwords
+                            )
+import Control.Monad    ( when )
+import Data.Word        ( Word8 )
+import Foreign          ( Storable(sizeOf, alignment, peekByteOff, peek,
+                                   pokeByteOff, poke)
+                        , Ptr, castPtr, plusPtr, advancePtr
+                        , copyArray )
+import Foreign.C        ( CInt(..), CWchar(..)
+                        , peekCWString, withCWStringLen )
+
+##include "windows_cconv.h"
+#include <windows.h>
+#include "alignment.h"
+#include "winnls_compat.h"
+
+----------------------------------------------------------------
+-- data types
+----------------------------------------------------------------
+
+newtype FILETIME = FILETIME DDWORD deriving (Show, Eq, Ord)
+
+data SYSTEMTIME = SYSTEMTIME {
+    wYear, wMonth, wDayOfWeek, wDay, wHour, wMinute, wSecond, wMilliseconds :: WORD }
+    deriving (Show, Eq, Ord)
+
+data TIME_ZONE_INFORMATION = TIME_ZONE_INFORMATION
+    { tziBias :: LONG
+    , tziStandardName :: String
+    , tziStandardDate :: SYSTEMTIME
+    , tziStandardBias :: LONG
+    , tziDaylightName :: String
+    , tziDaylightDate :: SYSTEMTIME
+    , tziDaylightBias :: LONG
+    } deriving (Show,Eq,Ord)
+
+data TimeZoneId = TzIdUnknown | TzIdStandard | TzIdDaylight
+    deriving (Show, Eq, Ord)
+
+data LASTINPUTINFO = LASTINPUTINFO DWORD deriving (Show)
+
+----------------------------------------------------------------
+-- Instances
+----------------------------------------------------------------
+
+instance Storable FILETIME where
+    sizeOf = const (#size FILETIME)
+    alignment _ = #alignment FILETIME
+    poke buf (FILETIME n) = do
+        (#poke FILETIME, dwLowDateTime) buf low
+        (#poke FILETIME, dwHighDateTime) buf hi
+        where (hi,low) = ddwordToDwords n
+    peek buf = do
+        low <- (#peek FILETIME, dwLowDateTime) buf
+        hi <- (#peek FILETIME, dwHighDateTime) buf
+        return $ FILETIME $ dwordsToDdword (hi,low)
+
+instance Storable SYSTEMTIME where
+    sizeOf _ = #size SYSTEMTIME
+    alignment _ = #alignment SYSTEMTIME
+    poke buf st = do
+         (#poke SYSTEMTIME, wYear)          buf (wYear st)
+         (#poke SYSTEMTIME, wMonth)         buf (wMonth st)
+         (#poke SYSTEMTIME, wDayOfWeek)     buf (wDayOfWeek st)
+         (#poke SYSTEMTIME, wDay)           buf (wDay st)
+         (#poke SYSTEMTIME, wHour)          buf (wHour st)
+         (#poke SYSTEMTIME, wMinute)        buf (wMinute st)
+         (#poke SYSTEMTIME, wSecond)        buf (wSecond st)
+         (#poke SYSTEMTIME, wMilliseconds)  buf (wMilliseconds st)
+    peek buf = do
+        year    <- (#peek SYSTEMTIME, wYear)        buf
+        month   <- (#peek SYSTEMTIME, wMonth)       buf
+        dow     <- (#peek SYSTEMTIME, wDayOfWeek)   buf
+        day     <- (#peek SYSTEMTIME, wDay)         buf
+        hour    <- (#peek SYSTEMTIME, wHour)        buf
+        mins    <- (#peek SYSTEMTIME, wMinute)      buf
+        sec     <- (#peek SYSTEMTIME, wSecond)      buf
+        ms      <- (#peek SYSTEMTIME, wMilliseconds) buf
+        return $ SYSTEMTIME year month dow day hour mins sec ms
+
+instance Storable TIME_ZONE_INFORMATION where
+    sizeOf _ = (#size TIME_ZONE_INFORMATION)
+    alignment _ = #alignment TIME_ZONE_INFORMATION
+    poke buf tzi = do
+        (#poke TIME_ZONE_INFORMATION, Bias) buf (tziBias tzi)
+        (#poke TIME_ZONE_INFORMATION, StandardDate) buf (tziStandardDate tzi)
+        (#poke TIME_ZONE_INFORMATION, StandardBias) buf (tziStandardBias tzi)
+        (#poke TIME_ZONE_INFORMATION, DaylightDate) buf (tziDaylightDate tzi)
+        (#poke TIME_ZONE_INFORMATION, DaylightBias) buf (tziDaylightBias tzi)
+        write buf (#offset TIME_ZONE_INFORMATION, StandardName) (tziStandardName tzi)
+        write buf (#offset TIME_ZONE_INFORMATION, DaylightName) (tziDaylightName tzi)
+        where
+            write buf_ offset str = withCWStringLen str $ \(c_str,len) -> do
+                when (len>31) $ fail "Storable TIME_ZONE_INFORMATION.poke: Too long string."
+                let len'  = len * sizeOf (undefined :: CWchar)
+                    start = (advancePtr (castPtr buf_) offset)
+                    end   = advancePtr start len'
+                copyArray start (castPtr c_str :: Ptr Word8) len'
+                poke (castPtr end) (0 :: CWchar)
+
+    peek buf = do
+        bias <- (#peek TIME_ZONE_INFORMATION, Bias)         buf
+        sdat <- (#peek TIME_ZONE_INFORMATION, StandardDate) buf
+        sbia <- (#peek TIME_ZONE_INFORMATION, StandardBias) buf
+        ddat <- (#peek TIME_ZONE_INFORMATION, DaylightDate) buf
+        dbia <- (#peek TIME_ZONE_INFORMATION, DaylightBias) buf
+        snam <- peekCWString (plusPtr buf (#offset TIME_ZONE_INFORMATION, StandardName))
+        dnam <- peekCWString (plusPtr buf (#offset TIME_ZONE_INFORMATION, DaylightName))
+        return $ TIME_ZONE_INFORMATION bias snam sdat sbia dnam ddat dbia
+
+instance Storable LASTINPUTINFO where
+    sizeOf = const (#size LASTINPUTINFO)
+    alignment = sizeOf
+    poke buf (LASTINPUTINFO t) = do
+        (#poke LASTINPUTINFO, cbSize) buf ((#size LASTINPUTINFO) :: UINT)
+        (#poke LASTINPUTINFO, dwTime) buf t
+    peek buf = do
+        t <- (#peek LASTINPUTINFO, dwTime) buf
+        return $ LASTINPUTINFO t
+
+foreign import WINDOWS_CCONV "windows.h GetSystemTime"
+    c_GetSystemTime :: Ptr SYSTEMTIME -> IO ()
+
+foreign import WINDOWS_CCONV "windows.h SetSystemTime"
+    c_SetSystemTime :: Ptr SYSTEMTIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h GetSystemTimeAsFileTime"
+    c_GetSystemTimeAsFileTime :: Ptr FILETIME -> IO ()
+
+foreign import WINDOWS_CCONV "windows.h GetLocalTime"
+    c_GetLocalTime :: Ptr SYSTEMTIME -> IO ()
+
+foreign import WINDOWS_CCONV "windows.h SetLocalTime"
+    c_SetLocalTime :: Ptr SYSTEMTIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h GetSystemTimeAdjustment"
+    c_GetSystemTimeAdjustment :: Ptr DWORD -> Ptr DWORD -> Ptr BOOL -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h GetTickCount" getTickCount :: IO DWORD
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetLastInputInfo"
+  c_GetLastInputInfo :: Ptr LASTINPUTINFO -> IO Bool
+
+foreign import WINDOWS_CCONV "windows.h SetSystemTimeAdjustment"
+    c_SetSystemTimeAdjustment :: DWORD -> BOOL -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h GetTimeZoneInformation"
+    c_GetTimeZoneInformation :: Ptr TIME_ZONE_INFORMATION -> IO DWORD
+
+foreign import WINDOWS_CCONV "windows.h SystemTimeToFileTime"
+    c_SystemTimeToFileTime :: Ptr SYSTEMTIME -> Ptr FILETIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h FileTimeToSystemTime"
+    c_FileTimeToSystemTime :: Ptr FILETIME -> Ptr SYSTEMTIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h GetFileTime"
+    c_GetFileTime :: HANDLE -> Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h SetFileTime"
+    c_SetFileTime :: HANDLE -> Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h FileTimeToLocalFileTime"
+    c_FileTimeToLocalFileTime :: Ptr FILETIME -> Ptr FILETIME -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h LocalFileTimeToFileTime"
+    c_LocalFileTimeToFileTime :: Ptr FILETIME -> Ptr FILETIME -> IO BOOL
+
+{-
+-- Windows XP SP1
+foreign import WINDOWS_CCONV "windows.h GetSystemTimes"
+    c_GetSystemTimes :: Ptr FILETIME -> Ptr FILETIME -> Ptr FILETIME -> IO BOOL
+getSystemTimes :: IO (FILETIME,FILETIME,FILETIME)
+getSystemTimes = alloca $ \idle -> alloca $ \kernel -> alloca $ \user -> do
+    failIf not "getSystemTimes: GetSystemTimes" $ c_GetSystemTimes idle kernel user
+    liftM3 (,,) (peek idle) (peek kernel) (peek user)
+-}
+
+{-
+-- Windows XP
+foreign import WINDOWS_CCONV "windows.h SystemTimeToTzSpecificLocalTime"
+    c_SystemTimeToTzSpecificLocalTime :: Ptr TIME_ZONE_INFORMATION -> Ptr SYSTEMTIME -> Ptr SYSTEMTIME -> IO BOOL
+systemTimeToTzSpecificLocalTime :: TIME_ZONE_INFORMATION -> SYSTEMTIME -> IO SYSTEMTIME
+systemTimeToTzSpecificLocalTime tzi st = with tzi $ \tzi -> with st $ \st -> alloca $ \res -> do
+    failIf not "systemTimeToTzSpecificLocalTime: SystemTimeToTzSpecificLocalTime" $
+        c_SystemTimeToTzSpecificLocalTime tzi st res
+    peek res
+
+foreign import WINDOWS_CCONV "windows.h TzSpecificLocalTimeToSystemTime"
+    c_TzSpecificLocalTimeToSystemTime :: Ptr TIME_ZONE_INFORMATION -> Ptr SYSTEMTIME -> Ptr SYSTEMTIME -> IO BOOL
+tzSpecificLocalTimeToSystemTime :: TIME_ZONE_INFORMATION -> SYSTEMTIME -> IO SYSTEMTIME
+tzSpecificLocalTimeToSystemTime tzi st = with tzi $ \tzi -> with st $ \st -> alloca $ \res -> do
+    failIf not "tzSpecificLocalTimeToSystemTime: TzSpecificLocalTimeToSystemTime" $
+        c_TzSpecificLocalTimeToSystemTime tzi st res
+    peek res
+-}
+
+foreign import WINDOWS_CCONV "windows.h QueryPerformanceFrequency"
+    c_QueryPerformanceFrequency :: Ptr LARGE_INTEGER -> IO BOOL
+
+foreign import WINDOWS_CCONV "windows.h QueryPerformanceCounter"
+    c_QueryPerformanceCounter:: Ptr LARGE_INTEGER -> IO BOOL
+
+type GetTimeFormatFlags = DWORD
+#{enum GetTimeFormatFlags,
+    , lOCALE_NOUSEROVERRIDE = LOCALE_NOUSEROVERRIDE
+    , lOCALE_USE_CP_ACP     = LOCALE_USE_CP_ACP
+    , tIME_NOMINUTESORSECONDS = TIME_NOMINUTESORSECONDS
+    , tIME_NOSECONDS        = TIME_NOSECONDS
+    , tIME_NOTIMEMARKER     = TIME_NOTIMEMARKER
+    , tIME_FORCE24HOURFORMAT= TIME_FORCE24HOURFORMAT
+    }
+
+foreign import WINDOWS_CCONV "windows.h GetTimeFormatEx"
+    c_GetTimeFormatEx :: LPCWSTR
+                      -> GetTimeFormatFlags
+                      -> Ptr SYSTEMTIME
+                      -> LPCWSTR
+                      -> LPWSTR
+                      -> CInt
+                      -> IO CInt
+
+foreign import WINDOWS_CCONV "windows.h GetTimeFormatW"
+    c_GetTimeFormat :: LCID -> GetTimeFormatFlags -> Ptr SYSTEMTIME -> LPCTSTR -> LPTSTR -> CInt -> IO CInt
diff --git a/System/Win32/Types.hs b/System/Win32/Types.hs
deleted file mode 100644
--- a/System/Win32/Types.hs
+++ /dev/null
@@ -1,307 +0,0 @@
-#if __GLASGOW_HASKELL__ >= 701
-{-# LANGUAGE Trustworthy #-}
-#endif
------------------------------------------------------------------------------
--- |
--- Module      :  System.Win32.Types
--- Copyright   :  (c) Alastair Reid, 1997-2003
--- License     :  BSD-style (see the file libraries/base/LICENSE)
---
--- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
--- Stability   :  provisional
--- Portability :  portable
---
--- A collection of FFI declarations for interfacing with Win32.
---
------------------------------------------------------------------------------
-
-module System.Win32.Types
-	( module System.Win32.Types
-	, nullPtr
-	) where
-
-import Data.Maybe
-import Foreign hiding (unsafePerformIO)
-import Foreign.C
-import Control.Exception
-import System.IO.Error
-import System.IO.Unsafe
-import Data.Char
-import Numeric (showHex)
-
-----------------------------------------------------------------
--- Platform specific definitions
---
--- Most typedefs and prototypes in Win32 are expressed in terms
--- of these types.  Try to follow suit - it'll make it easier to
--- get things working on Win64 (or whatever they call it on Alphas).
-----------------------------------------------------------------
-
-type BOOL          = Bool
-type BYTE          = Word8
-type UCHAR         = CUChar
-type USHORT        = Word16
-type UINT          = Word32
-type INT           = Int32
-type WORD          = Word16
-type DWORD         = Word32
-type LONG          = Int32
-type FLOAT         = Float
-type LARGE_INTEGER = Int64
-
--- Not really a basic type, but used in many places
-type DDWORD        = Word64
-
-----------------------------------------------------------------
-
-type MbString      = Maybe String
-type MbINT         = Maybe INT
-
-type ATOM          = UINT
-type WPARAM        = UINT
-type LPARAM        = LONG
-type LRESULT       = LONG
-type SIZE_T        = DWORD
-
-type MbATOM        = Maybe ATOM
-
-type HRESULT       = LONG
-
-----------------------------------------------------------------
--- Pointers
-----------------------------------------------------------------
-
-type Addr          = Ptr ()
-
-type LPVOID        = Ptr ()
-type LPBOOL        = Ptr BOOL
-type LPBYTE        = Ptr BYTE
-type PUCHAR        = Ptr UCHAR
-type LPDWORD       = Ptr DWORD
-type LPSTR         = Ptr CChar
-type LPCSTR        = LPSTR
-type LPWSTR        = Ptr CWchar
-type LPCWSTR       = LPWSTR
-type LPTSTR        = Ptr TCHAR
-type LPCTSTR       = LPTSTR
-type LPCTSTR_      = LPCTSTR
-
--- Optional things with defaults
-
-maybePtr :: Maybe (Ptr a) -> Ptr a
-maybePtr = fromMaybe nullPtr
-
-ptrToMaybe :: Ptr a -> Maybe (Ptr a)
-ptrToMaybe p = if p == nullPtr then Nothing else Just p
-
-maybeNum :: Num a => Maybe a -> a
-maybeNum = fromMaybe 0
-
-numToMaybe :: (Eq a, Num a) => a -> Maybe a
-numToMaybe n = if n == 0 then Nothing else Just n
-
-type MbLPVOID      = Maybe LPVOID
-type MbLPCSTR      = Maybe LPCSTR
-type MbLPCTSTR     = Maybe LPCTSTR
-
-----------------------------------------------------------------
--- Chars and strings
-----------------------------------------------------------------
-
-withTString    :: String -> (LPTSTR -> IO a) -> IO a
-withTStringLen :: String -> ((LPTSTR, Int) -> IO a) -> IO a
-peekTString    :: LPCTSTR -> IO String
-peekTStringLen :: (LPCTSTR, Int) -> IO String
-newTString     :: String -> IO LPCTSTR
-
--- UTF-16 version:
-type TCHAR     = CWchar
-withTString    = withCWString
-withTStringLen = withCWStringLen
-peekTString    = peekCWString
-peekTStringLen = peekCWStringLen
-newTString     = newCWString
-
-{- ANSI version:
-type TCHAR     = CChar
-withTString    = withCString
-withTStringLen = withCStringLen
-peekTString    = peekCString
-peekTStringLen = peekCStringLen
-newTString     = newCString
--}
-
-----------------------------------------------------------------
--- Handles
-----------------------------------------------------------------
-
-type   HANDLE      = Ptr ()
-type   ForeignHANDLE = ForeignPtr ()
-
-newForeignHANDLE :: HANDLE -> IO ForeignHANDLE
-newForeignHANDLE = newForeignPtr deleteObjectFinaliser
-
-handleToWord :: HANDLE -> UINT
-handleToWord = castPtrToUINT
-
-type   HKEY        = ForeignHANDLE
-type   PKEY        = HANDLE
-
-nullHANDLE :: HANDLE
-nullHANDLE = nullPtr
-
-type MbHANDLE      = Maybe HANDLE
-
-type   HINSTANCE   = Ptr ()
-type MbHINSTANCE   = Maybe HINSTANCE
-
-type   HMODULE     = Ptr ()
-type MbHMODULE     = Maybe HMODULE
-
-nullFinalHANDLE :: ForeignPtr a
-nullFinalHANDLE = unsafePerformIO (newForeignPtr_ nullPtr)
-
-iNVALID_HANDLE_VALUE :: HANDLE
-iNVALID_HANDLE_VALUE = castUINTToPtr 0xffffffff
-
-----------------------------------------------------------------
--- Errors
-----------------------------------------------------------------
-
-type ErrCode = DWORD
-
-failIf :: (a -> Bool) -> String -> IO a -> IO a
-failIf p wh act = do
-  v <- act
-  if p v then errorWin wh else return v
-
-failIf_ :: (a -> Bool) -> String -> IO a -> IO ()
-failIf_ p wh act = do
-  v <- act
-  if p v then errorWin wh else return ()
-
-failIfNull :: String -> IO (Ptr a) -> IO (Ptr a)
-failIfNull = failIf (== nullPtr)
-
-failIfZero :: (Eq a, Num a) => String -> IO a -> IO a
-failIfZero = failIf (== 0)
-
-failIfFalse_ :: String -> IO Bool -> IO ()
-failIfFalse_ = failIf_ not
-
-failUnlessSuccess :: String -> IO ErrCode -> IO ()
-failUnlessSuccess fn_name act = do
-  r <- act
-  if r == 0 then return () else failWith fn_name r
-
-failUnlessSuccessOr :: ErrCode -> String -> IO ErrCode -> IO Bool
-failUnlessSuccessOr val fn_name act = do
-  r <- act
-  if r == 0 then return False
-    else if r == val then return True
-    else failWith fn_name r
-
-errorWin :: String -> IO a
-errorWin fn_name = do
-  err_code <- getLastError
-  failWith fn_name err_code
-
-failWith :: String -> ErrCode -> IO a
-failWith fn_name err_code = do
-  c_msg <- getErrorMessage err_code
-  msg <- if c_msg == nullPtr
-           then return $ "Error 0x" ++ Numeric.showHex err_code ""
-           else do msg <- peekTString c_msg
-                   -- We ignore failure of freeing c_msg, given we're already failing
-                   _ <- localFree c_msg
-                   return msg
-  c_maperrno -- turn GetLastError() into errno, which errnoToIOError knows
-             -- how to convert to an IOException we can throw.
-             -- XXX we should really do this directly.
-  errno <- getErrno
-  let msg' = reverse $ dropWhile isSpace $ reverse msg -- drop trailing \n
-      ioerror = errnoToIOError fn_name errno Nothing Nothing
-                  `ioeSetErrorString` msg'
-  throw ioerror
-
-
-foreign import ccall unsafe "maperrno" -- in base/cbits/Win32Utils.c
-   c_maperrno :: IO ()
-
-----------------------------------------------------------------
--- Misc helpers
-----------------------------------------------------------------
-
-ddwordToDwords :: DDWORD -> (DWORD,DWORD)
-ddwordToDwords n =
-        (fromIntegral (n `shiftR` bitSize (undefined::DWORD))
-        ,fromIntegral (n .&. fromIntegral (maxBound :: DWORD)))
-
-dwordsToDdword:: (DWORD,DWORD) -> DDWORD
-dwordsToDdword (hi,low) = (fromIntegral low) .|. (fromIntegral hi `shiftL`bitSize hi)
-
-----------------------------------------------------------------
--- Primitives
-----------------------------------------------------------------
-
-{-# CFILES cbits/HsWin32.c #-}
-foreign import ccall "HsWin32.h &DeleteObjectFinaliser"
-  deleteObjectFinaliser :: FunPtr (Ptr a -> IO ())
-
-foreign import stdcall unsafe "windows.h LocalFree"
-  localFree :: Ptr a -> IO (Ptr a)
-
-foreign import stdcall unsafe "windows.h GetLastError"
-  getLastError :: IO ErrCode
-
-{-# CFILES cbits/errors.c #-}
-
-foreign import ccall unsafe "errors.h"
-  getErrorMessage :: DWORD -> IO LPWSTR
-
-{-# CFILES cbits/HsWin32.c #-}
-
-foreign import ccall unsafe "HsWin32.h"
-  lOWORD :: DWORD -> WORD
-
-foreign import ccall unsafe "HsWin32.h"
-  hIWORD :: DWORD -> WORD
-
-foreign import ccall unsafe "HsWin32.h"
-  castUINTToPtr :: UINT -> Ptr a
-
-foreign import ccall unsafe "HsWin32.h"
-  castPtrToUINT :: Ptr s -> UINT
-
-foreign import ccall unsafe "HsWin32.h"
-  castFunPtrToLONG :: FunPtr a -> LONG
-
-type LCID = DWORD
-
-type LANGID = WORD
-type SortID = WORD
-
-foreign import ccall unsafe "HsWin32.h prim_MAKELCID"
-  mAKELCID :: LANGID -> SortID -> LCID
-
-foreign import ccall unsafe "HsWin32.h prim_LANGIDFROMLCID"
-  lANGIDFROMLCID :: LCID -> LANGID
-
-foreign import ccall unsafe "HsWin32.h prim_SORTIDFROMLCID"
-  sORTIDFROMLCID :: LCID -> SortID
-
-type SubLANGID = WORD
-type PrimaryLANGID = WORD
-
-foreign import ccall unsafe "HsWin32.h prim_MAKELANGID"
-  mAKELANGID :: PrimaryLANGID -> SubLANGID -> LANGID
-
-foreign import ccall unsafe "HsWin32.h prim_PRIMARYLANGID"
-  pRIMARYLANGID :: LANGID -> PrimaryLANGID
-
-foreign import ccall unsafe "HsWin32.h prim_SUBLANGID"
-  sUBLANGID :: LANGID -> SubLANGID
-
-----------------------------------------------------------------
--- End
-----------------------------------------------------------------
diff --git a/System/Win32/Types.hsc b/System/Win32/Types.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/Types.hsc
@@ -0,0 +1,578 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Types
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.Types
+        ( module System.Win32.Types
+        , nullPtr
+        ) where
+
+import Control.Concurrent.MVar (readMVar)
+import Control.Exception (bracket, throwIO)
+import Data.Bits (shiftL, shiftR, (.|.), (.&.))
+import Data.Char (isSpace)
+import Data.Int (Int32, Int64, Int16)
+import Data.Maybe (fromMaybe)
+import Data.Typeable (cast)
+import Data.Word (Word8, Word16, Word32, Word64)
+import Foreign.C.Error (Errno(..), errnoToIOError)
+import Foreign.C.String (newCWString, withCWStringLen, CWString)
+import Foreign.C.String (peekCWString, peekCWStringLen, withCWString)
+import Foreign.C.Types (CChar, CUChar, CWchar, CInt(..), CIntPtr(..), CUIntPtr)
+import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, newForeignPtr_)
+import Foreign.Ptr (FunPtr, Ptr, nullPtr, ptrToIntPtr)
+import Foreign.StablePtr (StablePtr, freeStablePtr, newStablePtr)
+import Foreign (allocaArray)
+import GHC.IO.Exception
+import GHC.IO.FD (FD(..))
+import GHC.IO.Handle.FD (fdToHandle)
+import GHC.IO.Handle.Types (Handle(..), Handle__(..))
+import Numeric (showHex)
+import qualified System.IO as IO ()
+import System.IO.Error (ioeSetErrorString)
+import System.IO.Unsafe (unsafePerformIO)
+
+#if !MIN_VERSION_base(4,8,0)
+import Data.Word (Word)
+#endif
+
+#if MIN_VERSION_base(4,7,0)
+import Data.Bits (finiteBitSize)
+#else
+import Data.Bits (Bits, bitSize)
+
+finiteBitSize :: (Bits a) => a -> Int
+finiteBitSize = bitSize
+#endif
+
+##if defined(__IO_MANAGER_WINIO__)
+import Control.Monad (when, liftM2)
+import Foreign.C.Types (CUIntPtr(..))
+import Foreign.Marshal.Utils (fromBool, with)
+import Foreign (peek)
+import Foreign.Ptr (ptrToWordPtr)
+import GHC.IO.SubSystem ((<!>))
+import GHC.IO.Handle.Windows
+import GHC.IO.IOMode
+import GHC.IO.Windows.Handle (fromHANDLE, Io(), NativeHandle(), ConsoleHandle(),
+                              toHANDLE, handleToMode, optimizeFileAccess)
+import qualified GHC.Event.Windows as Mgr
+import GHC.IO.Device (IODeviceType(..), devType)
+##endif
+
+#include <fcntl.h>
+#include <windows.h>
+##include "windows_cconv.h"
+
+----------------------------------------------------------------
+-- Platform specific definitions
+--
+-- Most typedefs and prototypes in Win32 are expressed in terms
+-- of these types.  Try to follow suit - it'll make it easier to
+-- get things working on Win64 (or whatever they call it on Alphas).
+----------------------------------------------------------------
+
+type BOOL          = Bool
+type BYTE          = Word8
+type UCHAR         = CUChar
+type USHORT        = Word16
+type UINT          = Word32
+type INT           = Int32
+type WORD          = Word16
+type DWORD         = Word32
+type LONG          = Int32
+type FLOAT         = Float
+type LARGE_INTEGER = Int64
+
+type DWORD32       = Word32
+type DWORD64       = Word64
+type INT32         = Int32
+type INT64         = Int64
+type LONG32        = Int32
+type LONG64        = Int64
+type UINT32        = Word32
+type UINT64        = Word64
+type ULONG32       = Word32
+type ULONG64       = Word64
+type SHORT         = Int16
+
+type INT_PTR       = Ptr CInt
+type ULONG         = Word32
+type UINT_PTR      = Word
+type LONG_PTR      = CIntPtr
+type ULONG_PTR     = CUIntPtr
+type DWORD_PTR     = ULONG_PTR
+#ifdef _WIN64
+type HALF_PTR      = Ptr INT32
+#else
+type HALF_PTR      = Ptr SHORT
+#endif
+
+-- Not really a basic type, but used in many places
+type DDWORD        = Word64
+
+----------------------------------------------------------------
+
+type MbString      = Maybe String
+type MbINT         = Maybe INT
+
+type ATOM          = WORD
+type WPARAM        = UINT_PTR
+type LPARAM        = LONG_PTR
+type LRESULT       = LONG_PTR
+type SIZE_T        = ULONG_PTR
+
+type MbATOM        = Maybe ATOM
+
+type HRESULT       = LONG
+
+----------------------------------------------------------------
+-- Pointers
+----------------------------------------------------------------
+
+type Addr          = Ptr ()
+
+type LPVOID        = Ptr ()
+type LPBOOL        = Ptr BOOL
+type LPBYTE        = Ptr BYTE
+type PUCHAR        = Ptr UCHAR
+type LPDWORD       = Ptr DWORD
+type LPSTR         = Ptr CChar
+type LPCSTR        = LPSTR
+type LPWSTR        = Ptr CWchar
+type LPCWSTR       = LPWSTR
+type LPTSTR        = Ptr TCHAR
+type LPCTSTR       = LPTSTR
+type LPCTSTR_      = LPCTSTR
+
+-- Optional things with defaults
+
+maybePtr :: Maybe (Ptr a) -> Ptr a
+maybePtr = fromMaybe nullPtr
+
+ptrToMaybe :: Ptr a -> Maybe (Ptr a)
+ptrToMaybe p = if p == nullPtr then Nothing else Just p
+
+maybeNum :: Num a => Maybe a -> a
+maybeNum = fromMaybe 0
+
+numToMaybe :: (Eq a, Num a) => a -> Maybe a
+numToMaybe n = if n == 0 then Nothing else Just n
+
+type MbLPVOID      = Maybe LPVOID
+type MbLPCSTR      = Maybe LPCSTR
+type MbLPCTSTR     = Maybe LPCTSTR
+
+----------------------------------------------------------------
+-- Chars and strings
+----------------------------------------------------------------
+
+withTString    :: String -> (LPTSTR -> IO a) -> IO a
+withFilePath   :: FilePath -> (LPTSTR -> IO a) -> IO a
+withTStringLen :: String -> ((LPTSTR, Int) -> IO a) -> IO a
+peekTString    :: LPCTSTR -> IO String
+peekTStringLen :: (LPCTSTR, Int) -> IO String
+newTString     :: String -> IO LPCTSTR
+
+-- UTF-16 version:
+type TCHAR     = CWchar
+withTString    = withCWString
+withFilePath path = useAsCWStringSafe path
+withTStringLen = withCWStringLen
+peekTString    = peekCWString
+peekTStringLen = peekCWStringLen
+newTString     = newCWString
+
+{- ANSI version:
+type TCHAR     = CChar
+withTString    = withCString
+withTStringLen = withCStringLen
+peekTString    = peekCString
+peekTStringLen = peekCStringLen
+newTString     = newCString
+-}
+
+-- | Wrapper around 'useAsCString', checking the encoded 'FilePath' for internal NUL codepoints as these are
+-- disallowed in Windows filepaths. See https://gitlab.haskell.org/ghc/ghc/-/issues/13660
+useAsCWStringSafe :: FilePath -> (CWString -> IO a) -> IO a
+useAsCWStringSafe path f =
+    if '\NUL' `elem` path
+    then ioError err
+    else withCWString path f
+  where
+    err =
+        IOError
+          { ioe_handle = Nothing
+          , ioe_type = InvalidArgument
+          , ioe_location = "useAsCWStringSafe"
+          , ioe_description = "Windows filepaths must not contain internal NUL codepoints."
+          , ioe_errno = Nothing
+          , ioe_filename = Just path
+          }
+
+
+----------------------------------------------------------------
+-- Handles
+----------------------------------------------------------------
+
+type   HANDLE      = Ptr ()
+type   ForeignHANDLE = ForeignPtr ()
+
+newForeignHANDLE :: HANDLE -> IO ForeignHANDLE
+newForeignHANDLE = newForeignPtr deleteObjectFinaliser
+
+handleToWord :: HANDLE -> UINT_PTR
+handleToWord = castPtrToUINTPtr
+
+type   HKEY        = ForeignHANDLE
+type   PKEY        = HANDLE
+
+nullHANDLE :: HANDLE
+nullHANDLE = nullPtr
+
+type MbHANDLE      = Maybe HANDLE
+
+nullHINSTANCE :: HINSTANCE
+nullHINSTANCE = nullPtr
+
+type   HINSTANCE   = Ptr ()
+type MbHINSTANCE   = Maybe HINSTANCE
+
+type   HMODULE     = Ptr ()
+type MbHMODULE     = Maybe HMODULE
+
+nullFinalHANDLE :: ForeignPtr a
+nullFinalHANDLE = unsafePerformIO (newForeignPtr_ nullPtr)
+
+iNVALID_HANDLE_VALUE :: HANDLE
+iNVALID_HANDLE_VALUE = castUINTPtrToPtr maxBound
+
+iNVALID_SET_FILE_POINTER :: DWORD
+iNVALID_SET_FILE_POINTER = #const INVALID_SET_FILE_POINTER
+
+foreign import ccall "_open_osfhandle"
+  _open_osfhandle :: CIntPtr -> CInt -> IO CInt
+
+-- | Create a Haskell 'Handle' from a Windows 'HANDLE'.
+--
+-- Beware that this function allocates a new file descriptor. A consequence of
+-- this is that calling 'hANDLEToHandle' on the standard Windows handles will
+-- not give you 'IO.stdin', 'IO.stdout', or 'IO.stderr'. For example, if you
+-- run this code:
+--
+-- @
+-- import Graphics.Win32.Misc
+-- stdoutHANDLE <- getStdHandle sTD_OUTPUT_HANDLE
+-- stdout2 <- 'hANDLEToHandle' stdoutHANDLE
+-- @
+--
+-- Then although you can use @stdout2@ to write to standard output, it is not
+-- the case that @'IO.stdout' == stdout2@.
+hANDLEToHandle :: HANDLE -> IO Handle
+hANDLEToHandle handle = posix
+##if defined(__IO_MANAGER_WINIO__)
+     <!> native
+##endif
+  where
+##if defined(__IO_MANAGER_WINIO__)
+    native = do
+      -- Attach the handle to the I/O manager's CompletionPort.  This allows the
+      -- I/O manager to service requests for this Handle.
+      Mgr.associateHandle' handle
+      let hwnd = fromHANDLE handle :: Io NativeHandle
+      _type <- devType hwnd
+
+      -- Use the rts to enforce any file locking we may need.
+      mode <- handleToMode handle
+      let write_lock = mode /= ReadMode
+
+      case _type of
+        -- Regular files need to be locked.
+        -- See also Note [RTS File locking]
+        RegularFile -> do
+          optimizeFileAccess handle -- Set a few optimization flags on file handles.
+          (unique_dev, unique_ino) <- getUniqueFileInfo handle
+          r <- internal_lockFile
+                  (fromIntegral $ ptrToWordPtr handle) unique_dev unique_ino
+                  (fromBool write_lock)
+          when (r == -1)  $
+               ioException (IOError Nothing ResourceBusy "hANDLEToHandle"
+                                  "file is locked" Nothing Nothing)
+
+        -- I don't see a reason for blocking directories.  So unlike the FD
+        -- implementation I'll allow it.
+        _ -> return ()
+      mkHandleFromHANDLE hwnd Stream ("hwnd:" ++ show handle) mode Nothing
+
+    -- | getUniqueFileInfo assumes the C call to getUniqueFileInfo
+    -- succeeds.
+    getUniqueFileInfo :: HANDLE -> IO (Word64, Word64)
+    getUniqueFileInfo hnl = do
+      with 0 $ \devptr -> do
+        with 0 $ \inoptr -> do
+          internal_getUniqueFileInfo hnl devptr inoptr
+          liftM2 (,) (peek devptr) (peek inoptr)
+##endif
+    posix = _open_osfhandle (fromIntegral (ptrToIntPtr handle))
+                            (#const _O_BINARY) >>= fdToHandle
+
+##if defined(__IO_MANAGER_WINIO__)
+foreign import ccall unsafe "lockFile"
+  internal_lockFile :: CUIntPtr -> Word64 -> Word64 -> CInt -> IO CInt
+
+-- | Returns -1 on error. Otherwise writes two values representing
+-- the file into the given ptrs.
+foreign import ccall unsafe "get_unique_file_info_hwnd"
+  internal_getUniqueFileInfo :: HANDLE -> Ptr Word64 -> Ptr Word64 -> IO ()
+##endif
+
+foreign import ccall unsafe "_get_osfhandle"
+  c_get_osfhandle :: CInt -> IO HANDLE
+
+-- | Extract a Windows 'HANDLE' from a Haskell 'Handle' and perform
+-- an action on it.
+
+-- Originally authored by Max Bolingbroke in the ansi-terminal library
+withHandleToHANDLE :: Handle -> (HANDLE -> IO a) -> IO a
+##if defined(__IO_MANAGER_WINIO__)
+withHandleToHANDLE = withHandleToHANDLEPosix <!> withHandleToHANDLENative
+##else
+withHandleToHANDLE = withHandleToHANDLEPosix
+##endif
+
+##if defined(__IO_MANAGER_WINIO__)
+withHandleToHANDLENative :: Handle -> (HANDLE -> IO a) -> IO a
+withHandleToHANDLENative haskell_handle action =
+    -- Create a stable pointer to the Handle. This prevents the garbage collector
+    -- getting to it while we are doing horrible manipulations with it, and hence
+    -- stops it being finalized (and closed).
+    withStablePtr haskell_handle $ const $ do
+        -- Grab the write handle variable from the Handle
+        let write_handle_mvar = case haskell_handle of
+                FileHandle _ handle_mvar     -> handle_mvar
+                DuplexHandle _ _ handle_mvar -> handle_mvar
+
+        -- This is "write" MVar, we could also take the "read" one
+        windows_handle <- readMVar write_handle_mvar >>= handle_ToHANDLE
+
+        -- Do what the user originally wanted
+        action windows_handle
+  where
+    -- | Turn an existing Handle into a Win32 HANDLE. This function throws an
+    -- IOError if the Handle does not reference a HANDLE
+    handle_ToHANDLE :: Handle__ -> IO HANDLE
+    handle_ToHANDLE (Handle__{haDevice = dev}) =
+        case (cast dev :: Maybe (Io NativeHandle), cast dev :: Maybe (Io ConsoleHandle)) of
+          (Just hwnd, Nothing) -> return $ toHANDLE hwnd
+          (Nothing, Just hwnd) -> return $ toHANDLE hwnd
+          _                    -> throwErr "not a known HANDLE"
+
+    throwErr msg = ioException $ IOError (Just haskell_handle)
+      InappropriateType "withHandleToHANDLENative" msg Nothing Nothing
+##endif
+
+withHandleToHANDLEPosix :: Handle -> (HANDLE -> IO a) -> IO a
+withHandleToHANDLEPosix haskell_handle action =
+    -- Create a stable pointer to the Handle. This prevents the garbage collector
+    -- getting to it while we are doing horrible manipulations with it, and hence
+    -- stops it being finalized (and closed).
+    withStablePtr haskell_handle $ const $ do
+        -- Grab the write handle variable from the Handle
+        let write_handle_mvar = case haskell_handle of
+                FileHandle _ handle_mvar     -> handle_mvar
+                DuplexHandle _ _ handle_mvar -> handle_mvar
+                  -- This is "write" MVar, we could also take the "read" one
+
+        -- Get the FD from the algebraic data type
+        Just fd <- fmap (\(Handle__ { haDevice = dev }) -> fmap fdFD (cast dev))
+                 $ readMVar write_handle_mvar
+
+        -- Finally, turn that (C-land) FD into a HANDLE using msvcrt
+        windows_handle <- c_get_osfhandle fd
+        -- Do what the user originally wanted
+        action windows_handle
+
+withStablePtr :: a -> (StablePtr a -> IO b) -> IO b
+withStablePtr value = bracket (newStablePtr value) freeStablePtr
+
+----------------------------------------------------------------
+-- Errors
+----------------------------------------------------------------
+
+type ErrCode = DWORD
+
+failIf :: (a -> Bool) -> String -> IO a -> IO a
+failIf p wh act = do
+  v <- act
+  if p v then errorWin wh else return v
+
+failIf_ :: (a -> Bool) -> String -> IO a -> IO ()
+failIf_ p wh act = do
+  v <- act
+  if p v then errorWin wh else return ()
+
+failIfNeg :: (Num a, Ord a) => String -> IO a -> IO a
+failIfNeg = failIf (< 0)
+
+failIfNull :: String -> IO (Ptr a) -> IO (Ptr a)
+failIfNull = failIf (== nullPtr)
+
+failIfZero :: (Eq a, Num a) => String -> IO a -> IO a
+failIfZero = failIf (== 0)
+
+failIfFalse_ :: String -> IO Bool -> IO ()
+failIfFalse_ = failIf_ not
+
+failUnlessSuccess :: String -> IO ErrCode -> IO ()
+failUnlessSuccess fn_name act = do
+  r <- act
+  if r == 0 then return () else failWith fn_name r
+
+failUnlessSuccessOr :: ErrCode -> String -> IO ErrCode -> IO Bool
+failUnlessSuccessOr val fn_name act = do
+  r <- act
+  if r == 0 then return False
+    else if r == val then return True
+    else failWith fn_name r
+
+eRROR_INSUFFICIENT_BUFFER :: ErrCode
+eRROR_INSUFFICIENT_BUFFER = #const ERROR_INSUFFICIENT_BUFFER
+
+eRROR_MOD_NOT_FOUND :: ErrCode
+eRROR_MOD_NOT_FOUND = #const ERROR_MOD_NOT_FOUND
+
+eRROR_PROC_NOT_FOUND :: ErrCode
+eRROR_PROC_NOT_FOUND = #const ERROR_PROC_NOT_FOUND
+
+eERROR_ENVVAR_NOT_FOUND :: ErrCode
+eERROR_ENVVAR_NOT_FOUND = #const ERROR_ENVVAR_NOT_FOUND
+
+errorWin :: String -> IO a
+errorWin fn_name = do
+  err_code <- getLastError
+  failWith fn_name err_code
+
+failWith :: String -> ErrCode -> IO a
+failWith fn_name err_code = do
+  c_msg <- getErrorMessage err_code
+  msg <- if c_msg == nullPtr
+           then return $ "Error 0x" ++ Numeric.showHex err_code ""
+           else do msg <- peekTString c_msg
+                   -- We ignore failure of freeing c_msg, given we're already failing
+                   _ <- localFree c_msg
+                   return msg
+  -- turn GetLastError() into errno, which errnoToIOError knows how to convert
+  -- to an IOException we can throw.
+  errno <- c_maperrno_func err_code
+  let msg' = reverse $ dropWhile isSpace $ reverse msg -- drop trailing \n
+      ioerror = errnoToIOError fn_name errno Nothing Nothing
+                  `ioeSetErrorString` msg'
+  throwIO ioerror
+
+
+foreign import ccall unsafe "maperrno_func" -- in base/cbits/Win32Utils.c
+   c_maperrno_func :: ErrCode -> IO Errno
+
+----------------------------------------------------------------
+-- Misc helpers
+----------------------------------------------------------------
+
+ddwordToDwords :: DDWORD -> (DWORD,DWORD)
+ddwordToDwords n =
+        (fromIntegral (n `shiftR` finiteBitSize (undefined :: DWORD))
+        ,fromIntegral (n .&. fromIntegral (maxBound :: DWORD)))
+
+dwordsToDdword:: (DWORD,DWORD) -> DDWORD
+dwordsToDdword (hi,low) = (fromIntegral low) .|. (fromIntegral hi `shiftL` finiteBitSize hi)
+
+-- Support for API calls that are passed a fixed-size buffer and tell
+-- you via the return value if the buffer was too small.  In that
+-- case, we double the buffer size and try again.
+try :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO String
+try loc f n = do
+   e <- allocaArray (fromIntegral n) $ \lptstr -> do
+          r <- failIfZero loc $ f lptstr n
+          if (r > n) then return (Left r) else do
+            str <- peekTStringLen (lptstr, fromIntegral r)
+            return (Right str)
+   case e of
+        Left n'   -> try loc f n'
+        Right str -> return str
+
+----------------------------------------------------------------
+-- Primitives
+----------------------------------------------------------------
+
+{-# CFILES cbits/HsWin32.c #-}
+foreign import ccall "HsWin32.h &DeleteObjectFinaliser"
+  deleteObjectFinaliser :: FunPtr (Ptr a -> IO ())
+
+foreign import WINDOWS_CCONV unsafe "windows.h LocalFree"
+  localFree :: Ptr a -> IO (Ptr a)
+
+foreign import WINDOWS_CCONV unsafe "windows.h GetLastError"
+  getLastError :: IO ErrCode
+
+foreign import WINDOWS_CCONV unsafe "windows.h SetLastError"
+  setLastError :: ErrCode -> IO ()
+
+{-# CFILES cbits/errors.c #-}
+
+foreign import ccall unsafe "errors.h"
+  getErrorMessage :: DWORD -> IO LPWSTR
+
+{-# CFILES cbits/HsWin32.c #-}
+
+foreign import ccall unsafe "HsWin32.h"
+  lOWORD :: DWORD -> WORD
+
+foreign import ccall unsafe "HsWin32.h"
+  hIWORD :: DWORD -> WORD
+
+foreign import ccall unsafe "HsWin32.h"
+  castUINTPtrToPtr :: UINT_PTR -> Ptr a
+
+foreign import ccall unsafe "HsWin32.h"
+  castPtrToUINTPtr :: Ptr s -> UINT_PTR
+
+type LCID = DWORD
+
+type LANGID = WORD
+type SortID = WORD
+
+foreign import ccall unsafe "HsWin32.h prim_MAKELCID"
+  mAKELCID :: LANGID -> SortID -> LCID
+
+foreign import ccall unsafe "HsWin32.h prim_LANGIDFROMLCID"
+  lANGIDFROMLCID :: LCID -> LANGID
+
+foreign import ccall unsafe "HsWin32.h prim_SORTIDFROMLCID"
+  sORTIDFROMLCID :: LCID -> SortID
+
+type SubLANGID = WORD
+type PrimaryLANGID = WORD
+
+foreign import ccall unsafe "HsWin32.h prim_MAKELANGID"
+  mAKELANGID :: PrimaryLANGID -> SubLANGID -> LANGID
+
+foreign import ccall unsafe "HsWin32.h prim_PRIMARYLANGID"
+  pRIMARYLANGID :: LANGID -> PrimaryLANGID
+
+foreign import ccall unsafe "HsWin32.h prim_SUBLANGID"
+  sUBLANGID :: LANGID -> SubLANGID
+
+----------------------------------------------------------------
+-- End
+----------------------------------------------------------------
diff --git a/System/Win32/Utils.hs b/System/Win32/Utils.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Utils.hs
@@ -0,0 +1,410 @@
+{- |
+   Module      :  System.Win32.Utils
+   Copyright   :  2009 Balazs Komuves, 2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Utilities for calling Win32 API
+-}
+module System.Win32.Utils
+  ( try, tryWithoutNull, trySized, try'
+  -- * Maybe values
+  , maybePtr, ptrToMaybe, maybeNum, numToMaybe
+  , peekMaybe, withMaybe
+  -- * Format picture translation
+  , fromDateFormatPicture
+  , fromTimeFormatPicture
+  ) where
+
+import Control.Monad               ( unless )
+import Foreign.C.Types             ( CInt )
+import Foreign.Marshal.Array       ( allocaArray, peekArray )
+import Foreign.Marshal.Utils       ( with )
+import Foreign.Ptr                 ( Ptr, nullPtr )
+import Foreign.Storable            ( Storable(..) )
+import Text.ParserCombinators.ReadP ( ReadP, (<++), between, char, count
+                                    , readP_to_S, satisfy )
+
+
+import System.Win32.String         ( LPTSTR, peekTString, peekTStringLen
+                                   , withTStringBufferLen )
+import System.Win32.Types          ( BOOL, UINT, eRROR_INSUFFICIENT_BUFFER
+                                   , failIfZero, failWith, getLastError
+                                   , maybeNum, maybePtr, numToMaybe
+                                   , ptrToMaybe )
+import qualified System.Win32.Types ( try )
+import System.Win32.Word           ( DWORD, PDWORD )
+
+-- | Support for API calls that are passed a fixed-size buffer and tell
+-- you via the return value if the buffer was too small.  In that
+-- case, we extend the buffer size and try again.
+try :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO String
+try = System.Win32.Types.try
+{-# INLINE try #-}
+
+tryWithoutNull :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO String
+tryWithoutNull loc f n = do
+   e <- allocaArray (fromIntegral n) $ \lptstr -> do
+          r <- failIfZero loc $ f lptstr n
+          if r > n then return (Left r) else do
+            str <- peekTString lptstr
+            return (Right str)
+   case e of
+        Left r'   -> tryWithoutNull loc f r'
+        Right str -> return str
+
+try' :: Storable a => String -> (Ptr a -> PDWORD -> IO BOOL) -> DWORD -> IO [a]
+try' loc f n =
+   with n $ \n' -> do
+   e <- allocaArray (fromIntegral n) $ \lptstr -> do
+          flg <- f lptstr n'
+          unless flg $ do
+            err_code <- getLastError
+            unless (err_code == eRROR_INSUFFICIENT_BUFFER)
+              $ failWith loc err_code
+          r   <- peek n'
+          if r > n then return (Left r) else do
+            str <- peekArray (fromIntegral r) lptstr
+            return (Right str)
+   case e of
+        Left r'   -> try' loc f r'
+        Right str -> return str
+
+-- | Support for API calls that return the required size, in characters
+-- including a null character, of the buffer when passed a buffer size of zero.
+trySized :: String -> (LPTSTR -> CInt -> IO CInt) -> IO String
+trySized wh f = do
+    c_len <- failIfZero wh $ f nullPtr 0
+    let len = fromIntegral c_len
+    withTStringBufferLen len $ \(buf', len') -> do
+        let c_len' = fromIntegral len'
+        c_len'' <- failIfZero wh $ f buf' c_len'
+        let len'' = fromIntegral c_len''
+        peekTStringLen (buf', len'' - 1) -- Drop final null character
+
+-- | See also: 'Foreign.Marshal.Utils.maybePeek' function.
+peekMaybe :: Storable a => Ptr a -> IO (Maybe a)
+peekMaybe p =
+  if p == nullPtr
+    then return Nothing
+    else Just `fmap` peek p
+
+-- | See also: 'Foreign.Marshal.Utils.maybeWith' function.
+withMaybe :: Storable a => Maybe a -> (Ptr a -> IO b) -> IO b
+withMaybe Nothing  action = action nullPtr
+withMaybe (Just x) action = with x action
+
+-- | Type representing components of a Windows API day, month, year and era
+-- format picture.
+data DateFormatPicture
+  = Day
+  | Day0 -- Padded with zeros
+  | DayShort
+  | DayLong
+  | Month
+  | Month0 -- Padded with zeros
+  | MonthShort
+  | MonthLong
+  | YearVeryShort -- Year represented only by the last digit
+  | YearShort
+  | Year
+  | Era
+  | DateOther String
+  deriving (Eq, Show)
+
+fromDFP :: DateFormatPicture -> String
+fromDFP Day = "%-e" -- No padding
+fromDFP Day0 = "%d" -- Padded with zeros
+fromDFP DayShort = "%a" -- eg Tue
+fromDFP DayLong = "%A" -- eg Tuesday
+fromDFP Month = "%-m" -- No padding
+fromDFP Month0 = "%m" -- Padded with zeros
+fromDFP MonthShort = "%b" -- eg Jan
+fromDFP MonthLong = "%B" -- eg January
+fromDFP YearVeryShort = "%-y" -- No direct equivalent of a one digit year, so
+                              -- do not distinguish from a short year without
+                              -- padding
+fromDFP YearShort = "%y"
+fromDFP Year = "%Y"
+fromDFP Era = "" -- No equivalent
+fromDFP (DateOther cs) = escape cs
+
+escape :: String -> String
+escape [] = []
+escape (c:cs) = escape' c ++ escape cs
+ where
+  escape' '%' = "%%"
+  escape' '\t' = "%t"
+  escape' '\n' = "%n"
+  escape' c' = [c']
+
+d :: ReadP Char
+d = char 'd'
+
+day :: ReadP DateFormatPicture
+day = do
+  _ <- d
+  return Day
+
+day0 :: ReadP DateFormatPicture
+day0 = do
+  _ <- count 2 d
+  return Day0
+
+dayShort :: ReadP DateFormatPicture
+dayShort = do
+  _ <- count 3 d
+  return DayShort
+
+dayLong :: ReadP DateFormatPicture
+dayLong = do
+  _ <- count 4 d
+  return DayLong
+
+days :: ReadP DateFormatPicture
+days = dayLong <++ dayShort <++ day0 <++ day
+
+bigM :: ReadP Char
+bigM = char 'M'
+
+month :: ReadP DateFormatPicture
+month = do
+  _ <- bigM
+  return Month
+
+month0 :: ReadP DateFormatPicture
+month0 = do
+  _ <- count 2 bigM
+  return Month0
+
+monthShort :: ReadP DateFormatPicture
+monthShort = do
+  _ <- count 3 bigM
+  return MonthShort
+
+monthLong :: ReadP DateFormatPicture
+monthLong = do
+  _ <- count 4 bigM
+  return MonthLong
+
+months :: ReadP DateFormatPicture
+months = monthLong <++ monthShort <++ month0 <++ month
+
+y :: ReadP Char
+y = char 'y'
+
+yearVeryShort :: ReadP DateFormatPicture
+yearVeryShort = do
+  _ <- y
+  return YearVeryShort
+
+yearShort :: ReadP DateFormatPicture
+yearShort = do
+  _ <- count 2 y
+  return YearShort
+
+year :: ReadP DateFormatPicture
+year = do
+  _ <- count 5 y <++ count 4 y
+  return Year
+
+years :: ReadP DateFormatPicture
+years = year <++ yearShort <++ yearVeryShort
+
+g :: ReadP Char
+g = char 'g'
+
+era :: ReadP DateFormatPicture
+era = do
+  _ <- count 2 g <++ count 1 g
+  return Era
+
+quote :: ReadP Char
+quote = char '\''
+
+notQuote :: ReadP Char
+notQuote = satisfy (/= '\'')
+
+escQuote :: ReadP Char
+escQuote = do
+  _ <- count 2 quote
+  return '\''
+
+quotedChars :: ReadP String
+quotedChars = between quote quote $ greedy (escQuote <++ notQuote)
+
+-- | Although not documented at
+-- https://docs.microsoft.com/en-us/windows/win32/intl/day--month--year--and-era-format-pictures
+-- the format pictures used by Windows do not require all such characters to be
+-- enclosed in single quotation marks.
+nonDateSpecial :: ReadP Char
+nonDateSpecial = satisfy (\c -> c `notElem` ['d', 'M', 'y', 'g', '\''])
+
+nonDateSpecials :: ReadP String
+nonDateSpecials = greedy1 nonDateSpecial
+
+dateOther :: ReadP DateFormatPicture
+dateOther = do
+  chars <- greedy1 (nonDateSpecials <++ quotedChars)
+  return $ DateOther $ concat chars
+
+datePicture :: ReadP [DateFormatPicture]
+datePicture = greedy (days <++ months <++ years <++ era <++ dateOther)
+
+-- | Type representing components of a Windows API hours, minute, and second
+-- format picture.
+data TimeFormatPicture
+  = Hours12
+  | Hours012 -- Padded with zeros
+  | Hours24
+  | Hours024 -- Padded with zeros
+  | Minutes
+  | Minutes0 -- Padded with zeros
+  | Seconds
+  | Seconds0 -- Padded with zeros
+  | TimeMarkerShort -- One-character time marker string, eg "A" and "P"
+  | TimeMarker -- Multi-character time marker string, eg "AM" and "PM"
+  | TimeOther String
+  deriving (Eq, Show)
+
+fromTFP :: TimeFormatPicture -> String
+fromTFP Hours12 = "%-l" -- No padding
+fromTFP Hours012 = "%I" -- Padded with zeros
+fromTFP Hours24 = "%-k" -- No padding
+fromTFP Hours024 = "%H" -- Padded with zeros
+fromTFP Minutes = "%-M" -- No padding
+fromTFP Minutes0 = "%M" -- Padded with zeros
+fromTFP Seconds = "%-S" -- No padding
+fromTFP Seconds0 = "%S" -- Padded with zeros
+fromTFP TimeMarkerShort = "%p" -- No direct equivalent, so do not distinguish
+                               -- from TimeMarker
+fromTFP TimeMarker = "%p"
+fromTFP (TimeOther cs) = escape cs
+
+h :: ReadP Char
+h = char 'h'
+
+hours12 :: ReadP TimeFormatPicture
+hours12 = do
+  _ <- h
+  return Hours12
+
+hours012 :: ReadP TimeFormatPicture
+hours012 = do
+  _ <- count 2 h
+  return Hours012
+
+bigH :: ReadP Char
+bigH = char 'H'
+
+hours24 :: ReadP TimeFormatPicture
+hours24 = do
+  _ <- bigH
+  return Hours24
+
+hours024 :: ReadP TimeFormatPicture
+hours024 = do
+  _ <- count 2 bigH
+  return Hours024
+
+hours :: ReadP TimeFormatPicture
+hours = hours012 <++ hours12 <++ hours024 <++ hours24
+
+m :: ReadP Char
+m = char 'm'
+
+minute :: ReadP TimeFormatPicture
+minute = do
+  _ <- m
+  return Minutes
+
+minute0 :: ReadP TimeFormatPicture
+minute0 = do
+  _ <- count 2 m
+  return Minutes0
+
+minutes :: ReadP TimeFormatPicture
+minutes = minute0 <++ minute
+
+s :: ReadP Char
+s = char 's'
+
+second :: ReadP TimeFormatPicture
+second = do
+  _ <- s
+  return Seconds
+
+second0 :: ReadP TimeFormatPicture
+second0 = do
+  _ <- count 2 s
+  return Seconds0
+
+seconds :: ReadP TimeFormatPicture
+seconds = second0 <++ second
+
+t :: ReadP Char
+t = char 't'
+
+timeMarkerShort :: ReadP TimeFormatPicture
+timeMarkerShort = do
+  _ <- t
+  return TimeMarkerShort
+
+timeMarker :: ReadP TimeFormatPicture
+timeMarker = do
+  _ <- count 2 t
+  return TimeMarker
+
+timeMarkers :: ReadP TimeFormatPicture
+timeMarkers = timeMarker <++ timeMarkerShort
+
+-- | Although not documented at
+-- https://docs.microsoft.com/en-us/windows/win32/intl/hour--minute--and-second-format-pictures
+-- the format pictures used by Windows do not require all such characters to be
+-- enclosed in single quotation marks.
+nonTimeSpecial :: ReadP Char
+nonTimeSpecial = satisfy (\c -> c `notElem` ['h', 'H', 'm', 's', 't', '\''])
+
+nonTimeSpecials :: ReadP String
+nonTimeSpecials = greedy1 nonTimeSpecial
+
+timeOther :: ReadP TimeFormatPicture
+timeOther = do
+  chars <- greedy1 (nonTimeSpecials <++ quotedChars)
+  return $ TimeOther $ concat chars
+
+timePicture :: ReadP [TimeFormatPicture]
+timePicture = greedy (hours <++ minutes <++ seconds <++ timeMarkers <++
+                     timeOther)
+
+greedy :: ReadP a -> ReadP [a]
+greedy p = greedy1 p <++ return []
+
+greedy1 :: ReadP a -> ReadP [a]
+greedy1 p = do
+  first <- p
+  rest <- greedy p
+  return (first : rest)
+
+parseMaybe :: ReadP a -> String -> Maybe a
+parseMaybe parser input =
+  case readP_to_S parser input of
+    [] -> Nothing
+    ((result, _):_) -> Just result
+
+-- | Translate from a Windows API day, month, year, and era format picture to
+-- the closest corresponding format string used by
+-- 'Data.Time.Format.formatTime'.
+fromDateFormatPicture :: String -> Maybe String
+fromDateFormatPicture dfp =
+  fmap (concatMap fromDFP) $ parseMaybe datePicture dfp
+
+-- | Translate from a Windows API hours, minute, and second format picture to
+-- the closest corresponding format string used by
+-- 'Data.Time.Format.formatTime'.
+fromTimeFormatPicture :: String -> Maybe String
+fromTimeFormatPicture tfp =
+  fmap (concatMap fromTFP) $ parseMaybe timePicture tfp
diff --git a/System/Win32/WindowsString/Console.hsc b/System/Win32/WindowsString/Console.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Console.hsc
@@ -0,0 +1,180 @@
+{-# LANGUAGE PackageImports #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.WindowsString.Console
+-- Copyright   :  (c) University of Glasgow 2023
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32 Console API (WindowsString variant)
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.Console (
+        -- * Console mode
+        getConsoleMode,
+        setConsoleMode,
+        eNABLE_ECHO_INPUT,
+        eNABLE_EXTENDED_FLAGS,
+        eNABLE_INSERT_MODE,
+        eNABLE_LINE_INPUT,
+        eNABLE_MOUSE_INPUT,
+        eNABLE_PROCESSED_INPUT,
+        eNABLE_QUICK_EDIT_MODE,
+        eNABLE_WINDOW_INPUT,
+        eNABLE_VIRTUAL_TERMINAL_INPUT,
+        eNABLE_PROCESSED_OUTPUT,
+        eNABLE_WRAP_AT_EOL_OUTPUT,
+        eNABLE_VIRTUAL_TERMINAL_PROCESSING,
+        dISABLE_NEWLINE_AUTO_RETURN,
+        eNABLE_LVB_GRID_WORLDWIDE,
+        -- * Console code pages
+        getConsoleCP,
+        setConsoleCP,
+        getConsoleOutputCP,
+        setConsoleOutputCP,
+        -- * Ctrl events
+        CtrlEvent, cTRL_C_EVENT, cTRL_BREAK_EVENT,
+        generateConsoleCtrlEvent,
+        -- * Command line
+        commandLineToArgv,
+        getCommandLineW,
+        getArgs,
+        -- * Screen buffer
+        CONSOLE_SCREEN_BUFFER_INFO(..),
+        CONSOLE_SCREEN_BUFFER_INFOEX(..),
+        COORD(..),
+        SMALL_RECT(..),
+        COLORREF,
+        getConsoleScreenBufferInfo,
+        getCurrentConsoleScreenBufferInfo,
+        getConsoleScreenBufferInfoEx,
+        getCurrentConsoleScreenBufferInfoEx,
+
+        -- * Env
+        getEnv,
+        getEnvironment
+  ) where
+
+#include <windows.h>
+#include "alignment.h"
+##include "windows_cconv.h"
+#include "wincon_compat.h"
+
+import System.Win32.WindowsString.Types
+import System.Win32.WindowsString.String (withTStringBufferLen)
+import System.Win32.Console.Internal
+import System.Win32.Console hiding (getArgs, commandLineToArgv, getEnv, getEnvironment)
+import System.OsString.Windows
+import System.OsString.Internal.Types
+
+import Foreign.C.Types (CWchar)
+import Foreign.C.String (CWString)
+import Foreign.Ptr (plusPtr)
+import Foreign.Storable (Storable(..))
+import Foreign.Marshal.Array (peekArray, peekArray0)
+import Foreign.Marshal.Alloc (alloca)
+import GHC.IO (bracket)
+import GHC.IO.Exception (IOException(..), IOErrorType(OtherError))
+
+import Prelude hiding (break, length, tail)
+import qualified Prelude as P
+
+#if !MIN_VERSION_filepath(1,5,0)
+import Data.Coerce
+import qualified "filepath" System.OsPath.Data.ByteString.Short.Word16 as BC
+
+tail :: WindowsString -> WindowsString
+tail = coerce BC.tail
+
+break :: (WindowsChar -> Bool) -> WindowsString -> (WindowsString, WindowsString)
+break = coerce BC.break
+#endif
+
+
+-- | This function can be used to parse command line arguments and return
+--   the split up arguments as elements in a list.
+commandLineToArgv :: WindowsString -> IO [WindowsString]
+commandLineToArgv arg
+  | arg == mempty = return []
+  | otherwise = withTString arg $ \c_arg -> do
+       alloca $ \c_size -> do
+         res <- c_CommandLineToArgvW c_arg c_size
+         size <- peek c_size
+         args <- peekArray (fromIntegral size) res
+         _ <- localFree res
+         mapM peekTString args
+
+-- | Based on 'GetCommandLineW'. This behaves slightly different
+-- than 'System.Environment.getArgs'. See the online documentation:
+-- <https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getcommandlinew>
+getArgs :: IO [WindowsString]
+getArgs = do
+  getCommandLineW >>= peekTString >>= commandLineToArgv
+
+
+-- c_GetEnvironmentVariableW :: LPCWSTR -> LPWSTR -> DWORD -> IO DWORD
+getEnv :: WindowsString -> IO (Maybe WindowsString)
+getEnv name =
+  withTString name $ \c_name -> withTStringBufferLen maxLength $ \(buf, len) -> do
+    let c_len = fromIntegral len
+    c_len' <- c_GetEnvironmentVariableW c_name buf c_len
+    case c_len' of
+      0 -> do
+        err_code <- getLastError
+        if err_code  == eERROR_ENVVAR_NOT_FOUND
+        then return Nothing
+        else errorWin "GetEnvironmentVariableW"
+      _ | c_len' > fromIntegral maxLength ->
+            -- shouldn't happen, because we provide maxLength
+            ioError (IOError Nothing OtherError "GetEnvironmentVariableW" ("Unexpected return code: " <> show c_len') Nothing Nothing)
+        | otherwise -> do
+            let len' = fromIntegral c_len'
+            Just <$> peekTStringLen (buf, len')
+ where
+  -- according to https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
+  -- max characters (wide chars): 32767
+  -- => bytes = 32767 * 2 = 65534
+  -- +1 byte for NUL (although not needed I think)
+  maxLength :: Int
+  maxLength = 65535
+
+
+getEnvironment :: IO [(WindowsString, WindowsString)]
+getEnvironment = bracket c_GetEnvironmentStringsW c_FreeEnvironmentStrings $ \lpwstr -> do
+    strs <- builder lpwstr
+    return (divvy <$> strs)
+ where
+  divvy :: WindowsString -> (WindowsString, WindowsString)
+  divvy str =
+    case break (== unsafeFromChar '=') str of
+      (xs,ys)
+        | ys == mempty -> (xs,ys) -- don't barf (like Posix.getEnvironment)
+      (name, ys) -> let value = tail ys in (name,value)
+
+  builder :: LPWSTR -> IO [WindowsString]
+  builder ptr = go 0
+   where
+    go :: Int -> IO [WindowsString]
+    go off = do
+      (str, l) <- peekCWStringOff ptr off
+      if l == 0
+      then pure []
+      else (str:) <$> go (((l + 1) * 2) + off)
+
+
+peekCWStringOff :: CWString -> Int -> IO (WindowsString, Int)
+peekCWStringOff cp off = do
+  cs <- peekArray0 wNUL (cp `plusPtr` off)
+  return (cWcharsToChars cs, P.length cs)
+
+wNUL :: CWchar
+wNUL = 0
+
+cWcharsToChars :: [CWchar] -> WindowsString
+cWcharsToChars = pack . fmap (WindowsChar . fromIntegral)
+
diff --git a/System/Win32/WindowsString/DLL.hsc b/System/Win32/WindowsString/DLL.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/DLL.hsc
@@ -0,0 +1,67 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.DLL
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.DLL
+    ( module System.Win32.WindowsString.DLL
+    , module System.Win32.DLL
+    ) where
+
+import System.Win32.DLL hiding
+  (   disableThreadLibraryCalls
+    , freeLibrary
+    , getModuleFileName
+    , getModuleHandle
+    , getProcAddress
+    , loadLibrary
+    , loadLibraryEx
+    , setDllDirectory
+    , lOAD_LIBRARY_AS_DATAFILE
+    , lOAD_WITH_ALTERED_SEARCH_PATH
+  )
+import System.Win32.DLL.Internal
+import System.Win32.WindowsString.Types
+
+import Foreign
+import Data.Maybe (fromMaybe)
+import System.OsString.Windows
+import GHC.IO.Encoding.UTF16 ( mkUTF16le )
+import GHC.IO.Encoding.Failure ( CodingFailureMode(..) )
+
+getModuleFileName :: HMODULE -> IO WindowsString
+getModuleFileName hmod =
+  allocaArray 512 $ \ c_str -> do
+  failIfFalse_ "GetModuleFileName" $ c_GetModuleFileName hmod c_str 512
+  peekTString c_str
+
+getModuleHandle :: Maybe WindowsString -> IO HMODULE
+getModuleHandle mb_name =
+  maybeWith withTString mb_name $ \ c_name ->
+  failIfNull "GetModuleHandle" $ c_GetModuleHandle c_name
+
+loadLibrary :: WindowsString -> IO HMODULE
+loadLibrary name =
+  withTString name $ \ c_name ->
+  failIfNull "LoadLibrary" $ c_LoadLibrary c_name
+
+loadLibraryEx :: WindowsString -> HANDLE -> LoadLibraryFlags -> IO HMODULE
+loadLibraryEx name h flags =
+  withTString name $ \ c_name ->
+  failIfNull "LoadLibraryEx" $ c_LoadLibraryEx c_name h flags
+
+setDllDirectory :: Maybe WindowsString -> IO ()
+setDllDirectory name =
+  maybeWith withTString name $ \ c_name -> do
+    let nameS = name >>= either (const Nothing) Just . decodeWith (mkUTF16le TransliterateCodingFailure)
+    failIfFalse_ (unwords ["SetDllDirectory", fromMaybe "NULL" nameS]) $ c_SetDllDirectory c_name
+
diff --git a/System/Win32/WindowsString/DebugApi.hsc b/System/Win32/WindowsString/DebugApi.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/DebugApi.hsc
@@ -0,0 +1,33 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.WindowsString.DebugApi
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for using Windows DebugApi.
+--
+-----------------------------------------------------------------------------
+module System.Win32.WindowsString.DebugApi
+    ( module System.Win32.WindowsString.DebugApi
+    , module System.Win32.DebugApi
+    ) where
+
+import System.Win32.DebugApi.Internal
+import System.Win32.DebugApi hiding (outputDebugString)
+import System.Win32.WindowsString.Types   ( withTString )
+import System.OsString.Windows
+
+##include "windows_cconv.h"
+#include "windows.h"
+
+
+--------------------------------------------------------------------------
+-- On process being debugged
+
+outputDebugString :: WindowsString -> IO ()
+outputDebugString s = withTString s $ \c_s -> c_OutputDebugString c_s
+
diff --git a/System/Win32/WindowsString/File.hsc b/System/Win32/WindowsString/File.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/File.hsc
@@ -0,0 +1,269 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.File
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.File
+    ( deleteFile
+    , copyFile
+    , moveFile
+    , moveFileEx
+    , setCurrentDirectory
+    , createDirectory
+    , createDirectoryEx
+    , removeDirectory
+    , getBinaryType
+    , createFile
+    , setFileAttributes
+    , getFileAttributes
+    , getFileAttributesExStandard
+    , getTempFileName
+    , findFirstChangeNotification
+    , getFindDataFileName
+    , findFirstFile
+    , defineDosDevice
+    , getDiskFreeSpace
+    , setVolumeLabel
+    , getFileExInfoStandard
+    , getFileExMaxInfoLevel
+    , replaceFile
+    , module System.Win32.File
+    ) where
+
+import System.Win32.File.Internal
+import System.Win32.File hiding (
+    deleteFile
+  , copyFile
+  , moveFile
+  , moveFileEx
+  , setCurrentDirectory
+  , createDirectory
+  , createDirectoryEx
+  , removeDirectory
+  , getBinaryType
+  , createFile
+  , setFileAttributes
+  , getFileAttributes
+  , getFileAttributesExStandard
+  , getTempFileName
+  , findFirstChangeNotification
+  , getFindDataFileName
+  , findFirstFile
+  , defineDosDevice
+  , getDiskFreeSpace
+  , setVolumeLabel
+  , getFileExInfoStandard
+  , getFileExMaxInfoLevel
+  , replaceFile
+  )
+import System.Win32.WindowsString.Types
+import System.OsString.Windows
+import Unsafe.Coerce (unsafeCoerce)
+import Data.Maybe (fromMaybe)
+
+import Foreign hiding (void)
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+#include "alignment.h"
+
+deleteFile :: WindowsString -> IO ()
+deleteFile name =
+  withFilePath name $ \ c_name ->
+    failIfFalseWithRetry_ (unwords ["DeleteFile",show name]) $
+      c_DeleteFile c_name
+
+copyFile :: WindowsString -> WindowsString -> Bool -> IO ()
+copyFile src dest over =
+  withFilePath src $ \ c_src ->
+  withFilePath dest $ \ c_dest ->
+  failIfFalseWithRetry_ (unwords ["CopyFile",show src,show dest]) $
+    c_CopyFile c_src c_dest over
+
+moveFile :: WindowsString -> WindowsString -> IO ()
+moveFile src dest =
+  withFilePath src $ \ c_src ->
+  withFilePath dest $ \ c_dest ->
+  failIfFalseWithRetry_ (unwords ["MoveFile",show src,show dest]) $
+    c_MoveFile c_src c_dest
+
+moveFileEx :: WindowsString -> Maybe WindowsString -> MoveFileFlag -> IO ()
+moveFileEx src dest flags =
+  withFilePath src $ \ c_src ->
+  maybeWith withFilePath dest $ \ c_dest ->
+  failIfFalseWithRetry_ (unwords ["MoveFileEx",show src,show dest]) $
+    c_MoveFileEx c_src c_dest flags
+
+setCurrentDirectory :: WindowsString -> IO ()
+setCurrentDirectory name =
+  withFilePath name $ \ c_name ->
+  failIfFalse_ (unwords ["SetCurrentDirectory",show name]) $
+    c_SetCurrentDirectory c_name
+
+createDirectory :: WindowsString -> Maybe LPSECURITY_ATTRIBUTES -> IO ()
+createDirectory name mb_attr =
+  withFilePath name $ \ c_name ->
+  failIfFalseWithRetry_ (unwords ["CreateDirectory",show name]) $
+    c_CreateDirectory c_name (maybePtr mb_attr)
+
+createDirectoryEx :: WindowsString -> WindowsString -> Maybe LPSECURITY_ATTRIBUTES -> IO ()
+createDirectoryEx template name mb_attr =
+  withFilePath template $ \ c_template ->
+  withFilePath name $ \ c_name ->
+  failIfFalseWithRetry_ (unwords ["CreateDirectoryEx",show template,show name]) $
+    c_CreateDirectoryEx c_template c_name (maybePtr mb_attr)
+
+removeDirectory :: WindowsString -> IO ()
+removeDirectory name =
+  withFilePath name $ \ c_name ->
+  failIfFalseWithRetry_ (unwords ["RemoveDirectory",show name]) $
+    c_RemoveDirectory c_name
+
+getBinaryType :: WindowsString -> IO BinaryType
+getBinaryType name =
+  withFilePath name $ \ c_name ->
+  alloca $ \ p_btype -> do
+  failIfFalse_ (unwords ["GetBinaryType",show name]) $
+    c_GetBinaryType c_name p_btype
+  peek p_btype
+
+----------------------------------------------------------------
+-- HANDLE operations
+----------------------------------------------------------------
+
+createFile :: WindowsString -> AccessMode -> ShareMode -> Maybe LPSECURITY_ATTRIBUTES -> CreateMode -> FileAttributeOrFlag -> Maybe HANDLE -> IO HANDLE
+createFile name access share mb_attr mode flag mb_h =
+  withFilePath name $ \ c_name ->
+  failIfWithRetry (==iNVALID_HANDLE_VALUE) (unwords ["CreateFile",show name]) $
+    c_CreateFile c_name access share (maybePtr mb_attr) mode flag (maybePtr mb_h)
+
+setFileAttributes :: WindowsString -> FileAttributeOrFlag -> IO ()
+setFileAttributes name attr =
+  withFilePath name $ \ c_name ->
+  failIfFalseWithRetry_ (unwords ["SetFileAttributes",show name])
+    $ c_SetFileAttributes c_name attr
+
+getFileAttributes :: WindowsString -> IO FileAttributeOrFlag
+getFileAttributes name =
+  withFilePath name $ \ c_name ->
+  failIfWithRetry (== 0xFFFFFFFF) (unwords ["GetFileAttributes",show name]) $
+    c_GetFileAttributes c_name
+
+getFileAttributesExStandard :: WindowsString -> IO WIN32_FILE_ATTRIBUTE_DATA
+getFileAttributesExStandard name =  alloca $ \res -> do
+  withFilePath name $ \ c_name ->
+    failIfFalseWithRetry_ "getFileAttributesExStandard" $
+      c_GetFileAttributesEx c_name (unsafeCoerce getFileExInfoStandard) res
+  peek res
+
+-- | Get a unique temporary filename.
+--
+-- Calls 'GetTempFileNameW'.
+getTempFileName :: WindowsString     -- ^ directory for the temporary file (must be at most MAX_PATH - 14 characters long)
+                -> WindowsString     -- ^ prefix for the temporary file name
+                -> Maybe UINT        -- ^ if 'Nothing', a unique name is generated
+                                     --   otherwise a non-zero value is used as the unique part
+                -> IO (WindowsString, UINT)
+getTempFileName dir prefix unique = allocaBytes ((#const MAX_PATH) * sizeOf (undefined :: TCHAR)) $ \c_buf -> do
+  uid <- withFilePath dir $ \c_dir ->
+    withFilePath prefix $ \ c_prefix -> do
+      failIfZero "getTempFileName" $
+        c_GetTempFileNameW c_dir c_prefix (fromMaybe 0 unique) c_buf
+  fname <- peekTString c_buf
+  pure (fname, uid)
+
+replaceFile :: WindowsString -> WindowsString -> Maybe WindowsString -> DWORD -> IO ()
+replaceFile replacedFile replacementFile mBackupFile replaceFlags =
+  withFilePath replacedFile $ \ c_replacedFile ->
+    withFilePath replacementFile $ \ c_replacementFile ->
+      let getResult f = 
+            case mBackupFile of
+              Nothing -> f nullPtr
+              Just backupFile -> withFilePath backupFile f
+      in getResult $ \ c_backupFile ->
+           failIfFalse_ "ReplaceFile" $ c_ReplaceFile c_replacedFile c_replacementFile c_backupFile replaceFlags nullPtr nullPtr
+
+----------------------------------------------------------------
+-- File Notifications
+--
+-- Use these to initialise, "increment" and close a HANDLE you can wait
+-- on.
+----------------------------------------------------------------
+
+findFirstChangeNotification :: WindowsString -> Bool -> FileNotificationFlag -> IO HANDLE
+findFirstChangeNotification path watch flag =
+  withFilePath path $ \ c_path ->
+  failIfNull (unwords ["FindFirstChangeNotification",show path]) $
+    c_FindFirstChangeNotification c_path watch flag
+
+
+----------------------------------------------------------------
+-- Directories
+----------------------------------------------------------------
+
+
+getFindDataFileName :: FindData -> IO WindowsString
+getFindDataFileName fd = case unsafeCoerce fd of
+  (FindData fp) ->
+    withForeignPtr fp $ \p ->
+      peekTString ((# ptr WIN32_FIND_DATAW, cFileName ) p)
+
+findFirstFile :: WindowsString -> IO (HANDLE, FindData)
+findFirstFile str = do
+  fp_finddata <- mallocForeignPtrBytes (# const sizeof(WIN32_FIND_DATAW) )
+  withForeignPtr fp_finddata $ \p_finddata -> do
+    handle <- withFilePath str $ \tstr -> do
+                failIf (== iNVALID_HANDLE_VALUE) "findFirstFile" $
+                  c_FindFirstFile tstr p_finddata
+    return (handle, unsafeCoerce (FindData fp_finddata))
+
+
+----------------------------------------------------------------
+-- DOS Device flags
+----------------------------------------------------------------
+
+defineDosDevice :: DefineDosDeviceFlags -> WindowsString -> Maybe WindowsString -> IO ()
+defineDosDevice flags name path =
+  maybeWith withFilePath path $ \ c_path ->
+  withFilePath name $ \ c_name ->
+  failIfFalse_ "DefineDosDevice" $ c_DefineDosDevice flags c_name c_path
+
+----------------------------------------------------------------
+
+
+-- %fun GetDriveType :: Maybe String -> IO DriveType
+
+getDiskFreeSpace :: Maybe WindowsString -> IO (DWORD,DWORD,DWORD,DWORD)
+getDiskFreeSpace path =
+  maybeWith withFilePath path $ \ c_path ->
+  alloca $ \ p_sectors ->
+  alloca $ \ p_bytes ->
+  alloca $ \ p_nfree ->
+  alloca $ \ p_nclusters -> do
+  failIfFalse_ "GetDiskFreeSpace" $
+    c_GetDiskFreeSpace c_path p_sectors p_bytes p_nfree p_nclusters
+  sectors <- peek p_sectors
+  bytes <- peek p_bytes
+  nfree <- peek p_nfree
+  nclusters <- peek p_nclusters
+  return (sectors, bytes, nfree, nclusters)
+
+setVolumeLabel :: Maybe WindowsString -> Maybe WindowsString -> IO ()
+setVolumeLabel path name =
+  maybeWith withFilePath path $ \ c_path ->
+  maybeWith withFilePath name $ \ c_name ->
+  failIfFalse_ "SetVolumeLabel" $ c_SetVolumeLabel c_path c_name
+
+----------------------------------------------------------------
+-- End
+----------------------------------------------------------------
diff --git a/System/Win32/WindowsString/FileMapping.hsc b/System/Win32/WindowsString/FileMapping.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/FileMapping.hsc
@@ -0,0 +1,107 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.FileMapping
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32 mapped files.
+--
+-----------------------------------------------------------------------------
+module System.Win32.WindowsString.FileMapping
+    ( module System.Win32.WindowsString.FileMapping
+    , module System.Win32.FileMapping
+    ) where
+
+import System.Win32.FileMapping hiding
+    (
+      mapFile
+    , withMappedFile
+    , createFileMapping
+    , openFileMapping
+    )
+
+import System.Win32.FileMapping.Internal
+import System.Win32.WindowsString.Types   ( HANDLE, BOOL, withTString
+                            , failIf, DDWORD, ddwordToDwords
+                            , iNVALID_HANDLE_VALUE )
+import System.Win32.Mem
+import System.Win32.WindowsString.File
+import System.OsString.Windows
+import System.OsPath.Windows
+
+import Control.Exception        ( mask_, bracket )
+import Foreign                  ( nullPtr, maybeWith
+                                , ForeignPtr, newForeignPtr )
+
+##include "windows_cconv.h"
+
+#include "windows.h"
+
+---------------------------------------------------------------------------
+-- Derived functions
+---------------------------------------------------------------------------
+
+-- | Maps file fully and returns ForeignPtr and length of the mapped area.
+-- The mapped file is opened read-only and shared reading.
+mapFile :: WindowsPath -> IO (ForeignPtr a, Int)
+mapFile path = do
+    bracket
+        (createFile path gENERIC_READ fILE_SHARE_READ Nothing oPEN_EXISTING fILE_ATTRIBUTE_NORMAL Nothing)
+        (closeHandle)
+        $ \fh -> bracket
+            (createFileMapping (Just fh) pAGE_READONLY 0 Nothing)
+            (closeHandle)
+            $ \fm -> do
+                fi <- getFileInformationByHandle fh
+                fp <- mask_ $ do
+                    ptr <- mapViewOfFile fm fILE_MAP_READ 0 0
+                    newForeignPtr c_UnmapViewOfFileFinaliser ptr
+                return (fp, fromIntegral $ bhfiSize fi)
+
+-- | Opens an existing file and creates mapping object to it.
+withMappedFile
+    :: WindowsPath             -- ^ Path
+    -> Bool                 -- ^ Write? (False = read-only)
+    -> Maybe Bool           -- ^ Sharing mode, no sharing, share read, share read+write
+    -> (Integer -> MappedObject -> IO a) -- ^ Action
+    -> IO a
+withMappedFile path write share act =
+    bracket
+        (createFile path access share' Nothing oPEN_EXISTING fILE_ATTRIBUTE_NORMAL Nothing)
+        (closeHandle)
+        $ \fh -> bracket
+            (createFileMapping (Just fh) page 0 Nothing)
+            (closeHandle)
+            $ \fm -> do
+                bhfi <- getFileInformationByHandle fh
+                act (fromIntegral $ bhfiSize bhfi) (MappedObject fh fm mapaccess)
+    where
+        access    = if write then gENERIC_READ+gENERIC_WRITE else gENERIC_READ
+        page      = if write then pAGE_READWRITE else pAGE_READONLY
+        mapaccess = if write then fILE_MAP_ALL_ACCESS else fILE_MAP_READ
+        share' = case share of
+            Nothing     -> fILE_SHARE_NONE
+            Just False  -> fILE_SHARE_READ
+            Just True   -> fILE_SHARE_READ + fILE_SHARE_WRITE
+
+---------------------------------------------------------------------------
+-- API in Haskell
+---------------------------------------------------------------------------
+createFileMapping :: Maybe HANDLE -> ProtectFlags -> DDWORD -> Maybe WindowsString -> IO HANDLE
+createFileMapping mh flags mosize name =
+    maybeWith withTString name $ \c_name ->
+        failIf (==nullPtr) "createFileMapping: CreateFileMapping" $ c_CreateFileMapping handle nullPtr flags moshi moslow c_name
+    where
+        (moshi,moslow) = ddwordToDwords mosize
+        handle = maybe iNVALID_HANDLE_VALUE id mh
+
+openFileMapping :: FileMapAccess -> BOOL -> Maybe WindowsString -> IO HANDLE
+openFileMapping access inherit name =
+    maybeWith withTString name $ \c_name ->
+        failIf (==nullPtr) "openFileMapping: OpenFileMapping" $
+            c_OpenFileMapping access inherit c_name
+
diff --git a/System/Win32/WindowsString/HardLink.hs b/System/Win32/WindowsString/HardLink.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/HardLink.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.HardLink
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Handling hard link using Win32 API. [NTFS only]
+
+   Note: You should worry about file system type when use this module's function in your application:
+
+     * NTFS only supprts this functionality.
+
+     * ReFS doesn't support hard link currently.
+-}
+module System.Win32.WindowsString.HardLink
+  ( createHardLink
+  , createHardLink'
+  ) where
+
+import System.Win32.HardLink.Internal
+import System.Win32.WindowsString.File   ( failIfFalseWithRetry_ )
+import System.Win32.WindowsString.String ( withTString )
+import System.Win32.WindowsString.Types  ( nullPtr )
+import System.OsPath.Windows
+
+#include "windows_cconv.h"
+
+-- | NOTE: createHardLink is /flipped arguments/ to provide compatibility for Unix.
+-- 
+-- If you want to create hard link by Windows way, use 'createHardLink'' instead.
+createHardLink :: WindowsPath -- ^ Target file path
+               -> WindowsPath -- ^ Hard link name
+               -> IO ()
+createHardLink = flip createHardLink'
+
+createHardLink' :: WindowsPath -- ^ Hard link name
+                -> WindowsPath -- ^ Target file path
+                -> IO ()
+createHardLink' link target =
+   withTString target $ \c_target ->
+   withTString link   $ \c_link ->
+        failIfFalseWithRetry_ (unwords ["CreateHardLinkW",show link,show target]) $
+          c_CreateHardLink c_link c_target nullPtr
diff --git a/System/Win32/WindowsString/Info.hsc b/System/Win32/WindowsString/Info.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Info.hsc
@@ -0,0 +1,114 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Info
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.Info
+    ( module System.Win32.WindowsString.Info
+    , module System.Win32.Info
+    ) where
+
+import System.Win32.Info.Internal
+import System.Win32.Info hiding (
+    getSystemDirectory
+  , getWindowsDirectory
+  , getCurrentDirectory
+  , getTemporaryDirectory
+  , getFullPathName
+  , getLongPathName
+  , getShortPathName
+  , searchPath
+  , getUserName
+  )
+import Control.Exception (catch)
+import Foreign.Marshal.Alloc (alloca)
+import Foreign.Marshal.Utils (with, maybeWith)
+import Foreign.Marshal.Array (allocaArray)
+import Foreign.Ptr (nullPtr)
+import Foreign.Storable (Storable(..))
+import System.IO.Error (isDoesNotExistError)
+import System.Win32.WindowsString.Types (failIfFalse_, peekTStringLen, withTString, try)
+import System.OsPath.Windows
+
+#if !MIN_VERSION_base(4,6,0)
+import Prelude hiding (catch)
+#endif
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+#include "alignment.h"
+
+----------------------------------------------------------------
+-- Standard Directories
+----------------------------------------------------------------
+
+getSystemDirectory :: IO WindowsString
+getSystemDirectory = try "GetSystemDirectory" c_getSystemDirectory 512
+
+getWindowsDirectory :: IO WindowsString
+getWindowsDirectory = try "GetWindowsDirectory" c_getWindowsDirectory 512
+
+getCurrentDirectory :: IO WindowsString
+getCurrentDirectory = try "GetCurrentDirectory" (flip c_getCurrentDirectory) 512
+
+getTemporaryDirectory :: IO WindowsString
+getTemporaryDirectory = try "GetTempPath" (flip c_getTempPath) 512
+
+getFullPathName :: WindowsPath -> IO WindowsPath
+getFullPathName name = do
+  withTString name $ \ c_name ->
+    try "getFullPathName"
+      (\buf len -> c_GetFullPathName c_name len buf nullPtr) 512
+
+getLongPathName :: WindowsPath -> IO WindowsPath
+getLongPathName name = do
+  withTString name $ \ c_name ->
+    try "getLongPathName"
+      (c_GetLongPathName c_name) 512
+
+getShortPathName :: WindowsPath -> IO WindowsPath
+getShortPathName name = do
+  withTString name $ \ c_name ->
+    try "getShortPathName"
+      (c_GetShortPathName c_name) 512
+
+searchPath :: Maybe WindowsString -> WindowsPath -> Maybe WindowsString -> IO (Maybe WindowsPath)
+searchPath path filename ext =
+  maybe ($ nullPtr) withTString path $ \p_path ->
+  withTString filename $ \p_filename ->
+  maybeWith withTString ext      $ \p_ext ->
+  alloca $ \ppFilePart -> (do
+    s <- try "searchPath" (\buf len -> c_SearchPath p_path p_filename p_ext
+                          len buf ppFilePart) 512
+    return (Just s))
+     `catch` \e -> if isDoesNotExistError e
+                       then return Nothing
+                       else ioError e
+
+----------------------------------------------------------------
+-- User name
+----------------------------------------------------------------
+
+-- %fun GetUserName :: IO String
+
+getUserName :: IO WindowsString
+getUserName =
+  allocaArray 512 $ \ c_str ->
+    with 512 $ \ c_len -> do
+        failIfFalse_ "GetUserName" $ c_GetUserName c_str c_len
+        len <- peek c_len
+        peekTStringLen (c_str, fromIntegral len - 1)
+
+----------------------------------------------------------------
+-- End
+----------------------------------------------------------------
diff --git a/System/Win32/WindowsString/Path.hsc b/System/Win32/WindowsString/Path.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Path.hsc
@@ -0,0 +1,52 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Path
+-- Copyright   :  (c) Tamar Christina, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Tamar Christina <tamar@zhox.com>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.Path (
+   filepathRelativePathTo
+ , pathRelativePathTo
+ ) where
+
+import System.Win32.Path.Internal
+import System.Win32.WindowsString.Types
+import System.Win32.WindowsString.File
+import System.OsPath.Windows
+
+import Foreign
+
+##include "windows_cconv.h"
+
+#include <windows.h>
+
+filepathRelativePathTo :: WindowsPath -> WindowsPath -> IO WindowsPath
+filepathRelativePathTo from to =
+  withTString from $ \p_from ->
+  withTString to   $ \p_to   ->
+  allocaArray ((#const MAX_PATH) * (#size TCHAR)) $ \p_AbsPath -> do
+    _ <- failIfZero "PathRelativePathTo" (c_pathRelativePathTo p_AbsPath p_from fILE_ATTRIBUTE_DIRECTORY
+                                                                         p_to   fILE_ATTRIBUTE_NORMAL)
+    path <- peekTString p_AbsPath
+    _ <- localFree p_AbsPath
+    return path
+
+pathRelativePathTo :: WindowsPath -> FileAttributeOrFlag -> WindowsPath -> FileAttributeOrFlag -> IO WindowsPath
+pathRelativePathTo from from_attr to to_attr =
+  withTString from $ \p_from ->
+  withTString to   $ \p_to   ->
+  allocaArray ((#const MAX_PATH) * (#size TCHAR)) $ \p_AbsPath -> do
+    _ <- failIfZero "PathRelativePathTo" (c_pathRelativePathTo p_AbsPath p_from from_attr
+                                                                         p_to   to_attr)
+    path <- peekTString p_AbsPath
+    _ <- localFree p_AbsPath
+    return path
+
diff --git a/System/Win32/WindowsString/Shell.hsc b/System/Win32/WindowsString/Shell.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Shell.hsc
@@ -0,0 +1,59 @@
+{-# LANGUAGE Trustworthy #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.WindowsString.Shell
+-- Copyright   :  (c) The University of Glasgow 2009
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- Win32 stuff from shell32.dll
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.Shell (
+  sHGetFolderPath,
+  CSIDL,
+  cSIDL_PROFILE,
+  cSIDL_APPDATA,
+  cSIDL_WINDOWS,
+  cSIDL_PERSONAL,
+  cSIDL_LOCAL_APPDATA,
+  cSIDL_DESKTOPDIRECTORY,
+  cSIDL_PROGRAM_FILES,
+  SHGetFolderPathFlags,
+  sHGFP_TYPE_CURRENT,
+  sHGFP_TYPE_DEFAULT
+ ) where
+
+import System.OsString.Windows (WindowsString)
+import System.Win32.Shell.Internal
+import System.Win32.Shell hiding (sHGetFolderPath)
+import System.Win32.WindowsString.Types
+import Graphics.Win32.GDI.Types (HWND)
+
+import Foreign
+import Control.Monad
+
+##include "windows_cconv.h"
+
+-- for SHGetFolderPath stuff
+#define _WIN32_IE 0x500
+#include <windows.h>
+#include <shlobj.h>
+
+----------------------------------------------------------------
+-- SHGetFolderPath
+--
+-- XXX: this is deprecated in Vista and later
+----------------------------------------------------------------
+
+
+sHGetFolderPath :: HWND -> CSIDL -> HANDLE -> SHGetFolderPathFlags -> IO WindowsString
+sHGetFolderPath hwnd csidl hdl flags =
+  allocaBytes ((#const MAX_PATH) * (#size TCHAR)) $ \pstr -> do
+    r <- c_SHGetFolderPath hwnd csidl hdl flags pstr
+    when (r < 0) $ raiseUnsupported "sHGetFolderPath"
+    peekTString pstr
diff --git a/System/Win32/WindowsString/String.hs b/System/Win32/WindowsString/String.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/String.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE PackageImports #-}
+
+{- |
+   Module      :  System.Win32.String
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Utilities for primitive marshalling of Windows' C strings.
+-}
+module System.Win32.WindowsString.String
+  ( LPSTR, LPCSTR, LPWSTR, LPCWSTR
+  , TCHAR, LPTSTR, LPCTSTR, LPCTSTR_
+  , withTString, withTStringLen, peekTString, peekTStringLen
+  , newTString
+  , withTStringBuffer, withTStringBufferLen
+  ) where
+
+import System.Win32.String hiding
+  ( withTStringBuffer
+  , withTStringBufferLen
+  , withTString
+  , withTStringLen
+  , peekTString
+  , peekTStringLen
+  , newTString
+  )
+import System.Win32.WindowsString.Types
+import System.OsString.Internal.Types
+#if MIN_VERSION_filepath(1,5,0)
+import qualified "os-string" System.OsString.Data.ByteString.Short as SBS
+#else
+import qualified "filepath" System.OsPath.Data.ByteString.Short as SBS
+#endif
+import Data.Word (Word8)
+
+-- | Marshal a dummy Haskell string into a NUL terminated C wide string
+-- using temporary storage.
+--
+-- * the Haskell string is created by length parameter. And the Haskell
+--   string contains /only/ NUL characters.
+--
+-- * the memory is freed when the subcomputation terminates (either
+--   normally or via an exception), so the pointer to the temporary
+--   storage must /not/ be used after this.
+--
+withTStringBuffer :: Int -> (LPTSTR -> IO a) -> IO a
+withTStringBuffer maxLength
+  = let dummyBuffer = WindowsString $ SBS.pack $ replicate (if even maxLength then maxLength else maxLength + 1) _nul
+    in  withTString dummyBuffer
+
+-- | Marshal a dummy Haskell string into a C wide string (i.e. wide
+-- character array) in temporary storage, with explicit length
+-- information.
+--
+-- * the Haskell string is created by length parameter. And the Haskell
+--   string contains /only/ NUL characters.
+--
+-- * the memory is freed when the subcomputation terminates (either
+--   normally or via an exception), so the pointer to the temporary
+--   storage must /not/ be used after this.
+--
+withTStringBufferLen :: Int -> ((LPTSTR, Int) -> IO a) -> IO a
+withTStringBufferLen maxLength
+  = let dummyBuffer = WindowsString $ SBS.pack $ replicate (if even maxLength then maxLength else maxLength + 1) _nul
+    in  withTStringLen dummyBuffer
+
+
+_nul :: Word8
+_nul = 0x00
diff --git a/System/Win32/WindowsString/SymbolicLink.hsc b/System/Win32/WindowsString/SymbolicLink.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/SymbolicLink.hsc
@@ -0,0 +1,94 @@
+{-# LANGUAGE CPP #-}
+{- |
+   Module      :  System.Win32.SymbolicLink
+   Copyright   :  2012 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Handling symbolic link using Win32 API. [Vista of later and desktop app only]
+
+   Note: When using the createSymbolicLink* functions without the
+   SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE flag, you should worry about UAC
+   (User Account Control) when use this module's function in your application:
+
+     * require to use 'Run As Administrator' to run your application.
+
+     * or modify your application's manifect file to add
+       \<requestedExecutionLevel level='requireAdministrator' uiAccess='false'/\>.
+
+   Starting from Windows 10 version 1703 (Creators Update), after enabling
+   Developer Mode, users can create symbolic links without requiring the
+   Administrator privilege in the current process. Supply a 'True' flag in
+   addition to the target and link name to enable this behavior.
+-}
+module System.Win32.WindowsString.SymbolicLink
+  ( SymbolicLinkFlags
+  , sYMBOLIC_LINK_FLAG_FILE
+  , sYMBOLIC_LINK_FLAG_DIRECTORY
+  , sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+  , createSymbolicLink
+  , createSymbolicLink'
+  , createSymbolicLinkFile
+  , createSymbolicLinkDirectory
+  ) where
+
+import System.Win32.SymbolicLink.Internal
+import Data.Bits ((.|.))
+import System.Win32.WindowsString.Types
+import System.Win32.WindowsString.File ( failIfFalseWithRetry_ )
+import System.OsPath.Windows
+import Unsafe.Coerce (unsafeCoerce)
+
+##include "windows_cconv.h"
+
+-- | createSymbolicLink* functions don't check that file is exist or not.
+--
+-- NOTE: createSymbolicLink* functions are /flipped arguments/ to provide compatibility for Unix,
+-- except 'createSymbolicLink''.
+--
+-- If you want to create symbolic link by Windows way, use 'createSymbolicLink'' instead.
+createSymbolicLink :: WindowsPath -- ^ Target file path
+                   -> WindowsPath -- ^ Symbolic link name
+                   -> SymbolicLinkFlags -> IO ()
+createSymbolicLink = flip createSymbolicLink'
+
+createSymbolicLinkFile :: WindowsPath -- ^ Target file path
+                       -> WindowsPath -- ^ Symbolic link name
+                       -> Bool -- ^ Create the symbolic link with the unprivileged mode
+                       -> IO ()
+createSymbolicLinkFile target link unprivileged =
+  createSymbolicLink'
+    link
+    target
+    ( if unprivileged
+        then sYMBOLIC_LINK_FLAG_FILE .|. sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+        else sYMBOLIC_LINK_FLAG_FILE
+    )
+
+createSymbolicLinkDirectory :: WindowsPath -- ^ Target file path
+                            -> WindowsPath -- ^ Symbolic link name
+                            -> Bool -- ^ Create the symbolic link with the unprivileged mode
+                            -> IO ()
+createSymbolicLinkDirectory target link unprivileged =
+  createSymbolicLink'
+    link
+    target
+    ( if unprivileged
+        then
+          sYMBOLIC_LINK_FLAG_DIRECTORY
+            .|. sYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+        else sYMBOLIC_LINK_FLAG_DIRECTORY
+    )
+
+createSymbolicLink' :: WindowsPath -- ^ Symbolic link name
+                    -> WindowsPath -- ^ Target file path
+                    -> SymbolicLinkFlags -> IO ()
+createSymbolicLink' link target flag = do
+    withTString link $ \c_link ->
+      withTString target $ \c_target ->
+        failIfFalseWithRetry_ (unwords ["CreateSymbolicLink",show link,show target]) $
+          c_CreateSymbolicLink c_link c_target (unsafeCoerce flag)
+
diff --git a/System/Win32/WindowsString/Time.hsc b/System/Win32/WindowsString/Time.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Time.hsc
@@ -0,0 +1,60 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Time
+-- Copyright   :  (c) Esa Ilari Vuokko, 2006
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32 Time API.
+--
+-----------------------------------------------------------------------------
+module System.Win32.WindowsString.Time
+    ( module System.Win32.WindowsString.Time
+    , module System.Win32.Time
+    ) where
+
+import System.Win32.Time.Internal
+import System.Win32.Time hiding (getTimeFormatEx, getTimeFormat)
+
+import System.Win32.WindowsString.String  ( peekTStringLen, withTString )
+import System.Win32.WindowsString.Types   ( LCID, failIf )
+import System.Win32.Utils   ( trySized )
+
+import Foreign          ( Storable(sizeOf)
+                        , nullPtr, castPtr
+                        , with, allocaBytes )
+import Foreign.C        ( CWchar(..)
+                        , withCWString )
+import Foreign.Marshal.Utils (maybeWith)
+import System.OsString.Windows
+
+##include "windows_cconv.h"
+#include <windows.h>
+#include "alignment.h"
+#include "winnls_compat.h"
+
+
+getTimeFormatEx :: Maybe WindowsString
+                -> GetTimeFormatFlags
+                -> Maybe SYSTEMTIME
+                -> Maybe WindowsString
+                -> IO String
+getTimeFormatEx locale flags st fmt =
+    maybeWith withTString locale $ \c_locale ->
+        maybeWith with st $ \c_st ->
+            maybeWith withTString fmt $ \c_fmt -> do
+                let c_func = c_GetTimeFormatEx c_locale flags c_st c_fmt
+                trySized "GetTimeFormatEx" c_func
+
+getTimeFormat :: LCID -> GetTimeFormatFlags -> Maybe SYSTEMTIME -> Maybe String -> IO WindowsString
+getTimeFormat locale flags st fmt =
+    maybeWith with st $ \c_st ->
+    maybeWith withCWString fmt $ \c_fmt -> do
+        size <- c_GetTimeFormat locale flags c_st c_fmt nullPtr 0
+        allocaBytes ((fromIntegral size) * (sizeOf (undefined::CWchar))) $ \out -> do
+            size' <- failIf (==0) "getTimeFormat: GetTimeFormat" $
+                c_GetTimeFormat locale flags c_st c_fmt (castPtr out) size
+            peekTStringLen (out,fromIntegral size')
diff --git a/System/Win32/WindowsString/Types.hsc b/System/Win32/WindowsString/Types.hsc
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Types.hsc
@@ -0,0 +1,214 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE ViewPatterns #-}
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  System.Win32.Types
+-- Copyright   :  (c) Alastair Reid, 1997-2003
+-- License     :  BSD-style (see the file libraries/base/LICENSE)
+--
+-- Maintainer  :  Esa Ilari Vuokko <ei@vuokko.info>
+-- Stability   :  provisional
+-- Portability :  portable
+--
+-- A collection of FFI declarations for interfacing with Win32.
+--
+-----------------------------------------------------------------------------
+
+module System.Win32.WindowsString.Types
+        ( module System.Win32.WindowsString.Types
+        , module System.Win32.Types
+        ) where
+
+import System.Win32.Types hiding (
+    withTString
+  , withTStringLen
+  , peekTString
+  , peekTStringLen
+  , newTString
+  , failIf
+  , failIf_
+  , failIfNeg
+  , failIfNull
+  , failIfZero
+  , failIfFalse_
+  , failUnlessSuccess
+  , failUnlessSuccessOr
+  , errorWin
+  , failWith
+  , try
+  , withFilePath
+  , useAsCWStringSafe
+  )
+
+import Foreign.C.Types (CUIntPtr(..))
+import Foreign.C.String (CWString)
+import qualified System.OsPath.Windows as WS
+import System.OsPath.Windows (WindowsPath)
+import System.OsString.Windows (decodeWith, encodeWith)
+import System.OsString.Internal.Types
+#if MIN_VERSION_filepath(1,5,0)
+import "os-string" System.OsString.Encoding.Internal (decodeWithBaseWindows)
+import qualified "os-string" System.OsString.Data.ByteString.Short.Word16 as SBS
+import "os-string" System.OsString.Data.ByteString.Short.Word16 (
+#else
+import "filepath" System.OsPath.Encoding.Internal (decodeWithBaseWindows)
+import qualified "filepath" System.OsPath.Data.ByteString.Short.Word16 as SBS
+import "filepath" System.OsPath.Data.ByteString.Short.Word16 (
+#endif
+  packCWString,
+  packCWStringLen,
+  useAsCWString,
+  useAsCWStringLen,
+  newCWString
+  )
+import Data.Bifunctor (first)
+import Data.Char (isSpace)
+import Numeric (showHex)
+import qualified System.IO as IO ()
+import System.IO.Error (ioeSetErrorString)
+import Foreign (allocaArray)
+import Foreign.Ptr ( Ptr )
+import Foreign.C.Error ( errnoToIOError )
+import Control.Exception ( throwIO )
+import qualified Control.Exception as EX
+import GHC.Ptr (castPtr)
+import GHC.IO.Exception
+
+#if !MIN_VERSION_base(4,8,0)
+import Data.Word (Word)
+#endif
+
+import GHC.IO.Encoding.UTF16 ( mkUTF16le )
+import GHC.IO.Encoding.Failure ( CodingFailureMode(..) )
+
+#include <fcntl.h>
+#include <windows.h>
+#include <wchar.h>
+##include "windows_cconv.h"
+
+
+----------------------------------------------------------------
+-- Chars and strings
+----------------------------------------------------------------
+
+withTString    :: WindowsString -> (LPTSTR -> IO a) -> IO a
+withFilePath   :: WindowsPath -> (LPTSTR -> IO a) -> IO a
+withTStringLen :: WindowsString -> ((LPTSTR, Int) -> IO a) -> IO a
+peekTString    :: LPCTSTR -> IO WindowsString
+peekTStringLen :: (LPCTSTR, Int) -> IO WindowsString
+newTString     :: WindowsString -> IO LPCTSTR
+
+-- UTF-16 version:
+-- the casts are from 'Ptr Word16' to 'Ptr CWchar', which is safe
+withTString (WindowsString str) f    = useAsCWString str (\ptr -> f (castPtr ptr))
+withFilePath path = useAsCWStringSafe path
+withTStringLen (WindowsString str) f = useAsCWStringLen str (\(ptr, len) -> f (castPtr ptr, len))
+peekTString    = fmap WindowsString . packCWString . castPtr
+peekTStringLen = fmap WindowsString . packCWStringLen . first castPtr
+newTString (WindowsString str) = fmap castPtr $ newCWString str
+
+foreign import ccall unsafe "wchar.h wcslen" c_wcslen
+    :: CWString -> IO SIZE_T
+
+-- | Wrapper around 'useAsCString', checking the encoded 'FilePath' for internal NUL codepoints as these are
+-- disallowed in Windows filepaths. See https://gitlab.haskell.org/ghc/ghc/-/issues/13660
+useAsCWStringSafe :: WindowsPath -> (CWString -> IO a) -> IO a
+useAsCWStringSafe wp@(WS path) f = useAsCWString path $ \(castPtr -> ptr) -> do
+    let len = SBS.numWord16 path
+    clen <- c_wcslen ptr
+    if clen == fromIntegral len
+        then f ptr
+        else do
+          path' <- either (const (_toStr wp)) id <$> (EX.try @IOException) (decodeWithBaseWindows path)
+          ioError (err path')
+  where
+    _toStr = fmap WS.toChar . WS.unpack
+    err path' =
+        IOError
+          { ioe_handle = Nothing
+          , ioe_type = InvalidArgument
+          , ioe_location = "useAsCWStringSafe"
+          , ioe_description = "Windows filepaths must not contain internal NUL codepoints."
+          , ioe_errno = Nothing
+          , ioe_filename = Just path'
+          }
+
+----------------------------------------------------------------
+-- Errors
+----------------------------------------------------------------
+
+failIf :: (a -> Bool) -> String -> IO a -> IO a
+failIf p wh act = do
+  v <- act
+  if p v then errorWin wh else return v
+
+failIf_ :: (a -> Bool) -> String -> IO a -> IO ()
+failIf_ p wh act = do
+  v <- act
+  if p v then errorWin wh else return ()
+
+failIfNeg :: (Num a, Ord a) => String -> IO a -> IO a
+failIfNeg = failIf (< 0)
+
+failIfNull :: String -> IO (Ptr a) -> IO (Ptr a)
+failIfNull = failIf (== nullPtr)
+
+failIfZero :: (Eq a, Num a) => String -> IO a -> IO a
+failIfZero = failIf (== 0)
+
+failIfFalse_ :: String -> IO Bool -> IO ()
+failIfFalse_ = failIf_ not
+
+failUnlessSuccess :: String -> IO ErrCode -> IO ()
+failUnlessSuccess fn_name act = do
+  r <- act
+  if r == 0 then return () else failWith fn_name r
+
+failUnlessSuccessOr :: ErrCode -> String -> IO ErrCode -> IO Bool
+failUnlessSuccessOr val fn_name act = do
+  r <- act
+  if r == 0 then return False
+    else if r == val then return True
+    else failWith fn_name r
+
+
+errorWin :: String -> IO a
+errorWin fn_name = do
+  err_code <- getLastError
+  failWith fn_name err_code
+
+failWith :: String -> ErrCode -> IO a
+failWith fn_name err_code = do
+  c_msg <- getErrorMessage err_code
+
+  msg <- either (fail . show) pure . decodeWith (mkUTF16le TransliterateCodingFailure) =<< if c_msg == nullPtr
+           then either (fail . show) pure . encodeWith (mkUTF16le TransliterateCodingFailure) $ "Error 0x" ++ Numeric.showHex err_code ""
+           else do msg <- peekTString c_msg
+                   -- We ignore failure of freeing c_msg, given we're already failing
+                   _ <- localFree c_msg
+                   return msg
+  -- turn GetLastError() into errno, which errnoToIOError knows how to convert
+  -- to an IOException we can throw.
+  errno <- c_maperrno_func err_code
+  let msg' = reverse $ dropWhile isSpace $ reverse msg -- drop trailing \n
+      ioerror = errnoToIOError fn_name errno Nothing Nothing
+                  `ioeSetErrorString` msg'
+  throwIO ioerror
+
+
+-- Support for API calls that are passed a fixed-size buffer and tell
+-- you via the return value if the buffer was too small.  In that
+-- case, we double the buffer size and try again.
+try :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO WindowsString
+try loc f n = do
+   e <- allocaArray (fromIntegral n) $ \lptstr -> do
+          r <- failIfZero loc $ f lptstr n
+          if (r > n) then return (Left r) else do
+            str <- peekTStringLen (lptstr, fromIntegral r)
+            return (Right str)
+   case e of
+        Left n'   -> try loc f n'
+        Right str -> return str
diff --git a/System/Win32/WindowsString/Utils.hs b/System/Win32/WindowsString/Utils.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/WindowsString/Utils.hs
@@ -0,0 +1,63 @@
+{- |
+   Module      :  System.Win32.Utils
+   Copyright   :  2009 Balazs Komuves, 2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Utilities for calling Win32 API
+-}
+module System.Win32.WindowsString.Utils
+  ( module System.Win32.WindowsString.Utils
+  , module System.Win32.Utils
+  ) where
+
+import Foreign.C.Types             ( CInt )
+import Foreign.Marshal.Array       ( allocaArray )
+import Foreign.Ptr                 ( nullPtr )
+
+import System.Win32.Utils hiding
+  ( try
+  , tryWithoutNull
+  , trySized
+  )
+import System.Win32.WindowsString.String         ( LPTSTR, peekTString, peekTStringLen
+                                   , withTStringBufferLen )
+import System.Win32.WindowsString.Types          ( UINT
+                                   , failIfZero
+                                  )
+import qualified System.Win32.WindowsString.Types ( try )
+import System.OsString.Windows
+
+
+-- | Support for API calls that are passed a fixed-size buffer and tell
+-- you via the return value if the buffer was too small.  In that
+-- case, we extend the buffer size and try again.
+try :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO WindowsString
+try = System.Win32.WindowsString.Types.try
+{-# INLINE try #-}
+
+tryWithoutNull :: String -> (LPTSTR -> UINT -> IO UINT) -> UINT -> IO WindowsString
+tryWithoutNull loc f n = do
+   e <- allocaArray (fromIntegral n) $ \lptstr -> do
+          r <- failIfZero loc $ f lptstr n
+          if r > n then return (Left r) else do
+            str <- peekTString lptstr
+            return (Right str)
+   case e of
+        Left r'   -> tryWithoutNull loc f r'
+        Right str -> return str
+
+-- | Support for API calls that return the required size, in characters
+-- including a null character, of the buffer when passed a buffer size of zero.
+trySized :: String -> (LPTSTR -> CInt -> IO CInt) -> IO WindowsString
+trySized wh f = do
+    c_len <- failIfZero wh $ f nullPtr 0
+    let len = fromIntegral c_len
+    withTStringBufferLen len $ \(buf', len') -> do
+        let c_len' = fromIntegral len'
+        c_len'' <- failIfZero wh $ f buf' c_len'
+        let len'' = fromIntegral c_len''
+        peekTStringLen (buf', len'' - 1) -- Drop final null character
diff --git a/System/Win32/Word.hs b/System/Win32/Word.hs
new file mode 100644
--- /dev/null
+++ b/System/Win32/Word.hs
@@ -0,0 +1,23 @@
+{- |
+   Module      :  System.Win32.Word
+   Copyright   :  2013 shelarcy
+   License     :  BSD-style
+
+   Maintainer  :  shelarcy@gmail.com
+   Stability   :  Provisional
+   Portability :  Non-portable (Win32 API)
+
+   Windows' unsigned integer types and pointer type.
+-}
+module System.Win32.Word
+  ( WORD, DWORD, PDWORD, LPDWORD
+  , DWORDLONG, DDWORD
+  , DWORD32, DWORD64, DWORD_PTR
+  ) where
+import Data.Word          ( Word64 )
+import Foreign.Ptr        ( Ptr )
+import System.Win32.Types ( WORD, DWORD, LPDWORD, DDWORD,
+                            DWORD32, DWORD64, DWORD_PTR )
+
+type PDWORD = Ptr DWORD
+type DWORDLONG = Word64
diff --git a/Win32.cabal b/Win32.cabal
--- a/Win32.cabal
+++ b/Win32.cabal
@@ -1,25 +1,59 @@
-name:		Win32
-version:	2.2.2.0
-license:	BSD3
-license-file:	LICENSE
-author:		Alastair Reid
-copyright:	Alastair Reid, 1999-2003
-maintainer:	Bryan O'Sullivan <bos@serpentine.com>
+cabal-version:  2.0
+name:           Win32
+version:        2.14.2.2
+license:        BSD3
+license-file:   LICENSE
+author:         Alastair Reid, shelarcy, Tamar Christina
+copyright:      Alastair Reid, 1999-2003; shelarcy, 2012-2013; Tamar Christina, 2016-2020
+maintainer:     Haskell Libraries <libraries@haskell.org>
 bug-reports:    https://github.com/haskell/win32/issues
 homepage:       https://github.com/haskell/win32
-category:	System, Graphics
-synopsis:	A binding to part of the Win32 library
-description:	A binding to part of the Win32 library.
+category:       System, Graphics
+synopsis:       A binding to Windows Win32 API.
+description:    This library contains direct bindings to the Windows Win32 APIs for Haskell.
 build-type:     Simple
-cabal-version:  >=1.6
 extra-source-files:
-	include/diatemp.h include/dumpBMP.h include/ellipse.h include/errors.h
-	include/gettime.h include/Win32Aux.h include/win32debug.h
+    include/diatemp.h include/dumpBMP.h include/ellipse.h include/errors.h
+    include/Win32Aux.h include/win32debug.h include/alignment.h
+extra-doc-files:
+    changelog.md
+tested-with:
+    GHC == 9.2.7,
+    GHC == 9.4.5,
+    GHC == 9.6.7,
+    GHC == 9.8.4,
+    GHC == 9.10.1,
+    GHC == 9.12.1
 
+flag os-string
+  description: Use the new os-string package
+  default: False
+  manual: False
+
 Library
-    build-depends:	base >= 3 && < 5, bytestring
-    ghc-options:    -Wall -fno-warn-name-shadowing
-    cc-options:     -fno-strict-aliasing
+    default-language: Haskell2010
+    default-extensions: ForeignFunctionInterface, CPP
+    if impl(ghc >= 7.1)
+        default-extensions: NondecreasingIndentation
+
+    if !os(windows)
+        -- This package requires Windows to build
+        build-depends: unbuildable<0
+        buildable: False
+
+    build-depends:      base >= 4.5 && < 5
+
+    -- AFPP support
+    if impl(ghc >= 8.0)
+      if flag(os-string)
+        build-depends: filepath >= 1.5.0.0, os-string >= 2.0.0
+      else
+        build-depends: filepath >= 1.4.100.0 && < 1.5.0.0
+
+    -- Black list hsc2hs 0.68.6 which is horribly broken.
+    build-tool-depends: hsc2hs:hsc2hs > 0 && < 0.68.6 || > 0.68.6
+    ghc-options:        -Wall -fno-warn-name-shadowing
+    cc-options:         -fno-strict-aliasing
     exposed-modules:
         Graphics.Win32.GDI
         Graphics.Win32.GDI.Bitmap
@@ -43,13 +77,27 @@
         Graphics.Win32.Misc
         Graphics.Win32.Resource
         Graphics.Win32.Window
+        Graphics.Win32.LayeredWindow
+        Graphics.Win32.GDI.AlphaBlend
+        Graphics.Win32.Window.AnimateWindow
+        Graphics.Win32.Window.HotKey
+        Graphics.Win32.Window.IMM
+        Graphics.Win32.Window.ForegroundWindow
+        Graphics.Win32.Window.PostMessage
+
+        Media.Win32
+
         System.Win32
         System.Win32.DebugApi
         System.Win32.DLL
+        System.Win32.Event
         System.Win32.File
         System.Win32.FileMapping
+        System.Win32.NamedPipes
         System.Win32.Info
+        System.Win32.Path
         System.Win32.Mem
+        System.Win32.MinTTY
         System.Win32.NLS
         System.Win32.Process
         System.Win32.Registry
@@ -57,16 +105,63 @@
         System.Win32.Time
         System.Win32.Console
         System.Win32.Security
+        System.Win32.Semaphore
         System.Win32.Types
         System.Win32.Shell
-    extensions: ForeignFunctionInterface, CPP
-    if impl(ghc >= 7.1)
-        extensions: NondecreasingIndentation
+        System.Win32.Automation
+        System.Win32.Automation.Input
+        System.Win32.Automation.Input.Key
+        System.Win32.Automation.Input.Mouse
+        System.Win32.Console.CtrlHandler
+        System.Win32.Console.HWND
+        System.Win32.Console.Title
+        System.Win32.Encoding
+        System.Win32.Exception.Unsupported
+        System.Win32.HardLink
+        System.Win32.Info.Computer
+        System.Win32.Info.Version
+        System.Win32.String
+        System.Win32.SymbolicLink
+        System.Win32.Thread
+        System.Win32.Utils
+        System.Win32.Word
+
+    -- AFPP support
+    if impl(ghc >= 8.0)
+        exposed-modules:
+            System.Win32.WindowsString.Console
+            System.Win32.WindowsString.Types
+            System.Win32.WindowsString.DebugApi
+            System.Win32.WindowsString.DLL
+            System.Win32.WindowsString.Shell
+            System.Win32.WindowsString.String
+            System.Win32.WindowsString.File
+            System.Win32.WindowsString.Time
+            System.Win32.WindowsString.Info
+            System.Win32.WindowsString.FileMapping
+            System.Win32.WindowsString.HardLink
+            System.Win32.WindowsString.Path
+            System.Win32.WindowsString.SymbolicLink
+            System.Win32.WindowsString.Utils
+
+    other-modules:
+        System.Win32.Console.Internal
+        System.Win32.DebugApi.Internal
+        System.Win32.DLL.Internal
+        System.Win32.File.Internal
+        System.Win32.FileMapping.Internal
+        System.Win32.HardLink.Internal
+        System.Win32.Info.Internal
+        System.Win32.Path.Internal
+        System.Win32.Shell.Internal
+        System.Win32.SymbolicLink.Internal
+        System.Win32.Time.Internal
+
     extra-libraries:
-        "user32", "gdi32", "winmm", "advapi32", "shell32", "shfolder"
-    include-dirs: 	include
-    includes:	"HsWin32.h", "HsGDI.h", "WndProc.h"
-    install-includes:	"HsWin32.h", "HsGDI.h", "WndProc.h"
+        "user32", "gdi32", "winmm", "advapi32", "shell32", "shfolder", "shlwapi", "msimg32", "imm32"
+    ghc-options:      -Wall
+    include-dirs:     include
+    install-includes: "HsWin32.h", "HsGDI.h", "WndProc.h", "windows_cconv.h", "alphablend.h", "wincon_compat.h", "winternl_compat.h", "winuser_compat.h", "winreg_compat.h", "tlhelp32_compat.h", "winnls_compat.h", "winnt_compat.h", "namedpipeapi_compat.h"
     c-sources:
         cbits/HsGDI.c
         cbits/HsWin32.c
@@ -75,7 +170,9 @@
         cbits/dumpBMP.c
         cbits/ellipse.c
         cbits/errors.c
+        cbits/alphablend.c
+    cc-options: -Wall
 
 source-repository head
     type:     git
-    location: git://github.com/haskell/win32
+    location: https://github.com/haskell/win32
diff --git a/cbits/WndProc.c b/cbits/WndProc.c
--- a/cbits/WndProc.c
+++ b/cbits/WndProc.c
@@ -71,7 +71,7 @@
     LRESULT lr;
 
     if (hwnd) {
-	LONG wndprocptr = GetWindowLong(hwnd,GWL_USERDATA);
+	LONG_PTR wndprocptr = GetWindowLongPtr(hwnd,GWLP_USERDATA);
 	if (wndprocptr) {
 	    lr = ((LRESULT (*)(HWND,UINT,WPARAM,LPARAM))(wndprocptr))(hwnd,msg,wParam,lParam);
 #if 0
diff --git a/cbits/alphablend.c b/cbits/alphablend.c
new file mode 100644
--- /dev/null
+++ b/cbits/alphablend.c
@@ -0,0 +1,15 @@
+#include <alphablend.h>
+
+BOOL c_AlphaBlend ( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int hHeightDest
+                  , HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc
+                  , PBLENDFUNCTION pblendFunction)
+{
+    BLENDFUNCTION blendFunction;
+    blendFunction.BlendOp             = pblendFunction->BlendOp;
+    blendFunction.BlendFlags          = pblendFunction->BlendFlags;
+    blendFunction.SourceConstantAlpha = pblendFunction->SourceConstantAlpha;
+    blendFunction.AlphaFormat         = pblendFunction->AlphaFormat;
+    return AlphaBlend ( hdcDest, nXOriginDest, nYOriginDest, nWidthDest, hHeightDest
+                      , hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc
+                      , blendFunction);
+}
diff --git a/cbits/dumpBMP.c b/cbits/dumpBMP.c
--- a/cbits/dumpBMP.c
+++ b/cbits/dumpBMP.c
@@ -38,7 +38,6 @@
 {
     HANDLE      hFile;
     HBITMAP     hTmpBmp, hBmpOld;
-    BOOL        bSuccess;
     BITMAPFILEHEADER    bfh;
     LPBITMAPINFO pbmi;
     PBYTE       pBits;
@@ -48,7 +47,6 @@
 	DWORD 		dwBytesWritten;
 
 
-    bSuccess = TRUE;
 #if 0
     if (ghPal) {
         SelectPalette(hDC, ghPal, FALSE);
@@ -66,7 +64,7 @@
     // if BitCount != 0, color table will be retrieved
     //
     bmi.bmiHeader.biSize = 0x28;              // GDI need this to work
-    bmi.bmiHeader.biBitCount = 0;             // dont get the color table
+    bmi.bmiHeader.biBitCount = 0;             // don't get the color table
     if ((GetDIBits(hDC, hBmp, 0, 0, (LPSTR)NULL, &bmi, DIB_RGB_COLORS)) == 0) {
         fprintf(stderr, "GetDIBits failed!");
         return;
@@ -83,7 +81,7 @@
     }
 
     //
-    // Note: 24 bits per pixel has no color table.  So, we dont have to
+    // Note: 24 bits per pixel has no color table.  So, we don't have to
     // allocate memory for retrieving that.  Otherwise, we do.
     //
     pbmi = &bmi;                                      // assume no color table
@@ -112,11 +110,10 @@
         //
         if ((pbmi = (LPBITMAPINFO)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizBMI )) == NULL) {
             fprintf(stderr, "Failed in Memory Allocation for pbmi!");
-            bSuccess = FALSE;
             goto ErrExit1;
         }
         //
-        // Now that weve a bigger chunk of memory, lets copy the Bitmap
+        // Now that we've a bigger chunk of memory, lets copy the Bitmap
         // info header data over
         //
         pjTmp = (PBYTE)pbmi;
@@ -153,7 +150,6 @@
     //
     if (WriteFile(hFile, (LPCVOID)&bfh, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL) == -1) {
         fprintf(stderr, "Failed in WriteFile!");
-        bSuccess = FALSE;
         goto ErrExit3;
     }
 
@@ -167,12 +163,10 @@
         hBmpOld = SelectObject(hDC, hTmpBmp);
         if ((GetDIBits(hDC, hBmp, 0, pbmi->bmiHeader.biHeight, (LPSTR)pBits, pbmi, DIB_RGB_COLORS))==0){
             fprintf(stderr, "Failed in GetDIBits!");
-            bSuccess = FALSE;
             goto ErrExit4;
         }
     } else {
         fprintf(stderr, "Failed in creating bitmap!");
-        bSuccess = FALSE;
         goto ErrExit3;
     }
 
@@ -181,7 +175,6 @@
     //
     if (WriteFile(hFile, (LPCVOID)pbmi, sizBMI, &dwBytesWritten, NULL) == -1) {
         fprintf(stderr, "Failed in WriteFile!");
-        bSuccess = FALSE;
         goto ErrExit4;
     }
 
@@ -190,7 +183,6 @@
     //
     if (WriteFile(hFile, (LPCVOID)pBits, pbmi->bmiHeader.biSizeImage, &dwBytesWritten, NULL) == -1) {
         fprintf(stderr, "Failed in WriteFile!");
-        bSuccess = FALSE;
         goto ErrExit4;
     }
 
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,246 @@
+# Changelog for [`Win32` package](http://hackage.haskell.org/package/Win32)
+
+## 2.14.2.2 May 2026
+
+* Fix version bounds for older GHC and drop numeric underscore
+
+## 2.14.2.1 June 2025
+
+* Fix compilation of `ReplaceFileW` on some GHC versions [#245](https://github.com/haskell/win32/issues/245)
+
+## 2.14.2.0 May 2025
+
+* Add ReplaceFileW
+* Add support for Windows on Arm
+* Add ReadConsoleInput
+
+## 2.14.1.0 November 2024
+
+* Add getTempFileName
+* Add WindowsString variant for getEnv etc
+* Implement getEnv and getEnvironment
+
+## 2.14.0.0 January 2023
+
+* Add support for named pipes [#220](https://github.com/haskell/win32/pull/220)
+* Ensure that FilePaths don't contain interior NULs wrt [#218](https://github.com/haskell/win32/pull/218)
+* Add support for GetCommandLineW [#218](https://github.com/haskell/win32/pull/221)
+* Support filepath >= 1.5.0.0 and os-string [#226](https://github.com/haskell/win32/pull/226)
+* Remove unused imports [#225](https://github.com/haskell/win32/pull/225)
+
+## 2.13.4.0 October 2022
+
+* Add support for semaphores with `System.Win32.Semaphore` (See #214).
+* Add function `createFile_NoRetry` (see #208)
+* The type signatures for `loadLibrary` and `loadLibraryEx` now refer to
+  `HMODULE` instead of `HINSTANCE` for consistency with the official Win32
+  API documentation. Note that `HMODULE` and `HINSTANCE` are both type synonyms
+  for the same thing, so this only changes the presentation of these functions'
+  type signatures, not their behavior.
+
+## 2.13.3.0 July 2022
+
+* Add AFPP support (see #198)
+
+## 2.13.2.1 July 2022
+
+* Add function `createIcon` (see #194)
+* Add `WindowMessage` value `wM_SETICON` (see #194)
+* Add `WPARAM` values `iCON_SMALL`, `iCON_BIG` (see #194)
+* Add functions `getConsoleScreenBufferInfoEx` and
+  `getCurrentConsoleScreenBufferInfoEx`
+
+## 2.13.2.0 November 2021
+
+* Set maximum string size for getComputerName. (See #190)
+* Update withHandleToHANDLENative to handle duplex and console handles (See #191)
+
+## 2.13.1.0 November 2021
+
+* Fix a bug in which `System.Win32.MinTTY.isMinTTY` would incorrectly return
+  `False` on recent versions of MinTTY. (See #187)
+* Add all flags for CreateToolhelp32Snapshot.  (See #185)
+
+## 2.13.0.0 August 2021
+
+* Fix type of c_SetWindowLongPtr. See #180
+
+## 2.12.0.1 June 2021
+
+* A small fix for WinIO usage. See #177
+
+## 2.12.0.0 March 2021
+
+* Win32 for GHC 9.2.x
+* Add export lists to all modules, hiding numerous internal `c_` bindings.
+* Update the type of `setFileTime` to reflect the fact that the `FILETIME`
+  arguments are in fact `Maybe`s.
+
+## 2.11.1.0 February 2021
+
+* Make `System.Win32.NLS` re-export `CodePage` from `GHC.IO.Encoding.CodePage`
+  in `base` when compiled with `base-4.15` or later.
+
+## 2.11.0.0 January 2021
+
+* Remove function `mapFileBs`.
+
+## 2.10.1.0 October 2020
+
+* Add `System.Win32.Event` module
+* Add function `openEvent`
+* Add function `createEvent`
+* Add function `duplicateHandle`
+* Add function `setEvent`
+* Add function `resetEvent`
+* Add function `pulseEvent`
+* Add function `signalObjectAndWait`
+* Add function `waitForSingleObject`
+* Add function `waitForSingleObjectEx`
+* Add function `waitForMultipleObjects`
+* Add function `waitForMultipleObjectsEx`
+* Add enums `DUPLICATE_CLOSE_SOURCE`, `DUPLICATE_SAME_ACCESS`,
+  `EVENT_ALL_ACCESS`, `EVENT_MODIFY_STATE`, `WAIT_ABANDONED`,
+  `WAIT_IO_COMPLETION`, `WAIT_OBJECT_0`, `WAIT_TIMEOUT` and `WAIT_FAILED`.
+* Add struct `SECURITY_ATTRIBUTES`
+
+## 2.10.0.0 September 2020
+
+* Add function `isWindowVisible`
+* Add function `getLastInputInfo`
+* Add function `getTickCount`
+* Add function `getIdleTime`
+* Add `enumSystemLocalesEx`, `enumSystemLocalesEx'`,
+  `getSystemDefaultLocaleName`, `getUserDefaultLocaleName`, `isValidLocaleName`,
+  `getLocaleInfoEx`, `getTimeFormatEx` and `lCMapStringEx`
+* Add `trySized` - similar to `try` but for API calls that return the required
+  size of the buffer when passed a buffer size of zero.
+* Add `fromDateFormatPciture` and `fromTimeFormatPicture`, to translate from
+  Windows date and time format pictures to format strings used by the `time`
+  package.
+* Renamed fields of `COORD` and `SMALL_RECT` to avoid name clashes. (See #157)
+
+## 2.9.0.0 June 2020
+
+* `setWindowClosure` now returns the old window closure.
+* `defWindowProc` now assumes the data stored in `GWLP\_USERDATA`
+  is the window closure (in line with `setWindowClosure` and
+  the supplied C `genericWndProc`)
+* `defWindowProc` now frees the window closure
+* `getMessage` and `peekMessage` test for -1 to identify the error condition
+* Support creating symbolic links without Administrator privilege (See #147)
+* Support for `winio` the new Windows I/O manager.
+
+## 2.8.5.0 Dec 2019
+
+* Add `getConsoleMode` and `setConsoleMode` (See #137)
+
+## 2.8.4.0 *Oct 2019*
+
+* Added function `getWindowText`
+* Added function `getWindowTextLength`
+
+## 2.8.3.0 *Feb 2019*
+
+* Add `Module32FirstW` and `Module32NextW` (See #121)
+* Add `Virtual[Alloc/Free]Ex` (See #124)
+
+## 2.8.2.0 *Dec 2018*
+
+* Drop use of NegativeLiterals (See #118)
+
+## 2.8.1.0 *Nov 2018*
+
+* Fix broken links (See #116)
+* Remove unused CPP Lower bounds (See #114)
+* GHC 8.8 release
+
+## 2.8.0.0 *May 2018*
+
+* Deprecated `regQueryValueKey`. (See #105, #108)
+* Updated `regQueryValue` signature (See #108)
+* Add `regQueryDefaultValue` (See #108)
+* Add `regGetValue` and `RegTypeRestriction` (See #109)
+* Remove `sYNCHRONIZE` from System.Win32.Process, use System.Win32.File instead. (See #110)
+
+## 2.7.1.0 *April 2018*
+
+* Fixed `MOUSEINPUT` storable instance. (See #106)
+
+## 2.7.0.0 *March 2018*
+
+* Fixed `DWORD_PTR` type (See #99)
+* Add `lockFile` and `unlockFile` (See #103)
+
+## 2.6.2.0 *December 2017*
+
+* Add `setFilePointerEx` (See #94)
+* Add `getConsoleScreenBufferInfo` and `getCurrentConsoleScreenBufferInfo` (See #95)
+
+## 2.6.1.0 *November 2017*
+
+* Add `terminateProcessById` (See #91)
+
+## 2.6.0.0 *September 2017*
+
+* Make cabal error out on compilation on non-Windows OSes. (See #80)
+* Update cabal format to 1.10 and set language
+  default to Haskell2010. (See #81)
+* Use `Maybe` in wrappers for functions with nullable pointer parameters (See #83)
+* Improve cross compilation support. (See #87)
+
+## 2.5.4.1 *April 2017*
+
+* Fixed GetWindowLong on 32-bit Windows
+
+## 2.5.3.0 *March 2017*
+
+* Fix buffer overflow in `regSetValue`. (See #39)
+* Added `getPixel`. (See #37)
+* Drop dependency on `ntdll` because of incorrect import library on x86. (See #79)
+
+## 2.5.2.0 *March 2017*
+
+* Fix constant underflows with (-1) and unsigned numbers.
+* Add `commandLineToArgv`
+
+## 2.5.1.0 *Feb 2017*
+
+* Add `withHandleToHANDLE` (originally found in the `ansi-terminal` library)
+* fixed `PokeTZI` test
+
+## 2.5.0.0 *Jan 2017*
+
+* `failWith` (and the API calls that use it) now throw `IOError`s with proper
+  `IOErrorType`s.
+* Add function `findWindowByName`
+* Fix a bug in the implementation of `poke` for `TIME_ZONE_INFORMATION` which
+  would cause it to be marshalled incorrectly.
+* Add `System.Win32.MinTTY` module for detecting the presence of MinTTY.
+* Add `ULONG` type to `System.Win32.Types`.
+* Add function `failIfNeg` to `System.Win32.Types`, which fails if a negative
+  number is returned. This simulates the behavior of the `NT_SUCCESS` macro.
+* Merged package Win32-extras (See #16)
+* `Graphics.Win32.Misc.messageBox` safely imported now https://github.com/haskell/win32/pull/5
+* Fixed various alignment calls that were incorrect. These would result in an incorrect alignment
+  being returned on certain platforms. (See #66)
+
+## 2.4.0.0 *Nov 2016*
+
+* Add `windows_cconv.h` to the `install-includes` field of `Win32.cabal`,
+  allowing packages that transitively depend on `Win32` to use the
+  `WINDOWS_CCONV` CPP macro (which expands to `stdcall` or `ccall`
+  appropriately depending on the system architecture)
+* Added function `getLongPathName`
+* Added function `getShortPathName`
+* Added function `getUserName`
+* Added file attribute `fILE_ATTRIBUTE_REPARSE_POINT`
+* Added more [`File Access Rights` constants](https://msdn.microsoft.com/en-us/library/windows/desktop/gg258116%28v=vs.85%29.aspx)
+* Added function `getCurrentProcessId`
+* Added function `filepathRelativePathTo`
+* Added function `pathRelativePathTo`
+* Corrected 64 bit types (See #53)
+
+## 2.3.1.1 *May 2016*
+
+* Release for GHC 8.0.1
diff --git a/include/HsWin32.h b/include/HsWin32.h
--- a/include/HsWin32.h
+++ b/include/HsWin32.h
@@ -12,9 +12,8 @@
 # endif
 #endif
 
-INLINE UINT castPtrToUINT(void *p) { return (UINT)p; }
-INLINE void *castUINTToPtr(UINT n) { return (void *)n; }
-INLINE LONG castFunPtrToLONG(void *p) { return (LONG)p; }
+INLINE UINT_PTR castPtrToUINTPtr(void *p) { return (UINT_PTR)p; }
+INLINE void *castUINTPtrToPtr(UINT_PTR n) { return (void *)n; }
 INLINE WORD hIWORD(DWORD w) { return HIWORD(w); }
 INLINE WORD lOWORD(DWORD w) { return LOWORD(w); }
 
diff --git a/include/alignment.h b/include/alignment.h
new file mode 100644
--- /dev/null
+++ b/include/alignment.h
@@ -0,0 +1,3 @@
+#if __GLASGOW_HASKELL__ < 711
+#define hsc_alignment(t ) hsc_printf ( "%lu", (unsigned long)offsetof(struct {char x__; t(y__); }, y__));
+#endif
diff --git a/include/alphablend.h b/include/alphablend.h
new file mode 100644
--- /dev/null
+++ b/include/alphablend.h
@@ -0,0 +1,10 @@
+#ifndef _ALPHABLEND_H
+#define _ALPHABLEND_H
+
+#include <windows.h>
+
+BOOL c_AlphaBlend ( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int hHeightDest
+                  , HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc
+                  , PBLENDFUNCTION pblendFunction);
+
+#endif /* _ALPHABLEND_H */
diff --git a/include/gettime.h b/include/gettime.h
deleted file mode 100644
--- a/include/gettime.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#include <windows.h>
-
-/* Prototype missing from Cygwin B20.1 */
-
-extern DWORD WINAPI timeGetTime();
diff --git a/include/namedpipeapi_compat.h b/include/namedpipeapi_compat.h
new file mode 100644
--- /dev/null
+++ b/include/namedpipeapi_compat.h
@@ -0,0 +1,16 @@
+/* The version of wincon.h provided by the version of MSYS2 included with x86
+ * versions of GHC before GHC 7.10 excludes certain components introduced with
+ * Windows Vista.
+ */
+
+#ifndef NAMEDPIPEAPI_COMPAT_H
+#define NAMEDPIPEAPI_COMPAT_H
+
+#if defined(x86_64_HOST_ARCH) || __GLASGOW_HASKELL__ > 708
+#
+#else
+
+#define PIPE_ACCEPT_REMOTE_CLIENTS 0x0
+#define PIPE_REJECT_REMOTE_CLIENTS 0x8
+#endif /* GHC version check */
+#endif /* NAMEDPIPEAPI_COMPAT_H */
diff --git a/include/tlhelp32_compat.h b/include/tlhelp32_compat.h
new file mode 100644
--- /dev/null
+++ b/include/tlhelp32_compat.h
@@ -0,0 +1,26 @@
+#ifndef TLHELP32_COMPAT_H
+#define TLHELP32_COMPAT_H
+ /*
+ * tlhelp32.h is not included in MinGW, which was shipped with the 32-bit
+ * Windows version of GHC prior to the 7.10.3 release.
+ */
+#if __GLASGOW_HASKELL__ >= 710
+#else
+// Declarations from tlhelp32.h that Win32 requires
+#include <windows.h>
+
+// CreateToolhelp32Snapshot Flags
+// https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot
+
+#define TH32CS_INHERIT      0x80000000
+
+#define TH32CS_SNAPHEAPLIST 0x00000001
+#define TH32CS_SNAPPROCESS  0x00000002
+#define TH32CS_SNAPTHREAD   0x00000004
+#define TH32CS_SNAPMODULE   0x00000008
+#define TH32CS_SNAPMODULE32 0x00000010
+
+#define TH32CS_SNAPALL (TH32CS_SNAPHEAPLIST|TH32CS_SNAPPROCESS|TH32CS_SNAPTHREAD|TH32CS_SNAPMODULE)
+
+#endif
+#endif /* TLHELP32_COMPAT_H */
diff --git a/include/wincon_compat.h b/include/wincon_compat.h
new file mode 100644
--- /dev/null
+++ b/include/wincon_compat.h
@@ -0,0 +1,26 @@
+/* The version of wincon.h provided by the version of MSYS2 included with x86
+ * versions of GHC before GHC 7.10 excludes certain components introduced with
+ * Windows Vista.
+ */
+
+#ifndef WINCON_COMPAT_H
+#define WINCON_COMPAT_H
+
+#if defined(x86_64_HOST_ARCH) || __GLASGOW_HASKELL__ > 708
+#
+#else
+
+typedef struct _CONSOLE_SCREEN_BUFFER_INFOEX {
+  ULONG      cbSize;
+  COORD      dwSize;
+  COORD      dwCursorPosition;
+  WORD       wAttributes;
+  SMALL_RECT srWindow;
+  COORD      dwMaximumWindowSize;
+  WORD       wPopupAttributes;
+  WINBOOL    bFullscreenSupported;
+  COLORREF   ColorTable[16];
+} CONSOLE_SCREEN_BUFFER_INFOEX, *PCONSOLE_SCREEN_BUFFER_INFOEX;
+
+#endif /* GHC version check */
+#endif /* WINCON_COMPAT_H */
diff --git a/include/windows_cconv.h b/include/windows_cconv.h
new file mode 100644
--- /dev/null
+++ b/include/windows_cconv.h
@@ -0,0 +1,13 @@
+#ifndef __WINDOWS_CCONV_H
+#define __WINDOWS_CCONV_H
+
+#if defined(i386_HOST_ARCH)
+# define WINDOWS_CCONV stdcall
+#elif defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH)
+# define WINDOWS_CCONV ccall
+#else
+# error Unknown mingw32 arch
+#endif
+
+#endif
+
diff --git a/include/winnls_compat.h b/include/winnls_compat.h
new file mode 100644
--- /dev/null
+++ b/include/winnls_compat.h
@@ -0,0 +1,105 @@
+/* The version of winnls.h provided by the version of MSYS2 included with
+ * versions of GHC before GHC 7.10 excludes certain components introduced with
+ * Windows Vista.
+ */
+
+#ifndef WINNLS_COMPAT_H
+#define WINNLS_COMPAT_H
+
+#if __GLASGOW_HASKELL__ < 710
+// Locale information constants
+#define LOCALE_IGEOID 0x0000005b
+#define LOCALE_SCONSOLEFALLBACKNAME 0x0000006e
+#define LOCALE_SDURATION 0x0000005d
+#define LOCALE_SENGLISHCOUNTRYNAME 0x00001002
+#define LOCALE_SENGLISHLANGUAGENAME 0x00001001
+#define LOCALE_SISO3166CTRYNAME2 0x00000068
+#define LOCALE_SISO639LANGNAME2 0x00000067
+#define LOCALE_SKEYBOARDSTOINSTALL 0x0000005e
+#define LOCALE_SNAME 0x0000005c
+#define LOCALE_SNAN 0x00000069
+#define LOCALE_SNATIVECOUNTRYNAME 0x00000008
+#define LOCALE_SNEGINFINITY 0x0000006b
+#define LOCALE_SPARENT 0x0000006d
+#define LOCALE_SPOSINFINITY 0x0000006a
+#define LOCALE_SSCRIPTS 0x0000006c
+#define LOCALE_SSHORTESTDAYNAME1 0x00000060
+#define LOCALE_SSHORTESTDAYNAME2 0x00000061
+#define LOCALE_SSHORTESTDAYNAME3 0x00000062
+#define LOCALE_SSHORTESTDAYNAME4 0x00000063
+#define LOCALE_SSHORTESTDAYNAME5 0x00000064
+#define LOCALE_SSHORTESTDAYNAME6 0x00000065
+#define LOCALE_SSHORTESTDAYNAME7 0x00000066
+// Locale map flag constants
+#define LINGUISTIC_IGNORECASE 0x00000010
+#define LINGUISTIC_IGNOREDIACRITIC 0x00000020
+#define NORM_LINGUISTIC_CASING 0x08000000
+// Locale enumeration flag constants
+#define LOCALE_ALL                  0
+#define LOCALE_ALTERNATE_SORTS      0x00000004
+#define LOCALE_REPLACEMENT          0x00000008
+#define LOCALE_SUPPLEMENTAL         0x00000002
+#define LOCALE_WINDOWS              0x00000001
+// Other
+WINBASEAPI WINBOOL WINAPI IsValidLocaleName (LPCWSTR lpLocaleName);
+#endif
+
+#if __GLASGOW_HASKELL__ < 710 && defined(i386_HOST_ARCH)
+// Locale information constants
+#define LOCALE_IDEFAULTMACCODEPAGE 0x00001011
+// Other
+typedef struct _nlsversioninfoex {
+  DWORD dwNLSVersionInfoSize;
+  DWORD dwNLSVersion;
+  DWORD dwDefinedVersion;
+  DWORD dwEffectiveId;
+  GUID  guidCustomVersion;
+} NLSVERSIONINFOEX, *LPNLSVERSIONINFOEX;
+
+WINBASEAPI int WINAPI GetLocaleInfoEx(
+  LPCWSTR lpLocaleName,
+  LCTYPE LCType,
+  LPWSTR lpLCData,
+  int cchData
+);
+
+WINBASEAPI WINBOOL WINAPI GetNLSVersionEx(
+  NLS_FUNCTION function,
+  LPCWSTR lpLocaleName,
+  LPNLSVERSIONINFOEX lpVersionInformation
+);
+
+WINBASEAPI int WINAPI LCMapStringEx(
+  LPCWSTR lpLocaleName,
+  DWORD dwMapFlags,
+  LPCWSTR lpSrcStr,
+  int cchSrc,
+  LPWSTR lpDestStr,
+  int cchDest,
+  LPNLSVERSIONINFO lpVersionInformation,
+  LPVOID lpReserved,
+  LPARAM lParam
+);
+
+
+WINBASEAPI int WINAPI GetTimeFormatEx(
+  LPCWSTR lpLocaleName,
+  DWORD dwFlags,
+  const SYSTEMTIME *lpTime,
+  LPCWSTR lpFormat,
+  LPWSTR lpTimeStr,
+  int cchTime
+);
+
+WINBASEAPI int WINAPI GetSystemDefaultLocaleName(
+  LPWSTR lpLocaleName,
+  int cchLocaleName
+);
+
+WINBASEAPI int WINAPI GetUserDefaultLocaleName(
+  LPWSTR lpLocaleName,
+  int cchLocaleName
+);
+#endif
+
+#endif /* #ifndef WINNLS_COMPAT_H */
diff --git a/include/winnt_compat.h b/include/winnt_compat.h
new file mode 100644
--- /dev/null
+++ b/include/winnt_compat.h
@@ -0,0 +1,13 @@
+/* The version of winnt.h provided by the version of MSYS2 included with
+ * versions of GHC before GHC 7.10 excludes certain components introduced with
+ * Windows Vista.
+ */
+
+#ifndef WINNT_COMPAT_H
+#define WINNT_COMPAT_H
+
+#if __GLASGOW_HASKELL__ < 710 && defined(i386_HOST_ARCH)
+#define LOCALE_NAME_MAX_LENGTH 85
+#endif
+
+#endif /* #ifndef WINNT_COMPAT_H */
diff --git a/include/winreg_compat.h b/include/winreg_compat.h
new file mode 100644
--- /dev/null
+++ b/include/winreg_compat.h
@@ -0,0 +1,32 @@
+#ifndef WINREG_COMPAT_H
+#define WINREG_COMPAT_H
+
+#if defined(x86_64_HOST_ARCH) || __GLASGOW_HASKELL__ > 708
+#
+#else
+#define RRF_RT_REG_NONE 0x00000001
+#define RRF_RT_REG_SZ 0x00000002
+#define RRF_RT_REG_EXPAND_SZ 0x00000004
+#define RRF_RT_REG_BINARY 0x00000008
+#define RRF_RT_REG_DWORD 0x00000010
+#define RRF_RT_REG_MULTI_SZ 0x00000020
+#define RRF_RT_REG_QWORD 0x00000040
+
+#define RRF_RT_DWORD (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD)
+#define RRF_RT_QWORD (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD)
+#define RRF_RT_ANY 0x0000ffff
+
+#define RRF_NOEXPAND 0x10000000
+#define RRF_ZEROONFAILURE 0x20000000
+
+#endif
+
+#ifndef RRF_SUBKEY_WOW6464KEY
+#define RRF_SUBKEY_WOW6464KEY 0x00010000
+#endif
+
+#ifndef RRF_SUBKEY_WOW6432KEY
+#define RRF_SUBKEY_WOW6432KEY 0x00020000
+#endif
+
+#endif /* WINREG_COMPAT_H */
diff --git a/include/winternl_compat.h b/include/winternl_compat.h
new file mode 100644
--- /dev/null
+++ b/include/winternl_compat.h
@@ -0,0 +1,39 @@
+#ifndef WINTERNL_COMPAT_H
+#define WINTERNL_COMPAT_H
+
+/*
+ * winternl.h is not included in MinGW, which was shipped with the 32-bit
+ * Windows version of GHC prior to the 7.10.3 release.
+ */
+#if defined(x86_64_HOST_ARCH) || \
+   __GLASGOW_HASKELL__ >= 711 || \
+   (__GLASGOW_HASKELL__ == 710 && \
+    defined(__GLASGOW_HASKELL_PATCHLEVEL1__) && \
+    __GLASGOW_HASKELL_PATCHLEVEL1__ >= 2)
+# include <winternl.h>
+#else
+// Some declarations from winternl.h that we need in Win32
+# include <windows.h>
+
+typedef enum _OBJECT_INFORMATION_CLASS {
+   ObjectBasicInformation,
+   ObjectNameInformation,
+   ObjectTypeInformation,
+   ObjectAllInformation,
+   ObjectDataInformation
+} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
+
+typedef LONG NTSTATUS, *PNTSTATUS;
+
+typedef struct _UNICODE_STRING {
+  USHORT Length;
+  USHORT MaximumLength;
+  PWSTR  Buffer;
+} UNICODE_STRING, *PUNICODE_STRING;
+
+typedef struct _OBJECT_NAME_INFORMATION {
+  UNICODE_STRING Name;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+#endif
+
+#endif /* WINTERNL_COMPAT_H */
diff --git a/include/winuser_compat.h b/include/winuser_compat.h
new file mode 100644
--- /dev/null
+++ b/include/winuser_compat.h
@@ -0,0 +1,113 @@
+#ifndef WINUSER_COMPAT_H
+#define WINUSER_COMPAT_H
+
+/*
+ * winuser.h is missing some includes in MinGW, which was shipped with the 32-bit
+ * Windows version of GHC prior to the 7.10.3 release.
+ */
+#if defined(x86_64_HOST_ARCH) || \
+   __GLASGOW_HASKELL__ >= 711 || \
+   (__GLASGOW_HASKELL__ == 710 && \
+    defined(__GLASGOW_HASKELL_PATCHLEVEL1__) && \
+    __GLASGOW_HASKELL_PATCHLEVEL1__ >= 2)
+#else
+// Some declarations from winuser.h that we need in Win32
+# include <windows.h>
+
+#define VK_XBUTTON1 0x05
+#define VK_XBUTTON2 0x06
+
+#define VK_BROWSER_BACK 0xA6
+#define VK_BROWSER_FORWARD 0xA7
+#define VK_BROWSER_REFRESH 0xA8
+#define VK_BROWSER_STOP 0xA9
+#define VK_BROWSER_SEARCH 0xAA
+#define VK_BROWSER_FAVORITES 0xAB
+#define VK_BROWSER_HOME 0xAC
+#define VK_VOLUME_MUTE 0xAD
+#define VK_VOLUME_DOWN 0xAE
+#define VK_VOLUME_UP 0xAF
+#define VK_MEDIA_NEXT_TRACK 0xB0
+#define VK_MEDIA_PREV_TRACK 0xB1
+#define VK_MEDIA_STOP 0xB2
+#define VK_MEDIA_PLAY_PAUSE 0xB3
+#define VK_LAUNCH_MAIL 0xB4
+#define VK_LAUNCH_MEDIA_SELECT 0xB5
+#define VK_LAUNCH_APP1 0xB6
+#define VK_LAUNCH_APP2 0xB7
+
+#define VK_OEM_PLUS 0xBB
+#define VK_OEM_COMMA 0xBC
+#define VK_OEM_MINUS 0xBD
+#define VK_OEM_PERIOD 0xBE
+#define VK_OEM_102 0xE2
+
+#define VK_PACKET 0xE7
+
+#define LWA_COLORKEY 0x00000001
+#define LWA_ALPHA 0x00000002
+
+#define ULW_COLORKEY 0x00000001
+#define ULW_ALPHA 0x00000002
+#define ULW_OPAQUE 0x00000004
+#define ULW_EX_NORESIZE 0x00000008
+
+#define AW_HOR_POSITIVE 0x00000001
+#define AW_HOR_NEGATIVE 0x00000002
+#define AW_VER_POSITIVE 0x00000004
+#define AW_VER_NEGATIVE 0x00000008
+#define AW_CENTER 0x00000010
+#define AW_HIDE 0x00010000
+#define AW_ACTIVATE 0x00020000
+#define AW_SLIDE 0x00040000
+#define AW_BLEND 0x00080000
+
+#define INPUT_MOUSE 0
+#define INPUT_KEYBOARD 1
+#define INPUT_HARDWARE 2
+
+#define KEYEVENTF_UNICODE 0x0004
+#define KEYEVENTF_SCANCODE 0x0008
+
+#define MOUSEEVENTF_XDOWN 0x0080
+#define MOUSEEVENTF_XUP 0x0100
+
+#define XBUTTON1 0x0001
+#define XBUTTON2 0x0002
+
+#if __GLASGOW_HASKELL__ < 710
+typedef struct tagMOUSEINPUT {
+    LONG dx;
+    LONG dy;
+    DWORD mouseData;
+    DWORD dwFlags;
+    DWORD time;
+    ULONG_PTR dwExtraInfo;
+} MOUSEINPUT, *PMOUSEINPUT, *LPMOUSEINPUT;
+
+typedef struct tagKEYBDINPUT {
+    WORD wVk;
+    WORD wScan;
+    DWORD dwFlags;
+    DWORD time;
+    ULONG_PTR dwExtraInfo;
+} KEYBDINPUT, *PKEYBDINPUT, *LPKEYBDINPUT;
+
+typedef struct tagHARDWAREINPUT {
+    DWORD uMsg;
+    WORD wParamL;
+    WORD wParamH;
+} HARDWAREINPUT, *PHARDWAREINPUT, *LPHARDWAREINPUT;
+
+typedef struct tagINPUT {
+    DWORD type;
+    union {
+        MOUSEINPUT mi;
+        KEYBDINPUT ki;
+        HARDWAREINPUT hi;
+    } DUMMYUNIONNAME;
+} INPUT, *PINPUT, *LPINPUT;
+#endif
+
+#endif /* GHC Version check */
+#endif /* WINUSER_COMPAT_H */
