userenv_main.c 21 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Implementation of userenv.dll
 *
 * Copyright 2006 Mike McCormack for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>

23 24
#include "ntstatus.h"
#define WIN32_NO_STATUS
25 26 27
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
28
#include "winternl.h"
29
#include "winnls.h"
30
#include "sddl.h"
31
#include "objbase.h"
32
#include "userenv.h"
33 34 35 36 37

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL( userenv );

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
static BOOL get_reg_value(WCHAR *env, HKEY hkey, const WCHAR *name, WCHAR *val, DWORD size)
{
    DWORD type, res_size=0;

    if (RegQueryValueExW(hkey, name, 0, &type, NULL, &res_size) != ERROR_SUCCESS)
        return FALSE;

    if (type == REG_SZ)
    {
        if (res_size > size)
            return FALSE;

        return RegQueryValueExW(hkey, name, 0, NULL, (BYTE*)val, &size) == ERROR_SUCCESS;
    }
    else if (type == REG_EXPAND_SZ)
    {
        UNICODE_STRING us_buf, us_expanded;
        WCHAR *buf = HeapAlloc(GetProcessHeap(), 0, res_size);
        if (!buf)
            return FALSE;

        if (RegQueryValueExW(hkey, name, 0, NULL, (BYTE*)buf, &res_size) != ERROR_SUCCESS)
        {
            HeapFree(GetProcessHeap(), 0, buf);
            return FALSE;
        }

        RtlInitUnicodeString(&us_buf, buf);
        us_expanded.Buffer = val;
        us_expanded.MaximumLength = size;
        if (RtlExpandEnvironmentStrings_U(env, &us_buf, &us_expanded, &size) != STATUS_SUCCESS)
        {
            HeapFree(GetProcessHeap(), 0, buf);
            return FALSE;
        }

        HeapFree(GetProcessHeap(), 0, buf);
        return TRUE;
    }

    return FALSE;
}

81 82 83 84 85 86 87 88 89
static void set_env_var( WCHAR **env, const WCHAR *name, const WCHAR *val )
{
    UNICODE_STRING nameW, valW;

    RtlInitUnicodeString( &nameW, name );
    RtlInitUnicodeString( &valW, val );
    RtlSetEnvironmentVariable( env, &nameW, &valW );
}

90 91 92 93 94 95 96 97
static void set_registry_variables(WCHAR **env, HKEY hkey, DWORD type, BOOL set_path)
{
    UNICODE_STRING us_name, us_value;
    WCHAR name[1024], value[1024];
    DWORD ret, index, size;

    for (index = 0; ; index++)
    {
98
        size = ARRAY_SIZE(name);
99 100 101 102
        ret = RegEnumValueW(hkey, index, name, &size, NULL, NULL, NULL, NULL);
        if (ret != ERROR_SUCCESS)
            break;

103 104
        if (!wcsicmp(name, L"SystemRoot")) continue;
        if (!wcsicmp(name, L"SystemDrive")) continue;
105 106 107 108

        RtlInitUnicodeString(&us_name, name);
        us_value.Buffer = value;
        us_value.MaximumLength = sizeof(value);
109
        if (!wcsicmp(name, L"PATH") &&
110 111 112 113 114
                !RtlQueryEnvironmentVariable_U(*env, &us_name, &us_value))
        {
            if (!set_path)
                continue;

115
            size = lstrlenW(value)+1;
116 117 118 119 120
            if (!get_reg_value(*env, hkey, name, value+size,
                        sizeof(value)-size*sizeof(WCHAR)))
                continue;

            value[size] = ';';
121
            set_env_var(env, name, value);
122 123 124
            continue;
        }

125 126
        if (get_reg_value(*env, hkey, name, value, sizeof(value)) && value[0])
            set_env_var(env, name, value);
127 128 129 130 131 132 133 134 135 136 137 138
    }
}

static void set_wow64_environment(WCHAR **env)
{
    WCHAR buf[64];
    HKEY hkey;
    BOOL is_win64 = (sizeof(void *) > sizeof(int));
    BOOL is_wow64;

    IsWow64Process( GetCurrentProcess(), &is_wow64 );

139 140
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion",
            0, KEY_READ|KEY_WOW64_64KEY, &hkey))
141 142 143 144
        return;

    /* set the ProgramFiles variables */

145
    if (get_reg_value(*env, hkey, L"ProgramFilesDir", buf, sizeof(buf)))
146
    {
147 148
        if (is_win64 || is_wow64) set_env_var(env, L"ProgramW6432", buf);
        if (is_win64 || !is_wow64) set_env_var(env, L"ProgramFiles", buf);
149
    }
150
    if (get_reg_value(*env, hkey, L"ProgramFilesDir (x86)", buf, sizeof(buf)))
151
    {
152 153
        if (is_win64 || is_wow64) set_env_var(env, L"ProgramFiles(x86)", buf);
        if (is_wow64) set_env_var(env, L"ProgramFiles", buf);
154 155 156 157
    }

    /* set the CommonProgramFiles variables */

158
    if (get_reg_value(*env, hkey, L"CommonFilesDir", buf, sizeof(buf)))
159
    {
160 161
        if (is_win64 || is_wow64) set_env_var(env, L"CommonProgramW6432", buf);
        if (is_win64 || !is_wow64) set_env_var(env, L"CommonProgramFiles", buf);
162
    }
163
    if (get_reg_value(*env, hkey, L"CommonFilesDir (x86)", buf, sizeof(buf)))
164
    {
165 166
        if (is_win64 || is_wow64) set_env_var(env, L"CommonProgramFiles(x86)", buf);
        if (is_wow64) set_env_var(env, L"CommonProgramFiles", buf);
167 168 169 170 171
    }

    RegCloseKey(hkey);
}

172 173 174
BOOL WINAPI CreateEnvironmentBlock( LPVOID* lpEnvironment,
                     HANDLE hToken, BOOL bInherit )
{
175 176
    static const WCHAR env_keyW[] = L"System\\CurrentControlSet\\Control\\Session Manager\\Environment";
    static const WCHAR profile_keyW[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";
177 178 179 180

    WCHAR *env, buf[UNICODE_STRING_MAX_CHARS], profiles_dir[MAX_PATH];
    DWORD len;
    HKEY hkey, hsubkey;
181 182 183 184 185 186

    TRACE("%p %p %d\n", lpEnvironment, hToken, bInherit );

    if (!lpEnvironment)
        return FALSE;

187 188 189 190 191 192 193 194 195
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, env_keyW, 0, KEY_READ, &hkey) != ERROR_SUCCESS)
        return FALSE;

    if (RtlCreateEnvironment(bInherit, &env) != STATUS_SUCCESS)
    {
        RegCloseKey(hkey);
        return FALSE;
    }

196
    if (!GetEnvironmentVariableW(L"SystemRoot", buf, UNICODE_STRING_MAX_CHARS))
197
    {
198
        if (!get_reg_value(env, hkey, L"SystemRoot", buf, UNICODE_STRING_MAX_CHARS))
199 200 201 202 203
        {
            buf[0] = 0;
            WARN("SystemRoot variable not set\n");
        }
    }
204
    set_env_var(&env, L"SystemRoot", buf);
205

206
    if (!GetEnvironmentVariableW(L"SystemDrive", buf, UNICODE_STRING_MAX_CHARS))
207
    {
208
        if (!get_reg_value(env, hkey, L"SystemDrive", buf, UNICODE_STRING_MAX_CHARS))
209 210 211 212 213
        {
            buf[0] = 0;
            WARN("SystemDrive variable not set\n");
        }
    }
214
    set_env_var(&env, L"SystemDrive", buf);
215 216 217 218

    set_registry_variables(&env, hkey, REG_SZ, !bInherit);
    set_registry_variables(&env, hkey, REG_EXPAND_SZ, !bInherit);

219
    if (RegOpenKeyExW(hkey, L"Environment", 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
220 221 222 223 224 225
    {
        set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
        set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
        RegCloseKey(hsubkey);
    }

226
    if (RegOpenKeyExW(hkey, L"Volatile Environment", 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
227 228 229 230 231 232 233 234 235
    {
        set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
        set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
        RegCloseKey(hsubkey);
    }
    RegCloseKey(hkey);

    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, profile_keyW, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
    {
236
        if (get_reg_value(env, hkey, L"ProfilesDirectory", profiles_dir, MAX_PATH - sizeof(WCHAR)))
237
        {
238
            len = lstrlenW(profiles_dir);
239 240 241 242 243 244
            if (profiles_dir[len-1] != '\\')
            {
                profiles_dir[len++] = '\\';
                profiles_dir[len] = '\0';
            }

245
            if (get_reg_value(env, hkey, L"Public", buf, UNICODE_STRING_MAX_CHARS))
246
                set_env_var(&env, L"ALLUSERSPROFILE", buf);
247 248 249 250 251 252 253 254 255
        }
        else
        {
            profiles_dir[0] = 0;
        }

        RegCloseKey(hkey);
    }

256
    len = ARRAY_SIZE(buf);
257
    if (GetComputerNameW(buf, &len))
258
        set_env_var(&env, L"COMPUTERNAME", buf);
259 260 261 262 263 264 265

    set_wow64_environment(&env);

    if (!hToken)
    {
        if (profiles_dir[0])
        {
266
            len = lstrlenW(profiles_dir);
267
            if (len * sizeof(WCHAR) + sizeof(L"Default") < sizeof(buf))
268
            {
269 270
                wcscpy(buf, profiles_dir);
                wcscat(buf, L"Default");
271
                set_env_var(&env, L"USERPROFILE", buf);
272 273 274
            }
        }

275
        wcscpy(buf, L".Default");
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    }
    else
    {
        TOKEN_USER *token_user = NULL;
        SID_NAME_USE use;
        WCHAR *sidW;
        DWORD size, tmp=0;

        if (GetTokenInformation(hToken, TokenUser, NULL, 0, &len) ||
                GetLastError()!=ERROR_INSUFFICIENT_BUFFER ||
                !(token_user = HeapAlloc(GetProcessHeap(), 0, len)) ||
                !GetTokenInformation(hToken, TokenUser, token_user, len, &len) ||
                !ConvertSidToStringSidW(token_user->User.Sid, &sidW))
        {
            HeapFree(GetProcessHeap(), 0, token_user);
            RtlDestroyEnvironment(env);
            return FALSE;
        }

295
        len = lstrlenW(profiles_dir);
296 297 298 299 300 301
        memcpy(buf, profiles_dir, len*sizeof(WCHAR));

        size = UNICODE_STRING_MAX_CHARS-len;
        if (LookupAccountSidW(NULL, token_user->User.Sid,
                    buf+len, &size, NULL, &tmp, &use))
        {
302 303
            set_env_var(&env, L"USERNAME", buf+len);
            if (len) set_env_var(&env, L"USERPROFILE", buf);
304 305 306
        }

        HeapFree(GetProcessHeap(), 0, token_user);
307
        lstrcpyW(buf, sidW);
308 309 310 311 312
        LocalFree(sidW);
    }

    if (RegOpenKeyExW(HKEY_USERS, buf, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
    {
313
        if (RegOpenKeyExW(hkey, L"Environment", 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
314 315 316 317 318 319
        {
            set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
            set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
            RegCloseKey(hsubkey);
        }

320
        if (RegOpenKeyExW(hkey, L"Volatile Environment", 0, KEY_READ, &hsubkey) == ERROR_SUCCESS)
321 322 323 324 325 326 327 328 329 330
        {
            set_registry_variables(&env, hsubkey, REG_SZ, !bInherit);
            set_registry_variables(&env, hsubkey, REG_EXPAND_SZ, !bInherit);
            RegCloseKey(hsubkey);
        }
        RegCloseKey(hkey);
    }

    *lpEnvironment = env;
    return TRUE;
331 332
}

333 334 335 336 337 338 339 340 341 342 343
BOOL WINAPI DestroyEnvironmentBlock(LPVOID lpEnvironment)
{
    NTSTATUS r;

    TRACE("%p\n", lpEnvironment);
    r = RtlDestroyEnvironment(lpEnvironment);
    if (r == STATUS_SUCCESS)
        return TRUE;
    return FALSE;
}

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
BOOL WINAPI ExpandEnvironmentStringsForUserA( HANDLE hToken, LPCSTR lpSrc,
                     LPSTR lpDest, DWORD dwSize )
{
    BOOL ret;

    TRACE("%p %s %p %d\n", hToken, debugstr_a(lpSrc), lpDest, dwSize);

    ret = ExpandEnvironmentStringsA( lpSrc, lpDest, dwSize );
    TRACE("<- %s\n", debugstr_a(lpDest));
    return ret;
}

BOOL WINAPI ExpandEnvironmentStringsForUserW( HANDLE hToken, LPCWSTR lpSrc,
                     LPWSTR lpDest, DWORD dwSize )
{
    BOOL ret;

    TRACE("%p %s %p %d\n", hToken, debugstr_w(lpSrc), lpDest, dwSize);

    ret = ExpandEnvironmentStringsW( lpSrc, lpDest, dwSize );
    TRACE("<- %s\n", debugstr_w(lpDest));
    return ret;
}

368 369 370 371 372 373 374 375 376 377 378 379
BOOL WINAPI GetDefaultUserProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize )
{
    FIXME("%p %p\n", lpProfileDir, lpcchSize );
    return FALSE;
}

BOOL WINAPI GetDefaultUserProfileDirectoryW( LPWSTR lpProfileDir, LPDWORD lpcchSize )
{
    FIXME("%p %p\n", lpProfileDir, lpcchSize );
    return FALSE;
}

380 381 382
BOOL WINAPI GetUserProfileDirectoryA( HANDLE hToken, LPSTR lpProfileDir,
                     LPDWORD lpcchSize )
{
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    BOOL ret;
    WCHAR *dirW = NULL;

    TRACE( "%p %p %p\n", hToken, lpProfileDir, lpcchSize );

    if (!lpProfileDir || !lpcchSize)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
    if (!(dirW = HeapAlloc( GetProcessHeap(), 0, *lpcchSize * sizeof(WCHAR) )))
        return FALSE;

    if ((ret = GetUserProfileDirectoryW( hToken, dirW, lpcchSize )))
        WideCharToMultiByte( CP_ACP, 0, dirW, *lpcchSize, lpProfileDir, *lpcchSize, NULL, NULL );

    HeapFree( GetProcessHeap(), 0, dirW );
    return ret;
401 402 403 404 405
}

BOOL WINAPI GetUserProfileDirectoryW( HANDLE hToken, LPWSTR lpProfileDir,
                     LPDWORD lpcchSize )
{
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
    TOKEN_USER *t;
    WCHAR *userW = NULL, *dirW = NULL;
    DWORD len, dir_len, domain_len;
    SID_NAME_USE use;
    BOOL ret = FALSE;

    TRACE( "%p %p %p\n", hToken, lpProfileDir, lpcchSize );

    if (!lpcchSize)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    len = 0;
    GetTokenInformation( hToken, TokenUser, NULL, 0, &len );
    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
    if (!(t = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
    if (!GetTokenInformation( hToken, TokenUser, t, len, &len )) goto done;

426
    len = domain_len = 0;
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    LookupAccountSidW( NULL, t->User.Sid, NULL, &len, NULL, &domain_len, NULL );
    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto done;
    if (!(userW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) goto done;
    if (!LookupAccountSidW( NULL, t->User.Sid, userW, &len, NULL, &domain_len, &use )) goto done;

    dir_len = 0;
    GetProfilesDirectoryW( NULL, &dir_len );
    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto done;
    if (!(dirW = HeapAlloc( GetProcessHeap(), 0, (dir_len + 1) * sizeof(WCHAR) ))) goto done;
    if (!GetProfilesDirectoryW( dirW, &dir_len )) goto done;

    len += dir_len + 2;
    if (*lpcchSize < len)
    {
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
        *lpcchSize = len;
        goto done;
    }
445
    lstrcpyW( lpProfileDir, dirW );
446
    lstrcatW( lpProfileDir, L"\\" );
447
    lstrcatW( lpProfileDir, userW );
448 449 450 451
    *lpcchSize = len;
    ret = TRUE;

done:
452
    HeapFree( GetProcessHeap(), 0, t );
453 454 455
    HeapFree( GetProcessHeap(), 0, userW );
    HeapFree( GetProcessHeap(), 0, dirW );
    return ret;
456 457
}

458 459
static const char ProfileListA[] = "Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";

460 461
BOOL WINAPI GetProfilesDirectoryA( LPSTR lpProfilesDir, LPDWORD lpcchSize )
{
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
    static const char ProfilesDirectory[] = "ProfilesDirectory";
    LONG l;
    HKEY key;
    BOOL ret = FALSE;
    DWORD len = 0, expanded_len;
    LPSTR unexpanded_profiles_dir = NULL;

    TRACE("%p %p\n", lpProfilesDir, lpcchSize );

    if (!lpProfilesDir || !lpcchSize)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    l = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ProfileListA, 0, KEY_READ, &key);
    if (l)
    {
        SetLastError(l);
        return FALSE;
    }
    l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL, NULL, &len);
    if (l)
    {
        SetLastError(l);
        goto end;
    }
    unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len);
    if (!unexpanded_profiles_dir)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        goto end;
    }
    l = RegQueryValueExA(key, ProfilesDirectory, NULL, NULL,
                             (BYTE *)unexpanded_profiles_dir, &len);
    if (l)
    {
        SetLastError(l);
        goto end;
    }
    expanded_len = ExpandEnvironmentStringsA(unexpanded_profiles_dir, NULL, 0);
    /* The returned length doesn't include the NULL terminator. */
    if (*lpcchSize < expanded_len - 1)
    {
        *lpcchSize = expanded_len - 1;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        goto end;
    }
    *lpcchSize = expanded_len - 1;
    /* The return value is also the expected length. */
    ret = ExpandEnvironmentStringsA(unexpanded_profiles_dir, lpProfilesDir,
                                    expanded_len) - 1;
end:
    HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir);
    RegCloseKey(key);
    return ret;
518 519 520 521
}

BOOL WINAPI GetProfilesDirectoryW( LPWSTR lpProfilesDir, LPDWORD lpcchSize )
{
522 523 524 525 526 527 528 529
    LONG l;
    HKEY key;
    BOOL ret = FALSE;
    DWORD len = 0, expanded_len;
    LPWSTR unexpanded_profiles_dir = NULL;

    TRACE("%p %p\n", lpProfilesDir, lpcchSize );

530
    if (!lpcchSize)
531 532 533 534 535
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

536 537 538
    l = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                      L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList",
                      0, KEY_READ, &key);
539 540 541 542 543
    if (l)
    {
        SetLastError(l);
        return FALSE;
    }
544
    l = RegQueryValueExW(key, L"ProfilesDirectory", NULL, NULL, NULL, &len);
545 546 547 548 549 550 551 552 553 554 555
    if (l)
    {
        SetLastError(l);
        goto end;
    }
    unexpanded_profiles_dir = HeapAlloc(GetProcessHeap(), 0, len);
    if (!unexpanded_profiles_dir)
    {
        SetLastError(ERROR_OUTOFMEMORY);
        goto end;
    }
556
    l = RegQueryValueExW(key, L"ProfilesDirectory", NULL, NULL,
557 558 559 560 561 562 563 564
                             (BYTE *)unexpanded_profiles_dir, &len);
    if (l)
    {
        SetLastError(l);
        goto end;
    }
    expanded_len = ExpandEnvironmentStringsW(unexpanded_profiles_dir, NULL, 0);
    /* The returned length doesn't include the NULL terminator. */
565
    if (*lpcchSize < expanded_len - 1 || !lpProfilesDir)
566 567 568 569 570 571 572 573 574 575 576 577 578
    {
        *lpcchSize = expanded_len - 1;
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        goto end;
    }
    *lpcchSize = expanded_len - 1;
    /* The return value is also the expected length. */
    ret = ExpandEnvironmentStringsW(unexpanded_profiles_dir, lpProfilesDir,
                                    expanded_len) - 1;
end:
    HeapFree(GetProcessHeap(), 0, unexpanded_profiles_dir);
    RegCloseKey(key);
    return ret;
579 580
}

581 582 583 584 585 586 587 588 589 590 591 592
BOOL WINAPI GetAllUsersProfileDirectoryA( LPSTR lpProfileDir, LPDWORD lpcchSize )
{
    FIXME("%p %p\n", lpProfileDir, lpcchSize);
    return FALSE;
}

BOOL WINAPI GetAllUsersProfileDirectoryW( LPWSTR lpProfileDir, LPDWORD lpcchSize )
{
    FIXME("%p %p\n", lpProfileDir, lpcchSize);
    return FALSE;
}

593
BOOL WINAPI GetProfileType( DWORD *pdwFlags )
594 595
{
    FIXME("%p\n", pdwFlags );
596
    *pdwFlags = 0;
597 598 599
    return TRUE;
}

600 601 602 603 604 605
BOOL WINAPI LoadUserProfileA( HANDLE hToken, LPPROFILEINFOA lpProfileInfo )
{
    FIXME("%p %p\n", hToken, lpProfileInfo );
    lpProfileInfo->hProfile = HKEY_CURRENT_USER;
    return TRUE;
}
606

607 608 609 610 611 612 613
BOOL WINAPI LoadUserProfileW( HANDLE hToken, LPPROFILEINFOW lpProfileInfo )
{
    FIXME("%p %p\n", hToken, lpProfileInfo );
    lpProfileInfo->hProfile = HKEY_CURRENT_USER;
    return TRUE;
}

614 615 616 617 618 619 620 621 622 623 624
BOOL WINAPI RegisterGPNotification( HANDLE event, BOOL machine )
{
    FIXME("%p %d\n", event, machine );
    return TRUE;
}

BOOL WINAPI UnregisterGPNotification( HANDLE event )
{
    FIXME("%p\n", event );
    return TRUE;
}
625 626 627 628 629 630

BOOL WINAPI UnloadUserProfile( HANDLE hToken, HANDLE hProfile )
{
    FIXME("(%p, %p): stub\n", hToken, hProfile);
    return FALSE;
}
631

632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
HANDLE WINAPI EnterCriticalPolicySection(BOOL bMachine)
{
    FIXME("(%x)\n", bMachine);
    SetLastError(ERROR_ACCESS_DENIED);
    return NULL;
}

BOOL WINAPI LeaveCriticalPolicySection(HANDLE hSection)
{
    FIXME("(%p)\n", hSection);
    return TRUE;
}

DWORD WINAPI GetAppliedGPOListW(DWORD dwFlags, LPCWSTR pMachineName, PSID pSidUser, GUID *pGuidExtension,
        PGROUP_POLICY_OBJECTW *ppGPOList)
{
    FIXME("(%x %s %p %s %p)\n", dwFlags, debugstr_w(pMachineName), pSidUser, debugstr_guid(pGuidExtension), ppGPOList);
    return ERROR_ACCESS_DENIED;
}

652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
/******************************************************************************
 *              USERENV.138
 *
 * Create .lnk file
 *
 * PARAMETERS
 *   int     csidl               [in] well-known directory location to create link in
 *   LPCSTR lnk_dir              [in] directory (relative to directory specified by csidl) to create link in
 *   LPCSTR lnk_filename         [in] filename of the link file without .lnk extension
 *   LPCSTR lnk_target           [in] file/directory pointed to by link
 *   LPCSTR lnk_iconfile         [in] link icon resource filename
 *   DWORD   lnk_iconid          [in] link icon resource id in file referred by lnk_iconfile
 *   LPCSTR work_directory       [in] link target's work directory
 *   WORD    hotkey              [in] link hotkey (virtual key id)
 *   DWORD   win_state           [in] initial window size (SW_SHOWMAXIMIZED to start maximized,
 *                                     SW_SHOWMINNOACTIVE to start minimized, everything else is default state)
 *   LPCSTR comment              [in] comment - link's comment
 *   LPCSTR loc_filename_resfile [in] resource file which holds localized filename for this link file
 *   DWORD   loc_filename_resid  [in] resource id for this link file's localized filename
 *
 * RETURNS
 *    TRUE:  Link file was successfully created
 *    FALSE: Link file was not created
 */
BOOL WINAPI USERENV_138( int csidl, LPCSTR lnk_dir, LPCSTR lnk_filename,
            LPCSTR lnk_target, LPCSTR lnk_iconfile, DWORD lnk_iconid,
            LPCSTR work_directory, WORD hotkey, DWORD win_state, LPCSTR comment,
            LPCSTR loc_filename_resfile, DWORD loc_filename_resid)
{
    FIXME("(%d,%s,%s,%s,%s,%d,%s,0x%x,%d,%s,%s,%d) - stub\n", csidl, debugstr_a(lnk_dir),
            debugstr_a(lnk_filename), debugstr_a(lnk_target), debugstr_a(lnk_iconfile),
            lnk_iconid, debugstr_a(work_directory), hotkey, win_state,
            debugstr_a(comment), debugstr_a(loc_filename_resfile), loc_filename_resid );

    return FALSE;
}