Index: /trunk/src/VBox/Runtime/common/misc/uri.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/misc/uri.cpp	(revision 57980)
+++ /trunk/src/VBox/Runtime/common/misc/uri.cpp	(revision 57981)
@@ -48,5 +48,5 @@
     foo://example.com:8042/over/there?name=ferret#nose
     \_/   \______________/\_________/ \_________/ \__/
-     |           |            |            |        |
+     |           |             |           |        |
   scheme     authority       path        query   fragment
      |   _____________________|__
@@ -121,6 +121,6 @@
 static char *rtUriPercentDecodeN(const char *pszString, size_t cchString)
 {
-    AssertPtr(pszString);
-    Assert(strlen(pszString) >= cchString);
+    AssertPtrReturn(pszString, NULL);
+    AssertReturn(strlen(pszString) >= cchString, NULL);
 
     /*
@@ -478,5 +478,4 @@
 
 
-
 RTDECL(int) RTUriParse(const char *pszUri, PRTURIPARSED pParsed)
 {
@@ -683,8 +682,20 @@
     if (pszPath)
     {
-        /* Create the percent encoded strings and calculate the necessary uri length. */
+        /* Check if it's an UNC path. Skip any leading slashes. */
+        while (pszPath)
+        {
+            if (   *pszPath != '\\'
+                && *pszPath != '/')
+                break;
+            pszPath++;
+        }
+
+        /* Create the percent encoded strings and calculate the necessary URI length. */
         char *pszPath1 = rtUriPercentEncodeN(pszPath, RTSTR_MAX);
         if (pszPath1)
         {
+            /* Always change DOS slashes to Unix slashes. */
+            RTPathChangeToUnixSlashes(pszPath1, true); /** @todo Flags? */
+
             size_t cbSize = 7 /* file:// */ + strlen(pszPath1) + 1; /* plus zero byte */
             if (pszPath1[0] != '/')
@@ -693,5 +704,5 @@
             if (pszResult)
             {
-                /* Compose the target uri string. */
+                /* Compose the target URI string. */
                 *pszTmp = '\0';
                 RTStrCatP(&pszTmp, &cbSize, "file://");
@@ -703,4 +714,12 @@
         }
     }
+    else
+    {
+        char *pszResTmp;
+        int cchRes = RTStrAPrintf(&pszResTmp, "file://");
+        if (cchRes)
+            pszResult = pszResTmp;
+    }
+
     return pszResult;
 }
@@ -726,5 +745,5 @@
 #endif
 
-    /* Check that this is a file Uri */
+    /* Check that this is a file URI. */
     if (RTStrNICmp(pszUri, RT_STR_TUPLE("file:")) != 0)
         return NULL;
@@ -732,25 +751,98 @@
     RTURIPARSED Parsed;
     int rc = rtUriParse(pszUri, &Parsed);
-    if (RT_SUCCESS(rc) && Parsed.cchPath)
-    {
-        /* Special hack for DOS path like file:///c:/WINDOWS/clock.avi where we
-           have to drop the leading slash that was used to separate the authority
-           from the path. */
-        if (  uFormat == URI_FILE_FORMAT_WIN
-            && Parsed.cchPath >= 3
-            && pszUri[Parsed.offPath] == '/'
-            && pszUri[Parsed.offPath + 2] == ':'
-            && RT_C_IS_ALPHA(pszUri[Parsed.offPath + 1]) )
+    if (RT_SUCCESS(rc))
+    {
+        /* No path detected? Take authority as path then. */
+        if (!Parsed.cchPath)
+        {
+            Parsed.cchPath      = Parsed.cchAuthority;
+            Parsed.offPath      = Parsed.offAuthority;
+            Parsed.cchAuthority = 0;
+        }
+    }
+
+    if (   RT_SUCCESS(rc)
+        && Parsed.cchPath)
+    {
+        const char *pszPathOff = &pszUri[Parsed.offPath];
+        size_t cbResult = 0;
+
+        /* Skip the leading slash if a DOS drive letter (e.g. "C:") is detected right after it. */
+        if (   Parsed.cchPath >= 3
+            && pszPathOff[0]  == '/'        /* Leading slash. */
+            && RT_C_IS_ALPHA(pszPathOff[1]) /* Drive letter. */
+            && pszPathOff[2]  == ':')
         {
             Parsed.offPath++;
             Parsed.cchPath--;
-        }
-
-        char *pszPath = rtUriPercentDecodeN(&pszUri[Parsed.offPath], Parsed.cchPath);
-        if (uFormat == URI_FILE_FORMAT_UNIX)
-            return RTPathChangeToUnixSlashes(pszPath, true);
-        Assert(uFormat == URI_FILE_FORMAT_WIN);
-        return RTPathChangeToDosSlashes(pszPath, true);
-    }
+            pszPathOff++;
+        }
+
+        if (uFormat == URI_FILE_FORMAT_WIN)
+        {
+            /* Authority given? */
+            if (Parsed.cchAuthority)
+            {
+                /* Include authority as part of UNC path. */
+                cbResult += 2; /* UNC slashes "\\". */
+                cbResult += Parsed.cchAuthority;
+            }
+        }
+
+        cbResult += Parsed.cchPath;
+        cbResult += 1; /* Zero termination. */
+
+        /*
+         * Compose string.
+         */
+        int rc = VINF_SUCCESS;
+        char *pszResult;
+
+        do
+        {
+            char *pszTmp = pszResult = RTStrAlloc(cbResult);
+            if (pszTmp)
+            {
+                size_t cbTmp = cbResult;
+
+                if (uFormat == URI_FILE_FORMAT_WIN)
+                {
+                    /* If an authority is given, add the required UNC prefix. */
+                    if (Parsed.cchAuthority)
+                    {
+                        rc = RTStrCatP(&pszTmp, &cbTmp, "\\\\");
+                        if (RT_SUCCESS(rc))
+                            rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offAuthority], Parsed.cchAuthority);
+                    }
+                    else
+                    {
+
+                    }
+                }
+
+                if (RT_SUCCESS(rc))
+                    rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offPath], Parsed.cchPath);
+
+                if (RT_FAILURE(rc))
+                    RTStrFree(pszResult);
+            }
+            else
+                rc = VERR_NO_MEMORY;
+
+        } while (0);
+
+        if (RT_SUCCESS(rc))
+        {
+            AssertPtr(pszResult);
+            Assert(cbResult);
+            char *pszPath = rtUriPercentDecodeN(pszResult, cbResult - 1 /* Minus termination */);
+            RTStrFree(pszResult);
+            if (uFormat == URI_FILE_FORMAT_UNIX)
+                return RTPathChangeToUnixSlashes(pszPath, true);
+            Assert(uFormat == URI_FILE_FORMAT_WIN);
+            return RTPathChangeToDosSlashes(pszPath, true);
+        }
+    }
+
     return NULL;
 }
Index: /trunk/src/VBox/Runtime/testcase/tstRTUri.cpp
===================================================================
--- /trunk/src/VBox/Runtime/testcase/tstRTUri.cpp	(revision 57980)
+++ /trunk/src/VBox/Runtime/testcase/tstRTUri.cpp	(revision 57981)
@@ -36,4 +36,9 @@
 #include <iprt/test.h>
 
+#ifdef _DEBUG
+# ifdef RT_OS_WINDOWS
+#  include <Shlwapi.h> /* For generating the PathCreateFromUrl/UrlCreateFromPath reference on Windows. */
+# endif
+#endif
 
 /*********************************************************************************************************************************
@@ -314,4 +319,14 @@
     },
     {
+        NULL,
+        "file://",
+        URI_FILE_FORMAT_UNIX
+    },
+    {
+        NULL,
+        "file://",
+        URI_FILE_FORMAT_WIN
+    },
+    {
         "/",
         "file:///",
@@ -319,26 +334,109 @@
     },
     {
-        "/C:/over/ <>#%\"{}|^[]`/there",
-        "file:///C:%5Cover%5C%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere",
-        URI_FILE_FORMAT_UNIX
-    },
-    {
-        "\\over\\ <>#%\"{}|^[]`\\there",
-        "file:///over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there",
-        URI_FILE_FORMAT_WIN
-    },
-    {
-        "/",
-        "file:///",
-        URI_FILE_FORMAT_UNIX
-    },
-    {
         "\\",
         "file:///",
         URI_FILE_FORMAT_WIN
     },
+    {
+        "/foo/bar",
+        "file:///foo/bar",
+        URI_FILE_FORMAT_UNIX
+    },
+    {
+        "\\foo\\bar",
+        "file:///foo%5Cbar",
+        URI_FILE_FORMAT_WIN
+    },
+    {
+        "C:/over/ <>#%\"{}|^[]`/there",
+        "file:///C:/over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there",
+        URI_FILE_FORMAT_UNIX
+    },
+    {
+        "\\over\\ <>#%\"{}|^[]`\\there",
+        "file:///over%5C%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere",
+        URI_FILE_FORMAT_WIN
+    },
+    {
+        "/usr/bin/grep",
+        "file:///usr/bin/grep",
+        URI_FILE_FORMAT_UNIX
+    },
+    {
+        "\\usr\\bin\\grep",
+        "file:///usr%5Cbin%5Cgrep",
+        URI_FILE_FORMAT_WIN
+    },
+    {
+        "/unixserver/isos/files.lst",
+        "file:///unixserver/isos/files.lst",
+        URI_FILE_FORMAT_UNIX
+    },
+    {
+        "\\winserver\\isos\\files.lst",
+        "file:///winserver%5Cisos%5Cfiles.lst",
+        URI_FILE_FORMAT_WIN
+    },
+    {
+        "/myserver/isos/files.lst",
+        "file:///myserver/isos/files.lst",
+        URI_FILE_FORMAT_UNIX
+    },
+    {
+        "\\myserver\\isos\\files.lst",
+        "file:///myserver%5Cisos%5Cfiles.lst",
+        URI_FILE_FORMAT_WIN
+    }
 };
 
-
+/**
+ * For reference, taken from output of PathCreateFromUrl/UrlCreateFromPath on Windows:
+ *
+ * #0: Path=C:\over\ <>#%"{}|^[]`\there, URL=file:///C:%5Cover%5C%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere
+ *       PathCreateFromUrl: file:///C:%5Cover%5C%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere -> C:\over\ <>#%"{}|^[]`\there
+ *       UrlCreateFromPath: C:\over\ <>#%"{}|^[]`\there -> file:%2F%2F%2FC:%2Fover%2F%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%2Fthere
+ * #1: Path=/over/ <>#%"{}|^[]`/there, URL=file:///over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there
+ *       PathCreateFromUrl: file:///over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there -> \over\ <>#%"{}|^[]`\there
+ *       UrlCreateFromPath: /over/ <>#%"{}|^[]`/there -> file:%2F%2F%2Fover%2F%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%2Fthere
+ * #2: Path=<NULL>, URL=file://
+ *       PathCreateFromUrl: file:// ->
+ *       UrlCreateFromPath: <NULL> ->
+ * #3: Path=<NULL>, URL=file://
+ *       PathCreateFromUrl: file:// ->
+ *       UrlCreateFromPath: <NULL> ->
+ * #4: Path=/, URL=file:///
+ *       PathCreateFromUrl: file:/// ->
+ *       UrlCreateFromPath: / -> file:%2F%2F%2F
+ * #5: Path=/foo/bar, URL=file:///foo/bar
+ *       PathCreateFromUrl: file:///foo/bar -> \foo\bar
+ *       UrlCreateFromPath: /foo/bar -> file:%2F%2F%2Ffoo%2Fbar
+ * #6: Path=\foo\bar, URL=file:///foo%5Cbar
+ *       PathCreateFromUrl: file:///foo%5Cbar -> \foo\bar
+ *       UrlCreateFromPath: \foo\bar -> file:%2F%2F%2Ffoo%2Fbar
+ * #7: Path=C:/over/ <>#%"{}|^[]`/there, URL=file:///C:/over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there
+ *       PathCreateFromUrl: file:///C:/over/%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60/there -> C:\over\ <>#%"{}|^[]`\there
+ *       UrlCreateFromPath: C:/over/ <>#%"{}|^[]`/there -> file:%2F%2F%2FC:%2Fover%2F%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%2Fthere
+ * #8: Path=\over\ <>#%"{}|^[]`\there, URL=file:///over%5C%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere
+ *       PathCreateFromUrl: file:///over%5C%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%5Cthere -> \over\ <>#%"{}|^[]`\there
+ *       UrlCreateFromPath: \over\ <>#%"{}|^[]`\there -> file:%2F%2F%2Fover%2F%20%3C%3E%23%25%22%7B%7D%7C%5E%5B%5D%60%2Fthere
+ * #9: Path=/usr/bin/grep, URL=file:///usr/bin/grep
+ *       PathCreateFromUrl: file:///usr/bin/grep -> \usr\bin\grep
+ *       UrlCreateFromPath: /usr/bin/grep -> file:%2F%2F%2Fusr%2Fbin%2Fgrep
+ * #10: Path=\usr\bin\grep, URL=file:///usr%5Cbin%5Cgrep
+ *       PathCreateFromUrl: file:///usr%5Cbin%5Cgrep -> \usr\bin\grep
+ *       UrlCreateFromPath: \usr\bin\grep -> file:%2F%2F%2Fusr%2Fbin%2Fgrep
+ * #11: Path=/unixserver/isos/files.lst, URL=file:///unixserver/isos/files.lst
+ *       PathCreateFromUrl: file:///unixserver/isos/files.lst -> \unixserver\isos\files.lst
+ *       UrlCreateFromPath: /unixserver/isos/files.lst -> file:%2F%2F%2Funixserver%2Fisos%2Ffiles.lst
+ * #12: Path=\winserver\isos\files.lst, URL=file:///winserver%5Cisos%5Cfiles.lst
+ *       PathCreateFromUrl: file:///winserver%5Cisos%5Cfiles.lst -> \winserver\isos\files.lst
+ *       UrlCreateFromPath: \winserver\isos\files.lst -> file:%2F%2F%2Fwinserver%2Fisos%2Ffiles.lst
+ * #13: Path=/myserver/isos/files.lst, URL=file:///myserver/isos/files.lst
+ *       PathCreateFromUrl: file:///myserver/isos/files.lst -> \myserver\isos\files.lst
+ *       UrlCreateFromPath: /myserver/isos/files.lst -> file:%2F%2F%2Fmyserver%2Fisos%2Ffiles.lst
+ * #14: Path=\myserver\isos\files.lst, URL=file:///myserver%5Cisos%5Cfiles.lst
+ *       PathCreateFromUrl: file:///myserver%5Cisos%5Cfiles.lst -> \myserver\isos\files.lst
+ *       UrlCreateFromPath: \myserver\isos\files.lst -> file:%2F%2F%2Fmyserver%2Fisos%2Ffiles.lst
+ */
 
 static void tstCreate(size_t idxTest, const char *pszScheme, const char *pszAuthority, const char *pszPath, const char *pszQuery, const char *pszFragment, const char *pszTest)
@@ -363,9 +461,9 @@
     if (pszTest)
     {
-        RTTESTI_CHECK_MSG_RETV(pszResult, ("#%u: Result '%s' != '%s'", idxTest, pszResult, pszTest));
-        RTTESTI_CHECK_MSG(RTStrCmp(pszResult, pszTest) == 0, ("#%u: Result '%s' != '%s'", idxTest, pszResult, pszTest));
+        RTTESTI_CHECK_MSG_RETV(pszResult, ("#%u: Result '%s' != '%s'\n", idxTest, pszResult, pszTest));
+        RTTESTI_CHECK_MSG(RTStrCmp(pszResult, pszTest) == 0, ("#%u: Result '%s' != '%s'\n", idxTest, pszResult, pszTest));
     }
     else
-        RTTESTI_CHECK_MSG(!pszResult, ("#%u: Result '%s' != '%s'", idxTest, pszResult, pszTest));
+        RTTESTI_CHECK_MSG(!pszResult, ("#%u: Result '%s' != '%s'\n", idxTest, pszResult, pszTest));
 
     if (pszResult)
@@ -379,9 +477,9 @@
     if (pszTest)
     {
-        RTTESTI_CHECK_MSG_RETV(pszResult, ("#%u: Result '%s' != '%s'", idxTest, pszResult, pszTest));
-        RTTESTI_CHECK_MSG(RTStrCmp(pszResult, pszTest) == 0, ("#%u: Result '%s' != '%s'", idxTest, pszResult, pszTest));
+        RTTESTI_CHECK_MSG_RETV(pszResult, ("#%u: Result '%s' != '%s'\n", idxTest, pszResult, pszTest));
+        RTTESTI_CHECK_MSG(RTStrCmp(pszResult, pszTest) == 0, ("#%u: Result '%s' != '%s'\n", idxTest, pszResult, pszTest));
     }
     else
-        RTTESTI_CHECK_MSG(!pszResult, ("#%u: Result '%s' != '%s'", idxTest, pszResult, pszTest));
+        RTTESTI_CHECK_MSG(!pszResult, ("#%u: Result '%s' != '%s'\n", idxTest, pszResult, pszTest));
 
     if (pszResult)
@@ -441,12 +539,33 @@
                       g_aTests[i].pszCreated ? g_aTests[i].pszCreated : g_aTests[i].pszUri);
 
+#ifdef _DEBUG
+# ifdef RT_OS_WINDOWS
+    /* To generate the PathCreateFromUrl/UrlCreateFromPath reference on Windows. */
+    for (size_t i = 0; i < RT_ELEMENTS(g_apCreateFileURIs); ++i)
+    {
+        RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "#%u: Path=%s, URL=%s\n", i, g_apCreateFileURIs[i].pcszPath, g_apCreateFileURIs[i].pcszUri);
+        char szPath[255] = { 0 };
+        DWORD dw = 255;
+        PathCreateFromUrl(g_apCreateFileURIs[i].pcszUri, szPath, &dw, NULL);
+        RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "\tPathCreateFromUrl: %s -> %s\n", g_apCreateFileURIs[i].pcszUri, szPath);
+        char szURL[255] = { 0 };
+        dw = 255;
+        UrlCreateFromPath(g_apCreateFileURIs[i].pcszPath, szURL, &dw, NULL);
+        char szURLEsc[255] = { 0 };
+        dw = 255;
+        UrlEscape(szURL, szURLEsc, &dw, URL_ESCAPE_SEGMENT_ONLY);
+        RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "\tUrlCreateFromPath: %s -> %s\n", g_apCreateFileURIs[i].pcszPath, szURLEsc);
+    }
+# endif
+#endif
+
     /* File Uri path */
     RTTestISub("RTUriFilePath");
     for (size_t i = 0; i < RT_ELEMENTS(g_apCreateFileURIs); ++i)
-        tstFilePath(i, g_apCreateFileURIs[i].pcszUri, g_apCreateFileURIs[i].pcszPath, g_apCreateFileURIs[i].uFormat);
+        tstFilePath(i,  g_apCreateFileURIs[i].pcszUri, g_apCreateFileURIs[i].pcszPath, g_apCreateFileURIs[i].uFormat);
 
     /* File Uri creation */
     RTTestISub("RTUriFileCreate");
-    for (size_t i = 0; i < 3; ++i)
+    for (size_t i = 0; i < RT_ELEMENTS(g_apCreateFileURIs); ++i)
         tstFileCreate(i, g_apCreateFileURIs[i].pcszPath, g_apCreateFileURIs[i].pcszUri);
 
