|  | /* | 
|  | * Advpack main | 
|  | * | 
|  | * Copyright 2004 Huw D M Davies | 
|  | * Copyright 2005 Sami Aario | 
|  | * | 
|  | * 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> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winuser.h" | 
|  | #include "winreg.h" | 
|  | #include "winternl.h" | 
|  | #include "winnls.h" | 
|  | #include "setupapi.h" | 
|  | #include "advpub.h" | 
|  | #include "wine/unicode.h" | 
|  | #include "wine/debug.h" | 
|  | #include "advpack_private.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(advpack); | 
|  |  | 
|  | typedef HRESULT (WINAPI *DLLREGISTER) (void); | 
|  |  | 
|  | #define MAX_FIELD_LENGTH    512 | 
|  | #define PREFIX_LEN          5 | 
|  |  | 
|  | /* registry path of the Installed Components key for per-user stubs */ | 
|  | static const WCHAR setup_key[] = { | 
|  | 'S','O','F','T','W','A','R','E','\\', | 
|  | 'M','i','c','r','o','s','o','f','t','\\', | 
|  | 'A','c','t','i','v','e',' ','S','e','t','u','p','\\', | 
|  | 'I','n','s','t','a','l','l','e','d',' ', | 
|  | 'C','o','m','p','o','n','e','n','t','s',0 | 
|  | }; | 
|  |  | 
|  | /* Strip single quotes from a token - note size includes NULL */ | 
|  | static void strip_quotes(WCHAR *buffer, DWORD *size) | 
|  | { | 
|  | if (buffer[0] == '\'' && (*size > 1) && buffer[*size-2]=='\'') | 
|  | { | 
|  | *size -= 2; | 
|  | buffer[*size] = 0x00; | 
|  | memmove(buffer, buffer + 1, *size * sizeof(WCHAR)); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* parses the destination directory parameters from pszSection | 
|  | * the parameters are of the form: root,key,value,unknown,fallback | 
|  | * we first read the reg value root\\key\\value and if that fails, | 
|  | * use fallback as the destination directory | 
|  | */ | 
|  | static void get_dest_dir(HINF hInf, PCWSTR pszSection, PWSTR pszBuffer, DWORD dwSize) | 
|  | { | 
|  | INFCONTEXT context; | 
|  | WCHAR key[MAX_PATH + 2], value[MAX_PATH + 2]; | 
|  | WCHAR prefix[PREFIX_LEN + 2]; | 
|  | HKEY root, subkey = 0; | 
|  | DWORD size; | 
|  |  | 
|  | static const WCHAR hklm[] = {'H','K','L','M',0}; | 
|  | static const WCHAR hkcu[] = {'H','K','C','U',0}; | 
|  |  | 
|  | /* load the destination parameters */ | 
|  | SetupFindFirstLineW(hInf, pszSection, NULL, &context); | 
|  | SetupGetStringFieldW(&context, 1, prefix, PREFIX_LEN + 2, &size); | 
|  | strip_quotes(prefix, &size); | 
|  | SetupGetStringFieldW(&context, 2, key, MAX_PATH + 2, &size); | 
|  | strip_quotes(key, &size); | 
|  | SetupGetStringFieldW(&context, 3, value, MAX_PATH + 2, &size); | 
|  | strip_quotes(value, &size); | 
|  |  | 
|  | if (!lstrcmpW(prefix, hklm)) | 
|  | root = HKEY_LOCAL_MACHINE; | 
|  | else if (!lstrcmpW(prefix, hkcu)) | 
|  | root = HKEY_CURRENT_USER; | 
|  | else | 
|  | root = NULL; | 
|  |  | 
|  | size = dwSize * sizeof(WCHAR); | 
|  |  | 
|  | /* fallback to the default destination dir if reg fails */ | 
|  | if (RegOpenKeyW(root, key, &subkey) || | 
|  | RegQueryValueExW(subkey, value, NULL, NULL, (LPBYTE)pszBuffer, &size)) | 
|  | { | 
|  | SetupGetStringFieldW(&context, 5, pszBuffer, dwSize, &size); | 
|  | strip_quotes(pszBuffer, &size); | 
|  | } | 
|  |  | 
|  | if (subkey) RegCloseKey(subkey); | 
|  | } | 
|  |  | 
|  | /* loads the LDIDs specified in the install section of an INF */ | 
|  | void set_ldids(HINF hInf, LPCWSTR pszInstallSection, LPCWSTR pszWorkingDir) | 
|  | { | 
|  | WCHAR field[MAX_FIELD_LENGTH]; | 
|  | WCHAR line[MAX_FIELD_LENGTH]; | 
|  | WCHAR dest[MAX_PATH]; | 
|  | INFCONTEXT context; | 
|  | DWORD size; | 
|  | int ldid; | 
|  |  | 
|  | static const WCHAR source_dir[] = {'S','o','u','r','c','e','D','i','r',0}; | 
|  |  | 
|  | static const WCHAR custDestW[] = { | 
|  | 'C','u','s','t','o','m','D','e','s','t','i','n','a','t','i','o','n',0 | 
|  | }; | 
|  |  | 
|  | if (!SetupGetLineTextW(NULL, hInf, pszInstallSection, custDestW, | 
|  | field, MAX_FIELD_LENGTH, &size)) | 
|  | return; | 
|  |  | 
|  | if (!SetupFindFirstLineW(hInf, field, NULL, &context)) | 
|  | return; | 
|  |  | 
|  | do | 
|  | { | 
|  | LPWSTR value, ptr, key, key_copy = NULL; | 
|  | DWORD flags = 0; | 
|  |  | 
|  | SetupGetLineTextW(&context, NULL, NULL, NULL, | 
|  | line, MAX_FIELD_LENGTH, &size); | 
|  |  | 
|  | /* SetupGetLineTextW returns the value if there is only one key, but | 
|  | * returns the whole line if there is more than one key | 
|  | */ | 
|  | if (!(value = strchrW(line, '='))) | 
|  | { | 
|  | SetupGetStringFieldW(&context, 0, NULL, 0, &size); | 
|  | key = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); | 
|  | key_copy = key; | 
|  | SetupGetStringFieldW(&context, 0, key, size, &size); | 
|  | value = line; | 
|  | } | 
|  | else | 
|  | { | 
|  | key = line; | 
|  | *(value++) = '\0'; | 
|  | } | 
|  |  | 
|  | /* remove leading whitespace from the value */ | 
|  | while (*value == ' ') | 
|  | value++; | 
|  |  | 
|  | /* Extract the flags */ | 
|  | ptr = strchrW(value, ','); | 
|  | if (ptr) { | 
|  | *ptr = '\0'; | 
|  | flags = atolW(ptr+1); | 
|  | } | 
|  |  | 
|  | /* set dest to pszWorkingDir if key is SourceDir */ | 
|  | if (pszWorkingDir && !lstrcmpiW(value, source_dir)) | 
|  | lstrcpynW(dest, pszWorkingDir, MAX_PATH); | 
|  | else | 
|  | get_dest_dir(hInf, value, dest, MAX_PATH); | 
|  |  | 
|  | /* If prompting required, provide dialog to request path */ | 
|  | if (flags & 0x04) | 
|  | FIXME("Need to support changing paths - default will be used\n"); | 
|  |  | 
|  | /* set all ldids to dest */ | 
|  | while ((ptr = get_parameter(&key, ','))) | 
|  | { | 
|  | ldid = atolW(ptr); | 
|  | SetupSetDirectoryIdW(hInf, ldid, dest); | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, key_copy); | 
|  | } while (SetupFindNextLine(&context, &context)); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           CloseINFEngine (ADVPACK.@) | 
|  | * | 
|  | * Closes a handle to an INF file opened with OpenINFEngine. | 
|  | * | 
|  | * PARAMS | 
|  | *   hInf [I] Handle to the INF file to close. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: E_FAIL. | 
|  | */ | 
|  | HRESULT WINAPI CloseINFEngine(HINF hInf) | 
|  | { | 
|  | TRACE("(%p)\n", hInf); | 
|  |  | 
|  | if (!hInf) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | SetupCloseInfFile(hInf); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           DllMain (ADVPACK.@) | 
|  | */ | 
|  | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | 
|  | { | 
|  | TRACE("(%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); | 
|  |  | 
|  | if (fdwReason == DLL_PROCESS_ATTACH) | 
|  | DisableThreadLibraryCalls(hinstDLL); | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *              IsNTAdmin	(ADVPACK.@) | 
|  | * | 
|  | * Checks if the user has admin privileges. | 
|  | * | 
|  | * PARAMS | 
|  | *   reserved  [I] Reserved.  Must be 0. | 
|  | *   pReserved [I] Reserved.  Must be NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *   TRUE if user has admin rights, FALSE otherwise. | 
|  | */ | 
|  | BOOL WINAPI IsNTAdmin(DWORD reserved, LPDWORD pReserved) | 
|  | { | 
|  | SID_IDENTIFIER_AUTHORITY SidAuthority = {SECURITY_NT_AUTHORITY}; | 
|  | PTOKEN_GROUPS pTokenGroups; | 
|  | BOOL bSidFound = FALSE; | 
|  | DWORD dwSize, i; | 
|  | HANDLE hToken; | 
|  | PSID pSid; | 
|  |  | 
|  | TRACE("(%d, %p)\n", reserved, pReserved); | 
|  |  | 
|  | if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) | 
|  | return FALSE; | 
|  |  | 
|  | if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize)) | 
|  | { | 
|  | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | 
|  | { | 
|  | CloseHandle(hToken); | 
|  | return FALSE; | 
|  | } | 
|  | } | 
|  |  | 
|  | pTokenGroups = HeapAlloc(GetProcessHeap(), 0, dwSize); | 
|  | if (!pTokenGroups) | 
|  | { | 
|  | CloseHandle(hToken); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | if (!GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwSize, &dwSize)) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, pTokenGroups); | 
|  | CloseHandle(hToken); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | CloseHandle(hToken); | 
|  |  | 
|  | if (!AllocateAndInitializeSid(&SidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, | 
|  | DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid)) | 
|  | { | 
|  | HeapFree(GetProcessHeap(), 0, pTokenGroups); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < pTokenGroups->GroupCount; i++) | 
|  | { | 
|  | if (EqualSid(pSid, pTokenGroups->Groups[i].Sid)) | 
|  | { | 
|  | bSidFound = TRUE; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, pTokenGroups); | 
|  | FreeSid(pSid); | 
|  |  | 
|  | return bSidFound; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             NeedRebootInit  (ADVPACK.@) | 
|  | * | 
|  | * Sets up conditions for reboot checking. | 
|  | * | 
|  | * RETURNS | 
|  | *   Value required by NeedReboot. | 
|  | */ | 
|  | DWORD WINAPI NeedRebootInit(VOID) | 
|  | { | 
|  | FIXME("(VOID): stub\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             NeedReboot      (ADVPACK.@) | 
|  | * | 
|  | * Determines whether a reboot is required. | 
|  | * | 
|  | * PARAMS | 
|  | *   dwRebootCheck [I] Value from NeedRebootInit. | 
|  | * | 
|  | * RETURNS | 
|  | *   TRUE if a reboot is needed, FALSE otherwise. | 
|  | * | 
|  | * BUGS | 
|  | *   Unimplemented. | 
|  | */ | 
|  | BOOL WINAPI NeedReboot(DWORD dwRebootCheck) | 
|  | { | 
|  | FIXME("(%d): stub\n", dwRebootCheck); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             OpenINFEngineA   (ADVPACK.@) | 
|  | * | 
|  | * See OpenINFEngineW. | 
|  | */ | 
|  | HRESULT WINAPI OpenINFEngineA(LPCSTR pszInfFilename, LPCSTR pszInstallSection, | 
|  | DWORD dwFlags, HINF *phInf, PVOID pvReserved) | 
|  | { | 
|  | UNICODE_STRING filenameW, installW; | 
|  | HRESULT res; | 
|  |  | 
|  | TRACE("(%s, %s, %d, %p, %p)\n", debugstr_a(pszInfFilename), | 
|  | debugstr_a(pszInstallSection), dwFlags, phInf, pvReserved); | 
|  |  | 
|  | if (!pszInfFilename || !phInf) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename); | 
|  | RtlCreateUnicodeStringFromAsciiz(&installW, pszInstallSection); | 
|  |  | 
|  | res = OpenINFEngineW(filenameW.Buffer, installW.Buffer, | 
|  | dwFlags, phInf, pvReserved); | 
|  |  | 
|  | RtlFreeUnicodeString(&filenameW); | 
|  | RtlFreeUnicodeString(&installW); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             OpenINFEngineW   (ADVPACK.@) | 
|  | * | 
|  | * Opens and returns a handle to an INF file to be used by | 
|  | * TranslateInfStringEx to continuously translate the INF file. | 
|  | * | 
|  | * PARAMS | 
|  | *   pszInfFilename    [I] Filename of the INF to open. | 
|  | *   pszInstallSection [I] Name of the Install section in the INF. | 
|  | *   dwFlags           [I] See advpub.h. | 
|  | *   phInf             [O] Handle to the loaded INF file. | 
|  | *   pvReserved        [I] Reserved.  Must be NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: E_FAIL. | 
|  | */ | 
|  | HRESULT WINAPI OpenINFEngineW(LPCWSTR pszInfFilename, LPCWSTR pszInstallSection, | 
|  | DWORD dwFlags, HINF *phInf, PVOID pvReserved) | 
|  | { | 
|  | TRACE("(%s, %s, %d, %p, %p)\n", debugstr_w(pszInfFilename), | 
|  | debugstr_w(pszInstallSection), dwFlags, phInf, pvReserved); | 
|  |  | 
|  | if (!pszInfFilename || !phInf) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | *phInf = SetupOpenInfFileW(pszInfFilename, NULL, INF_STYLE_WIN4, NULL); | 
|  | if (*phInf == INVALID_HANDLE_VALUE) | 
|  | return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | 
|  |  | 
|  | set_ldids(*phInf, pszInstallSection, NULL); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             RebootCheckOnInstallA   (ADVPACK.@) | 
|  | * | 
|  | * See RebootCheckOnInstallW. | 
|  | */ | 
|  | HRESULT WINAPI RebootCheckOnInstallA(HWND hWnd, LPCSTR pszINF, | 
|  | LPCSTR pszSec, DWORD dwReserved) | 
|  | { | 
|  | UNICODE_STRING infW, secW; | 
|  | HRESULT res; | 
|  |  | 
|  | TRACE("(%p, %s, %s, %d)\n", hWnd, debugstr_a(pszINF), | 
|  | debugstr_a(pszSec), dwReserved); | 
|  |  | 
|  | if (!pszINF || !pszSec) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&infW, pszINF); | 
|  | RtlCreateUnicodeStringFromAsciiz(&secW, pszSec); | 
|  |  | 
|  | res = RebootCheckOnInstallW(hWnd, infW.Buffer, secW.Buffer, dwReserved); | 
|  |  | 
|  | RtlFreeUnicodeString(&infW); | 
|  | RtlFreeUnicodeString(&secW); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             RebootCheckOnInstallW   (ADVPACK.@) | 
|  | * | 
|  | * Checks if a reboot is required for an installed INF section. | 
|  | * | 
|  | * PARAMS | 
|  | *   hWnd       [I] Handle to the window used for messages. | 
|  | *   pszINF     [I] Filename of the INF file. | 
|  | *   pszSec     [I] INF section to check. | 
|  | *   dwReserved [I] Reserved.  Must be 0. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK - Reboot is needed if the INF section is installed. | 
|  | *            S_FALSE - Reboot is not needed. | 
|  | *   Failure: HRESULT of GetLastError(). | 
|  | * | 
|  | * NOTES | 
|  | *   if pszSec is NULL, RebootCheckOnInstall checks the DefaultInstall | 
|  | *   or DefaultInstall.NT section. | 
|  | * | 
|  | * BUGS | 
|  | *   Unimplemented. | 
|  | */ | 
|  | HRESULT WINAPI RebootCheckOnInstallW(HWND hWnd, LPCWSTR pszINF, | 
|  | LPCWSTR pszSec, DWORD dwReserved) | 
|  | { | 
|  | FIXME("(%p, %s, %s, %d): stub\n", hWnd, debugstr_w(pszINF), | 
|  | debugstr_w(pszSec), dwReserved); | 
|  |  | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | /* registers the OCX if do_reg is TRUE, unregisters it otherwise */ | 
|  | HRESULT do_ocx_reg(HMODULE hocx, BOOL do_reg) | 
|  | { | 
|  | DLLREGISTER reg_func; | 
|  |  | 
|  | if (do_reg) | 
|  | reg_func = (DLLREGISTER)GetProcAddress(hocx, "DllRegisterServer"); | 
|  | else | 
|  | reg_func = (DLLREGISTER)GetProcAddress(hocx, "DllUnregisterServer"); | 
|  |  | 
|  | if (!reg_func) | 
|  | return E_FAIL; | 
|  |  | 
|  | reg_func(); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             RegisterOCX    (ADVPACK.@) | 
|  | * | 
|  | * Registers an OCX. | 
|  | * | 
|  | * PARAMS | 
|  | *   hWnd    [I] Handle to the window used for the display. | 
|  | *   hInst   [I] Instance of the process. | 
|  | *   cmdline [I] Contains parameters in the order OCX,flags,param. | 
|  | *   show    [I] How the window should be shown. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: E_FAIL. | 
|  | * | 
|  | * NOTES | 
|  | *   OCX - Filename of the OCX to register. | 
|  | *   flags - Controls the operation of RegisterOCX. | 
|  | *    'I' Call DllRegisterServer and DllInstall. | 
|  | *    'N' Only call DllInstall. | 
|  | *   param - Command line passed to DllInstall. | 
|  | */ | 
|  | HRESULT WINAPI RegisterOCX(HWND hWnd, HINSTANCE hInst, LPCSTR cmdline, INT show) | 
|  | { | 
|  | LPWSTR ocx_filename, str_flags, param; | 
|  | LPWSTR cmdline_copy, cmdline_ptr; | 
|  | UNICODE_STRING cmdlineW; | 
|  | HRESULT hr = E_FAIL; | 
|  | HMODULE hm = NULL; | 
|  | DWORD size; | 
|  |  | 
|  | TRACE("(%s)\n", debugstr_a(cmdline)); | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&cmdlineW, cmdline); | 
|  |  | 
|  | size = (lstrlenW(cmdlineW.Buffer) + 1) * sizeof(WCHAR); | 
|  | cmdline_copy = HeapAlloc(GetProcessHeap(), 0, size); | 
|  | cmdline_ptr = cmdline_copy; | 
|  | lstrcpyW(cmdline_copy, cmdlineW.Buffer); | 
|  |  | 
|  | ocx_filename = get_parameter(&cmdline_ptr, ','); | 
|  | if (!ocx_filename || !*ocx_filename) | 
|  | goto done; | 
|  |  | 
|  | str_flags = get_parameter(&cmdline_ptr, ','); | 
|  | param = get_parameter(&cmdline_ptr, ','); | 
|  |  | 
|  | hm = LoadLibraryExW(ocx_filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); | 
|  | if (!hm) | 
|  | goto done; | 
|  |  | 
|  | hr = do_ocx_reg(hm, TRUE); | 
|  |  | 
|  | done: | 
|  | FreeLibrary(hm); | 
|  | HeapFree(GetProcessHeap(), 0, cmdline_copy); | 
|  | RtlFreeUnicodeString(&cmdlineW); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             SetPerUserSecValuesA   (ADVPACK.@) | 
|  | * | 
|  | * See SetPerUserSecValuesW. | 
|  | */ | 
|  | HRESULT WINAPI SetPerUserSecValuesA(PERUSERSECTIONA* pPerUser) | 
|  | { | 
|  | PERUSERSECTIONW perUserW; | 
|  |  | 
|  | TRACE("(%p)\n", pPerUser); | 
|  |  | 
|  | if (!pPerUser) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | MultiByteToWideChar(CP_ACP, 0, pPerUser->szGUID, -1, perUserW.szGUID, | 
|  | sizeof(perUserW.szGUID) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, pPerUser->szDispName, -1, perUserW.szDispName, | 
|  | sizeof(perUserW.szDispName) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, pPerUser->szLocale, -1, perUserW.szLocale, | 
|  | sizeof(perUserW.szLocale) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, pPerUser->szStub, -1, perUserW.szStub, | 
|  | sizeof(perUserW.szStub) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, pPerUser->szVersion, -1, perUserW.szVersion, | 
|  | sizeof(perUserW.szVersion) / sizeof(WCHAR)); | 
|  | MultiByteToWideChar(CP_ACP, 0, pPerUser->szCompID, -1, perUserW.szCompID, | 
|  | sizeof(perUserW.szCompID) / sizeof(WCHAR)); | 
|  | perUserW.dwIsInstalled = pPerUser->dwIsInstalled; | 
|  | perUserW.bRollback = pPerUser->bRollback; | 
|  |  | 
|  | return SetPerUserSecValuesW(&perUserW); | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             SetPerUserSecValuesW   (ADVPACK.@) | 
|  | * | 
|  | * Prepares the per-user stub values under IsInstalled\{GUID} that | 
|  | * control the per-user installation. | 
|  | * | 
|  | * PARAMS | 
|  | *   pPerUser [I] Per-user stub values. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: E_FAIL. | 
|  | */ | 
|  | HRESULT WINAPI SetPerUserSecValuesW(PERUSERSECTIONW* pPerUser) | 
|  | { | 
|  | HKEY setup, guid; | 
|  |  | 
|  | static const WCHAR stub_path[] = {'S','t','u','b','P','a','t','h',0}; | 
|  | static const WCHAR version[] = {'V','e','r','s','i','o','n',0}; | 
|  | static const WCHAR locale[] = {'L','o','c','a','l','e',0}; | 
|  | static const WCHAR compid[] = {'C','o','m','p','o','n','e','n','t','I','D',0}; | 
|  | static const WCHAR isinstalled[] = {'I','s','I','n','s','t','a','l','l','e','d',0}; | 
|  |  | 
|  | TRACE("(%p)\n", pPerUser); | 
|  |  | 
|  | if (!pPerUser || !*pPerUser->szGUID) | 
|  | return S_OK; | 
|  |  | 
|  | if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, setup_key, 0, NULL, 0, KEY_WRITE, | 
|  | NULL, &setup, NULL)) | 
|  | { | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | if (RegCreateKeyExW(setup, pPerUser->szGUID, 0, NULL, 0, KEY_ALL_ACCESS, | 
|  | NULL, &guid, NULL)) | 
|  | { | 
|  | RegCloseKey(setup); | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | if (*pPerUser->szStub) | 
|  | { | 
|  | RegSetValueExW(guid, stub_path, 0, REG_SZ, (LPBYTE)pPerUser->szStub, | 
|  | (lstrlenW(pPerUser->szStub) + 1) * sizeof(WCHAR)); | 
|  | } | 
|  |  | 
|  | if (*pPerUser->szVersion) | 
|  | { | 
|  | RegSetValueExW(guid, version, 0, REG_SZ, (LPBYTE)pPerUser->szVersion, | 
|  | (lstrlenW(pPerUser->szVersion) + 1) * sizeof(WCHAR)); | 
|  | } | 
|  |  | 
|  | if (*pPerUser->szLocale) | 
|  | { | 
|  | RegSetValueExW(guid, locale, 0, REG_SZ, (LPBYTE)pPerUser->szLocale, | 
|  | (lstrlenW(pPerUser->szLocale) + 1) * sizeof(WCHAR)); | 
|  | } | 
|  |  | 
|  | if (*pPerUser->szCompID) | 
|  | { | 
|  | RegSetValueExW(guid, compid, 0, REG_SZ, (LPBYTE)pPerUser->szCompID, | 
|  | (lstrlenW(pPerUser->szCompID) + 1) * sizeof(WCHAR)); | 
|  | } | 
|  |  | 
|  | if (*pPerUser->szDispName) | 
|  | { | 
|  | RegSetValueExW(guid, NULL, 0, REG_SZ, (LPBYTE)pPerUser->szDispName, | 
|  | (lstrlenW(pPerUser->szDispName) + 1) * sizeof(WCHAR)); | 
|  | } | 
|  |  | 
|  | RegSetValueExW(guid, isinstalled, 0, REG_DWORD, | 
|  | (LPBYTE)&pPerUser->dwIsInstalled, sizeof(DWORD)); | 
|  |  | 
|  | RegCloseKey(guid); | 
|  | RegCloseKey(setup); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             TranslateInfStringA   (ADVPACK.@) | 
|  | * | 
|  | * See TranslateInfStringW. | 
|  | */ | 
|  | HRESULT WINAPI TranslateInfStringA(LPCSTR pszInfFilename, LPCSTR pszInstallSection, | 
|  | LPCSTR pszTranslateSection, LPCSTR pszTranslateKey, LPSTR pszBuffer, | 
|  | DWORD dwBufferSize, PDWORD pdwRequiredSize, PVOID pvReserved) | 
|  | { | 
|  | UNICODE_STRING filenameW, installW; | 
|  | UNICODE_STRING translateW, keyW; | 
|  | LPWSTR bufferW; | 
|  | HRESULT res; | 
|  | DWORD len = 0; | 
|  |  | 
|  | TRACE("(%s, %s, %s, %s, %p, %d, %p, %p)\n", | 
|  | debugstr_a(pszInfFilename), debugstr_a(pszInstallSection), | 
|  | debugstr_a(pszTranslateSection), debugstr_a(pszTranslateKey), | 
|  | pszBuffer, dwBufferSize,pdwRequiredSize, pvReserved); | 
|  |  | 
|  | if (!pszInfFilename || !pszTranslateSection || | 
|  | !pszTranslateKey || !pdwRequiredSize) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename); | 
|  | RtlCreateUnicodeStringFromAsciiz(&installW, pszInstallSection); | 
|  | RtlCreateUnicodeStringFromAsciiz(&translateW, pszTranslateSection); | 
|  | RtlCreateUnicodeStringFromAsciiz(&keyW, pszTranslateKey); | 
|  |  | 
|  | res = TranslateInfStringW(filenameW.Buffer, installW.Buffer, | 
|  | translateW.Buffer, keyW.Buffer, NULL, | 
|  | dwBufferSize, &len, NULL); | 
|  |  | 
|  | if (res == S_OK) | 
|  | { | 
|  | bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | 
|  |  | 
|  | res = TranslateInfStringW(filenameW.Buffer, installW.Buffer, | 
|  | translateW.Buffer, keyW.Buffer, bufferW, | 
|  | len, &len, NULL); | 
|  | if (res == S_OK) | 
|  | { | 
|  | *pdwRequiredSize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, | 
|  | NULL, 0, NULL, NULL); | 
|  |  | 
|  | if (dwBufferSize >= *pdwRequiredSize) | 
|  | { | 
|  | WideCharToMultiByte(CP_ACP, 0, bufferW, -1, pszBuffer, | 
|  | dwBufferSize, NULL, NULL); | 
|  | } | 
|  | else | 
|  | res = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, bufferW); | 
|  | } | 
|  |  | 
|  | RtlFreeUnicodeString(&filenameW); | 
|  | RtlFreeUnicodeString(&installW); | 
|  | RtlFreeUnicodeString(&translateW); | 
|  | RtlFreeUnicodeString(&keyW); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             TranslateInfStringW   (ADVPACK.@) | 
|  | * | 
|  | * Translates the value of a specified key in an inf file into the | 
|  | * current locale by expanding string macros. | 
|  | * | 
|  | * PARAMS | 
|  | *   pszInfFilename      [I] Filename of the inf file. | 
|  | *   pszInstallSection   [I] | 
|  | *   pszTranslateSection [I] Inf section where the key exists. | 
|  | *   pszTranslateKey     [I] Key to translate. | 
|  | *   pszBuffer           [O] Contains the translated string on exit. | 
|  | *   dwBufferSize        [I] Size on input of pszBuffer. | 
|  | *   pdwRequiredSize     [O] Length of the translated key. | 
|  | *   pvReserved          [I] Reserved, must be NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: An hresult error code. | 
|  | */ | 
|  | HRESULT WINAPI TranslateInfStringW(LPCWSTR pszInfFilename, LPCWSTR pszInstallSection, | 
|  | LPCWSTR pszTranslateSection, LPCWSTR pszTranslateKey, LPWSTR pszBuffer, | 
|  | DWORD dwBufferSize, PDWORD pdwRequiredSize, PVOID pvReserved) | 
|  | { | 
|  | HINF hInf; | 
|  | HRESULT hret = S_OK; | 
|  |  | 
|  | TRACE("(%s, %s, %s, %s, %p, %d, %p, %p)\n", | 
|  | debugstr_w(pszInfFilename), debugstr_w(pszInstallSection), | 
|  | debugstr_w(pszTranslateSection), debugstr_w(pszTranslateKey), | 
|  | pszBuffer, dwBufferSize,pdwRequiredSize, pvReserved); | 
|  |  | 
|  | if (!pszInfFilename || !pszTranslateSection || | 
|  | !pszTranslateKey || !pdwRequiredSize) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | hInf = SetupOpenInfFileW(pszInfFilename, NULL, INF_STYLE_WIN4, NULL); | 
|  | if (hInf == INVALID_HANDLE_VALUE) | 
|  | return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); | 
|  |  | 
|  | set_ldids(hInf, pszInstallSection, NULL); | 
|  |  | 
|  | if (!SetupGetLineTextW(NULL, hInf, pszTranslateSection, pszTranslateKey, | 
|  | pszBuffer, dwBufferSize, pdwRequiredSize)) | 
|  | { | 
|  | if (dwBufferSize < *pdwRequiredSize) | 
|  | hret = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | 
|  | else | 
|  | hret = SPAPI_E_LINE_NOT_FOUND; | 
|  | } | 
|  |  | 
|  | SetupCloseInfFile(hInf); | 
|  | return hret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             TranslateInfStringExA   (ADVPACK.@) | 
|  | * | 
|  | * See TranslateInfStringExW. | 
|  | */ | 
|  | HRESULT WINAPI TranslateInfStringExA(HINF hInf, LPCSTR pszInfFilename, | 
|  | LPCSTR pszTranslateSection, LPCSTR pszTranslateKey, | 
|  | LPSTR pszBuffer, DWORD dwBufferSize, | 
|  | PDWORD pdwRequiredSize, PVOID pvReserved) | 
|  | { | 
|  | UNICODE_STRING filenameW, sectionW, keyW; | 
|  | LPWSTR bufferW; | 
|  | HRESULT res; | 
|  | DWORD len = 0; | 
|  |  | 
|  | TRACE("(%p, %s, %s, %s, %s, %d, %p, %p)\n", hInf, debugstr_a(pszInfFilename), | 
|  | debugstr_a(pszTranslateSection), debugstr_a(pszTranslateKey), | 
|  | debugstr_a(pszBuffer), dwBufferSize, pdwRequiredSize, pvReserved); | 
|  |  | 
|  | if (!pszInfFilename || !pszTranslateSection || | 
|  | !pszTranslateKey || !pdwRequiredSize) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename); | 
|  | RtlCreateUnicodeStringFromAsciiz(§ionW, pszTranslateSection); | 
|  | RtlCreateUnicodeStringFromAsciiz(&keyW, pszTranslateKey); | 
|  |  | 
|  | res = TranslateInfStringExW(hInf, filenameW.Buffer, sectionW.Buffer, | 
|  | keyW.Buffer, NULL, 0, &len, NULL); | 
|  |  | 
|  | if (res == S_OK) | 
|  | { | 
|  | bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | 
|  |  | 
|  | res = TranslateInfStringExW(hInf, filenameW.Buffer, sectionW.Buffer, | 
|  | keyW.Buffer, bufferW, len, &len, NULL); | 
|  |  | 
|  | if (res == S_OK) | 
|  | { | 
|  | *pdwRequiredSize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, | 
|  | NULL, 0, NULL, NULL); | 
|  |  | 
|  | if (dwBufferSize >= *pdwRequiredSize) | 
|  | { | 
|  | WideCharToMultiByte(CP_ACP, 0, bufferW, -1, pszBuffer, | 
|  | dwBufferSize, NULL, NULL); | 
|  | } | 
|  | else | 
|  | res = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | 
|  | } | 
|  |  | 
|  | HeapFree(GetProcessHeap(), 0, bufferW); | 
|  | } | 
|  |  | 
|  | RtlFreeUnicodeString(&filenameW); | 
|  | RtlFreeUnicodeString(§ionW); | 
|  | RtlFreeUnicodeString(&keyW); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             TranslateInfStringExW   (ADVPACK.@) | 
|  | * | 
|  | * Using a handle to an INF file opened with OpenINFEngine, translates | 
|  | * the value of a specified key in an inf file into the current locale | 
|  | * by expanding string macros. | 
|  | * | 
|  | * PARAMS | 
|  | *   hInf                [I] Handle to the INF file. | 
|  | *   pszInfFilename      [I] Filename of the INF file. | 
|  | *   pszTranslateSection [I] Inf section where the key exists. | 
|  | *   pszTranslateKey     [I] Key to translate. | 
|  | *   pszBuffer           [O] Contains the translated string on exit. | 
|  | *   dwBufferSize        [I] Size on input of pszBuffer. | 
|  | *   pdwRequiredSize     [O] Length of the translated key. | 
|  | *   pvReserved          [I] Reserved.  Must be NULL. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: E_FAIL. | 
|  | * | 
|  | * NOTES | 
|  | *   To use TranslateInfStringEx to translate an INF file continuously, | 
|  | *   open the INF file with OpenINFEngine, call TranslateInfStringEx as | 
|  | *   many times as needed, then release the handle with CloseINFEngine. | 
|  | *   When translating more than one keys, this method is more efficient | 
|  | *   than calling TranslateInfString, because the INF file is only | 
|  | *   opened once. | 
|  | */ | 
|  | HRESULT WINAPI TranslateInfStringExW(HINF hInf, LPCWSTR pszInfFilename, | 
|  | LPCWSTR pszTranslateSection, LPCWSTR pszTranslateKey, | 
|  | LPWSTR pszBuffer, DWORD dwBufferSize, | 
|  | PDWORD pdwRequiredSize, PVOID pvReserved) | 
|  | { | 
|  | TRACE("(%p, %s, %s, %s, %s, %d, %p, %p)\n", hInf, debugstr_w(pszInfFilename), | 
|  | debugstr_w(pszTranslateSection), debugstr_w(pszTranslateKey), | 
|  | debugstr_w(pszBuffer), dwBufferSize, pdwRequiredSize, pvReserved); | 
|  |  | 
|  | if (!hInf || !pszInfFilename || !pszTranslateSection || !pszTranslateKey) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (!SetupGetLineTextW(NULL, hInf, pszTranslateSection, pszTranslateKey, | 
|  | pszBuffer, dwBufferSize, pdwRequiredSize)) | 
|  | { | 
|  | if (dwBufferSize < *pdwRequiredSize) | 
|  | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | 
|  |  | 
|  | return SPAPI_E_LINE_NOT_FOUND; | 
|  | } | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             UserInstStubWrapperA   (ADVPACK.@) | 
|  | * | 
|  | * See UserInstStubWrapperW. | 
|  | */ | 
|  | HRESULT WINAPI UserInstStubWrapperA(HWND hWnd, HINSTANCE hInstance, | 
|  | LPSTR pszParms, INT nShow) | 
|  | { | 
|  | UNICODE_STRING parmsW; | 
|  | HRESULT res; | 
|  |  | 
|  | TRACE("(%p, %p, %s, %i)\n", hWnd, hInstance, debugstr_a(pszParms), nShow); | 
|  |  | 
|  | if (!pszParms) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&parmsW, pszParms); | 
|  |  | 
|  | res = UserInstStubWrapperW(hWnd, hInstance, parmsW.Buffer, nShow); | 
|  |  | 
|  | RtlFreeUnicodeString(&parmsW); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             UserInstStubWrapperW   (ADVPACK.@) | 
|  | * | 
|  | * Launches the user stub wrapper specified by the RealStubPath | 
|  | * registry value under Installed Components\szParms. | 
|  | * | 
|  | * PARAMS | 
|  | *   hWnd      [I] Handle to the window used for the display. | 
|  | *   hInstance [I] Instance of the process. | 
|  | *   szParms   [I] The GUID of the installation. | 
|  | *   show      [I] How the window should be shown. | 
|  | * | 
|  | * RETURNS | 
|  | *   Success: S_OK. | 
|  | *   Failure: E_FAIL. | 
|  | * | 
|  | * TODO | 
|  | *   If the type of the StubRealPath value is REG_EXPAND_SZ, then | 
|  | *   we should call ExpandEnvironmentStrings on the value and | 
|  | *   launch the result. | 
|  | */ | 
|  | HRESULT WINAPI UserInstStubWrapperW(HWND hWnd, HINSTANCE hInstance, | 
|  | LPWSTR pszParms, INT nShow) | 
|  | { | 
|  | HKEY setup, guid; | 
|  | WCHAR stub[MAX_PATH]; | 
|  | DWORD size = MAX_PATH; | 
|  | HRESULT hr = S_OK; | 
|  | BOOL res; | 
|  |  | 
|  | static const WCHAR real_stub_path[] = { | 
|  | 'R','e','a','l','S','t','u','b','P','a','t','h',0 | 
|  | }; | 
|  |  | 
|  | TRACE("(%p, %p, %s, %i)\n", hWnd, hInstance, debugstr_w(pszParms), nShow); | 
|  |  | 
|  | if (!pszParms || !*pszParms) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, setup_key, 0, KEY_READ, &setup)) | 
|  | { | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | if (RegOpenKeyExW(setup, pszParms, 0, KEY_READ, &guid)) | 
|  | { | 
|  | RegCloseKey(setup); | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | res = RegQueryValueExW(guid, real_stub_path, NULL, NULL, (LPBYTE)stub, &size); | 
|  | if (res || !*stub) | 
|  | goto done; | 
|  |  | 
|  | /* launch the user stub wrapper */ | 
|  | hr = launch_exe(stub, NULL, NULL); | 
|  |  | 
|  | done: | 
|  | RegCloseKey(setup); | 
|  | RegCloseKey(guid); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             UserUnInstStubWrapperA   (ADVPACK.@) | 
|  | * | 
|  | * See UserUnInstStubWrapperW. | 
|  | */ | 
|  | HRESULT WINAPI UserUnInstStubWrapperA(HWND hWnd, HINSTANCE hInstance, | 
|  | LPSTR pszParms, INT nShow) | 
|  | { | 
|  | UNICODE_STRING parmsW; | 
|  | HRESULT res; | 
|  |  | 
|  | TRACE("(%p, %p, %s, %i)\n", hWnd, hInstance, debugstr_a(pszParms), nShow); | 
|  |  | 
|  | if (!pszParms) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | RtlCreateUnicodeStringFromAsciiz(&parmsW, pszParms); | 
|  |  | 
|  | res = UserUnInstStubWrapperW(hWnd, hInstance, parmsW.Buffer, nShow); | 
|  |  | 
|  | RtlFreeUnicodeString(&parmsW); | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *             UserUnInstStubWrapperW   (ADVPACK.@) | 
|  | */ | 
|  | HRESULT WINAPI UserUnInstStubWrapperW(HWND hWnd, HINSTANCE hInstance, | 
|  | LPWSTR pszParms, INT nShow) | 
|  | { | 
|  | FIXME("(%p, %p, %s, %i): stub\n", hWnd, hInstance, debugstr_w(pszParms), nShow); | 
|  |  | 
|  | return E_FAIL; | 
|  | } |