Index: /trunk/include/iprt/env.h
===================================================================
--- /trunk/include/iprt/env.h	(revision 95791)
+++ /trunk/include/iprt/env.h	(revision 95792)
@@ -324,4 +324,16 @@
 RTDECL(int) RTEnvUnsetEx(RTENV Env, const char *pszVar);
 
+
+/**
+ * Returns the value of a environment variable from the default
+ * environment block in a heap buffer.
+ *
+ * @returns Pointer to a string containing the value, free it using RTStrFree.
+ *          NULL if the variable was not found or we're out of memory.
+ *
+ * @param   pszVar      The environment variable name (UTF-8).
+ */
+RTDECL(char *) RTEnvDup(const char *pszVar);
+
 /**
  * Duplicates the value of a environment variable if it exists.
Index: /trunk/include/iprt/mangling.h
===================================================================
--- /trunk/include/iprt/mangling.h	(revision 95791)
+++ /trunk/include/iprt/mangling.h	(revision 95792)
@@ -880,4 +880,5 @@
 # define RTEnvCreateChangeRecordEx                      RT_MANGLER(RTEnvCreateChangeRecordEx)
 # define RTEnvDestroy                                   RT_MANGLER(RTEnvDestroy)
+# define RTEnvDup                                       RT_MANGLER(RTEnvDup)
 # define RTEnvDupEx                                     RT_MANGLER(RTEnvDupEx)
 # define RTEnvExist                                     RT_MANGLER(RTEnvExist)
Index: /trunk/src/VBox/Runtime/r3/win/env-win.cpp
===================================================================
--- /trunk/src/VBox/Runtime/r3/win/env-win.cpp	(revision 95791)
+++ /trunk/src/VBox/Runtime/r3/win/env-win.cpp	(revision 95792)
@@ -31,4 +31,7 @@
 #include <iprt/env.h>
 
+#ifdef IPRT_NO_CRT
+# include <iprt/asm.h>
+#endif
 #include <iprt/alloca.h>
 #include <iprt/assert.h>
@@ -38,11 +41,34 @@
 #include <iprt/utf16.h>
 
-#include <stdlib.h>
-#include <errno.h>
+#ifndef IPRT_NO_CRT
+# include <stdlib.h>
+# include <errno.h>
+#endif
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+#ifdef IPRT_NO_CRT
+static uint32_t volatile g_idxGetEnvBufs = 0;
+static char             *g_apszGetEnvBufs[64]; /* leak */
+#endif
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
+
 
 
 RTDECL(bool) RTEnvExistsBad(const char *pszVar)
 {
+#ifndef IPRT_NO_CRT
     return RTEnvGetBad(pszVar) != NULL;
+#else
+    return RTEnvExistsUtf8(pszVar);
+#endif
 }
 
@@ -50,5 +76,9 @@
 RTDECL(bool) RTEnvExist(const char *pszVar)
 {
+#ifndef IPRT_NO_CRT
     return RTEnvExistsBad(pszVar);
+#else
+    return RTEnvExistsUtf8(pszVar);
+#endif
 }
 
@@ -61,5 +91,12 @@
     int rc = RTStrToUtf16(pszVar, &pwszVar);
     AssertRCReturn(rc, false);
+
+#ifndef IPRT_NO_CRT
     bool fRet = _wgetenv(pwszVar) != NULL;
+#else
+    DWORD dwRet = GetEnvironmentVariableW(pwszVar, NULL, 0);
+    bool fRet = dwRet != 0;
+#endif
+
     RTUtf16Free(pwszVar);
     return fRet;
@@ -70,5 +107,22 @@
 {
     AssertReturn(strchr(pszVar, '=') == NULL, NULL);
+#ifndef IPRT_NO_CRT
     return getenv(pszVar);
+#else
+    /*
+     * Query the value into heap buffer which we give a lifetime of 64
+     * RTEnvGetBad calls.
+     */
+    char *pszValue = RTEnvDup(pszVar);
+    if (pszValue)
+    {
+        RTMEM_MAY_LEAK(pszValue); /* Quite possible we'll leak this, but the leak is limited to 64 values. */
+
+        uint32_t idx = ASMAtomicIncU32(&g_idxGetEnvBufs) % RT_ELEMENTS(g_apszGetEnvBufs);
+        char *pszOld = (char *)ASMAtomicXchgPtr((void * volatile *)&g_apszGetEnvBufs[idx], pszValue);
+        RTStrFree(pszOld);
+    }
+    return pszValue;
+#endif
 }
 
@@ -91,22 +145,130 @@
         *pcchActual = 0;
 
+    /*
+     * Convert the name to UTF-16.
+     */
     PRTUTF16 pwszVar;
     int rc = RTStrToUtf16(pszVar, &pwszVar);
     AssertRCReturn(rc, rc);
 
-    /** @todo Consider _wgetenv_s or GetEnvironmentVariableW here to avoid the
-     *        potential race with a concurrent _wputenv/_putenv. */
-    PCRTUTF16 pwszValue = _wgetenv(pwszVar);
+    /*
+     * Query the variable.  First try with a medium sized stack buffer (too
+     * small for your typical PATH, but large enough for most other things).
+     */
+    RTUTF16  wszValue[512];
+    uint32_t cwcValueBuf   = RT_ELEMENTS(wszValue);
+    PRTUTF16 pwszValue     = wszValue;
+    PRTUTF16 pwszValueFree = NULL;
+
+    for (unsigned iTry = 0;; iTry++)
+    {
+        /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW.
+           Note! Assume that the CRT transparently updates the process
+                 environment and that we don't need to use _wgetenv_s here. */
+        SetLastError(NO_ERROR);
+        DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
+        DWORD const dwErr       = GetLastError();
+
+        if (cwcValueRet < cwcValueBuf)
+        {
+            if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
+            {
+                if (cbValue)
+                    rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszValue, cbValue, pcchActual);
+                else
+                    rc = RTUtf16CalcUtf8LenEx(pwszValue, cwcValueRet, pcchActual);
+            }
+            else
+            {
+                Assert(cwcValueRet == 0);
+                Assert(dwErr != NO_ERROR);
+                rc = RTErrConvertFromWin32(dwErr);
+            }
+            break;
+        }
+
+        /*
+         * Insufficient buffer, so increase it.  The first re-try will use the
+         * returned size, further re-tries will max out with a multiple of the
+         * stack buffer till we reaches 32KB chars (128 loops).
+         */
+        Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
+        RTMemTmpFree(pwszValueFree);
+        AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
+
+        cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
+        pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
+        AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
+    }
+
+    RTMemTmpFree(pwszValueFree);
     RTUtf16Free(pwszVar);
-    if (pwszValue)
-    {
-        if (cbValue)
-            rc = RTUtf16ToUtf8Ex(pwszValue, RTSTR_MAX, &pszValue, cbValue, pcchActual);
-        else
-            rc = RTUtf16CalcUtf8LenEx(pwszValue, RTSTR_MAX, pcchActual);
-    }
-    else
-        rc = VERR_ENV_VAR_NOT_FOUND;
-    return rc;
+    return rc;
+}
+
+
+RTDECL(char *) RTEnvDup(const char *pszVar)
+{
+    AssertPtrReturn(pszVar, NULL);
+
+    /*
+     * Convert the name to UTF-16.
+     */
+    PRTUTF16 pwszVar;
+    int rc = RTStrToUtf16(pszVar, &pwszVar);
+    AssertRCReturn(rc, NULL);
+
+    /*
+     * Query the variable.  First try with a medium sized stack buffer (too
+     * small for your typical PATH, but large enough for most other things).
+     */
+    char    *pszRet = NULL;
+    RTUTF16  wszValue[512];
+    uint32_t cwcValueBuf   = RT_ELEMENTS(wszValue);
+    PRTUTF16 pwszValue     = wszValue;
+    PRTUTF16 pwszValueFree = NULL;
+
+    for (unsigned iTry = 0;; iTry++)
+    {
+        /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW.
+           Note! Assume that the CRT transparently updates the process
+                 environment and that we don't need to use _wgetenv_s here. */
+        SetLastError(NO_ERROR);
+        DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
+        DWORD const dwErr       = GetLastError();
+
+        if (cwcValueRet < cwcValueBuf)
+        {
+            if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
+            {
+                rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszRet, 0, NULL);
+                if (RT_FAILURE(rc))
+                    pszRet = NULL;
+            }
+            else
+            {
+                Assert(cwcValueRet == 0);
+                Assert(dwErr != NO_ERROR);
+            }
+            break;
+        }
+
+        /*
+         * Insufficient buffer, so increase it.  The first re-try will use the
+         * returned size, further re-tries will max out with a multiple of the
+         * stack buffer till we reaches 32KB chars (128 loops).
+         */
+        Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
+        RTMemTmpFree(pwszValueFree);
+        AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
+
+        cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
+        pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
+        AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
+    }
+
+    RTMemTmpFree(pwszValueFree);
+    RTUtf16Free(pwszVar);
+    return pszRet;
 }
 
@@ -114,8 +276,12 @@
 RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue)
 {
+#ifndef IPRT_NO_CRT
     /** @todo putenv is a source memory leaks. deal with this on a per system basis. */
     if (!putenv((char *)pszVarEqualValue))
         return 0;
     return RTErrConvertFromErrno(errno);
+#else
+    return RTEnvPutUtf8(pszVarEqualValue);
+#endif
 }
 
@@ -123,5 +289,9 @@
 RTDECL(int) RTEnvPut(const char *pszVarEqualValue)
 {
+#ifndef IPRT_NO_CRT
     return RTEnvPutBad(pszVarEqualValue);
+#else
+    return RTEnvPutUtf8(pszVarEqualValue);
+#endif
 }
 
@@ -133,8 +303,36 @@
     if (RT_SUCCESS(rc))
     {
+#ifndef IPRT_NO_CRT
         if (!_wputenv(pwszVarEqualValue))
             rc = VINF_SUCCESS;
         else
             rc = RTErrConvertFromErrno(errno);
+#else
+        PRTUTF16 pwszValue = RTUtf16Chr(pwszVarEqualValue, '=');
+        if (pwszValue)
+        {
+            *pwszValue++ = '\0';
+
+            SetLastError(*pwszValue ? ERROR_OUTOFMEMORY : ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
+            if (SetEnvironmentVariableW(pwszVarEqualValue, *pwszValue ? pwszValue : NULL))
+                rc = VINF_SUCCESS;
+            else
+            {
+                DWORD dwErr = GetLastError();
+                if (dwErr == ERROR_ENVVAR_NOT_FOUND)
+                {
+                    Assert(!*pwszValue);
+                    rc = VINF_SUCCESS;
+                }
+                else
+                {
+                    Assert(*pwszValue);
+                    rc = RTErrConvertFromWin32(GetLastError());
+                }
+            }
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+#endif
         RTUtf16Free(pwszVarEqualValue);
     }
@@ -146,23 +344,21 @@
 RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue)
 {
+#ifndef IPRT_NO_CRT
     AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME);
-
-    /* make a local copy and feed it to putenv. */
-    const size_t cchVar = strlen(pszVar);
-    const size_t cchValue = strlen(pszValue);
-    char *pszTmp = (char *)alloca(cchVar + cchValue + 2 + !*pszValue);
-    memcpy(pszTmp, pszVar, cchVar);
-    pszTmp[cchVar] = '=';
-    if (*pszValue)
-        memcpy(pszTmp + cchVar + 1, pszValue, cchValue + 1);
+    int rc;
+    if (!RTEnvExist(pszVar))
+        rc = VINF_ENV_VAR_NOT_FOUND;
     else
     {
-        pszTmp[cchVar + 1] = ' '; /* wrong, but putenv will remove it otherwise. */
-        pszTmp[cchVar + 2] = '\0';
-    }
-
-    if (!putenv(pszTmp))
-        return 0;
-    return RTErrConvertFromErrno(errno);
+        errno_t rcErrno = _putenv_s(pszVar, *pszValue ? pszValue : " " /* wrong, but will be treated as unset otherwise */);
+        if (rcErrno == 0)
+            rc = VINF_SUCCESS;
+        else
+            rc = RTErrConvertFromErrno(rcErrno);
+    }
+    return rc;
+#else
+    return RTEnvSetUtf8(pszVar, pszValue);
+#endif
 }
 
@@ -170,5 +366,9 @@
 RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue)
 {
+#ifndef IPRT_NO_CRT
     return RTEnvSetBad(pszVar, pszValue);
+#else
+    return RTEnvSetUtf8(pszVar, pszValue);
+#endif
 }
 
@@ -179,34 +379,29 @@
 int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue)
 {
-    size_t cwcVar;
-    int rc = RTStrCalcUtf16LenEx(pchVar, cchVar, &cwcVar);
+    PRTUTF16 pwszVar;
+    int rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszVar, 0, NULL);
     if (RT_SUCCESS(rc))
     {
-        size_t cwcValue;
-        rc = RTStrCalcUtf16LenEx(pszValue, RTSTR_MAX, &cwcValue);
+        PRTUTF16 pwszValue;
+        rc = RTStrToUtf16(pszValue, &pwszValue);
         if (RT_SUCCESS(rc))
         {
-            PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAlloc((cwcVar + 1 + cwcValue + 1) * sizeof(RTUTF16));
-            if (pwszTmp)
-            {
-                rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszTmp, cwcVar + 1, NULL);
-                if (RT_SUCCESS(rc))
-                {
-                    PRTUTF16 pwszTmpValue = &pwszTmp[cwcVar];
-                    *pwszTmpValue++ = '=';
-                    rc = RTStrToUtf16Ex(pszValue, RTSTR_MAX, &pwszTmpValue, cwcValue + 1, NULL);
-                    if (RT_SUCCESS(rc))
-                    {
-                        if (!_wputenv(pwszTmp))
-                            rc = VINF_SUCCESS;
-                        else
-                            rc = RTErrConvertFromErrno(errno);
-                    }
-                }
-                RTMemTmpFree(pwszTmp);
-            }
-            else
-                rc = VERR_NO_TMP_MEMORY;
-        }
+#ifndef IPRT_NO_CRT
+            errno_t rcErrno = _wputenv_s(pwszVar,
+                                         *pwszValue ? pwszValue : L" " /* wrong, but will be treated as unset otherwise */);
+            if (rcErrno == 0)
+                rc = VINF_SUCCESS;
+            else
+                rc = RTErrConvertFromErrno(rcErrno);
+#else
+            SetLastError(ERROR_OUTOFMEMORY); /* The API did not always set the last error. */
+            if (SetEnvironmentVariableW(pwszVar, pwszValue))
+                rc = VINF_SUCCESS;
+            else
+                rc = RTErrConvertFromWin32(GetLastError());
+#endif
+            RTUtf16Free(pwszValue);
+        }
+        RTUtf16Free(pwszVar);
     }
     return rc;
@@ -224,33 +419,21 @@
 RTDECL(int) RTEnvUnsetBad(const char *pszVar)
 {
+#ifndef IPRT_NO_CRT
     AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
-
-    /*
-     * Check that it exists first.
-     */
+    int rc;
     if (!RTEnvExist(pszVar))
-        return VINF_ENV_VAR_NOT_FOUND;
-
-    /*
-     * Ok, try remove it.
-     */
-#ifdef RT_OS_WINDOWS
-    /* Use putenv(var=) since Windows does not have unsetenv(). */
-    size_t cchVar = strlen(pszVar);
-    char *pszBuf = (char *)alloca(cchVar + 2);
-    memcpy(pszBuf, pszVar, cchVar);
-    pszBuf[cchVar]     = '=';
-    pszBuf[cchVar + 1] = '\0';
-
-    if (!putenv(pszBuf))
-        return VINF_SUCCESS;
-
-#else
-    /* This is the preferred function as putenv() like used above does neither work on Solaris nor on Darwin. */
-    if (!unsetenv((char*)pszVar))
-        return VINF_SUCCESS;
-#endif
-
-    return RTErrConvertFromErrno(errno);
+        rc = VINF_ENV_VAR_NOT_FOUND;
+    else
+    {
+        errno_t rcErrno = _putenv_s(pszVar, NULL);
+        if (rcErrno == 0)
+            rc = VINF_SUCCESS;
+        else
+            rc = RTErrConvertFromErrno(rcErrno);
+    }
+    return rc;
+#else
+    return RTEnvUnsetUtf8(pszVar);
+#endif
 }
 
@@ -258,5 +441,9 @@
 RTDECL(int) RTEnvUnset(const char *pszVar)
 {
+#ifndef IPRT_NO_CRT
     return RTEnvUnsetBad(pszVar);
+#else
+    return RTEnvUnsetUtf8(pszVar);
+#endif
 }
 
@@ -266,25 +453,32 @@
     AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
 
-    size_t cwcVar;
-    int rc = RTStrCalcUtf16LenEx(pszVar, RTSTR_MAX, &cwcVar);
+    PRTUTF16 pwszVar;
+    int rc = RTStrToUtf16(pszVar, &pwszVar);
     if (RT_SUCCESS(rc))
     {
-        PRTUTF16 pwszTmp = (PRTUTF16)RTMemTmpAlloc((cwcVar + 1 + 1) * sizeof(RTUTF16));
-        if (pwszTmp)
-        {
-            rc = RTStrToUtf16Ex(pszVar, RTSTR_MAX, &pwszTmp, cwcVar + 1, NULL);
-            if (RT_SUCCESS(rc))
-            {
-                pwszTmp[cwcVar] = '=';
-                pwszTmp[cwcVar + 1] = '\0';
-                if (!_wputenv(pwszTmp))
-                    rc = VINF_SUCCESS;
-                else
-                    rc = RTErrConvertFromErrno(errno);
-            }
-            RTMemTmpFree(pwszTmp);
-        }
-    }
-    return rc;
-}
-
+#ifndef IPRT_NO_CRT
+        if (_wgetenv(pwszVar))
+        {
+            errno_t rcErrno = _wputenv_s(pwszVar, NULL);
+            if (rcErrno == 0)
+                rc = VINF_SUCCESS;
+            else
+                rc = RTErrConvertFromErrno(rcErrno);
+        }
+        else
+            rc = VINF_ENV_VAR_NOT_FOUND;
+#else
+        SetLastError(ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
+        if (SetEnvironmentVariableW(pwszVar, NULL))
+            rc = VINF_SUCCESS;
+        else
+        {
+            DWORD dwErr = GetLastError();
+            rc = dwErr == ERROR_ENVVAR_NOT_FOUND ? VINF_ENV_VAR_NOT_FOUND : RTErrConvertFromWin32(dwErr);
+        }
+#endif
+        RTUtf16Free(pwszVar);
+    }
+    return rc;
+}
+
Index: /trunk/src/VBox/Runtime/win/RTErrConvertFromWin32.cpp
===================================================================
--- /trunk/src/VBox/Runtime/win/RTErrConvertFromWin32.cpp	(revision 95791)
+++ /trunk/src/VBox/Runtime/win/RTErrConvertFromWin32.cpp	(revision 95792)
@@ -207,4 +207,6 @@
         case ERROR_UNRECOGNIZED_VOLUME:     return VERR_MEDIA_NOT_RECOGNIZED;
         case ERROR_ELEVATION_REQUIRED:      return VERR_PROC_ELEVATION_REQUIRED;
+
+        case ERROR_ENVVAR_NOT_FOUND:        return VERR_ENV_VAR_NOT_FOUND;
 
 
