comctl32: A couple fixes for tab icon offsets.
[wine] / dlls / advpack / advpack.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "winver.h"
29 #include "winternl.h"
30 #include "winnls.h"
31 #include "setupapi.h"
32 #include "advpub.h"
33 #include "wine/unicode.h"
34 #include "wine/debug.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 /* parses the destination directory parameters from pszSection
44  * the parameters are of the form: root,key,value,unknown,fallback
45  * we first read the reg value root\\key\\value and if that fails,
46  * use fallback as the destination directory
47  */
48 static void get_dest_dir(HINF hInf, PCWSTR pszSection, PWSTR pszBuffer, DWORD dwSize)
49 {
50     INFCONTEXT context;
51     WCHAR key[MAX_PATH], value[MAX_PATH];
52     WCHAR prefix[PREFIX_LEN];
53     HKEY root, subkey;
54     DWORD size;
55
56     static const WCHAR hklm[] = {'H','K','L','M',0};
57     static const WCHAR hkcu[] = {'H','K','C','U',0};
58
59     /* load the destination parameters */
60     SetupFindFirstLineW(hInf, pszSection, NULL, &context);
61     SetupGetStringFieldW(&context, 1, prefix, PREFIX_LEN, &size);
62     SetupGetStringFieldW(&context, 2, key, MAX_PATH, &size);
63     SetupGetStringFieldW(&context, 3, value, MAX_PATH, &size);
64
65     if (!lstrcmpW(prefix, hklm))
66         root = HKEY_LOCAL_MACHINE;
67     else if (!lstrcmpW(prefix, hkcu))
68         root = HKEY_CURRENT_USER;
69     else
70         root = NULL;
71
72     size = dwSize * sizeof(WCHAR);
73
74     /* fallback to the default destination dir if reg fails */
75     if (RegOpenKeyW(root, key, &subkey) ||
76         RegQueryValueExW(subkey, value, NULL, NULL, (LPBYTE)pszBuffer, &size))
77     {
78         SetupGetStringFieldW(&context, 6, pszBuffer, dwSize, NULL);
79     }
80
81     RegCloseKey(subkey);
82 }
83
84 /* loads the LDIDs specified in the install section of an INF */
85 static void set_ldids(HINF hInf, LPCWSTR pszInstallSection)
86 {
87     WCHAR field[MAX_FIELD_LENGTH];
88     WCHAR key[MAX_FIELD_LENGTH];
89     WCHAR dest[MAX_PATH];
90     INFCONTEXT context;
91     DWORD size;
92     int ldid;
93
94     static const WCHAR custDestW[] = {
95         'C','u','s','t','o','m','D','e','s','t','i','n','a','t','i','o','n',0
96     };
97
98     if (!SetupGetLineTextW(NULL, hInf, pszInstallSection, custDestW,
99                            field, MAX_FIELD_LENGTH, &size))
100         return;
101
102     if (!SetupFindFirstLineW(hInf, field, NULL, &context))
103         return;
104
105     do
106     {
107         SetupGetIntField(&context, 0, &ldid);
108         SetupGetLineTextW(&context, NULL, NULL, NULL,
109                           key, MAX_FIELD_LENGTH, &size);
110
111         get_dest_dir(hInf, key, dest, MAX_PATH);
112
113         SetupSetDirectoryIdW(hInf, ldid, dest);
114     } while (SetupFindNextLine(&context, &context));
115 }
116
117 /***********************************************************************
118  *           CloseINFEngine (ADVPACK.@)
119  *
120  * Closes a handle to an INF file opened with OpenINFEngine.
121  *
122  * PARAMS
123  *   hInf [I] Handle to the INF file to close.
124  *
125  * RETURNS
126  *   Success: S_OK.
127  *   Failure: E_FAIL.
128  */
129 HRESULT WINAPI CloseINFEngine(HINF hInf)
130 {
131     TRACE("(%p)\n", hInf);
132
133     if (!hInf)
134         return E_INVALIDARG;
135
136     SetupCloseInfFile(hInf);
137     return S_OK;
138 }
139
140 /***********************************************************************
141  *           DllMain (ADVPACK.@)
142  */
143 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
144 {
145     TRACE("(%p, %ld, %p)\n",hinstDLL, fdwReason, lpvReserved);
146
147     if (fdwReason == DLL_PROCESS_ATTACH)
148         DisableThreadLibraryCalls(hinstDLL);
149
150     return TRUE;
151 }
152
153 /***********************************************************************
154  *              IsNTAdmin       (ADVPACK.@)
155  *
156  * Checks if the user has admin privileges.
157  *
158  * PARAMS
159  *   reserved  [I] Reserved.  Must be 0.
160  *   pReserved [I] Reserved.  Must be NULL.
161  *
162  * RETURNS
163  *   TRUE if user has admin rights, FALSE otherwise.
164  */
165 BOOL WINAPI IsNTAdmin( DWORD reserved, LPDWORD pReserved )
166 {
167     SID_IDENTIFIER_AUTHORITY SidAuthority = {SECURITY_NT_AUTHORITY};
168     PTOKEN_GROUPS pTokenGroups;
169     BOOL bSidFound = FALSE;
170     DWORD dwSize, i;
171     HANDLE hToken;
172     PSID pSid;
173
174     TRACE("(0x%08lx, %p)\n", reserved, pReserved);
175
176     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
177         return FALSE;
178
179     if (!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize))
180     {
181         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
182         {
183             CloseHandle(hToken);
184             return FALSE;
185         }
186     }
187
188     pTokenGroups = HeapAlloc(GetProcessHeap(), 0, dwSize);
189     if (!pTokenGroups)
190     {
191         CloseHandle(hToken);
192         return FALSE;
193     }
194
195     if (!GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwSize, &dwSize))
196     {
197         HeapFree(GetProcessHeap(), 0, pTokenGroups);
198         CloseHandle(hToken);
199         return FALSE;
200     }
201
202     CloseHandle(hToken);
203
204     if (!AllocateAndInitializeSid(&SidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
205                                   DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSid))
206     {
207         HeapFree(GetProcessHeap(), 0, pTokenGroups);
208         return FALSE;
209     }
210
211     for (i = 0; i < pTokenGroups->GroupCount; i++)
212     {
213         if (EqualSid(pSid, pTokenGroups->Groups[i].Sid))
214         {
215             bSidFound = TRUE;
216             break;
217         }
218     }
219
220     HeapFree(GetProcessHeap(), 0, pTokenGroups);
221     FreeSid(pSid);
222
223     return bSidFound;
224 }
225
226 /***********************************************************************
227  *             NeedRebootInit  (ADVPACK.@)
228  *
229  * Sets up conditions for reboot checking.
230  *
231  * RETURNS
232  *   Value required by NeedReboot.
233  */
234 DWORD WINAPI NeedRebootInit(VOID)
235 {
236     FIXME("(): stub\n");
237     return 0;
238 }
239
240 /***********************************************************************
241  *             NeedReboot      (ADVPACK.@)
242  *
243  * Determines whether a reboot is required.
244  *
245  * PARAMS
246  *   dwRebootCheck [I] Value from NeedRebootInit.
247  *
248  * RETURNS
249  *   TRUE if a reboot is needed, FALSE otherwise.
250  *
251  * BUGS
252  *   Unimplemented.
253  */
254 BOOL WINAPI NeedReboot(DWORD dwRebootCheck)
255 {
256     FIXME("(0x%08lx): stub\n", dwRebootCheck);
257     return FALSE;
258 }
259
260 /***********************************************************************
261  *             OpenINFEngineA   (ADVPACK.@)
262  *
263  * See OpenINFEngineW.
264  */
265 HRESULT WINAPI OpenINFEngineA(LPCSTR pszInfFilename, LPCSTR pszInstallSection,
266                               DWORD dwFlags, HINF *phInf, PVOID pvReserved)
267 {
268     UNICODE_STRING filenameW, installW;
269     HRESULT res;
270
271     TRACE("(%p, %p, %ld, %p, %p)\n", pszInfFilename, pszInstallSection,
272           dwFlags, phInf, pvReserved);
273
274     if (!pszInfFilename || !phInf)
275         return E_INVALIDARG;
276
277     RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename);
278     RtlCreateUnicodeStringFromAsciiz(&installW, pszInstallSection);
279
280     res = OpenINFEngineW(filenameW.Buffer, installW.Buffer,
281                          dwFlags, phInf, pvReserved);
282
283     RtlFreeUnicodeString(&filenameW);
284     RtlFreeUnicodeString(&installW);
285
286     return res;
287 }
288
289 /***********************************************************************
290  *             OpenINFEngineW   (ADVPACK.@)
291  *
292  * Opens and returns a handle to an INF file to be used by
293  * TranslateInfStringEx to continuously translate the INF file.
294  *
295  * PARAMS
296  *   pszInfFilename    [I] Filename of the INF to open.
297  *   pszInstallSection [I] Name of the Install section in the INF.
298  *   dwFlags           [I] See advpub.h.
299  *   phInf             [O] Handle to the loaded INF file.
300  *   pvReserved        [I] Reserved.  Must be NULL.
301  *
302  * RETURNS
303  *   Success: S_OK.
304  *   Failure: E_FAIL.
305  */
306 HRESULT WINAPI OpenINFEngineW(LPCWSTR pszInfFilename, LPCWSTR pszInstallSection,
307                               DWORD dwFlags, HINF *phInf, PVOID pvReserved)
308 {
309     TRACE("(%p, %p, %ld, %p, %p)\n", debugstr_w(pszInfFilename),
310           debugstr_w(pszInstallSection), dwFlags, phInf, pvReserved);
311
312     if (!pszInfFilename || !phInf)
313         return E_INVALIDARG;
314
315     *phInf = SetupOpenInfFileW(pszInfFilename, NULL, INF_STYLE_WIN4, NULL);
316     if (*phInf == INVALID_HANDLE_VALUE)
317         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
318
319     set_ldids(*phInf, pszInstallSection);
320     
321     return S_OK;
322 }
323
324 /***********************************************************************
325  *             RebootCheckOnInstallA   (ADVPACK.@)
326  *
327  * See RebootCheckOnInstallW.
328  */
329 HRESULT WINAPI RebootCheckOnInstallA(HWND hWnd, LPCSTR pszINF,
330                                      LPSTR pszSec, DWORD dwReserved)
331 {
332     UNICODE_STRING infW, secW;
333     HRESULT res;
334
335     TRACE("(%p, %s, %s, %ld)\n", hWnd, debugstr_a(pszINF),
336           debugstr_a(pszSec), dwReserved);
337
338     if (!pszINF || !pszSec)
339         return E_INVALIDARG;
340
341     RtlCreateUnicodeStringFromAsciiz(&infW, pszINF);
342     RtlCreateUnicodeStringFromAsciiz(&secW, pszSec);
343
344     res = RebootCheckOnInstallW(hWnd, infW.Buffer, secW.Buffer, dwReserved);
345
346     RtlFreeUnicodeString(&infW);
347     RtlFreeUnicodeString(&secW);
348
349     return res;
350 }
351
352 /***********************************************************************
353  *             RebootCheckOnInstallW   (ADVPACK.@)
354  *
355  * Checks if a reboot is required for an installed INF section.
356  *
357  * PARAMS
358  *   hWnd       [I] Handle to the window used for messages.
359  *   pszINF     [I] Filename of the INF file.
360  *   pszSec     [I] INF section to check.
361  *   dwReserved [I] Reserved.  Must be 0.
362  *
363  * RETURNS
364  *   Success: S_OK - Reboot is needed if the INF section is installed.
365  *            S_FALSE - Reboot is not needed.
366  *   Failure: HRESULT of GetLastError().
367  *
368  * NOTES
369  *   if pszSec is NULL, RebootCheckOnInstall checks the DefaultInstall
370  *   or DefaultInstall.NT section.
371  *
372  * BUGS
373  *   Unimplemented.
374  */
375 HRESULT WINAPI RebootCheckOnInstallW(HWND hWnd, LPCWSTR pszINF,
376                                      LPWSTR pszSec, DWORD dwReserved)
377 {
378     FIXME("(%p, %s, %s, %ld) stub\n", hWnd, debugstr_w(pszINF),
379           debugstr_w(pszSec), dwReserved);
380
381     return E_FAIL;
382 }
383
384 /***********************************************************************
385  *             RegisterOCX    (ADVPACK.@)
386  */
387 void WINAPI RegisterOCX( HWND hWnd, HINSTANCE hInst, LPCSTR cmdline, INT show )
388 {
389     WCHAR wszBuff[MAX_PATH];
390     WCHAR* pwcComma;
391     HMODULE hm;
392     DLLREGISTER pfnRegister;
393     HRESULT hr;
394
395     TRACE("(%s)\n", cmdline);
396
397     MultiByteToWideChar(CP_ACP, 0, cmdline, strlen(cmdline), wszBuff, MAX_PATH);
398     if ((pwcComma = strchrW( wszBuff, ',' ))) *pwcComma = 0;
399
400     TRACE("Parsed DLL name (%s)\n", debugstr_w(wszBuff));
401
402     hm = LoadLibraryExW(wszBuff, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
403     if (!hm)
404     {
405         ERR("Couldn't load DLL: %s\n", debugstr_w(wszBuff));
406         return;
407     }
408
409     pfnRegister = (DLLREGISTER)GetProcAddress(hm, "DllRegisterServer");
410     if (pfnRegister == NULL)
411     {
412         ERR("DllRegisterServer entry point not found\n");
413     }
414     else
415     {
416         hr = pfnRegister();
417         if (hr != S_OK)
418         {
419             ERR("DllRegisterServer entry point returned %08lx\n", hr);
420         }
421     }
422
423     TRACE("Successfully registered OCX\n");
424
425     FreeLibrary(hm);
426 }
427
428 /***********************************************************************
429  *             SetPerUserSecValuesA   (ADVPACK.@)
430  *
431  * Prepares the per-user stub values under IsInstalled\{GUID} that
432  * control the per-user installation.
433  *
434  * PARAMS
435  *   pPerUser [I] Per-user stub values.
436  *
437  * RETURNS
438  *   Success: S_OK.
439  *   Failure: E_FAIL.
440  *
441  * BUGS
442  *   Unimplemented.
443  */
444 HRESULT WINAPI SetPerUserSecValuesA(PERUSERSECTIONA* pPerUser)
445 {
446     FIXME("(%p) stub\n", pPerUser);
447
448     return E_FAIL;
449 }
450
451 /***********************************************************************
452  *             TranslateInfStringA   (ADVPACK.@)
453  *
454  * See TranslateInfStringW.
455  */
456 HRESULT WINAPI TranslateInfStringA(LPCSTR pszInfFilename, LPCSTR pszInstallSection,
457                 LPCSTR pszTranslateSection, LPCSTR pszTranslateKey, LPSTR pszBuffer,
458                 DWORD dwBufferSize, PDWORD pdwRequiredSize, PVOID pvReserved)
459 {
460     UNICODE_STRING filenameW, installW;
461     UNICODE_STRING translateW, keyW;
462     LPWSTR bufferW;
463     HRESULT res;
464     DWORD len = 0;
465
466     TRACE("(%s %s %s %s %p %ld %p %p)\n",
467           debugstr_a(pszInfFilename), debugstr_a(pszInstallSection),
468           debugstr_a(pszTranslateSection), debugstr_a(pszTranslateKey),
469           pszBuffer, dwBufferSize,pdwRequiredSize, pvReserved);
470
471     if (!pszInfFilename || !pszTranslateSection ||
472         !pszTranslateKey || !pdwRequiredSize)
473         return E_INVALIDARG;
474
475     RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename);
476     RtlCreateUnicodeStringFromAsciiz(&installW, pszInstallSection);
477     RtlCreateUnicodeStringFromAsciiz(&translateW, pszTranslateSection);
478     RtlCreateUnicodeStringFromAsciiz(&keyW, pszTranslateKey);
479
480     res = TranslateInfStringW(filenameW.Buffer, installW.Buffer,
481                               translateW.Buffer, keyW.Buffer, NULL,
482                               dwBufferSize, &len, NULL);
483
484     if (res == S_OK)
485     {
486         bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
487
488         res = TranslateInfStringW(filenameW.Buffer, installW.Buffer,
489                                   translateW.Buffer, keyW.Buffer, bufferW,
490                                   len, &len, NULL);
491         if (res == S_OK)
492         {
493             *pdwRequiredSize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1,
494                                                    NULL, 0, NULL, NULL);
495
496             if (dwBufferSize >= *pdwRequiredSize)
497             {
498                 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, pszBuffer,
499                                     dwBufferSize, NULL, NULL);
500             }
501             else
502                 res = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
503         }
504         
505         HeapFree(GetProcessHeap(), 0, bufferW);
506     }
507
508     RtlFreeUnicodeString(&filenameW);
509     RtlFreeUnicodeString(&installW);
510     RtlFreeUnicodeString(&translateW);
511     RtlFreeUnicodeString(&keyW);
512
513     return res;
514 }
515
516 /***********************************************************************
517  *             TranslateInfStringW   (ADVPACK.@)
518  *
519  * Translates the value of a specified key in an inf file into the
520  * current locale by expanding string macros.
521  *
522  * PARAMS
523  *   pszInfFilename      [I] Filename of the inf file.
524  *   pszInstallSection   [I]
525  *   pszTranslateSection [I] Inf section where the key exists.
526  *   pszTranslateKey     [I] Key to translate.
527  *   pszBuffer           [O] Contains the translated string on exit.
528  *   dwBufferSize        [I] Size on input of pszBuffer.
529  *   pdwRequiredSize     [O] Length of the translated key.
530  *   pvReserved          [I] Reserved, must be NULL.
531  *
532  * RETURNS
533  *   Success: S_OK.
534  *   Failure: An hresult error code.
535  */
536 HRESULT WINAPI TranslateInfStringW(LPCWSTR pszInfFilename, LPCWSTR pszInstallSection,
537                 LPCWSTR pszTranslateSection, LPCWSTR pszTranslateKey, LPWSTR pszBuffer,
538                 DWORD dwBufferSize, PDWORD pdwRequiredSize, PVOID pvReserved)
539 {
540     HINF hInf;
541
542     TRACE("(%s %s %s %s %p %ld %p %p)\n",
543           debugstr_w(pszInfFilename), debugstr_w(pszInstallSection),
544           debugstr_w(pszTranslateSection), debugstr_w(pszTranslateKey),
545           pszBuffer, dwBufferSize,pdwRequiredSize, pvReserved);
546
547     if (!pszInfFilename || !pszTranslateSection ||
548         !pszTranslateKey || !pdwRequiredSize)
549         return E_INVALIDARG;
550
551     hInf = SetupOpenInfFileW(pszInfFilename, NULL, INF_STYLE_WIN4, NULL);
552     if (hInf == INVALID_HANDLE_VALUE)
553         return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
554
555     set_ldids(hInf, pszInstallSection);
556
557     if (!SetupGetLineTextW(NULL, hInf, pszTranslateSection, pszTranslateKey,
558                            pszBuffer, dwBufferSize, pdwRequiredSize))
559     {
560         if (dwBufferSize < *pdwRequiredSize)
561             return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
562
563         return SPAPI_E_LINE_NOT_FOUND;
564     }
565
566     SetupCloseInfFile(hInf);
567     return S_OK;
568 }
569
570 /***********************************************************************
571  *             TranslateInfStringExA   (ADVPACK.@)
572  *
573  * See TranslateInfStringExW.
574  */
575 HRESULT WINAPI TranslateInfStringExA(HINF hInf, LPCSTR pszInfFilename,
576                                     LPCSTR pszTranslateSection, LPCSTR pszTranslateKey,
577                                     LPSTR pszBuffer, DWORD dwBufferSize,
578                                     PDWORD pdwRequiredSize, PVOID pvReserved)
579 {
580     UNICODE_STRING filenameW, sectionW, keyW;
581     LPWSTR bufferW;
582     HRESULT res;
583     DWORD len = 0;
584
585     TRACE("(%p, %p, %p, %p, %p, %ld, %p, %p)\n", hInf, debugstr_a(pszInfFilename),
586           debugstr_a(pszTranslateSection), debugstr_a(pszTranslateKey),
587           debugstr_a(pszBuffer), dwBufferSize, pdwRequiredSize, pvReserved);
588
589     if (!pszInfFilename || !pszTranslateSection ||
590         !pszTranslateKey || !pdwRequiredSize)
591         return E_INVALIDARG;
592
593     RtlCreateUnicodeStringFromAsciiz(&filenameW, pszInfFilename);
594     RtlCreateUnicodeStringFromAsciiz(&sectionW, pszTranslateSection);
595     RtlCreateUnicodeStringFromAsciiz(&keyW, pszTranslateKey);
596
597     res = TranslateInfStringExW(hInf, filenameW.Buffer, sectionW.Buffer,
598                                 keyW.Buffer, NULL, 0, &len, NULL);
599
600     if (res == S_OK)
601     {
602         bufferW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
603
604         res = TranslateInfStringExW(hInf, filenameW.Buffer, sectionW.Buffer,
605                                 keyW.Buffer, bufferW, len, &len, NULL);
606
607         if (res == S_OK)
608         {
609             *pdwRequiredSize = WideCharToMultiByte(CP_ACP, 0, bufferW, -1,
610                                                    NULL, 0, NULL, NULL);
611
612             if (dwBufferSize >= *pdwRequiredSize)
613             {
614                 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, pszBuffer,
615                                     dwBufferSize, NULL, NULL);
616             }
617             else
618                 res = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
619         }
620         
621         HeapFree(GetProcessHeap(), 0, bufferW);
622     }
623
624     RtlFreeUnicodeString(&filenameW);
625     RtlFreeUnicodeString(&sectionW);
626     RtlFreeUnicodeString(&keyW);
627
628     return res;
629 }
630
631 /***********************************************************************
632  *             TranslateInfStringExW   (ADVPACK.@)
633  *
634  * Using a handle to an INF file opened with OpenINFEngine, translates
635  * the value of a specified key in an inf file into the current locale
636  * by expanding string macros.
637  *
638  * PARAMS
639  *   hInf                [I] Handle to the INF file.
640  *   pszInfFilename      [I] Filename of the INF file.
641  *   pszTranslateSection [I] Inf section where the key exists.
642  *   pszTranslateKey     [I] Key to translate.
643  *   pszBuffer           [O] Contains the translated string on exit.
644  *   dwBufferSize        [I] Size on input of pszBuffer.
645  *   pdwRequiredSize     [O] Length of the translated key.
646  *   pvReserved          [I] Reserved.  Must be NULL.
647  *
648  * RETURNS
649  *   Success: S_OK.
650  *   Failure: E_FAIL.
651  *
652  * NOTES
653  *   To use TranslateInfStringEx to translate an INF file continuously,
654  *   open the INF file with OpenINFEngine, call TranslateInfStringEx as
655  *   many times as needed, then release the handle with CloseINFEngine.
656  *   When translating more than one keys, this method is more efficient
657  *   than calling TranslateInfString, because the INF file is only
658  *   opened once.
659  */
660 HRESULT WINAPI TranslateInfStringExW(HINF hInf, LPCWSTR pszInfFilename,
661                                      LPCWSTR pszTranslateSection, LPCWSTR pszTranslateKey,
662                                      LPWSTR pszBuffer, DWORD dwBufferSize,
663                                      PDWORD pdwRequiredSize, PVOID pvReserved)
664 {
665     TRACE("(%p, %p, %p, %p, %p, %ld, %p, %p)\n", hInf, debugstr_w(pszInfFilename),
666           debugstr_w(pszTranslateSection), debugstr_w(pszTranslateKey),
667           debugstr_w(pszBuffer), dwBufferSize, pdwRequiredSize, pvReserved);
668
669     if (!hInf || !pszInfFilename || !pszTranslateSection || !pszTranslateKey)
670         return E_INVALIDARG;
671
672     if (!SetupGetLineTextW(NULL, hInf, pszTranslateSection, pszTranslateKey,
673                            pszBuffer, dwBufferSize, pdwRequiredSize))
674     {
675         if (dwBufferSize < *pdwRequiredSize)
676             return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
677
678         return SPAPI_E_LINE_NOT_FOUND;
679     }
680
681     return S_OK;   
682 }
683
684 /***********************************************************************
685  *             UserInstStubWrapperA   (ADVPACK.@)
686  *
687  * See UserInstStubWrapperW.
688  */
689 HRESULT WINAPI UserInstStubWrapperA(HWND hWnd, HINSTANCE hInstance,
690                                    LPSTR pszParms, INT nShow)
691 {
692     UNICODE_STRING parmsW;
693     HRESULT res;
694
695     TRACE("(%p, %p, %p, %i)\n", hWnd, hInstance, pszParms, nShow);
696
697     if (!pszParms)
698         return E_INVALIDARG;
699
700     RtlCreateUnicodeStringFromAsciiz(&parmsW, pszParms);
701
702     res = UserInstStubWrapperW(hWnd, hInstance, parmsW.Buffer, nShow);
703
704     RtlFreeUnicodeString(&parmsW);
705
706     return res;
707 }
708
709 /***********************************************************************
710  *             UserInstStubWrapperW   (ADVPACK.@)
711  */
712 HRESULT WINAPI UserInstStubWrapperW(HWND hWnd, HINSTANCE hInstance,
713                                     LPWSTR pszParms, INT nShow)
714 {
715     FIXME("(%p, %p, %p, %i) stub\n", hWnd, hInstance, pszParms, nShow);
716
717     return E_FAIL;
718 }
719
720 /***********************************************************************
721  *             UserUnInstStubWrapperA   (ADVPACK.@)
722  *
723  * See UserUnInstStubWrapperW.
724  */
725 HRESULT WINAPI UserUnInstStubWrapperA(HWND hWnd, HINSTANCE hInstance,
726                                       LPSTR pszParms, INT nShow)
727 {
728     UNICODE_STRING parmsW;
729     HRESULT res;
730
731     TRACE("(%p, %p, %p, %i)\n", hWnd, hInstance, pszParms, nShow);
732
733     if (!pszParms)
734         return E_INVALIDARG;
735
736     RtlCreateUnicodeStringFromAsciiz(&parmsW, pszParms);
737
738     res = UserUnInstStubWrapperW(hWnd, hInstance, parmsW.Buffer, nShow);
739
740     RtlFreeUnicodeString(&parmsW);
741
742     return res;
743 }
744
745 /***********************************************************************
746  *             UserUnInstStubWrapperW   (ADVPACK.@)
747  */
748 HRESULT WINAPI UserUnInstStubWrapperW(HWND hWnd, HINSTANCE hInstance,
749                                       LPWSTR pszParms, INT nShow)
750 {
751     FIXME("(%p, %p, %p, %i) stub\n", hWnd, hInstance, pszParms, nShow);
752
753     return E_FAIL;
754 }