xref: /reactos/dll/win32/advpack/advpack.c (revision 4561998a)
1 /*
2  * Advpack main
3  *
4  * Copyright 2004 Huw D M Davies
5  * Copyright 2005 Sami Aario
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "winreg.h"
28 #include "winternl.h"
29 #include "winnls.h"
30 #include "setupapi.h"
31 #include "advpub.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34 #include "advpack_private.h"
35 
36 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
37 
38 typedef HRESULT (WINAPI *DLLREGISTER) (void);
39 
40 #define MAX_FIELD_LENGTH    512
41 #define PREFIX_LEN          5
42 
43 /* registry path of the Installed Components key for per-user stubs */
44 static const WCHAR setup_key[] = {
45     'S','O','F','T','W','A','R','E','\\',
46     'M','i','c','r','o','s','o','f','t','\\',
47     'A','c','t','i','v','e',' ','S','e','t','u','p','\\',
48     'I','n','s','t','a','l','l','e','d',' ',
49     'C','o','m','p','o','n','e','n','t','s',0
50 };
51 
52 /* Strip single quotes from a token - note size includes NULL */
53 static void strip_quotes(WCHAR *buffer, DWORD *size)
54 {
55     if (buffer[0] == '\'' && (*size > 1) && buffer[*size-2]=='\'')
56     {
57         *size -= 2;
58         buffer[*size] = 0x00;
59         memmove(buffer, buffer + 1, *size * sizeof(WCHAR));
60     }
61 }
62 
63 /* parses the destination directory parameters from pszSection
64  * the parameters are of the form: root,key,value,unknown,fallback
65  * we first read the reg value root\\key\\value and if that fails,
66  * use fallback as the destination directory
67  */
68 static void get_dest_dir(HINF hInf, PCWSTR pszSection, PWSTR pszBuffer, DWORD dwSize)
69 {
70     INFCONTEXT context;
71     WCHAR key[MAX_PATH + 2], value[MAX_PATH + 2];
72     WCHAR prefix[PREFIX_LEN + 2];
73     HKEY root, subkey = 0;
74     DWORD size;
75 
76     static const WCHAR hklm[] = {'H','K','L','M',0};
77     static const WCHAR hkcu[] = {'H','K','C','U',0};
78 
79     /* load the destination parameters */
80     SetupFindFirstLineW(hInf, pszSection, NULL, &context);
81     SetupGetStringFieldW(&context, 1, prefix, PREFIX_LEN + 2, &size);
82     strip_quotes(prefix, &size);
83     SetupGetStringFieldW(&context, 2, key, MAX_PATH + 2, &size);
84     strip_quotes(key, &size);
85     SetupGetStringFieldW(&context, 3, value, MAX_PATH + 2, &size);
86     strip_quotes(value, &size);
87 
88     if (!lstrcmpW(prefix, hklm))
89         root = HKEY_LOCAL_MACHINE;
90     else if (!lstrcmpW(prefix, hkcu))
91         root = HKEY_CURRENT_USER;
92     else
93         root = NULL;
94 
95     size = dwSize * sizeof(WCHAR);
96 
97     /* fallback to the default destination dir if reg fails */
98     if (RegOpenKeyW(root, key, &subkey) ||
99         RegQueryValueExW(subkey, value, NULL, NULL, (LPBYTE)pszBuffer, &size))
100     {
101         SetupGetStringFieldW(&context, 5, pszBuffer, dwSize, &size);
102         strip_quotes(pszBuffer, &size);
103     }
104 
105     if (subkey) RegCloseKey(subkey);
106 }
107 
108 /* loads the LDIDs specified in the install section of an INF */
109 void set_ldids(HINF hInf, LPCWSTR pszInstallSection, LPCWSTR pszWorkingDir)
110 {
111     WCHAR field[MAX_FIELD_LENGTH];
112     WCHAR line[MAX_FIELD_LENGTH];
113     WCHAR dest[MAX_PATH];
114     INFCONTEXT context;
115     DWORD size;
116     int ldid;
117 
118     static const WCHAR source_dir[] = {'S','o','u','r','c','e','D','i','r',0};
119 
120     static const WCHAR custDestW[] = {
121         'C','u','s','t','o','m','D','e','s','t','i','n','a','t','i','o','n',0
122     };
123 
124     if (!SetupGetLineTextW(NULL, hInf, pszInstallSection, custDestW,
125                            field, MAX_FIELD_LENGTH, &size))
126         return;
127 
128     if (!SetupFindFirstLineW(hInf, field, NULL, &context))
129         return;
130 
131     do
132     {
133         LPWSTR value, ptr, key, key_copy = NULL;
134         DWORD flags = 0;
135 
136         SetupGetLineTextW(&context, NULL, NULL, NULL,
137                           line, MAX_FIELD_LENGTH, &size);
138 
139         /* SetupGetLineTextW returns the value if there is only one key, but
140          * returns the whole line if there is more than one key
141          */
142         if (!(value = strchrW(line, '=')))
143         {
144             SetupGetStringFieldW(&context, 0, NULL, 0, &size);
145             key = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
146             key_copy = key;
147             SetupGetStringFieldW(&context, 0, key, size, &size);
148             value = line;
149         }
150         else
151         {
152             key = line;
153             *(value++) = '\0';
154         }
155 
156         /* remove leading whitespace from the value */
157         while (*value == ' ')
158             value++;
159 
160         /* Extract the flags */
161         ptr = strchrW(value, ',');
162         if (ptr) {
163             *ptr = '\0';
164             flags = atolW(ptr+1);
165         }
166 
167         /* set dest to pszWorkingDir if key is SourceDir */
168         if (pszWorkingDir && !lstrcmpiW(value, source_dir))
169             lstrcpynW(dest, pszWorkingDir, MAX_PATH);
170         else
171             get_dest_dir(hInf, value, dest, MAX_PATH);
172 
173         /* If prompting required, provide dialog to request path */
174         if (flags & 0x04)
175             FIXME("Need to support changing paths - default will be used\n");
176 
177         /* set all ldids to dest */
178         while ((ptr = get_parameter(&key, ',', FALSE)))
179         {
180             ldid = atolW(ptr);
181             SetupSetDirectoryIdW(hInf, ldid, dest);
182         }
183         HeapFree(GetProcessHeap(), 0, key_copy);
184     } while (SetupFindNextLine(&context, &context));
185 }
186 
187 /***********************************************************************
188  *           CloseINFEngine (ADVPACK.@)
189  *
190  * Closes a handle to an INF file opened with OpenINFEngine.
191  *
192  * PARAMS
193  *   hInf [I] Handle to the INF file to close.
194  *
195  * RETURNS
196  *   Success: S_OK.
197  *   Failure: E_FAIL.
198  */
199 HRESULT WINAPI CloseINFEngine(HINF hInf)
200 {
201     TRACE("(%p)\n", hInf);
202 
203     if (!hInf)
204         return E_INVALIDARG;
205 
206     SetupCloseInfFile(hInf);
207     return S_OK;
208 }
209 
210 /***********************************************************************
211  *              IsNTAdmin	(ADVPACK.@)
212  *
213  * Checks if the user has admin privileges.
214  *
215  * PARAMS
216  *   reserved  [I] Reserved.  Must be 0.
217  *   pReserved [I] Reserved.  Must be NULL.
218  *
219  * RETURNS
220  *   TRUE if user has admin rights, FALSE otherwise.
221  */
222 BOOL WINAPI IsNTAdmin(DWORD reserved, LPDWORD pReserved)
223 {
224     SID_IDENTIFIER_AUTHORITY SidAuthority = {SECURITY_NT_AUTHORITY};
225     PTOKEN_GROUPS pTokenGroups;
226     BOOL bSidFound = FALSE;
227     DWORD dwSize, i;
228     HANDLE hToken;
229     PSID pSid;
230 
231     TRACE("(%d, %p)\n", reserved, pReserved);
232 
233     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
234         return FALSE;
235 
236     if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize))
237     {
238         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
239         {
240             CloseHandle(hToken);
241             return FALSE;
242         }
243     }
244 
245     pTokenGroups = HeapAlloc(GetProcessHeap(), 0, dwSize);
246     if (!pTokenGroups)
247     {
248         CloseHandle(hToken);
249         return FALSE;
250     }
251 
252     if (!GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwSize, &dwSize))
253     {
254         HeapFree(GetProcessHeap(), 0, pTokenGroups);
255         CloseHandle(hToken);
256         return FALSE;
257     }
258 
259     CloseHandle(hToken);
260 
261     if (!AllocateAndInitializeSid(&SidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
262                                   DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid))
263     {
264         HeapFree(GetProcessHeap(), 0, pTokenGroups);
265         return FALSE;
266     }
267 
268     for (i = 0; i < pTokenGroups->GroupCount; i++)
269     {
270         if (EqualSid(pSid, pTokenGroups->Groups[i].Sid))
271         {
272             bSidFound = TRUE;
273             break;
274         }
275     }
276 
277     HeapFree(GetProcessHeap(), 0, pTokenGroups);
278     FreeSid(pSid);
279 
280     return bSidFound;
281 }
282 
283 /***********************************************************************
284  *             NeedRebootInit  (ADVPACK.@)
285  *
286  * Sets up conditions for reboot checking.
287  *
288  * RETURNS
289  *   Value required by NeedReboot.
290  */
291 DWORD WINAPI NeedRebootInit(VOID)
292 {
293     FIXME("(VOID): stub\n");
294     return 0;
295 }
296 
297 /***********************************************************************
298  *             NeedReboot      (ADVPACK.@)
299  *
300  * Determines whether a reboot is required.
301  *
302  * PARAMS
303  *   dwRebootCheck [I] Value from NeedRebootInit.
304  *
305  * RETURNS
306  *   TRUE if a reboot is needed, FALSE otherwise.
307  *
308  * BUGS
309  *   Unimplemented.
310  */
311 BOOL WINAPI NeedReboot(DWORD dwRebootCheck)
312 {
313     FIXME("(%d): stub\n", dwRebootCheck);
314     return FALSE;
315 }
316 
317 /***********************************************************************
318  *             OpenINFEngineA   (ADVPACK.@)
319  *
320  * See OpenINFEngineW.
321  */
322 HRESULT WINAPI OpenINFEngineA(LPCSTR pszInfFilename, LPCSTR pszInstallSection,
323                               DWORD dwFlags, HINF *phInf, PVOID pvReserved)
324 {
325     UNICODE_STRING filenameW, installW;
326     HRESULT res;
327 
328     TRACE("(%s, %s, %d, %p, %p)\n", debugstr_a(pszInfFilename),
329           debugstr_a(pszInstallSection), dwFlags, phInf, pvReserved);
330 
331     if (!pszInfFilename || !phInf)
332         return E_INVALIDARG;
333 
334     RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename);
335     RtlCreateUnicodeStringFromAsciiz(&installW, pszInstallSection);
336 
337     res = OpenINFEngineW(filenameW.Buffer, installW.Buffer,
338                          dwFlags, phInf, pvReserved);
339 
340     RtlFreeUnicodeString(&filenameW);
341     RtlFreeUnicodeString(&installW);
342 
343     return res;
344 }
345 
346 /***********************************************************************
347  *             OpenINFEngineW   (ADVPACK.@)
348  *
349  * Opens and returns a handle to an INF file to be used by
350  * TranslateInfStringEx to continuously translate the INF file.
351  *
352  * PARAMS
353  *   pszInfFilename    [I] Filename of the INF to open.
354  *   pszInstallSection [I] Name of the Install section in the INF.
355  *   dwFlags           [I] See advpub.h.
356  *   phInf             [O] Handle to the loaded INF file.
357  *   pvReserved        [I] Reserved.  Must be NULL.
358  *
359  * RETURNS
360  *   Success: S_OK.
361  *   Failure: E_FAIL.
362  */
363 HRESULT WINAPI OpenINFEngineW(LPCWSTR pszInfFilename, LPCWSTR pszInstallSection,
364                               DWORD dwFlags, HINF *phInf, PVOID pvReserved)
365 {
366     TRACE("(%s, %s, %d, %p, %p)\n", debugstr_w(pszInfFilename),
367           debugstr_w(pszInstallSection), dwFlags, phInf, pvReserved);
368 
369     if (!pszInfFilename || !phInf)
370         return E_INVALIDARG;
371 
372     *phInf = SetupOpenInfFileW(pszInfFilename, NULL, INF_STYLE_WIN4, NULL);
373     if (*phInf == INVALID_HANDLE_VALUE)
374         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
375 
376     set_ldids(*phInf, pszInstallSection, NULL);
377 
378     return S_OK;
379 }
380 
381 /***********************************************************************
382  *             RebootCheckOnInstallA   (ADVPACK.@)
383  *
384  * See RebootCheckOnInstallW.
385  */
386 HRESULT WINAPI RebootCheckOnInstallA(HWND hWnd, LPCSTR pszINF,
387                                      LPCSTR pszSec, DWORD dwReserved)
388 {
389     UNICODE_STRING infW, secW;
390     HRESULT res;
391 
392     TRACE("(%p, %s, %s, %d)\n", hWnd, debugstr_a(pszINF),
393           debugstr_a(pszSec), dwReserved);
394 
395     if (!pszINF || !pszSec)
396         return E_INVALIDARG;
397 
398     RtlCreateUnicodeStringFromAsciiz(&infW, pszINF);
399     RtlCreateUnicodeStringFromAsciiz(&secW, pszSec);
400 
401     res = RebootCheckOnInstallW(hWnd, infW.Buffer, secW.Buffer, dwReserved);
402 
403     RtlFreeUnicodeString(&infW);
404     RtlFreeUnicodeString(&secW);
405 
406     return res;
407 }
408 
409 /***********************************************************************
410  *             RebootCheckOnInstallW   (ADVPACK.@)
411  *
412  * Checks if a reboot is required for an installed INF section.
413  *
414  * PARAMS
415  *   hWnd       [I] Handle to the window used for messages.
416  *   pszINF     [I] Filename of the INF file.
417  *   pszSec     [I] INF section to check.
418  *   dwReserved [I] Reserved.  Must be 0.
419  *
420  * RETURNS
421  *   Success: S_OK - Reboot is needed if the INF section is installed.
422  *            S_FALSE - Reboot is not needed.
423  *   Failure: HRESULT of GetLastError().
424  *
425  * NOTES
426  *   if pszSec is NULL, RebootCheckOnInstall checks the DefaultInstall
427  *   or DefaultInstall.NT section.
428  *
429  * BUGS
430  *   Unimplemented.
431  */
432 HRESULT WINAPI RebootCheckOnInstallW(HWND hWnd, LPCWSTR pszINF,
433                                      LPCWSTR pszSec, DWORD dwReserved)
434 {
435     FIXME("(%p, %s, %s, %d): stub\n", hWnd, debugstr_w(pszINF),
436           debugstr_w(pszSec), dwReserved);
437 
438     return E_FAIL;
439 }
440 
441 /* registers the OCX if do_reg is TRUE, unregisters it otherwise */
442 HRESULT do_ocx_reg(HMODULE hocx, BOOL do_reg, const WCHAR *flags, const WCHAR *param)
443 {
444     DLLREGISTER reg_func;
445 
446     if (do_reg)
447         reg_func = (DLLREGISTER)GetProcAddress(hocx, "DllRegisterServer");
448     else
449         reg_func = (DLLREGISTER)GetProcAddress(hocx, "DllUnregisterServer");
450 
451     if (!reg_func)
452         return E_FAIL;
453 
454     reg_func();
455     return S_OK;
456 }
457 
458 /***********************************************************************
459  *             RegisterOCX    (ADVPACK.@)
460  *
461  * Registers an OCX.
462  *
463  * PARAMS
464  *   hWnd    [I] Handle to the window used for the display.
465  *   hInst   [I] Instance of the process.
466  *   cmdline [I] Contains parameters in the order OCX,flags,param.
467  *   show    [I] How the window should be shown.
468  *
469  * RETURNS
470  *   Success: S_OK.
471  *   Failure: E_FAIL.
472  *
473  * NOTES
474  *   OCX - Filename of the OCX to register.
475  *   flags - Controls the operation of RegisterOCX.
476  *    'I' Call DllRegisterServer and DllInstall.
477  *    'N' Only call DllInstall.
478  *   param - Command line passed to DllInstall.
479  */
480 HRESULT WINAPI RegisterOCX(HWND hWnd, HINSTANCE hInst, LPCSTR cmdline, INT show)
481 {
482     LPWSTR ocx_filename, str_flags, param;
483     LPWSTR cmdline_copy, cmdline_ptr;
484     UNICODE_STRING cmdlineW;
485     HRESULT hr = E_FAIL;
486     HMODULE hm = NULL;
487     DWORD size;
488 
489     TRACE("(%s)\n", debugstr_a(cmdline));
490 
491     RtlCreateUnicodeStringFromAsciiz(&cmdlineW, cmdline);
492 
493     size = (lstrlenW(cmdlineW.Buffer) + 1) * sizeof(WCHAR);
494     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, size);
495     cmdline_ptr = cmdline_copy;
496     lstrcpyW(cmdline_copy, cmdlineW.Buffer);
497 
498     ocx_filename = get_parameter(&cmdline_ptr, ',', TRUE);
499     if (!ocx_filename || !*ocx_filename)
500         goto done;
501 
502     str_flags = get_parameter(&cmdline_ptr, ',', TRUE);
503     param = get_parameter(&cmdline_ptr, ',', TRUE);
504 
505     hm = LoadLibraryExW(ocx_filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
506     if (!hm)
507         goto done;
508 
509     hr = do_ocx_reg(hm, TRUE, str_flags, param);
510 
511 done:
512     FreeLibrary(hm);
513     HeapFree(GetProcessHeap(), 0, cmdline_copy);
514     RtlFreeUnicodeString(&cmdlineW);
515 
516     return hr;
517 }
518 
519 /***********************************************************************
520  *             SetPerUserSecValuesA   (ADVPACK.@)
521  *
522  * See SetPerUserSecValuesW.
523  */
524 HRESULT WINAPI SetPerUserSecValuesA(PERUSERSECTIONA* pPerUser)
525 {
526     PERUSERSECTIONW perUserW;
527 
528     TRACE("(%p)\n", pPerUser);
529 
530     if (!pPerUser)
531         return E_INVALIDARG;
532 
533     MultiByteToWideChar(CP_ACP, 0, pPerUser->szGUID, -1, perUserW.szGUID, ARRAY_SIZE(perUserW.szGUID));
534     MultiByteToWideChar(CP_ACP, 0, pPerUser->szDispName, -1, perUserW.szDispName, ARRAY_SIZE(perUserW.szDispName));
535     MultiByteToWideChar(CP_ACP, 0, pPerUser->szLocale, -1, perUserW.szLocale, ARRAY_SIZE(perUserW.szLocale));
536     MultiByteToWideChar(CP_ACP, 0, pPerUser->szStub, -1, perUserW.szStub, ARRAY_SIZE(perUserW.szStub));
537     MultiByteToWideChar(CP_ACP, 0, pPerUser->szVersion, -1, perUserW.szVersion, ARRAY_SIZE(perUserW.szVersion));
538     MultiByteToWideChar(CP_ACP, 0, pPerUser->szCompID, -1, perUserW.szCompID, ARRAY_SIZE(perUserW.szCompID));
539     perUserW.dwIsInstalled = pPerUser->dwIsInstalled;
540     perUserW.bRollback = pPerUser->bRollback;
541 
542     return SetPerUserSecValuesW(&perUserW);
543 }
544 
545 /***********************************************************************
546  *             SetPerUserSecValuesW   (ADVPACK.@)
547  *
548  * Prepares the per-user stub values under IsInstalled\{GUID} that
549  * control the per-user installation.
550  *
551  * PARAMS
552  *   pPerUser [I] Per-user stub values.
553  *
554  * RETURNS
555  *   Success: S_OK.
556  *   Failure: E_FAIL.
557  */
558 HRESULT WINAPI SetPerUserSecValuesW(PERUSERSECTIONW* pPerUser)
559 {
560     HKEY setup, guid;
561 
562     static const WCHAR stub_path[] = {'S','t','u','b','P','a','t','h',0};
563     static const WCHAR version[] = {'V','e','r','s','i','o','n',0};
564     static const WCHAR locale[] = {'L','o','c','a','l','e',0};
565     static const WCHAR compid[] = {'C','o','m','p','o','n','e','n','t','I','D',0};
566     static const WCHAR isinstalled[] = {'I','s','I','n','s','t','a','l','l','e','d',0};
567 
568     TRACE("(%p)\n", pPerUser);
569 
570     if (!pPerUser || !*pPerUser->szGUID)
571         return S_OK;
572 
573     if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, setup_key, 0, NULL, 0, KEY_WRITE,
574                         NULL, &setup, NULL))
575     {
576         return E_FAIL;
577     }
578 
579     if (RegCreateKeyExW(setup, pPerUser->szGUID, 0, NULL, 0, KEY_ALL_ACCESS,
580                         NULL, &guid, NULL))
581     {
582         RegCloseKey(setup);
583         return E_FAIL;
584     }
585 
586     if (*pPerUser->szStub)
587     {
588         RegSetValueExW(guid, stub_path, 0, REG_SZ, (LPBYTE)pPerUser->szStub,
589                        (lstrlenW(pPerUser->szStub) + 1) * sizeof(WCHAR));
590     }
591 
592     if (*pPerUser->szVersion)
593     {
594         RegSetValueExW(guid, version, 0, REG_SZ, (LPBYTE)pPerUser->szVersion,
595                        (lstrlenW(pPerUser->szVersion) + 1) * sizeof(WCHAR));
596     }
597 
598     if (*pPerUser->szLocale)
599     {
600         RegSetValueExW(guid, locale, 0, REG_SZ, (LPBYTE)pPerUser->szLocale,
601                        (lstrlenW(pPerUser->szLocale) + 1) * sizeof(WCHAR));
602     }
603 
604     if (*pPerUser->szCompID)
605     {
606         RegSetValueExW(guid, compid, 0, REG_SZ, (LPBYTE)pPerUser->szCompID,
607                        (lstrlenW(pPerUser->szCompID) + 1) * sizeof(WCHAR));
608     }
609 
610     if (*pPerUser->szDispName)
611     {
612         RegSetValueExW(guid, NULL, 0, REG_SZ, (LPBYTE)pPerUser->szDispName,
613                        (lstrlenW(pPerUser->szDispName) + 1) * sizeof(WCHAR));
614     }
615 
616     RegSetValueExW(guid, isinstalled, 0, REG_DWORD,
617                    (LPBYTE)&pPerUser->dwIsInstalled, sizeof(DWORD));
618 
619     RegCloseKey(guid);
620     RegCloseKey(setup);
621 
622     return S_OK;
623 }
624 
625 /***********************************************************************
626  *             TranslateInfStringA   (ADVPACK.@)
627  *
628  * See TranslateInfStringW.
629  */
630 HRESULT WINAPI TranslateInfStringA(LPCSTR pszInfFilename, LPCSTR pszInstallSection,
631                 LPCSTR pszTranslateSection, LPCSTR pszTranslateKey, LPSTR pszBuffer,
632                 DWORD dwBufferSize, PDWORD pdwRequiredSize, PVOID pvReserved)
633 {
634     UNICODE_STRING filenameW, installW;
635     UNICODE_STRING translateW, keyW;
636     LPWSTR bufferW;
637     HRESULT res;
638     DWORD len = 0;
639 
640     TRACE("(%s, %s, %s, %s, %p, %d, %p, %p)\n",
641           debugstr_a(pszInfFilename), debugstr_a(pszInstallSection),
642           debugstr_a(pszTranslateSection), debugstr_a(pszTranslateKey),
643           pszBuffer, dwBufferSize,pdwRequiredSize, pvReserved);
644 
645     if (!pszInfFilename || !pszTranslateSection ||
646         !pszTranslateKey || !pdwRequiredSize)
647         return E_INVALIDARG;
648 
649     RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename);
650     RtlCreateUnicodeStringFromAsciiz(&installW, pszInstallSection);
651     RtlCreateUnicodeStringFromAsciiz(&translateW, pszTranslateSection);
652     RtlCreateUnicodeStringFromAsciiz(&keyW, pszTranslateKey);
653 
654     res = TranslateInfStringW(filenameW.Buffer, installW.Buffer,
655                               translateW.Buffer, keyW.Buffer, NULL,
656                               dwBufferSize, &len, NULL);
657 
658     if (res == S_OK)
659     {
660         bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
661 
662         res = TranslateInfStringW(filenameW.Buffer, installW.Buffer,
663                                   translateW.Buffer, keyW.Buffer, bufferW,
664                                   len, &len, NULL);
665         if (res == S_OK)
666         {
667             *pdwRequiredSize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1,
668                                                    NULL, 0, NULL, NULL);
669 
670             if (dwBufferSize >= *pdwRequiredSize)
671             {
672                 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, pszBuffer,
673                                     dwBufferSize, NULL, NULL);
674             }
675             else
676                 res = E_NOT_SUFFICIENT_BUFFER;
677         }
678 
679         HeapFree(GetProcessHeap(), 0, bufferW);
680     }
681 
682     RtlFreeUnicodeString(&filenameW);
683     RtlFreeUnicodeString(&installW);
684     RtlFreeUnicodeString(&translateW);
685     RtlFreeUnicodeString(&keyW);
686 
687     return res;
688 }
689 
690 /***********************************************************************
691  *             TranslateInfStringW   (ADVPACK.@)
692  *
693  * Translates the value of a specified key in an inf file into the
694  * current locale by expanding string macros.
695  *
696  * PARAMS
697  *   pszInfFilename      [I] Filename of the inf file.
698  *   pszInstallSection   [I]
699  *   pszTranslateSection [I] Inf section where the key exists.
700  *   pszTranslateKey     [I] Key to translate.
701  *   pszBuffer           [O] Contains the translated string on exit.
702  *   dwBufferSize        [I] Size on input of pszBuffer.
703  *   pdwRequiredSize     [O] Length of the translated key.
704  *   pvReserved          [I] Reserved, must be NULL.
705  *
706  * RETURNS
707  *   Success: S_OK.
708  *   Failure: An hresult error code.
709  */
710 HRESULT WINAPI TranslateInfStringW(LPCWSTR pszInfFilename, LPCWSTR pszInstallSection,
711                 LPCWSTR pszTranslateSection, LPCWSTR pszTranslateKey, LPWSTR pszBuffer,
712                 DWORD dwBufferSize, PDWORD pdwRequiredSize, PVOID pvReserved)
713 {
714     HINF hInf;
715     HRESULT hret = S_OK;
716 
717     TRACE("(%s, %s, %s, %s, %p, %d, %p, %p)\n",
718           debugstr_w(pszInfFilename), debugstr_w(pszInstallSection),
719           debugstr_w(pszTranslateSection), debugstr_w(pszTranslateKey),
720           pszBuffer, dwBufferSize,pdwRequiredSize, pvReserved);
721 
722     if (!pszInfFilename || !pszTranslateSection ||
723         !pszTranslateKey || !pdwRequiredSize)
724         return E_INVALIDARG;
725 
726     hInf = SetupOpenInfFileW(pszInfFilename, NULL, INF_STYLE_WIN4, NULL);
727     if (hInf == INVALID_HANDLE_VALUE)
728         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
729 
730     set_ldids(hInf, pszInstallSection, NULL);
731 
732     if (!SetupGetLineTextW(NULL, hInf, pszTranslateSection, pszTranslateKey,
733                            pszBuffer, dwBufferSize, pdwRequiredSize))
734     {
735         if (dwBufferSize < *pdwRequiredSize)
736             hret = E_NOT_SUFFICIENT_BUFFER;
737         else
738             hret = SPAPI_E_LINE_NOT_FOUND;
739     }
740 
741     SetupCloseInfFile(hInf);
742     return hret;
743 }
744 
745 /***********************************************************************
746  *             TranslateInfStringExA   (ADVPACK.@)
747  *
748  * See TranslateInfStringExW.
749  */
750 HRESULT WINAPI TranslateInfStringExA(HINF hInf, LPCSTR pszInfFilename,
751                                     LPCSTR pszTranslateSection, LPCSTR pszTranslateKey,
752                                     LPSTR pszBuffer, DWORD dwBufferSize,
753                                     PDWORD pdwRequiredSize, PVOID pvReserved)
754 {
755     UNICODE_STRING filenameW, sectionW, keyW;
756     LPWSTR bufferW;
757     HRESULT res;
758     DWORD len = 0;
759 
760     TRACE("(%p, %s, %s, %s, %p, %d, %p, %p)\n", hInf, debugstr_a(pszInfFilename),
761           debugstr_a(pszTranslateSection), debugstr_a(pszTranslateKey),
762           pszBuffer, dwBufferSize, pdwRequiredSize, pvReserved);
763 
764     if (!pszInfFilename || !pszTranslateSection ||
765         !pszTranslateKey || !pdwRequiredSize)
766         return E_INVALIDARG;
767 
768     RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename);
769     RtlCreateUnicodeStringFromAsciiz(&sectionW, pszTranslateSection);
770     RtlCreateUnicodeStringFromAsciiz(&keyW, pszTranslateKey);
771 
772     res = TranslateInfStringExW(hInf, filenameW.Buffer, sectionW.Buffer,
773                                 keyW.Buffer, NULL, 0, &len, NULL);
774 
775     if (res == S_OK)
776     {
777         bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
778 
779         res = TranslateInfStringExW(hInf, filenameW.Buffer, sectionW.Buffer,
780                                 keyW.Buffer, bufferW, len, &len, NULL);
781 
782         if (res == S_OK)
783         {
784             *pdwRequiredSize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1,
785                                                    NULL, 0, NULL, NULL);
786 
787             if (dwBufferSize >= *pdwRequiredSize)
788             {
789                 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, pszBuffer,
790                                     dwBufferSize, NULL, NULL);
791             }
792             else
793                 res = E_NOT_SUFFICIENT_BUFFER;
794         }
795 
796         HeapFree(GetProcessHeap(), 0, bufferW);
797     }
798 
799     RtlFreeUnicodeString(&filenameW);
800     RtlFreeUnicodeString(&sectionW);
801     RtlFreeUnicodeString(&keyW);
802 
803     return res;
804 }
805 
806 /***********************************************************************
807  *             TranslateInfStringExW   (ADVPACK.@)
808  *
809  * Using a handle to an INF file opened with OpenINFEngine, translates
810  * the value of a specified key in an inf file into the current locale
811  * by expanding string macros.
812  *
813  * PARAMS
814  *   hInf                [I] Handle to the INF file.
815  *   pszInfFilename      [I] Filename of the INF file.
816  *   pszTranslateSection [I] Inf section where the key exists.
817  *   pszTranslateKey     [I] Key to translate.
818  *   pszBuffer           [O] Contains the translated string on exit.
819  *   dwBufferSize        [I] Size on input of pszBuffer.
820  *   pdwRequiredSize     [O] Length of the translated key.
821  *   pvReserved          [I] Reserved.  Must be NULL.
822  *
823  * RETURNS
824  *   Success: S_OK.
825  *   Failure: E_FAIL.
826  *
827  * NOTES
828  *   To use TranslateInfStringEx to translate an INF file continuously,
829  *   open the INF file with OpenINFEngine, call TranslateInfStringEx as
830  *   many times as needed, then release the handle with CloseINFEngine.
831  *   When translating more than one keys, this method is more efficient
832  *   than calling TranslateInfString, because the INF file is only
833  *   opened once.
834  */
835 HRESULT WINAPI TranslateInfStringExW(HINF hInf, LPCWSTR pszInfFilename,
836                                      LPCWSTR pszTranslateSection, LPCWSTR pszTranslateKey,
837                                      LPWSTR pszBuffer, DWORD dwBufferSize,
838                                      PDWORD pdwRequiredSize, PVOID pvReserved)
839 {
840     TRACE("(%p, %s, %s, %s, %p, %d, %p, %p)\n", hInf, debugstr_w(pszInfFilename),
841           debugstr_w(pszTranslateSection), debugstr_w(pszTranslateKey),
842           pszBuffer, dwBufferSize, pdwRequiredSize, pvReserved);
843 
844     if (!hInf || !pszInfFilename || !pszTranslateSection || !pszTranslateKey)
845         return E_INVALIDARG;
846 
847     if (!SetupGetLineTextW(NULL, hInf, pszTranslateSection, pszTranslateKey,
848                            pszBuffer, dwBufferSize, pdwRequiredSize))
849     {
850         if (dwBufferSize < *pdwRequiredSize)
851             return E_NOT_SUFFICIENT_BUFFER;
852 
853         return SPAPI_E_LINE_NOT_FOUND;
854     }
855 
856     return S_OK;
857 }
858 
859 /***********************************************************************
860  *             UserInstStubWrapperA   (ADVPACK.@)
861  *
862  * See UserInstStubWrapperW.
863  */
864 HRESULT WINAPI UserInstStubWrapperA(HWND hWnd, HINSTANCE hInstance,
865                                    LPSTR pszParms, INT nShow)
866 {
867     UNICODE_STRING parmsW;
868     HRESULT res;
869 
870     TRACE("(%p, %p, %s, %i)\n", hWnd, hInstance, debugstr_a(pszParms), nShow);
871 
872     if (!pszParms)
873         return E_INVALIDARG;
874 
875     RtlCreateUnicodeStringFromAsciiz(&parmsW, pszParms);
876 
877     res = UserInstStubWrapperW(hWnd, hInstance, parmsW.Buffer, nShow);
878 
879     RtlFreeUnicodeString(&parmsW);
880 
881     return res;
882 }
883 
884 /***********************************************************************
885  *             UserInstStubWrapperW   (ADVPACK.@)
886  *
887  * Launches the user stub wrapper specified by the RealStubPath
888  * registry value under Installed Components\szParms.
889  *
890  * PARAMS
891  *   hWnd      [I] Handle to the window used for the display.
892  *   hInstance [I] Instance of the process.
893  *   szParms   [I] The GUID of the installation.
894  *   show      [I] How the window should be shown.
895  *
896  * RETURNS
897  *   Success: S_OK.
898  *   Failure: E_FAIL.
899  *
900  * TODO
901  *   If the type of the StubRealPath value is REG_EXPAND_SZ, then
902  *   we should call ExpandEnvironmentStrings on the value and
903  *   launch the result.
904  */
905 HRESULT WINAPI UserInstStubWrapperW(HWND hWnd, HINSTANCE hInstance,
906                                     LPWSTR pszParms, INT nShow)
907 {
908     HKEY setup, guid;
909     WCHAR stub[MAX_PATH];
910     DWORD size = MAX_PATH;
911     HRESULT hr = S_OK;
912     BOOL res;
913 
914     static const WCHAR real_stub_path[] = {
915         'R','e','a','l','S','t','u','b','P','a','t','h',0
916     };
917 
918     TRACE("(%p, %p, %s, %i)\n", hWnd, hInstance, debugstr_w(pszParms), nShow);
919 
920     if (!pszParms || !*pszParms)
921         return E_INVALIDARG;
922 
923     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, setup_key, 0, KEY_READ, &setup))
924     {
925         return E_FAIL;
926     }
927 
928     if (RegOpenKeyExW(setup, pszParms, 0, KEY_READ, &guid))
929     {
930         RegCloseKey(setup);
931         return E_FAIL;
932     }
933 
934     res = RegQueryValueExW(guid, real_stub_path, NULL, NULL, (LPBYTE)stub, &size);
935     if (res || !*stub)
936         goto done;
937 
938     /* launch the user stub wrapper */
939     hr = launch_exe(stub, NULL, NULL);
940 
941 done:
942     RegCloseKey(setup);
943     RegCloseKey(guid);
944 
945     return hr;
946 }
947 
948 /***********************************************************************
949  *             UserUnInstStubWrapperA   (ADVPACK.@)
950  *
951  * See UserUnInstStubWrapperW.
952  */
953 HRESULT WINAPI UserUnInstStubWrapperA(HWND hWnd, HINSTANCE hInstance,
954                                       LPSTR pszParms, INT nShow)
955 {
956     UNICODE_STRING parmsW;
957     HRESULT res;
958 
959     TRACE("(%p, %p, %s, %i)\n", hWnd, hInstance, debugstr_a(pszParms), nShow);
960 
961     if (!pszParms)
962         return E_INVALIDARG;
963 
964     RtlCreateUnicodeStringFromAsciiz(&parmsW, pszParms);
965 
966     res = UserUnInstStubWrapperW(hWnd, hInstance, parmsW.Buffer, nShow);
967 
968     RtlFreeUnicodeString(&parmsW);
969 
970     return res;
971 }
972 
973 /***********************************************************************
974  *             UserUnInstStubWrapperW   (ADVPACK.@)
975  */
976 HRESULT WINAPI UserUnInstStubWrapperW(HWND hWnd, HINSTANCE hInstance,
977                                       LPWSTR pszParms, INT nShow)
978 {
979     FIXME("(%p, %p, %s, %i): stub\n", hWnd, hInstance, debugstr_w(pszParms), nShow);
980 
981     return E_FAIL;
982 }
983