advpack: Leave room for the NULL terminator in the working_dir string.
[wine] / dlls / advpack / install.c
1 /*
2  * Advpack install functions
3  *
4  * Copyright 2006 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #include <stdlib.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/debug.h"
34 #include "wine/unicode.h"
35 #include "advpack_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
38
39 #define SPAPI_ERROR     0xE0000000L
40 #define SPAPI_PREFIX    0x800F0000L
41 #define SPAPI_MASK      0xFFFFL
42 #define HRESULT_FROM_SPAPI(x)   ((x & SPAPI_MASK) | SPAPI_PREFIX)
43
44 #define ADV_HRESULT(x)  ((x & SPAPI_ERROR) ? HRESULT_FROM_SPAPI(x) : HRESULT_FROM_WIN32(x))
45
46 /* contains information about a specific install instance */
47 typedef struct _ADVInfo
48 {
49     HINF hinf;
50     LPWSTR inf_filename;
51     LPWSTR install_sec;
52     LPWSTR working_dir;
53     DWORD flags;
54     BOOL need_reboot;
55 } ADVInfo;
56
57 typedef HRESULT (*iterate_fields_func)(HINF hinf, PCWSTR field, void *arg);
58
59 /* Advanced INF commands */
60 static const WCHAR RegisterOCXs[] = {'R','e','g','i','s','t','e','r','O','C','X','s',0};
61 static const WCHAR RunPostSetupCommands[] = {
62     'R','u','n','P','o','s','t','S','e','t','u','p','C','o','m','m','a','n','d','s',0
63 };
64
65 /* Advanced INF callbacks */
66 static HRESULT register_ocxs_callback(HINF hinf, PCWSTR field, void *arg)
67 {
68     HMODULE hm;
69     INFCONTEXT context;
70     HRESULT hr = S_OK;
71
72     BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
73     
74     for (; ok; ok = SetupFindNextLine(&context, &context))
75     {
76         WCHAR buffer[MAX_INF_STRING_LENGTH];
77
78         /* get OCX filename */
79         if (!SetupGetStringFieldW(&context, 1, buffer,
80                                   sizeof(buffer) / sizeof(WCHAR), NULL))
81             continue;
82
83         hm = LoadLibraryExW(buffer, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
84         if (!hm)
85         {
86             hr = E_FAIL;
87             continue;
88         }
89
90         if (do_ocx_reg(hm, TRUE))
91             hr = E_FAIL;
92
93         FreeLibrary(hm);
94     }
95
96     return hr;
97 }
98
99 static HRESULT run_post_setup_commands_callback(HINF hinf, PCWSTR field, void *arg)
100 {
101     ADVInfo *info = (ADVInfo *)arg;
102     INFCONTEXT context;
103     HRESULT hr = S_OK;
104     DWORD size;
105
106     BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
107
108     for (; ok; ok = SetupFindNextLine(&context, &context))
109     {
110         WCHAR buffer[MAX_INF_STRING_LENGTH];
111
112         if (!SetupGetLineTextW(&context, NULL, NULL, NULL, buffer,
113                                MAX_INF_STRING_LENGTH, &size))
114             continue;
115
116         if (launch_exe(buffer, info->working_dir, NULL))
117             hr = E_FAIL;
118     }
119
120     return hr;
121 }
122
123 /* sequentially returns pointers to parameters in a parameter list
124  * returns NULL if the parameter is empty, e.g. one,,three  */
125 LPWSTR get_parameter(LPWSTR *params, WCHAR separator)
126 {
127     LPWSTR token = *params;
128
129     if (!*params)
130         return NULL;
131
132     *params = strchrW(*params, separator);
133     if (*params)
134         *(*params)++ = '\0';
135
136     if (!*token)
137         return NULL;
138
139     return token;
140 }
141
142 static BOOL is_full_path(LPWSTR path)
143 {
144     const int MIN_PATH_LEN = 3;
145
146     if (!path || lstrlenW(path) < MIN_PATH_LEN)
147         return FALSE;
148
149     if (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'))
150         return TRUE;
151
152     return FALSE;
153 }
154
155 /* retrieves the contents of a field, dynamically growing the buffer if necessary */
156 static WCHAR *get_field_string(INFCONTEXT *context, DWORD index, WCHAR *buffer,
157                                WCHAR *static_buffer, DWORD *size)
158 {
159     DWORD required;
160
161     if (SetupGetStringFieldW(context, index, buffer, *size, &required)) return buffer;
162
163     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
164     {
165         /* now grow the buffer */
166         if (buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
167         if (!(buffer = HeapAlloc(GetProcessHeap(), 0, required*sizeof(WCHAR)))) return NULL;
168         *size = required;
169         if (SetupGetStringFieldW(context, index, buffer, *size, &required)) return buffer;
170     }
171
172     if (buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
173     return NULL;
174 }
175
176 /* iterates over all fields of a certain key of a certain section */
177 static HRESULT iterate_section_fields(HINF hinf, PCWSTR section, PCWSTR key,
178                                       iterate_fields_func callback, void *arg)
179 {
180     WCHAR static_buffer[200];
181     WCHAR *buffer = static_buffer;
182     DWORD size = sizeof(static_buffer) / sizeof(WCHAR);
183     INFCONTEXT context;
184     HRESULT hr = E_FAIL;
185
186     BOOL ok = SetupFindFirstLineW(hinf, section, key, &context);
187     while (ok)
188     {
189         UINT i, count = SetupGetFieldCount(&context);
190
191         for (i = 1; i <= count; i++)
192         {
193             if (!(buffer = get_field_string(&context, i, buffer, static_buffer, &size)))
194                 goto done;
195
196             if ((hr = callback(hinf, buffer, arg)) != S_OK)
197                 goto done;
198         }
199
200         ok = SetupFindNextMatchLineW(&context, key, &context);
201     }
202
203     hr = S_OK;
204
205  done:
206     if (buffer && buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
207     return hr;
208 }
209
210 /* performs a setupapi-level install of the INF file */
211 static HRESULT spapi_install(ADVInfo *info)
212 {
213     BOOL ret;
214     HRESULT res;
215     PVOID context;
216
217     context = SetupInitDefaultQueueCallbackEx(NULL, INVALID_HANDLE_VALUE, 0, 0, NULL);
218     if (!context)
219         return ADV_HRESULT(GetLastError());
220
221     ret = SetupInstallFromInfSectionW(NULL, info->hinf, info->install_sec,
222                                       SPINST_FILES, NULL, info->working_dir,
223                                       SP_COPY_NEWER, SetupDefaultQueueCallbackW,
224                                       context, NULL, NULL);
225     if (!ret)
226     {
227         res = ADV_HRESULT(GetLastError());
228         SetupTermDefaultQueueCallback(context);
229
230         return res;
231     }
232
233     SetupTermDefaultQueueCallback(context);
234
235     ret = SetupInstallFromInfSectionW(NULL, info->hinf, info->install_sec,
236                                       SPINST_INIFILES | SPINST_REGISTRY,
237                                       HKEY_LOCAL_MACHINE, NULL, 0,
238                                       NULL, NULL, NULL, NULL);
239     if (!ret)
240         return ADV_HRESULT(GetLastError());
241
242     return S_OK;
243 }
244
245 /* processes the Advanced INF commands */
246 static HRESULT adv_install(ADVInfo *info)
247 {
248     HRESULT hr;
249
250     hr = iterate_section_fields(info->hinf, info->install_sec,
251                                 RegisterOCXs, register_ocxs_callback, NULL);
252     if (hr != S_OK)
253         return hr;
254
255     hr = iterate_section_fields(info->hinf, info->install_sec, RunPostSetupCommands,
256                                 run_post_setup_commands_callback, info);
257     if (hr != S_OK)
258         return hr;
259
260     return hr;
261 }
262
263 /* loads the INF file and performs checks on it */
264 HRESULT install_init(LPCWSTR inf_filename, LPCWSTR install_sec,
265                      LPCWSTR working_dir, DWORD flags, ADVInfo *info)
266 {
267     DWORD len;
268     LPCWSTR ptr;
269
270     static const WCHAR default_install[] = {
271         'D','e','f','a','u','l','t','I','n','s','t','a','l','l',0
272     };
273
274     len = lstrlenW(inf_filename);
275
276     info->inf_filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
277     if (!info->inf_filename)
278         return E_OUTOFMEMORY;
279
280     lstrcpyW(info->inf_filename, inf_filename);
281
282     /* FIXME: determine the proper platform to install (NTx86, etc) */
283     if (!install_sec || !*install_sec)
284     {
285         len = sizeof(default_install) - 1;
286         ptr = default_install;
287     }
288     else
289     {
290         len = lstrlenW(install_sec);
291         ptr = install_sec;
292     }
293
294     info->install_sec = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
295     if (!info->install_sec)
296         return E_OUTOFMEMORY;
297
298     lstrcpyW(info->install_sec, ptr);
299
300     /* FIXME: need to get the real working directory */
301     if (!working_dir || !*working_dir)
302     {
303         ptr = strrchrW(info->inf_filename, '\\');
304         len = ptr - info->inf_filename + 1;
305         ptr = info->inf_filename;
306     }
307     else
308     {
309         len = lstrlenW(working_dir) + 1;
310         ptr = working_dir;
311     }
312
313     info->working_dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
314     if (!info->working_dir)
315         return E_OUTOFMEMORY;
316
317     lstrcpynW(info->working_dir, ptr, len);
318
319     info->hinf = SetupOpenInfFileW(info->inf_filename, NULL, INF_STYLE_WIN4, NULL);
320     if (info->hinf == INVALID_HANDLE_VALUE)
321         return ADV_HRESULT(GetLastError());
322
323     set_ldids(info->hinf, info->install_sec, info->working_dir);
324
325     /* FIXME: check that the INF is advanced */
326
327     info->flags = flags;
328     info->need_reboot = FALSE;
329
330     return S_OK;
331 }
332
333 /* release the install instance information */
334 void install_release(ADVInfo *info)
335 {
336     if (info->hinf && info->hinf != INVALID_HANDLE_VALUE)
337         SetupCloseInfFile(info->hinf);
338
339     HeapFree(GetProcessHeap(), 0, info->inf_filename);
340     HeapFree(GetProcessHeap(), 0, info->install_sec);
341     HeapFree(GetProcessHeap(), 0, info->working_dir);
342 }
343
344 /* this structure very closely resembles parameters of RunSetupCommand() */
345 typedef struct
346 {
347     HWND hwnd;
348     LPCSTR title;
349     LPCSTR inf_name;
350     LPCSTR dir;
351     LPCSTR section_name;
352 } SETUPCOMMAND_PARAMS;
353
354 /***********************************************************************
355  *      DoInfInstall  (ADVPACK.@)
356  *
357  * Install an INF section.
358  *
359  * PARAMS
360  *  setup [I] Structure containing install information.
361  *
362  * RETURNS
363  *   S_OK                                Everything OK
364  *   HRESULT_FROM_WIN32(GetLastError())  Some other error
365  */
366 HRESULT WINAPI DoInfInstall(const SETUPCOMMAND_PARAMS *setup)
367 {
368     BOOL ret;
369     HINF hinf;
370     void *callback_context;
371
372     TRACE("(%p)\n", setup);
373
374     hinf = SetupOpenInfFileA(setup->inf_name, NULL, INF_STYLE_WIN4, NULL);
375     if (hinf == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError());
376
377     callback_context = SetupInitDefaultQueueCallback(setup->hwnd);
378
379     ret = SetupInstallFromInfSectionA(NULL, hinf, setup->section_name, SPINST_ALL,
380                                       NULL, NULL, 0, SetupDefaultQueueCallbackA,
381                                       callback_context, NULL, NULL);
382     SetupTermDefaultQueueCallback(callback_context);
383     SetupCloseInfFile(hinf);
384
385     return ret ? S_OK : HRESULT_FROM_WIN32(GetLastError());
386 }
387
388 /***********************************************************************
389  *             ExecuteCabA    (ADVPACK.@)
390  *
391  * See ExecuteCabW.
392  */
393 HRESULT WINAPI ExecuteCabA(HWND hwnd, CABINFOA* pCab, LPVOID pReserved)
394 {
395     UNICODE_STRING cab, inf, section;
396     CABINFOW cabinfo;
397     HRESULT hr;
398
399     TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
400
401     if (!pCab)
402         return E_INVALIDARG;
403
404     if (pCab->pszCab)
405     {
406         RtlCreateUnicodeStringFromAsciiz(&cab, pCab->pszCab);
407         cabinfo.pszCab = cab.Buffer;
408     }
409     else
410         cabinfo.pszCab = NULL;
411
412     RtlCreateUnicodeStringFromAsciiz(&inf, pCab->pszInf);
413     RtlCreateUnicodeStringFromAsciiz(&section, pCab->pszSection);
414     
415     MultiByteToWideChar(CP_ACP, 0, pCab->szSrcPath, -1, cabinfo.szSrcPath,
416                         sizeof(cabinfo.szSrcPath) / sizeof(WCHAR));
417
418     cabinfo.pszInf = inf.Buffer;
419     cabinfo.pszSection = section.Buffer;
420     cabinfo.dwFlags = pCab->dwFlags;
421
422     hr = ExecuteCabW(hwnd, &cabinfo, pReserved);
423
424     if (pCab->pszCab)
425         RtlFreeUnicodeString(&cab);
426
427     RtlFreeUnicodeString(&inf);
428     RtlFreeUnicodeString(&section);
429
430     return hr;
431 }
432
433 /***********************************************************************
434  *             ExecuteCabW    (ADVPACK.@)
435  * 
436  * Installs the INF file extracted from a specified cabinet file.
437  * 
438  * PARAMS
439  *   hwnd      [I] Handle to the window used for the display.
440  *   pCab      [I] Information about the cabinet file.
441  *   pReserved [I] Reserved.  Must be NULL.
442  * 
443  * RETURNS
444  *   Success: S_OK.
445  *   Failure: E_FAIL.
446  */
447 HRESULT WINAPI ExecuteCabW(HWND hwnd, CABINFOW* pCab, LPVOID pReserved)
448 {
449     ADVInfo info;
450     HRESULT hr;
451
452     TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
453
454     ZeroMemory(&info, sizeof(ADVInfo));
455
456     if (pCab->pszCab && *pCab->pszCab)
457         FIXME("Cab archive not extracted!\n");
458
459     hr = install_init(pCab->pszInf, pCab->pszSection, pCab->szSrcPath, pCab->dwFlags, &info);
460     if (hr != S_OK)
461         goto done;
462
463     hr = spapi_install(&info);
464     if (hr != S_OK)
465         goto done;
466
467     hr = adv_install(&info);
468
469 done:
470     install_release(&info);
471
472     return hr;
473 }
474
475 /***********************************************************************
476  *      LaunchINFSectionA   (ADVPACK.@)
477  *
478  * See LaunchINFSectionW.
479  */
480 INT WINAPI LaunchINFSectionA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
481 {
482     UNICODE_STRING cmd;
483     HRESULT hr;
484
485     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
486
487     RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
488
489     hr = LaunchINFSectionW(hWnd, hInst, cmd.Buffer, show);
490
491     RtlFreeUnicodeString(&cmd);
492
493     return hr;
494 }
495
496 /***********************************************************************
497  *      LaunchINFSectionW   (ADVPACK.@)
498  *
499  * Installs an INF section without BACKUP/ROLLBACK capabilities.
500  *
501  * PARAMS
502  *   hWnd    [I] Handle to parent window, NULL for desktop.
503  *   hInst   [I] Instance of the process.
504  *   cmdline [I] Contains parameters in the order INF,section,flags,reboot.
505  *   show    [I] How the window should be shown.
506  *
507  * RETURNS
508  *  Success: S_OK.
509  *  Failure: S_FALSE
510  *
511  * NOTES
512  *  INF - Filename of the INF to launch.
513  *  section - INF section to install.
514  *  flags - see advpub.h.
515  *  reboot - smart reboot behavior
516  *    'A' Always reboot.
517  *    'I' Reboot if needed (default).
518  *    'N' No reboot.
519  */
520 INT WINAPI LaunchINFSectionW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
521 {
522     ADVInfo info;
523     LPWSTR cmdline_copy, cmdline_ptr;
524     LPWSTR inf_filename, install_sec;
525     LPWSTR str_flags;
526     DWORD flags = 0;
527     HRESULT hr = S_OK;
528
529     TRACE("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_w(cmdline), show);
530
531     if (!cmdline)
532         return E_INVALIDARG;
533
534     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
535     cmdline_ptr = cmdline_copy;
536     lstrcpyW(cmdline_copy, cmdline);
537
538     inf_filename = get_parameter(&cmdline_ptr, ',');
539     install_sec = get_parameter(&cmdline_ptr, ',');
540
541     str_flags = get_parameter(&cmdline_ptr, ',');
542     if (str_flags)
543         flags = atolW(str_flags);
544
545     ZeroMemory(&info, sizeof(ADVInfo));
546
547     hr = install_init(inf_filename, install_sec, NULL, flags, &info);
548     if (hr != S_OK)
549         goto done;
550
551     hr = spapi_install(&info);
552     if (hr != S_OK)
553         goto done;
554
555     hr = adv_install(&info);
556
557 done:
558     install_release(&info);
559     HeapFree(GetProcessHeap(), 0, cmdline_copy);
560
561     return hr;
562 }
563
564 /***********************************************************************
565  *      LaunchINFSectionExA (ADVPACK.@)
566  *
567  * See LaunchINFSectionExW.
568  */
569 HRESULT WINAPI LaunchINFSectionExA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
570 {
571     UNICODE_STRING cmd;
572     HRESULT hr;
573
574     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
575
576     RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
577
578     hr = LaunchINFSectionExW(hWnd, hInst, cmd.Buffer, show);
579
580     RtlFreeUnicodeString(&cmd);
581
582     return hr;
583 }
584
585 /***********************************************************************
586  *      LaunchINFSectionExW (ADVPACK.@)
587  *
588  * Installs an INF section with BACKUP/ROLLBACK capabilities.
589  *
590  * PARAMS
591  *   hWnd    [I] Handle to parent window, NULL for desktop.
592  *   hInst   [I] Instance of the process.
593  *   cmdline [I] Contains parameters in the order INF,section,CAB,flags,reboot.
594  *   show    [I] How the window should be shown.
595  *
596  * RETURNS
597  *  Success: S_OK.
598  *  Failure: E_FAIL.
599  *
600  * NOTES
601  *  INF - Filename of the INF to launch.
602  *  section - INF section to install.
603  *  flags - see advpub.h.
604  *  reboot - smart reboot behavior
605  *    'A' Always reboot.
606  *    'I' Reboot if needed (default).
607  *    'N' No reboot.
608  *
609  * BUGS
610  *  Doesn't handle the reboot flag.
611  */
612 HRESULT WINAPI LaunchINFSectionExW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
613 {
614     LPWSTR cmdline_copy, cmdline_ptr;
615     LPWSTR flags, ptr;
616     CABINFOW cabinfo;
617     HRESULT hr = S_OK;
618
619     TRACE("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_w(cmdline), show);
620
621     if (!cmdline)
622         return E_INVALIDARG;
623
624     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
625     cmdline_ptr = cmdline_copy;
626     lstrcpyW(cmdline_copy, cmdline);
627
628     cabinfo.pszInf = get_parameter(&cmdline_ptr, ',');
629     cabinfo.pszSection = get_parameter(&cmdline_ptr, ',');
630     cabinfo.pszCab = get_parameter(&cmdline_ptr, ',');
631     *cabinfo.szSrcPath = '\0';
632
633     flags = get_parameter(&cmdline_ptr, ',');
634     if (flags)
635         cabinfo.dwFlags = atolW(flags);
636
637     /* get the source path from the cab filename */
638     if (cabinfo.pszCab && *cabinfo.pszCab)
639     {
640         if (!is_full_path(cabinfo.pszCab))
641             goto done;
642
643         lstrcpyW(cabinfo.szSrcPath, cabinfo.pszCab);
644         ptr = strrchrW(cabinfo.szSrcPath, '\\');
645         *(++ptr) = '\0';
646     }
647
648     hr = ExecuteCabW(hWnd, &cabinfo, NULL);
649
650 done:
651     HeapFree(GetProcessHeap(), 0, cmdline_copy);
652
653     return hr;
654 }
655
656 HRESULT launch_exe(LPCWSTR cmd, LPCWSTR dir, HANDLE *phEXE)
657 {
658     STARTUPINFOW si;
659     PROCESS_INFORMATION pi;
660
661     if (phEXE) *phEXE = NULL;
662
663     ZeroMemory(&pi, sizeof(pi));
664     ZeroMemory(&si, sizeof(si));
665     si.cb = sizeof(si);
666
667     if (!CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, FALSE,
668                         CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP,
669                         NULL, dir, &si, &pi))
670     {
671         return HRESULT_FROM_WIN32(GetLastError());
672     }
673
674     CloseHandle(pi.hThread);
675
676     if (phEXE)
677     {
678         *phEXE = pi.hProcess;
679         return S_ASYNCHRONOUS;
680     }
681
682     /* wait for the child process to finish */
683     WaitForSingleObject(pi.hProcess, INFINITE);
684     CloseHandle(pi.hProcess);
685
686     return S_OK;
687 }
688
689 /***********************************************************************
690  *      RunSetupCommandA  (ADVPACK.@)
691  *
692  * See RunSetupCommandW.
693  */
694 HRESULT WINAPI RunSetupCommandA(HWND hWnd, LPCSTR szCmdName,
695                                 LPCSTR szInfSection, LPCSTR szDir,
696                                 LPCSTR lpszTitle, HANDLE *phEXE,
697                                 DWORD dwFlags, LPVOID pvReserved)
698 {
699     UNICODE_STRING cmdname, infsec;
700     UNICODE_STRING dir, title;
701     HRESULT hr;
702
703     TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
704           hWnd, debugstr_a(szCmdName), debugstr_a(szInfSection),
705           debugstr_a(szDir), debugstr_a(lpszTitle),
706           phEXE, dwFlags, pvReserved);
707
708     if (!szCmdName || !szDir)
709         return E_INVALIDARG;
710
711     RtlCreateUnicodeStringFromAsciiz(&cmdname, szCmdName);
712     RtlCreateUnicodeStringFromAsciiz(&infsec, szInfSection);
713     RtlCreateUnicodeStringFromAsciiz(&dir, szDir);
714     RtlCreateUnicodeStringFromAsciiz(&title, lpszTitle);
715
716     hr = RunSetupCommandW(hWnd, cmdname.Buffer, infsec.Buffer, dir.Buffer,
717                           title.Buffer, phEXE, dwFlags, pvReserved);
718
719     RtlFreeUnicodeString(&cmdname);
720     RtlFreeUnicodeString(&infsec);
721     RtlFreeUnicodeString(&dir);
722     RtlFreeUnicodeString(&title);
723
724     return hr;
725 }
726
727 /***********************************************************************
728  *      RunSetupCommandW  (ADVPACK.@)
729  *
730  * Executes an install section in an INF file or a program.
731  *
732  * PARAMS
733  *   hWnd          [I] Handle to parent window, NULL for quiet mode
734  *   szCmdName     [I] Inf or EXE filename to execute
735  *   szInfSection  [I] Inf section to install, NULL for DefaultInstall
736  *   szDir         [I] Path to extracted files
737  *   szTitle       [I] Title of all dialogs
738  *   phEXE         [O] Handle of EXE to wait for
739  *   dwFlags       [I] Flags; see include/advpub.h
740  *   pvReserved    [I] Reserved
741  *
742  * RETURNS
743  *   S_OK                                 Everything OK
744  *   S_ASYNCHRONOUS                       OK, required to wait on phEXE
745  *   ERROR_SUCCESS_REBOOT_REQUIRED        Reboot required
746  *   E_INVALIDARG                         Invalid argument given
747  *   HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION)
748  *                                        Not supported on this Windows version
749  *   E_UNEXPECTED                         Unexpected error
750  *   HRESULT_FROM_WIN32(GetLastError())   Some other error
751  *
752  * BUGS
753  *   INF install unimplemented.
754  */
755 HRESULT WINAPI RunSetupCommandW(HWND hWnd, LPCWSTR szCmdName,
756                                 LPCWSTR szInfSection, LPCWSTR szDir,
757                                 LPCWSTR lpszTitle, HANDLE *phEXE,
758                                 DWORD dwFlags, LPVOID pvReserved)
759 {
760     ADVInfo info;
761     HRESULT hr;
762
763     TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
764           hWnd, debugstr_w(szCmdName), debugstr_w(szInfSection),
765           debugstr_w(szDir), debugstr_w(lpszTitle),
766           phEXE, dwFlags, pvReserved);
767
768     if (dwFlags)
769         FIXME("Unhandled flags: 0x%08lx\n", dwFlags);
770
771     if (!szCmdName || !szDir)
772         return E_INVALIDARG;
773
774     if (!(dwFlags & RSC_FLAG_INF))
775         return launch_exe(szCmdName, szDir, phEXE);
776
777     ZeroMemory(&info, sizeof(ADVInfo));
778
779     hr = install_init(szCmdName, szInfSection, szDir, dwFlags, &info);
780     if (hr != S_OK)
781         goto done;
782
783     hr = spapi_install(&info);
784     if (hr != S_OK)
785         goto done;
786
787     hr = adv_install(&info);
788
789 done:
790     install_release(&info);
791
792     return hr;
793 }