advpack: Implement ExecuteCabW.
[wine/dcerpc.git] / dlls / advpack / install.c
blobaf8213ec85f443cd9793033c29d750d29cc34b1a
1 /*
2 * Advpack install functions
4 * Copyright 2006 James Hawkins
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.
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.
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
21 #include <stdarg.h>
22 #include <stdlib.h>
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"
37 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
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)
44 #define ADV_HRESULT(x) ((x & SPAPI_ERROR) ? HRESULT_FROM_SPAPI(x) : HRESULT_FROM_WIN32(x))
46 /* contains information about a specific install instance */
47 typedef struct _ADVInfo
49 HINF hinf;
50 LPWSTR inf_filename;
51 LPWSTR install_sec;
52 LPWSTR working_dir;
53 DWORD flags;
54 BOOL need_reboot;
55 } ADVInfo;
57 typedef HRESULT (*iterate_fields_func)(HINF hinf, PCWSTR field, void *arg);
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
65 /* Advanced INF callbacks */
66 static HRESULT register_ocxs_callback(HINF hinf, PCWSTR field, void *arg)
68 HMODULE hm;
69 INFCONTEXT context;
70 HRESULT hr = S_OK;
72 BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
74 for (; ok; ok = SetupFindNextLine(&context, &context))
76 WCHAR buffer[MAX_INF_STRING_LENGTH];
78 /* get OCX filename */
79 if (!SetupGetStringFieldW(&context, 1, buffer,
80 sizeof(buffer) / sizeof(WCHAR), NULL))
81 continue;
83 hm = LoadLibraryExW(buffer, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
84 if (!hm)
86 hr = E_FAIL;
87 continue;
90 if (do_ocx_reg(hm, TRUE))
91 hr = E_FAIL;
93 FreeLibrary(hm);
96 return hr;
99 static HRESULT run_post_setup_commands_callback(HINF hinf, PCWSTR field, void *arg)
101 ADVInfo *info = (ADVInfo *)arg;
102 INFCONTEXT context;
103 HRESULT hr = S_OK;
104 DWORD size;
106 BOOL ok = SetupFindFirstLineW(hinf, field, NULL, &context);
108 for (; ok; ok = SetupFindNextLine(&context, &context))
110 WCHAR buffer[MAX_INF_STRING_LENGTH];
112 if (!SetupGetLineTextW(&context, NULL, NULL, NULL, buffer,
113 MAX_INF_STRING_LENGTH, &size))
114 continue;
116 if (launch_exe(buffer, info->working_dir, NULL))
117 hr = E_FAIL;
120 return hr;
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)
127 LPWSTR token = *params;
129 if (!*params)
130 return NULL;
132 *params = strchrW(*params, separator);
133 if (*params)
134 *(*params)++ = '\0';
136 if (!*token)
137 return NULL;
139 return token;
142 static BOOL is_full_path(LPWSTR path)
144 const int MIN_PATH_LEN = 3;
146 if (!path || lstrlenW(path) < MIN_PATH_LEN)
147 return FALSE;
149 if (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'))
150 return TRUE;
152 return FALSE;
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)
159 DWORD required;
161 if (SetupGetStringFieldW(context, index, buffer, *size, &required)) return buffer;
163 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
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;
172 if (buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
173 return NULL;
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)
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;
186 BOOL ok = SetupFindFirstLineW(hinf, section, key, &context);
187 while (ok)
189 UINT i, count = SetupGetFieldCount(&context);
191 for (i = 1; i <= count; i++)
193 if (!(buffer = get_field_string(&context, i, buffer, static_buffer, &size)))
194 goto done;
196 if ((hr = callback(hinf, buffer, arg)) != S_OK)
197 goto done;
200 ok = SetupFindNextMatchLineW(&context, key, &context);
203 hr = S_OK;
205 done:
206 if (buffer && buffer != static_buffer) HeapFree(GetProcessHeap(), 0, buffer);
207 return hr;
210 /* performs a setupapi-level install of the INF file */
211 static HRESULT spapi_install(ADVInfo *info)
213 BOOL ret;
214 HRESULT res;
215 PVOID context;
217 context = SetupInitDefaultQueueCallbackEx(NULL, INVALID_HANDLE_VALUE, 0, 0, NULL);
218 if (!context)
219 return ADV_HRESULT(GetLastError());
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)
227 res = ADV_HRESULT(GetLastError());
228 SetupTermDefaultQueueCallback(context);
230 return res;
233 SetupTermDefaultQueueCallback(context);
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());
242 return S_OK;
245 /* processes the Advanced INF commands */
246 static HRESULT adv_install(ADVInfo *info)
248 HRESULT hr;
250 hr = iterate_section_fields(info->hinf, info->install_sec,
251 RegisterOCXs, register_ocxs_callback, NULL);
252 if (hr != S_OK)
253 return hr;
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;
260 return hr;
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)
267 DWORD len;
268 LPCWSTR ptr;
270 static const WCHAR default_install[] = {
271 'D','e','f','a','u','l','t','I','n','s','t','a','l','l',0
274 len = lstrlenW(inf_filename);
276 info->inf_filename = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
277 if (!info->inf_filename)
278 return E_OUTOFMEMORY;
280 lstrcpyW(info->inf_filename, inf_filename);
282 /* FIXME: determine the proper platform to install (NTx86, etc) */
283 if (!install_sec || !*install_sec)
285 len = sizeof(default_install) - 1;
286 ptr = default_install;
288 else
290 len = lstrlenW(install_sec);
291 ptr = install_sec;
294 info->install_sec = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
295 if (!info->install_sec)
296 return E_OUTOFMEMORY;
298 lstrcpyW(info->install_sec, ptr);
300 /* FIXME: need to get the real working directory */
301 if (!working_dir || !*working_dir)
303 ptr = strrchrW(info->inf_filename, '\\');
304 len = ptr - info->inf_filename + 1;
305 ptr = info->inf_filename;
307 else
309 len = lstrlenW(working_dir);
310 ptr = working_dir;
313 info->working_dir = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
314 if (!info->working_dir)
315 return E_OUTOFMEMORY;
317 lstrcpynW(info->working_dir, ptr, len);
319 info->hinf = SetupOpenInfFileW(info->inf_filename, NULL, INF_STYLE_WIN4, NULL);
320 if (info->hinf == INVALID_HANDLE_VALUE)
321 return ADV_HRESULT(GetLastError());
323 set_ldids(info->hinf, info->install_sec, info->working_dir);
325 /* FIXME: check that the INF is advanced */
327 info->flags = flags;
328 info->need_reboot = FALSE;
330 return S_OK;
333 /* release the install instance information */
334 void install_release(ADVInfo *info)
336 if (info->hinf && info->hinf != INVALID_HANDLE_VALUE)
337 SetupCloseInfFile(info->hinf);
339 HeapFree(GetProcessHeap(), 0, info->inf_filename);
340 HeapFree(GetProcessHeap(), 0, info->install_sec);
341 HeapFree(GetProcessHeap(), 0, info->working_dir);
344 /* this structure very closely resembles parameters of RunSetupCommand() */
345 typedef struct
347 HWND hwnd;
348 LPCSTR title;
349 LPCSTR inf_name;
350 LPCSTR dir;
351 LPCSTR section_name;
352 } SETUPCOMMAND_PARAMS;
354 /***********************************************************************
355 * DoInfInstall (ADVPACK.@)
357 * Install an INF section.
359 * PARAMS
360 * setup [I] Structure containing install information.
362 * RETURNS
363 * S_OK Everything OK
364 * HRESULT_FROM_WIN32(GetLastError()) Some other error
366 HRESULT WINAPI DoInfInstall(const SETUPCOMMAND_PARAMS *setup)
368 BOOL ret;
369 HINF hinf;
370 void *callback_context;
372 TRACE("(%p)\n", setup);
374 hinf = SetupOpenInfFileA(setup->inf_name, NULL, INF_STYLE_WIN4, NULL);
375 if (hinf == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError());
377 callback_context = SetupInitDefaultQueueCallback(setup->hwnd);
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);
385 return ret ? S_OK : HRESULT_FROM_WIN32(GetLastError());
388 /***********************************************************************
389 * ExecuteCabA (ADVPACK.@)
391 * See ExecuteCabW.
393 HRESULT WINAPI ExecuteCabA(HWND hwnd, CABINFOA* pCab, LPVOID pReserved)
395 UNICODE_STRING cab, inf, section;
396 CABINFOW cabinfo;
397 HRESULT hr;
399 TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
401 if (!pCab)
402 return E_INVALIDARG;
404 if (pCab->pszCab)
406 RtlCreateUnicodeStringFromAsciiz(&cab, pCab->pszCab);
407 cabinfo.pszCab = cab.Buffer;
409 else
410 cabinfo.pszCab = NULL;
412 RtlCreateUnicodeStringFromAsciiz(&inf, pCab->pszInf);
413 RtlCreateUnicodeStringFromAsciiz(&section, pCab->pszSection);
415 MultiByteToWideChar(CP_ACP, 0, pCab->szSrcPath, -1, cabinfo.szSrcPath,
416 sizeof(cabinfo.szSrcPath) / sizeof(WCHAR));
418 cabinfo.pszInf = inf.Buffer;
419 cabinfo.pszSection = section.Buffer;
420 cabinfo.dwFlags = pCab->dwFlags;
422 hr = ExecuteCabW(hwnd, &cabinfo, pReserved);
424 if (pCab->pszCab)
425 RtlFreeUnicodeString(&cab);
427 RtlFreeUnicodeString(&inf);
428 RtlFreeUnicodeString(&section);
430 return hr;
433 /***********************************************************************
434 * ExecuteCabW (ADVPACK.@)
436 * Installs the INF file extracted from a specified cabinet file.
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.
443 * RETURNS
444 * Success: S_OK.
445 * Failure: E_FAIL.
447 HRESULT WINAPI ExecuteCabW(HWND hwnd, CABINFOW* pCab, LPVOID pReserved)
449 ADVInfo info;
450 HRESULT hr;
452 TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
454 ZeroMemory(&info, sizeof(ADVInfo));
456 if (pCab->pszCab && *pCab->pszCab)
457 FIXME("Cab archive not extracted!\n");
459 hr = install_init(pCab->pszInf, pCab->pszSection, pCab->szSrcPath, pCab->dwFlags, &info);
460 if (hr != S_OK)
461 goto done;
463 hr = spapi_install(&info);
464 if (hr != S_OK)
465 goto done;
467 hr = adv_install(&info);
469 done:
470 install_release(&info);
472 return S_OK;
475 /***********************************************************************
476 * LaunchINFSectionA (ADVPACK.@)
478 * See LaunchINFSectionW.
480 INT WINAPI LaunchINFSectionA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
482 UNICODE_STRING cmd;
483 HRESULT hr;
485 TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
487 RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
489 hr = LaunchINFSectionW(hWnd, hInst, cmd.Buffer, show);
491 RtlFreeUnicodeString(&cmd);
493 return hr;
496 /***********************************************************************
497 * LaunchINFSectionW (ADVPACK.@)
499 * Installs an INF section without BACKUP/ROLLBACK capabilities.
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.
507 * RETURNS
508 * Success: S_OK.
509 * Failure: S_FALSE
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.
520 * BUGS
521 * Unimplemented.
523 INT WINAPI LaunchINFSectionW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
525 FIXME("(%p, %p, %s, %i): stub\n", hWnd, hInst, debugstr_w(cmdline), show);
526 return 0;
529 /***********************************************************************
530 * LaunchINFSectionExA (ADVPACK.@)
532 * See LaunchINFSectionExW.
534 HRESULT WINAPI LaunchINFSectionExA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
536 UNICODE_STRING cmd;
537 HRESULT hr;
539 TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
541 RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
543 hr = LaunchINFSectionExW(hWnd, hInst, cmd.Buffer, show);
545 RtlFreeUnicodeString(&cmd);
547 return hr;
550 /***********************************************************************
551 * LaunchINFSectionExW (ADVPACK.@)
553 * Installs an INF section with BACKUP/ROLLBACK capabilities.
555 * PARAMS
556 * hWnd [I] Handle to parent window, NULL for desktop.
557 * hInst [I] Instance of the process.
558 * cmdline [I] Contains parameters in the order INF,section,CAB,flags,reboot.
559 * show [I] How the window should be shown.
561 * RETURNS
562 * Success: S_OK.
563 * Failure: E_FAIL.
565 * NOTES
566 * INF - Filename of the INF to launch.
567 * section - INF section to install.
568 * flags - see advpub.h.
569 * reboot - smart reboot behavior
570 * 'A' Always reboot.
571 * 'I' Reboot if needed (default).
572 * 'N' No reboot.
574 * BUGS
575 * Doesn't handle the reboot flag.
577 HRESULT WINAPI LaunchINFSectionExW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
579 LPWSTR cmdline_copy, cmdline_ptr;
580 LPWSTR flags, ptr;
581 CABINFOW cabinfo;
582 HRESULT hr = S_OK;
584 TRACE("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_w(cmdline), show);
586 if (!cmdline)
587 return E_INVALIDARG;
589 cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
590 cmdline_ptr = cmdline_copy;
591 lstrcpyW(cmdline_copy, cmdline);
593 cabinfo.pszInf = get_parameter(&cmdline_ptr, ',');
594 cabinfo.pszSection = get_parameter(&cmdline_ptr, ',');
595 cabinfo.pszCab = get_parameter(&cmdline_ptr, ',');
597 flags = get_parameter(&cmdline_ptr, ',');
598 if (flags)
599 cabinfo.dwFlags = atolW(flags);
601 /* get the source path from the cab filename */
602 if (cabinfo.pszCab && *cabinfo.pszCab)
604 if (!is_full_path(cabinfo.pszCab))
605 goto done;
607 lstrcpyW(cabinfo.szSrcPath, cabinfo.pszCab);
608 ptr = strrchrW(cabinfo.szSrcPath, '\\');
609 *(++ptr) = '\0';
612 hr = ExecuteCabW(hWnd, &cabinfo, NULL);
614 done:
615 HeapFree(GetProcessHeap(), 0, cmdline_copy);
617 return hr;
620 HRESULT launch_exe(LPCWSTR cmd, LPCWSTR dir, HANDLE *phEXE)
622 STARTUPINFOW si;
623 PROCESS_INFORMATION pi;
625 if (phEXE) *phEXE = NULL;
627 ZeroMemory(&pi, sizeof(pi));
628 ZeroMemory(&si, sizeof(si));
629 si.cb = sizeof(si);
631 if (!CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, FALSE,
632 CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP,
633 NULL, dir, &si, &pi))
635 return HRESULT_FROM_WIN32(GetLastError());
638 CloseHandle(pi.hThread);
640 if (phEXE)
642 *phEXE = pi.hProcess;
643 return S_ASYNCHRONOUS;
646 /* wait for the child process to finish */
647 WaitForSingleObject(pi.hProcess, INFINITE);
648 CloseHandle(pi.hProcess);
650 return S_OK;
653 /***********************************************************************
654 * RunSetupCommandA (ADVPACK.@)
656 * See RunSetupCommandW.
658 HRESULT WINAPI RunSetupCommandA(HWND hWnd, LPCSTR szCmdName,
659 LPCSTR szInfSection, LPCSTR szDir,
660 LPCSTR lpszTitle, HANDLE *phEXE,
661 DWORD dwFlags, LPVOID pvReserved)
663 UNICODE_STRING cmdname, infsec;
664 UNICODE_STRING dir, title;
665 HRESULT hr;
667 TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
668 hWnd, debugstr_a(szCmdName), debugstr_a(szInfSection),
669 debugstr_a(szDir), debugstr_a(lpszTitle),
670 phEXE, dwFlags, pvReserved);
672 if (!szCmdName || !szDir)
673 return E_INVALIDARG;
675 RtlCreateUnicodeStringFromAsciiz(&cmdname, szCmdName);
676 RtlCreateUnicodeStringFromAsciiz(&infsec, szInfSection);
677 RtlCreateUnicodeStringFromAsciiz(&dir, szDir);
678 RtlCreateUnicodeStringFromAsciiz(&title, lpszTitle);
680 hr = RunSetupCommandW(hWnd, cmdname.Buffer, infsec.Buffer, dir.Buffer,
681 title.Buffer, phEXE, dwFlags, pvReserved);
683 RtlFreeUnicodeString(&cmdname);
684 RtlFreeUnicodeString(&infsec);
685 RtlFreeUnicodeString(&dir);
686 RtlFreeUnicodeString(&title);
688 return hr;
691 /***********************************************************************
692 * RunSetupCommandW (ADVPACK.@)
694 * Executes an install section in an INF file or a program.
696 * PARAMS
697 * hWnd [I] Handle to parent window, NULL for quiet mode
698 * szCmdName [I] Inf or EXE filename to execute
699 * szInfSection [I] Inf section to install, NULL for DefaultInstall
700 * szDir [I] Path to extracted files
701 * szTitle [I] Title of all dialogs
702 * phEXE [O] Handle of EXE to wait for
703 * dwFlags [I] Flags; see include/advpub.h
704 * pvReserved [I] Reserved
706 * RETURNS
707 * S_OK Everything OK
708 * S_ASYNCHRONOUS OK, required to wait on phEXE
709 * ERROR_SUCCESS_REBOOT_REQUIRED Reboot required
710 * E_INVALIDARG Invalid argument given
711 * HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION)
712 * Not supported on this Windows version
713 * E_UNEXPECTED Unexpected error
714 * HRESULT_FROM_WIN32(GetLastError()) Some other error
716 * BUGS
717 * INF install unimplemented.
719 HRESULT WINAPI RunSetupCommandW(HWND hWnd, LPCWSTR szCmdName,
720 LPCWSTR szInfSection, LPCWSTR szDir,
721 LPCWSTR lpszTitle, HANDLE *phEXE,
722 DWORD dwFlags, LPVOID pvReserved)
724 ADVInfo info;
725 HRESULT hr;
727 TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
728 hWnd, debugstr_w(szCmdName), debugstr_w(szInfSection),
729 debugstr_w(szDir), debugstr_w(lpszTitle),
730 phEXE, dwFlags, pvReserved);
732 if (dwFlags)
733 FIXME("Unhandled flags: 0x%08lx\n", dwFlags);
735 if (!szCmdName || !szDir)
736 return E_INVALIDARG;
738 if (!(dwFlags & RSC_FLAG_INF))
739 return launch_exe(szCmdName, szDir, phEXE);
741 ZeroMemory(&info, sizeof(ADVInfo));
743 hr = install_init(szCmdName, szInfSection, szDir, dwFlags, &info);
744 if (hr != S_OK)
745 goto done;
747 hr = spapi_install(&info);
748 if (hr != S_OK)
749 goto done;
751 hr = adv_install(&info);
753 done:
754 install_release(&info);
756 return hr;