msi: Fix the add_feature_entry helper function.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 #define ADV_SUCCESS     0
47 #define ADV_FAILURE     1
48
49 /* contains information about a specific install instance */
50 typedef struct _ADVInfo
51 {
52     HINF hinf;
53     LPWSTR inf_filename;
54     LPWSTR install_sec;
55     LPWSTR working_dir;
56     DWORD flags;
57     BOOL need_reboot;
58 } ADVInfo;
59
60 typedef HRESULT (*iterate_fields_func)(HINF hinf, PCWSTR field, void *arg);
61
62 /* Advanced INF commands */
63 static const WCHAR CheckAdminRights[] = {
64     'C','h','e','c','k','A','d','m','i','n','R','i','g','h','t','s',0
65 };
66 static const WCHAR DelDirs[] = {'D','e','l','D','i','r','s',0};
67 static const WCHAR PerUserInstall[] = {'P','e','r','U','s','e','r','I','n','s','t','a','l','l',0};
68 static const WCHAR RegisterOCXs[] = {'R','e','g','i','s','t','e','r','O','C','X','s',0};
69 static const WCHAR RunPreSetupCommands[] = {
70     'R','u','n','P','r','e','S','e','t','u','p','C','o','m','m','a','n','d','s',0
71 };
72 static const WCHAR RunPostSetupCommands[] = {
73     'R','u','n','P','o','s','t','S','e','t','u','p','C','o','m','m','a','n','d','s',0
74 };
75
76 /* Advanced INF callbacks */
77 static HRESULT del_dirs_callback(HINF hinf, PCWSTR field, void *arg)
78 {
79     INFCONTEXT context;
80     HRESULT hr = S_OK;
81     DWORD size;
82
83     BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
84     
85     for (; ok; ok = SetupFindNextLine(&context, &context))
86     {
87         WCHAR directory[MAX_INF_STRING_LENGTH];
88
89         if (!SetupGetLineTextW(&context, NULL, NULL, NULL, directory,
90                                MAX_INF_STRING_LENGTH, &size))
91             continue;
92
93         if (DelNodeW(directory, ADN_DEL_IF_EMPTY))
94             hr = E_FAIL;
95     }
96
97     return hr;
98 }
99
100 static HRESULT per_user_install_callback(HINF hinf, PCWSTR field, void *arg)
101 {
102     PERUSERSECTIONW per_user;
103     INFCONTEXT context;
104     DWORD size;
105
106     static const WCHAR disp_name[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
107     static const WCHAR version[] = {'V','e','r','s','i','o','n',0};
108     static const WCHAR is_installed[] = {'I','s','I','n','s','t','a','l','l','e','d',0};
109     static const WCHAR comp_id[] = {'C','o','m','p','o','n','e','n','t','I','D',0};
110     static const WCHAR guid[] = {'G','U','I','D',0};
111     static const WCHAR locale[] = {'L','o','c','a','l','e',0};
112     static const WCHAR stub_path[] = {'S','t','u','b','P','a','t','h',0};
113
114     per_user.bRollback = FALSE;
115     per_user.dwIsInstalled = 0;
116
117     SetupGetLineTextW(NULL, hinf, field, disp_name, per_user.szDispName,
118                      sizeof(per_user.szDispName) / sizeof(WCHAR), &size);
119
120     SetupGetLineTextW(NULL, hinf, field, version, per_user.szVersion,
121                      sizeof(per_user.szVersion) / sizeof(WCHAR), &size);
122
123     if (SetupFindFirstLineW(hinf, field, is_installed, &context))
124     {
125         SetupGetIntField(&context, 1, (PINT)&per_user.dwIsInstalled);
126     }
127
128     SetupGetLineTextW(NULL, hinf, field, comp_id, per_user.szCompID,
129                      sizeof(per_user.szCompID) / sizeof(WCHAR), &size);
130
131     SetupGetLineTextW(NULL, hinf, field, guid, per_user.szGUID,
132                      sizeof(per_user.szGUID) / sizeof(WCHAR), &size);
133
134     SetupGetLineTextW(NULL, hinf, field, locale, per_user.szLocale,
135                      sizeof(per_user.szLocale) / sizeof(WCHAR), &size);
136
137     SetupGetLineTextW(NULL, hinf, field, stub_path, per_user.szStub,
138                      sizeof(per_user.szStub) / sizeof(WCHAR), &size);
139
140     return SetPerUserSecValuesW(&per_user);
141 }
142
143 static HRESULT register_ocxs_callback(HINF hinf, PCWSTR field, void *arg)
144 {
145     HMODULE hm;
146     INFCONTEXT context;
147     HRESULT hr = S_OK;
148
149     BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
150     
151     for (; ok; ok = SetupFindNextLine(&context, &context))
152     {
153         WCHAR buffer[MAX_INF_STRING_LENGTH];
154
155         /* get OCX filename */
156         if (!SetupGetStringFieldW(&context, 1, buffer,
157                                   sizeof(buffer) / sizeof(WCHAR), NULL))
158             continue;
159
160         hm = LoadLibraryExW(buffer, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
161         if (!hm)
162             continue;
163
164         if (do_ocx_reg(hm, TRUE))
165             hr = E_FAIL;
166
167         FreeLibrary(hm);
168     }
169
170     return hr;
171 }
172
173 static HRESULT run_setup_commands_callback(HINF hinf, PCWSTR field, void *arg)
174 {
175     ADVInfo *info = (ADVInfo *)arg;
176     INFCONTEXT context;
177     HRESULT hr = S_OK;
178     DWORD size;
179
180     BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
181
182     for (; ok; ok = SetupFindNextLine(&context, &context))
183     {
184         WCHAR buffer[MAX_INF_STRING_LENGTH];
185
186         if (!SetupGetLineTextW(&context, NULL, NULL, NULL, buffer,
187                                MAX_INF_STRING_LENGTH, &size))
188             continue;
189
190         if (launch_exe(buffer, info->working_dir, NULL))
191             hr = E_FAIL;
192     }
193
194     return hr;
195 }
196
197 /* sequentially returns pointers to parameters in a parameter list
198  * returns NULL if the parameter is empty, e.g. one,,three  */
199 LPWSTR get_parameter(LPWSTR *params, WCHAR separator)
200 {
201     LPWSTR token = *params;
202
203     if (!*params)
204         return NULL;
205
206     *params = strchrW(*params, separator);
207     if (*params)
208         *(*params)++ = '\0';
209
210     if (!*token)
211         return NULL;
212
213     return token;
214 }
215
216 static BOOL is_full_path(LPWSTR path)
217 {
218     const int MIN_PATH_LEN = 3;
219
220     if (!path || lstrlenW(path) < MIN_PATH_LEN)
221         return FALSE;
222
223     if (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'))
224         return TRUE;
225
226     return FALSE;
227 }
228
229 /* retrieves the contents of a field, dynamically growing the buffer if necessary */
230 static WCHAR *get_field_string(INFCONTEXT *context, DWORD index, WCHAR *buffer,
231                                WCHAR *static_buffer, DWORD *size)
232 {
233     DWORD required;
234
235     if (SetupGetStringFieldW(context, index, buffer, *size, &required)) return buffer;
236
237     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
238     {
239         /* now grow the buffer */
240         if (buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
241         if (!(buffer = HeapAlloc(GetProcessHeap(), 0, required*sizeof(WCHAR)))) return NULL;
242         *size = required;
243         if (SetupGetStringFieldW(context, index, buffer, *size, &required)) return buffer;
244     }
245
246     if (buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
247     return NULL;
248 }
249
250 /* iterates over all fields of a certain key of a certain section */
251 static HRESULT iterate_section_fields(HINF hinf, PCWSTR section, PCWSTR key,
252                                       iterate_fields_func callback, void *arg)
253 {
254     WCHAR static_buffer[200];
255     WCHAR *buffer = static_buffer;
256     DWORD size = sizeof(static_buffer) / sizeof(WCHAR);
257     INFCONTEXT context;
258     HRESULT hr = E_FAIL;
259
260     BOOL ok = SetupFindFirstLineW(hinf, section, key, &context);
261     while (ok)
262     {
263         UINT i, count = SetupGetFieldCount(&context);
264
265         for (i = 1; i <= count; i++)
266         {
267             if (!(buffer = get_field_string(&context, i, buffer, static_buffer, &size)))
268                 goto done;
269
270             if ((hr = callback(hinf, buffer, arg)) != S_OK)
271                 goto done;
272         }
273
274         ok = SetupFindNextMatchLineW(&context, key, &context);
275     }
276
277     hr = S_OK;
278
279  done:
280     if (buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
281     return hr;
282 }
283
284 static HRESULT check_admin_rights(ADVInfo *info)
285 {
286     INT check;
287     INFCONTEXT context;
288     HRESULT hr = S_OK;
289
290     if (!SetupFindFirstLineW(info->hinf, info->install_sec,
291                              CheckAdminRights, &context))
292         return S_OK;
293
294     if (!SetupGetIntField(&context, 1, &check))
295         return S_OK;
296
297     if (check == 1)
298         hr = IsNTAdmin(0, NULL) ? S_OK : E_FAIL;
299
300     return hr;
301 }
302
303 /* performs a setupapi-level install of the INF file */
304 static HRESULT spapi_install(ADVInfo *info)
305 {
306     BOOL ret;
307     HRESULT res;
308     PVOID context;
309
310     context = SetupInitDefaultQueueCallbackEx(NULL, INVALID_HANDLE_VALUE, 0, 0, NULL);
311     if (!context)
312         return ADV_HRESULT(GetLastError());
313
314     ret = SetupInstallFromInfSectionW(NULL, info->hinf, info->install_sec,
315                                       SPINST_FILES, NULL, info->working_dir,
316                                       SP_COPY_NEWER, SetupDefaultQueueCallbackW,
317                                       context, NULL, NULL);
318     if (!ret)
319     {
320         res = ADV_HRESULT(GetLastError());
321         SetupTermDefaultQueueCallback(context);
322
323         return res;
324     }
325
326     SetupTermDefaultQueueCallback(context);
327
328     ret = SetupInstallFromInfSectionW(NULL, info->hinf, info->install_sec,
329                                       SPINST_INIFILES | SPINST_REGISTRY,
330                                       HKEY_LOCAL_MACHINE, NULL, 0,
331                                       NULL, NULL, NULL, NULL);
332     if (!ret)
333         return ADV_HRESULT(GetLastError());
334
335     return S_OK;
336 }
337
338 /* processes the Advanced INF commands */
339 static HRESULT adv_install(ADVInfo *info)
340 {
341     HRESULT hr;
342
343     hr = check_admin_rights(info);
344     if (hr != S_OK)
345         return hr;
346
347     hr = iterate_section_fields(info->hinf, info->install_sec, RunPreSetupCommands,
348                                 run_setup_commands_callback, info);
349     if (hr != S_OK)
350         return hr;
351
352     hr = iterate_section_fields(info->hinf, info->install_sec,
353                                 RegisterOCXs, register_ocxs_callback, NULL);
354     if (hr != S_OK)
355         return hr;
356
357     hr = iterate_section_fields(info->hinf, info->install_sec,
358                                 PerUserInstall, per_user_install_callback, info);
359     if (hr != S_OK)
360         return hr;
361
362     hr = iterate_section_fields(info->hinf, info->install_sec, RunPostSetupCommands,
363                                 run_setup_commands_callback, info);
364     if (hr != S_OK)
365         return hr;
366
367     hr = iterate_section_fields(info->hinf, info->install_sec,
368                                 DelDirs, del_dirs_callback, info);
369     if (hr != S_OK)
370         return hr;
371
372     return hr;
373 }
374
375 /* loads the INF file and performs checks on it */
376 HRESULT install_init(LPCWSTR inf_filename, LPCWSTR install_sec,
377                      LPCWSTR working_dir, DWORD flags, ADVInfo *info)
378 {
379     DWORD len;
380     LPCWSTR ptr;
381
382     static const WCHAR default_install[] = {
383         'D','e','f','a','u','l','t','I','n','s','t','a','l','l',0
384     };
385
386     len = lstrlenW(inf_filename);
387
388     info->inf_filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
389     if (!info->inf_filename)
390         return E_OUTOFMEMORY;
391
392     lstrcpyW(info->inf_filename, inf_filename);
393
394     /* FIXME: determine the proper platform to install (NTx86, etc) */
395     if (!install_sec || !*install_sec)
396     {
397         len = sizeof(default_install) - 1;
398         ptr = default_install;
399     }
400     else
401     {
402         len = lstrlenW(install_sec);
403         ptr = install_sec;
404     }
405
406     info->install_sec = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
407     if (!info->install_sec)
408         return E_OUTOFMEMORY;
409
410     lstrcpyW(info->install_sec, ptr);
411
412     /* FIXME: need to get the real working directory */
413     if (!working_dir || !*working_dir)
414     {
415         ptr = strrchrW(info->inf_filename, '\\');
416         len = ptr - info->inf_filename + 1;
417         ptr = info->inf_filename;
418     }
419     else
420     {
421         len = lstrlenW(working_dir) + 1;
422         ptr = working_dir;
423     }
424
425     info->working_dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
426     if (!info->working_dir)
427         return E_OUTOFMEMORY;
428
429     lstrcpynW(info->working_dir, ptr, len);
430
431     info->hinf = SetupOpenInfFileW(info->inf_filename, NULL, INF_STYLE_WIN4, NULL);
432     if (info->hinf == INVALID_HANDLE_VALUE)
433         return ADV_HRESULT(GetLastError());
434
435     set_ldids(info->hinf, info->install_sec, info->working_dir);
436
437     /* FIXME: check that the INF is advanced */
438
439     info->flags = flags;
440     info->need_reboot = FALSE;
441
442     return S_OK;
443 }
444
445 /* release the install instance information */
446 void install_release(ADVInfo *info)
447 {
448     if (info->hinf && info->hinf != INVALID_HANDLE_VALUE)
449         SetupCloseInfFile(info->hinf);
450
451     HeapFree(GetProcessHeap(), 0, info->inf_filename);
452     HeapFree(GetProcessHeap(), 0, info->install_sec);
453     HeapFree(GetProcessHeap(), 0, info->working_dir);
454 }
455
456 /* this structure very closely resembles parameters of RunSetupCommand() */
457 typedef struct
458 {
459     HWND hwnd;
460     LPCSTR title;
461     LPCSTR inf_name;
462     LPCSTR dir;
463     LPCSTR section_name;
464 } SETUPCOMMAND_PARAMS;
465
466 typedef struct
467 {
468     HWND hwnd;
469     LPCWSTR title;
470     LPCWSTR inf_name;
471     LPCWSTR dir;
472     LPCWSTR section_name;
473 } SETUPCOMMAND_PARAMSW;
474
475 /* internal: see DoInfInstall */
476 static HRESULT DoInfInstallW(const SETUPCOMMAND_PARAMSW *setup)
477 {
478     ADVInfo info;
479     HRESULT hr;
480
481     TRACE("(%p)\n", setup);
482
483     ZeroMemory(&info, sizeof(ADVInfo));
484
485     hr = install_init(setup->inf_name, setup->section_name, setup->dir, 0, &info);
486     if (hr != S_OK)
487         goto done;
488
489     hr = spapi_install(&info);
490     if (hr != S_OK)
491         goto done;
492
493     hr = adv_install(&info);
494
495 done:
496     install_release(&info);
497
498     return S_OK;
499 }
500
501 /***********************************************************************
502  *      DoInfInstall  (ADVPACK.@)
503  *
504  * Install an INF section.
505  *
506  * PARAMS
507  *  setup [I] Structure containing install information.
508  *
509  * RETURNS
510  *   S_OK                                Everything OK
511  *   HRESULT_FROM_WIN32(GetLastError())  Some other error
512  */
513 HRESULT WINAPI DoInfInstall(const SETUPCOMMAND_PARAMS *setup)
514 {
515     UNICODE_STRING title, inf, section, dir;
516     SETUPCOMMAND_PARAMSW params;
517     HRESULT hr;
518
519     if (!setup)
520         return E_INVALIDARG;
521
522     RtlCreateUnicodeStringFromAsciiz(&title, setup->title);
523     RtlCreateUnicodeStringFromAsciiz(&inf, setup->inf_name);
524     RtlCreateUnicodeStringFromAsciiz(&section, setup->section_name);
525     RtlCreateUnicodeStringFromAsciiz(&dir, setup->dir);
526
527     params.title = title.Buffer;
528     params.inf_name = inf.Buffer;
529     params.section_name = section.Buffer;
530     params.dir = dir.Buffer;
531     params.hwnd = setup->hwnd;
532
533     hr = DoInfInstallW(&params);
534
535     RtlFreeUnicodeString(&title);
536     RtlFreeUnicodeString(&inf);
537     RtlFreeUnicodeString(&section);
538     RtlFreeUnicodeString(&dir);
539
540     return hr;
541 }
542
543 /***********************************************************************
544  *             ExecuteCabA    (ADVPACK.@)
545  *
546  * See ExecuteCabW.
547  */
548 HRESULT WINAPI ExecuteCabA(HWND hwnd, CABINFOA* pCab, LPVOID pReserved)
549 {
550     UNICODE_STRING cab, inf, section;
551     CABINFOW cabinfo;
552     HRESULT hr;
553
554     TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
555
556     if (!pCab)
557         return E_INVALIDARG;
558
559     if (pCab->pszCab)
560     {
561         RtlCreateUnicodeStringFromAsciiz(&cab, pCab->pszCab);
562         cabinfo.pszCab = cab.Buffer;
563     }
564     else
565         cabinfo.pszCab = NULL;
566
567     RtlCreateUnicodeStringFromAsciiz(&inf, pCab->pszInf);
568     RtlCreateUnicodeStringFromAsciiz(&section, pCab->pszSection);
569     
570     MultiByteToWideChar(CP_ACP, 0, pCab->szSrcPath, -1, cabinfo.szSrcPath,
571                         sizeof(cabinfo.szSrcPath) / sizeof(WCHAR));
572
573     cabinfo.pszInf = inf.Buffer;
574     cabinfo.pszSection = section.Buffer;
575     cabinfo.dwFlags = pCab->dwFlags;
576
577     hr = ExecuteCabW(hwnd, &cabinfo, pReserved);
578
579     if (pCab->pszCab)
580         RtlFreeUnicodeString(&cab);
581
582     RtlFreeUnicodeString(&inf);
583     RtlFreeUnicodeString(&section);
584
585     return hr;
586 }
587
588 /***********************************************************************
589  *             ExecuteCabW    (ADVPACK.@)
590  * 
591  * Installs the INF file extracted from a specified cabinet file.
592  * 
593  * PARAMS
594  *   hwnd      [I] Handle to the window used for the display.
595  *   pCab      [I] Information about the cabinet file.
596  *   pReserved [I] Reserved.  Must be NULL.
597  * 
598  * RETURNS
599  *   Success: S_OK.
600  *   Failure: E_FAIL.
601  */
602 HRESULT WINAPI ExecuteCabW(HWND hwnd, CABINFOW* pCab, LPVOID pReserved)
603 {
604     ADVInfo info;
605     HRESULT hr;
606
607     TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
608
609     ZeroMemory(&info, sizeof(ADVInfo));
610
611     if (pCab->pszCab && *pCab->pszCab)
612         FIXME("Cab archive not extracted!\n");
613
614     hr = install_init(pCab->pszInf, pCab->pszSection, pCab->szSrcPath, pCab->dwFlags, &info);
615     if (hr != S_OK)
616         goto done;
617
618     hr = spapi_install(&info);
619     if (hr != S_OK)
620         goto done;
621
622     hr = adv_install(&info);
623
624 done:
625     install_release(&info);
626
627     return hr;
628 }
629
630 /***********************************************************************
631  *      LaunchINFSectionA   (ADVPACK.@)
632  *
633  * See LaunchINFSectionW.
634  */
635 INT WINAPI LaunchINFSectionA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
636 {
637     UNICODE_STRING cmd;
638     HRESULT hr;
639
640     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
641
642     if (!cmdline)
643         return ADV_FAILURE;
644
645     RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
646
647     hr = LaunchINFSectionW(hWnd, hInst, cmd.Buffer, show);
648
649     RtlFreeUnicodeString(&cmd);
650
651     return hr;
652 }
653
654 /***********************************************************************
655  *      LaunchINFSectionW   (ADVPACK.@)
656  *
657  * Installs an INF section without BACKUP/ROLLBACK capabilities.
658  *
659  * PARAMS
660  *   hWnd    [I] Handle to parent window, NULL for desktop.
661  *   hInst   [I] Instance of the process.
662  *   cmdline [I] Contains parameters in the order INF,section,flags,reboot.
663  *   show    [I] How the window should be shown.
664  *
665  * RETURNS
666  *  Success: ADV_SUCCESS.
667  *  Failure: ADV_FAILURE.
668  *
669  * NOTES
670  *  INF - Filename of the INF to launch.
671  *  section - INF section to install.
672  *  flags - see advpub.h.
673  *  reboot - smart reboot behavior
674  *    'A' Always reboot.
675  *    'I' Reboot if needed (default).
676  *    'N' No reboot.
677  */
678 INT WINAPI LaunchINFSectionW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
679 {
680     ADVInfo info;
681     LPWSTR cmdline_copy, cmdline_ptr;
682     LPWSTR inf_filename, install_sec;
683     LPWSTR str_flags;
684     DWORD flags = 0;
685     HRESULT hr = S_OK;
686
687     TRACE("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_w(cmdline), show);
688
689     if (!cmdline)
690         return ADV_FAILURE;
691
692     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
693     cmdline_ptr = cmdline_copy;
694     lstrcpyW(cmdline_copy, cmdline);
695
696     inf_filename = get_parameter(&cmdline_ptr, ',');
697     install_sec = get_parameter(&cmdline_ptr, ',');
698
699     str_flags = get_parameter(&cmdline_ptr, ',');
700     if (str_flags)
701         flags = atolW(str_flags);
702
703     ZeroMemory(&info, sizeof(ADVInfo));
704
705     hr = install_init(inf_filename, install_sec, NULL, flags, &info);
706     if (hr != S_OK)
707         goto done;
708
709     hr = spapi_install(&info);
710     if (hr != S_OK)
711         goto done;
712
713     hr = adv_install(&info);
714
715 done:
716     install_release(&info);
717     HeapFree(GetProcessHeap(), 0, cmdline_copy);
718
719     return SUCCEEDED(hr) ? ADV_SUCCESS : ADV_FAILURE;
720 }
721
722 /***********************************************************************
723  *      LaunchINFSectionExA (ADVPACK.@)
724  *
725  * See LaunchINFSectionExW.
726  */
727 HRESULT WINAPI LaunchINFSectionExA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
728 {
729     UNICODE_STRING cmd;
730     HRESULT hr;
731
732     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
733
734     if (!cmdline)
735         return ADV_FAILURE;
736
737     RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
738
739     hr = LaunchINFSectionExW(hWnd, hInst, cmd.Buffer, show);
740
741     RtlFreeUnicodeString(&cmd);
742
743     return hr;
744 }
745
746 /***********************************************************************
747  *      LaunchINFSectionExW (ADVPACK.@)
748  *
749  * Installs an INF section with BACKUP/ROLLBACK capabilities.
750  *
751  * PARAMS
752  *   hWnd    [I] Handle to parent window, NULL for desktop.
753  *   hInst   [I] Instance of the process.
754  *   cmdline [I] Contains parameters in the order INF,section,CAB,flags,reboot.
755  *   show    [I] How the window should be shown.
756  *
757  * RETURNS
758  *  Success: ADV_SUCCESS.
759  *  Failure: ADV_FAILURE.
760  *
761  * NOTES
762  *  INF - Filename of the INF to launch.
763  *  section - INF section to install.
764  *  flags - see advpub.h.
765  *  reboot - smart reboot behavior
766  *    'A' Always reboot.
767  *    'I' Reboot if needed (default).
768  *    'N' No reboot.
769  *
770  * BUGS
771  *  Doesn't handle the reboot flag.
772  */
773 HRESULT WINAPI LaunchINFSectionExW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
774 {
775     LPWSTR cmdline_copy, cmdline_ptr;
776     LPWSTR flags, ptr;
777     CABINFOW cabinfo;
778     HRESULT hr = S_OK;
779
780     TRACE("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_w(cmdline), show);
781
782     if (!cmdline)
783         return ADV_FAILURE;
784
785     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
786     cmdline_ptr = cmdline_copy;
787     lstrcpyW(cmdline_copy, cmdline);
788
789     cabinfo.pszInf = get_parameter(&cmdline_ptr, ',');
790     cabinfo.pszSection = get_parameter(&cmdline_ptr, ',');
791     cabinfo.pszCab = get_parameter(&cmdline_ptr, ',');
792     *cabinfo.szSrcPath = '\0';
793
794     flags = get_parameter(&cmdline_ptr, ',');
795     if (flags)
796         cabinfo.dwFlags = atolW(flags);
797
798     /* get the source path from the cab filename */
799     if (cabinfo.pszCab && *cabinfo.pszCab)
800     {
801         if (!is_full_path(cabinfo.pszCab))
802             goto done;
803
804         lstrcpyW(cabinfo.szSrcPath, cabinfo.pszCab);
805         ptr = strrchrW(cabinfo.szSrcPath, '\\');
806         *(++ptr) = '\0';
807     }
808
809     hr = ExecuteCabW(hWnd, &cabinfo, NULL);
810
811 done:
812     HeapFree(GetProcessHeap(), 0, cmdline_copy);
813
814     return SUCCEEDED(hr) ? ADV_SUCCESS : ADV_FAILURE;
815 }
816
817 HRESULT launch_exe(LPCWSTR cmd, LPCWSTR dir, HANDLE *phEXE)
818 {
819     STARTUPINFOW si;
820     PROCESS_INFORMATION pi;
821
822     if (phEXE) *phEXE = NULL;
823
824     ZeroMemory(&pi, sizeof(pi));
825     ZeroMemory(&si, sizeof(si));
826     si.cb = sizeof(si);
827
828     if (!CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, FALSE,
829                         CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP,
830                         NULL, dir, &si, &pi))
831     {
832         return HRESULT_FROM_WIN32(GetLastError());
833     }
834
835     CloseHandle(pi.hThread);
836
837     if (phEXE)
838     {
839         *phEXE = pi.hProcess;
840         return S_ASYNCHRONOUS;
841     }
842
843     /* wait for the child process to finish */
844     WaitForSingleObject(pi.hProcess, INFINITE);
845     CloseHandle(pi.hProcess);
846
847     return S_OK;
848 }
849
850 /***********************************************************************
851  *      RunSetupCommandA  (ADVPACK.@)
852  *
853  * See RunSetupCommandW.
854  */
855 HRESULT WINAPI RunSetupCommandA(HWND hWnd, LPCSTR szCmdName,
856                                 LPCSTR szInfSection, LPCSTR szDir,
857                                 LPCSTR lpszTitle, HANDLE *phEXE,
858                                 DWORD dwFlags, LPVOID pvReserved)
859 {
860     UNICODE_STRING cmdname, infsec;
861     UNICODE_STRING dir, title;
862     HRESULT hr;
863
864     TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
865           hWnd, debugstr_a(szCmdName), debugstr_a(szInfSection),
866           debugstr_a(szDir), debugstr_a(lpszTitle),
867           phEXE, dwFlags, pvReserved);
868
869     if (!szCmdName || !szDir)
870         return E_INVALIDARG;
871
872     RtlCreateUnicodeStringFromAsciiz(&cmdname, szCmdName);
873     RtlCreateUnicodeStringFromAsciiz(&infsec, szInfSection);
874     RtlCreateUnicodeStringFromAsciiz(&dir, szDir);
875     RtlCreateUnicodeStringFromAsciiz(&title, lpszTitle);
876
877     hr = RunSetupCommandW(hWnd, cmdname.Buffer, infsec.Buffer, dir.Buffer,
878                           title.Buffer, phEXE, dwFlags, pvReserved);
879
880     RtlFreeUnicodeString(&cmdname);
881     RtlFreeUnicodeString(&infsec);
882     RtlFreeUnicodeString(&dir);
883     RtlFreeUnicodeString(&title);
884
885     return hr;
886 }
887
888 /***********************************************************************
889  *      RunSetupCommandW  (ADVPACK.@)
890  *
891  * Executes an install section in an INF file or a program.
892  *
893  * PARAMS
894  *   hWnd          [I] Handle to parent window, NULL for quiet mode
895  *   szCmdName     [I] Inf or EXE filename to execute
896  *   szInfSection  [I] Inf section to install, NULL for DefaultInstall
897  *   szDir         [I] Path to extracted files
898  *   szTitle       [I] Title of all dialogs
899  *   phEXE         [O] Handle of EXE to wait for
900  *   dwFlags       [I] Flags; see include/advpub.h
901  *   pvReserved    [I] Reserved
902  *
903  * RETURNS
904  *   S_OK                                 Everything OK
905  *   S_ASYNCHRONOUS                       OK, required to wait on phEXE
906  *   ERROR_SUCCESS_REBOOT_REQUIRED        Reboot required
907  *   E_INVALIDARG                         Invalid argument given
908  *   HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION)
909  *                                        Not supported on this Windows version
910  *   E_UNEXPECTED                         Unexpected error
911  *   HRESULT_FROM_WIN32(GetLastError())   Some other error
912  */
913 HRESULT WINAPI RunSetupCommandW(HWND hWnd, LPCWSTR szCmdName,
914                                 LPCWSTR szInfSection, LPCWSTR szDir,
915                                 LPCWSTR lpszTitle, HANDLE *phEXE,
916                                 DWORD dwFlags, LPVOID pvReserved)
917 {
918     ADVInfo info;
919     HRESULT hr;
920
921     TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
922           hWnd, debugstr_w(szCmdName), debugstr_w(szInfSection),
923           debugstr_w(szDir), debugstr_w(lpszTitle),
924           phEXE, dwFlags, pvReserved);
925
926     if (dwFlags & RSC_FLAG_UPDHLPDLLS)
927         FIXME("Unhandled flag: RSC_FLAG_UPDHLPDLLS\n");
928
929     if (!szCmdName || !szDir)
930         return E_INVALIDARG;
931
932     if (!(dwFlags & RSC_FLAG_INF))
933         return launch_exe(szCmdName, szDir, phEXE);
934
935     ZeroMemory(&info, sizeof(ADVInfo));
936
937     hr = install_init(szCmdName, szInfSection, szDir, dwFlags, &info);
938     if (hr != S_OK)
939         goto done;
940
941     hr = spapi_install(&info);
942     if (hr != S_OK)
943         goto done;
944
945     hr = adv_install(&info);
946
947 done:
948     install_release(&info);
949
950     return hr;
951 }