diff --git a/Shelly.hs b/Shelly.hs
--- a/Shelly.hs
+++ b/Shelly.hs
@@ -99,7 +99,7 @@
 import qualified Data.Text.Lazy.IO as TIO
 import qualified Data.Text.Encoding as TE
 import qualified Data.Text.Encoding.Error as TE
-import System.Process( CmdSpec(..), StdStream(CreatePipe), CreateProcess(..), createProcess, waitForProcess, ProcessHandle )
+import System.Process( CmdSpec(..), StdStream(CreatePipe), CreateProcess(..), createProcess, waitForProcess, terminateProcess, ProcessHandle )
 import System.IO.Error (isPermissionError)
 
 import qualified Data.Text.Lazy as LT
@@ -252,23 +252,18 @@
   liftIO (writeIORef stateVar newState)
 
 -- FIXME: find the full path to the exe from PATH
-runCommand :: FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)
-runCommand exe args = do
-  st <- get
-  shellyProcess st $
+runCommand :: State -> FilePath -> [Text] -> IO (Handle, Handle, Handle, ProcessHandle)
+runCommand st exe args = shellyProcess st $
     RawCommand (unpack exe) (map LT.unpack args)
 
-runCommandNoEscape :: FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)
-runCommandNoEscape exe args = do
-  st <- get
-  shellyProcess st $
+runCommandNoEscape :: State -> FilePath -> [Text] -> IO (Handle, Handle, Handle, ProcessHandle)
+runCommandNoEscape st exe args = shellyProcess st $
     ShellCommand $ LT.unpack $ LT.intercalate " " (toTextIgnore exe : args)
 
 
-shellyProcess :: State -> CmdSpec -> Sh (Handle, Handle, Handle, ProcessHandle)
+shellyProcess :: State -> CmdSpec -> IO (Handle, Handle, Handle, ProcessHandle)
 shellyProcess st cmdSpec =  do
-  (Just hin, Just hout, Just herr, pHandle) <- liftIO $
-    createProcess $ CreateProcess {
+  (Just hin, Just hout, Just herr, pHandle) <- createProcess CreateProcess {
         cmdspec = cmdSpec
       , cwd = Just $ unpack $ sDirectory st
       , env = Just $ sEnvironment st
@@ -829,37 +824,46 @@
     when (sPrintCommands state) $ echo cmdString
     trace cmdString
 
-    (inH,outH,errH,procH) <- sRun state exe args
-
-    case mStdin of
-      Just input ->
-        liftIO $ TIO.hPutStr inH input >> hClose inH
-        -- stdin is cleared from state below
-      Nothing -> return ()
+    (ex, errs, outV) <- liftIO $ bracketOnWindowsError (sRun state state exe args)
+      (\(_,_,_,procH) -> (terminateProcess procH))
+      (\(inH,outH,errH,procH) -> do
+        case mStdin of
+          Just input ->
+            TIO.hPutStr inH input >> hClose inH
+            -- stdin is cleared from state below
+          Nothing -> return ()
 
-    errV <- liftIO newEmptyMVar
-    outV <- liftIO newEmptyMVar
+        errV <- newEmptyMVar
+        outV' <- newEmptyMVar
 
-    liftIO_ $ forkIO $ printGetContent errH stderr >>= putMVar errV
-    -- liftIO_ $ forkIO $ getContent errH >>= putMVar errV
-    if sPrintStdout state
-      then
-        liftIO_ $ forkIO $ printFoldHandleLines start cb outH stdout >>= putMVar outV
-      else
-        liftIO_ $ forkIO $ foldHandleLines start cb outH >>= putMVar outV
+        _ <- forkIO $ printGetContent errH stderr >>= putMVar errV
+        -- liftIO_ $ forkIO $ getContent errH >>= putMVar errV
+        _ <- if sPrintStdout state
+          then
+            forkIO $ printFoldHandleLines start cb outH stdout >>= putMVar outV'
+          else
+            forkIO $ foldHandleLines start cb outH >>= putMVar outV'
 
-    errs <- liftIO $ takeMVar errV
-    ex <- liftIO $ waitForProcess procH
+        errs' <- takeMVar errV
+        ex' <- waitForProcess procH
+        return (ex', errs', outV')
+      )
 
     let code = case ex of
                  ExitSuccess -> 0
                  ExitFailure n -> n
-
     modify $ \state' -> state' { sStderr = errs , sCode = code }
-
     liftIO $ case (sErrExit state, ex) of
       (True,  ExitFailure n) -> throwIO $ RunFailed exe args n errs
       _                      -> takeMVar outV
+
+  where -- Windows does not terminate spawned processes, so we must bracket.
+#if !defined(mingw32_HOST_OS)
+    bracketOnWindowsError = bracketOnError
+#else
+    bracketOnWindowsError acquire _ main = acquire >> main
+#endif
+
 
 -- | The output of last external command. See 'run'.
 lastStderr :: Sh Text
diff --git a/Shelly/Base.hs b/Shelly/Base.hs
--- a/Shelly/Base.hs
+++ b/Shelly/Base.hs
@@ -54,18 +54,19 @@
 runSh :: Sh a -> IORef State -> IO a
 runSh = runReaderT . unSh
 
-data State = State   { sCode :: Int
-                     , sStdin :: Maybe Text -- ^ stdin for the command to be run
-                     , sStderr :: Text
-                     , sDirectory :: FilePath
-                     , sPrintStdout :: Bool   -- ^ print stdout of command that is executed
-                     , sPrintCommands :: Bool -- ^ print command that is executed
-                     , sRun :: FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)
-                     , sEnvironment :: [(String, String)]
-                     , sTracing :: Bool
-                     , sTrace :: B.Builder
-                     , sErrExit :: Bool
-                     }
+data State = State 
+   { sCode :: Int -- ^ exit code for command that ran
+   , sStdin :: Maybe Text -- ^ stdin for the command to be run
+   , sStderr :: Text -- ^ stderr for command that ran
+   , sDirectory :: FilePath -- ^ working directory
+   , sPrintStdout :: Bool   -- ^ print stdout of command that is executed
+   , sPrintCommands :: Bool -- ^ print command that is executed
+   , sRun :: State -> FilePath -> [Text] -> IO (Handle, Handle, Handle, ProcessHandle) -- ^ command runner, a different runner is used when escaping, probably better to just hold the escaping flag
+   , sEnvironment :: [(String, String)]
+   , sTracing :: Bool -- ^ should we trace command execution
+   , sTrace :: B.Builder -- ^ the trace of command execution
+   , sErrExit :: Bool -- ^ should we exit immediately on any error
+   }
 
 -- | A monadic-conditional version of the "when" guard.
 whenM :: Monad m => m Bool -> m () -> m ()
diff --git a/shelly.cabal b/shelly.cabal
--- a/shelly.cabal
+++ b/shelly.cabal
@@ -1,6 +1,6 @@
 Name:       shelly
 
-Version:     0.15.3.1
+Version:     0.15.3.2
 Synopsis:    shell-like (systems) programming in Haskell
 
 Description: Shelly provides convenient systems programming in Haskell,
