diff --git a/src/modules/system/nt.c b/src/modules/system/nt.c
index b4f775555f9a27efeec4b8efc71fbb0699eb22b1..6d719caeec4150b9acbb79e4000d2aa76e402528 100644
--- a/src/modules/system/nt.c
+++ b/src/modules/system/nt.c
@@ -2695,7 +2695,7 @@ static void f_NetWkstaUserEnum(INT32 args)
   }
 }
 
-/*! @decl string normalize_path(string path)
+/*! @decl string(8bit) normalize_path(string(8bit) path)
  *!
  *!   Normalize an existing Windows file system path.
  *!
@@ -2748,66 +2748,42 @@ static void f_normalize_path(INT32 args)
 {
   struct pike_string *str;
   struct string_builder res;
-  char *file;
+  size_t file_len;
+  p_wchar1 *pname, *file;
+  char *utf8;
   ONERROR res_uwp, file_uwp;
   DWORD ret;
 
   get_all_args("normalize_path", args, "%S", &str);
 
-  init_string_builder(&res, 0);
-  SET_ONERROR (res_uwp, free_string_builder, &res);
-
-#ifdef WANT_GETLONGPATHNAME_WRAPPER
-
-  file = str->str;
-  if (file[str->len - 1] == '/' || file[str->len - 1] == '\\') {
-    /* Add a '.' if the path ends with slash(es). This works just as
-     * well as removing all trailing slashes (even for files), but it
-     * has the benefit that we don't get the cwd when the input is
-     * e.g. "c:\\". */
-    file = xalloc(str->len + 2);
-    SET_ONERROR (file_uwp, free, file);
-    memcpy(file, str->str, str->len);
-    file[str->len] = '.';
-    file[str->len + 1] = 0;
-  }
-
-  ret = str->len;    /* Guess that the result will have the same length... */
-  do{
-    string_builder_allocate(&res, ret, 0);
-    /* NOTE: Use the emulated GetLongPathName(), since it normalizes all
-     * components of the path.
-     */
-    ret = Emulate_GetLongPathName(file, res.s->str, res.malloced);
-    if (!ret) {
-      unsigned long err = GetLastError();
-      set_errno_from_win32_error (err);
-      throw_nt_error("normalize_path", err);
-    }
-  } while (ret > (size_t) res.malloced);
-
-  if (file != str->str) {
-    free (file);
-    UNSET_ONERROR (file_uwp);
-  }
-
-#else  /* !WANT_GETLONGPATHNAME_WRAPPER */
+  pname = pike_dwim_utf8_to_utf16(str->str);
+  if (!pname) SIMPLE_OUT_OF_MEMORY_ERROR("normalize_path", str->len * 2);
+  SET_ONERROR(file_uwp, free, pname);
 
-  /* Haven't got Emulate_GetLongPathName. We essentially do what it
+  /* Haven't got Emulate_GetLongPathNameW(). We essentially do what it
    * does. This appears to be the only reliable way to normalize a
-   * path. (GetLongPathName doesn't always correct upper/lower case
+   * path. (GetLongPathNameW() doesn't always correct upper/lower case
    * differences, and opening the file to use e.g.
-   * GetFinalPathNameByHandle on it might not work if it's already
-   * opened for exclusive access.) */
+   * GetFinalPathNameByHandle() on it might not work if it's already
+   * opened for exclusive access.)
+   */
 
   /* First convert to an absolute path. */
-  file = xalloc (MAX_PATH);
+  file_len = MAX_PATH;
+  do {
+    file = xalloc (file_len * sizeof(p_wchar1));
+    ret = GetFullPathNameW (pname, file_len, file, NULL);
+    if (ret >= file_len) {
+      /* Buffer to small. Reallocate. */
+      free(file);
+      file_len = ret+1;
+      continue;
+    }
+    break;
+  } while(1);
+  CALL_AND_UNSET_ONERROR(file_uwp);
   SET_ONERROR (file_uwp, free, file);
-  ret = GetFullPathName (str->str, MAX_PATH, file, NULL);
-  if (ret > MAX_PATH) {
-    errno = ENAMETOOLONG;
-    throw_nt_error ("normalize_path", ERROR_BUFFER_OVERFLOW);
-  }
+
   if (!ret) {
     unsigned int err = GetLastError();
     set_errno_from_win32_error (err);
@@ -2816,9 +2792,6 @@ static void f_normalize_path(INT32 args)
 
   {
     LPSHELLFOLDER isf;
-    LPWSTR wfile;
-    ONERROR wfile_uwp;
-    size_t l;
     PIDLIST_ABSOLUTE idl;
     HRESULT hres;
 
@@ -2826,14 +2799,7 @@ static void f_normalize_path(INT32 args)
       /* Use a nondescript error code. */
       throw_nt_error ("normalize_path", errno = ERROR_INVALID_DATA);
 
-    l = strlen (file);
-    wfile = malloc ((l + 1) * 2);
-    if (!wfile) SIMPLE_OUT_OF_MEMORY_ERROR ("normalize_path", (l + 1) * 2);
-    SET_ONERROR (wfile_uwp, free, wfile);
-    wfile[l] = 0;
-    while (l--) wfile[l] = (unsigned char) file[l];
-
-    hres = isf->lpVtbl->ParseDisplayName (isf, NULL, NULL, wfile,
+    hres = isf->lpVtbl->ParseDisplayName (isf, NULL, NULL, file,
 					  NULL, &idl, NULL);
     if (hres != S_OK) {
       errno = (HRESULT_FACILITY (hres) == FACILITY_WIN32 ?
@@ -2843,29 +2809,36 @@ static void f_normalize_path(INT32 args)
       throw_nt_error ("normalize_path", errno);
     }
 
-    /* FIXME: Detect and handle windows unicode mode. */
-    if (!SHGetPathFromIDList (idl, file)) {
+    /* NB: Ensure that the buffer isn't likely to be overrun
+     *     by the call to SHGetPathFromIDListW() below.
+     */
+    file_len = (wcslen(file) + 1) * 2;
+    if (file_len < MAX_PATH) file_len = MAX_PATH;
+    init_string_builder_alloc(&res, file_len, 1);
+    SET_ONERROR (res_uwp, free_string_builder, &res);
+
+    if (!SHGetPathFromIDListW(idl, STR1(res.s))) {
+      /* FIXME: Use SHGetPathFromIDListExW() (Vista/2008 and later),
+       *        to ensure that we don't overrun the buffer.
+       *        It however doesn't seem to be documented how
+       *        to detect whether it fails due to a too small
+       *        buffer or for some other reason, so we couldn't
+       *        use it to grow the res buffer and retry.
+       */
       CoTaskMemFree (idl);
       throw_nt_error ("normalize_path", errno = ERROR_INVALID_DATA);
     }
-    ret = strlen (file);
+    res.s->len = wcslen(STR1(res.s));
 
     CoTaskMemFree (idl);
-    free (wfile);
-    UNSET_ONERROR (wfile_uwp);
   }
 
-  string_builder_strcat (&res, file);
-
-  free (file);
-  UNSET_ONERROR (file_uwp);
-
-#endif	/* !WANT_GETLONGPATHNAME_WRAPPER */
+  file = STR1(res.s);
 
   /* Remove trailing slashes, except after a drive letter. */
   {
-    ptrdiff_t l = (ptrdiff_t) ret - 1;
-    file = res.s->str;
+    ptrdiff_t l = (ptrdiff_t) res.s->len-1;
+
     if(l >= 0 && file[l]=='\\')
     {
       do l--;
@@ -2877,19 +2850,28 @@ static void f_normalize_path(INT32 args)
   }
 
   /* Convert host and share in an UNC path to lowercase since Windows
-   * Shell doesn't do that consistently. */
+   * Shell doesn't do that consistently.
+   */
   if (file[0] == '\\' && file[1] == '\\') {
     size_t i;
     for (i = 2; file[i] && file[i] != '\\'; i++)
-      file[i] = tolower (file[i]);
+      if (file[i] < 256)
+	file[i] = tolower (file[i]);
     if (file[i] == '\\')
       for (i++; file[i] && file[i] != '\\'; i++)
-	file[i] = tolower (file[i]);
+	if (file[i] < 256)
+	  file[i] = tolower (file[i]);
   }
 
   pop_n_elems(args);
-  push_string(finish_string_builder(&res));
-  UNSET_ONERROR (res_uwp);
+
+  utf8 = pike_utf16_to_utf8(file);
+  if (!utf8) SIMPLE_OUT_OF_MEMORY_ERROR("normalize_path", res.s->len * 2);
+  CALL_AND_UNSET_ONERROR (res_uwp);
+  CALL_AND_UNSET_ONERROR (file_uwp);
+
+  push_text(utf8);
+  free(utf8);
 }
 
 /*! @decl int GetFileAttributes(string filename)