advpack: Use the default queue callback when calling SetupInstallFromInfSection.
[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
36 WINE_DEFAULT_DEBUG_CHANNEL(advpack);
37
38 #define SPAPI_ERROR     0xE0000000L
39 #define SPAPI_PREFIX    0x800F0000L
40 #define SPAPI_MASK      0xFFFFL
41 #define HRESULT_FROM_SPAPI(x)   ((x & SPAPI_MASK) | SPAPI_PREFIX)
42
43 #define ADV_HRESULT(x)  ((x & SPAPI_ERROR) ? HRESULT_FROM_SPAPI(x) : HRESULT_FROM_WIN32(x))
44
45 /* sequentially returns pointers to parameters in a parameter list
46  * returns NULL if the parameter is empty, e.g. one,,three  */
47 LPWSTR get_parameter(LPWSTR *params, WCHAR separator)
48 {
49     LPWSTR token = *params;
50
51     if (!*params)
52         return NULL;
53
54     *params = strchrW(*params, separator);
55     if (*params)
56         *(*params)++ = '\0';
57
58     if (!*token)
59         return NULL;
60
61     return token;
62 }
63
64 static BOOL is_full_path(LPWSTR path)
65 {
66     const int MIN_PATH_LEN = 3;
67
68     if (!path || lstrlenW(path) < MIN_PATH_LEN)
69         return FALSE;
70
71     if (path[1] == ':' || (path[0] == '\\' && path[1] == '\\'))
72         return TRUE;
73
74     return FALSE;
75 }
76
77 /* performs a setupapi-level install of the INF file */
78 static HRESULT spapi_install(HINF hinf, LPCWSTR install_sec, LPCWSTR source_path)
79 {
80     BOOL ret;
81     HRESULT res;
82     PVOID context;
83
84     context = SetupInitDefaultQueueCallbackEx(NULL, INVALID_HANDLE_VALUE, 0, 0, NULL);
85     if (!context)
86         return ADV_HRESULT(GetLastError());
87
88     ret = SetupInstallFromInfSectionW(NULL, hinf, install_sec, SPINST_FILES,
89                                       NULL, source_path, SP_COPY_NEWER,
90                                       SetupDefaultQueueCallbackW,
91                                       context, NULL, NULL);
92     if (!ret)
93     {
94         res = ADV_HRESULT(GetLastError());
95         SetupTermDefaultQueueCallback(context);
96
97         return res;
98     }
99
100     SetupTermDefaultQueueCallback(context);
101
102     ret = SetupInstallFromInfSectionW(NULL, hinf, install_sec,
103                                       SPINST_INIFILES | SPINST_REGISTRY,
104                                       HKEY_LOCAL_MACHINE, NULL, 0,
105                                       NULL, NULL, NULL, NULL);
106     if (!ret)
107         return ADV_HRESULT(GetLastError());
108
109     return S_OK;
110 }
111
112 /* this structure very closely resembles parameters of RunSetupCommand() */
113 typedef struct
114 {
115     HWND hwnd;
116     LPCSTR title;
117     LPCSTR inf_name;
118     LPCSTR dir;
119     LPCSTR section_name;
120 } SETUPCOMMAND_PARAMS;
121
122 /***********************************************************************
123  *      DoInfInstall  (ADVPACK.@)
124  *
125  * Install an INF section.
126  *
127  * PARAMS
128  *  setup [I] Structure containing install information.
129  *
130  * RETURNS
131  *   S_OK                                Everything OK
132  *   HRESULT_FROM_WIN32(GetLastError())  Some other error
133  */
134 HRESULT WINAPI DoInfInstall(const SETUPCOMMAND_PARAMS *setup)
135 {
136     BOOL ret;
137     HINF hinf;
138     void *callback_context;
139
140     TRACE("%p, %s, %s, %s, %s\n", setup->hwnd, debugstr_a(setup->title),
141           debugstr_a(setup->inf_name), debugstr_a(setup->dir),
142           debugstr_a(setup->section_name));
143
144     hinf = SetupOpenInfFileA(setup->inf_name, NULL, INF_STYLE_WIN4, NULL);
145     if (hinf == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError());
146
147     callback_context = SetupInitDefaultQueueCallback(setup->hwnd);
148
149     ret = SetupInstallFromInfSectionA(NULL, hinf, setup->section_name, SPINST_ALL,
150                                       NULL, NULL, 0, SetupDefaultQueueCallbackA,
151                                       callback_context, NULL, NULL);
152     SetupTermDefaultQueueCallback(callback_context);
153     SetupCloseInfFile(hinf);
154
155     return ret ? S_OK : HRESULT_FROM_WIN32(GetLastError());
156 }
157
158 /***********************************************************************
159  *             ExecuteCabA    (ADVPACK.@)
160  *
161  * See ExecuteCabW.
162  */
163 HRESULT WINAPI ExecuteCabA(HWND hwnd, CABINFOA* pCab, LPVOID pReserved)
164 {
165     UNICODE_STRING cab, inf, section;
166     CABINFOW cabinfo;
167     HRESULT hr;
168
169     TRACE("(%p, %p, %p)\n", hwnd, pCab, pReserved);
170
171     if (!pCab)
172         return E_INVALIDARG;
173
174     if (pCab->pszCab)
175     {
176         RtlCreateUnicodeStringFromAsciiz(&cab, pCab->pszCab);
177         cabinfo.pszCab = cab.Buffer;
178     }
179     else
180         cabinfo.pszCab = NULL;
181
182     RtlCreateUnicodeStringFromAsciiz(&inf, pCab->pszInf);
183     RtlCreateUnicodeStringFromAsciiz(&section, pCab->pszSection);
184     
185     MultiByteToWideChar(CP_ACP, 0, pCab->szSrcPath, -1, cabinfo.szSrcPath,
186                         sizeof(cabinfo.szSrcPath) / sizeof(WCHAR));
187
188     cabinfo.pszInf = inf.Buffer;
189     cabinfo.pszSection = section.Buffer;
190     cabinfo.dwFlags = pCab->dwFlags;
191
192     hr = ExecuteCabW(hwnd, &cabinfo, pReserved);
193
194     if (pCab->pszCab)
195         RtlFreeUnicodeString(&cab);
196
197     RtlFreeUnicodeString(&inf);
198     RtlFreeUnicodeString(&section);
199
200     return hr;
201 }
202
203 /***********************************************************************
204  *             ExecuteCabW    (ADVPACK.@)
205  * 
206  * Installs the INF file extracted from a specified cabinet file.
207  * 
208  * PARAMS
209  *   hwnd      [I] Handle to the window used for the display.
210  *   pCab      [I] Information about the cabinet file.
211  *   pReserved [I] Reserved.  Must be NULL.
212  * 
213  * RETURNS
214  *   Success: S_OK.
215  *   Failure: E_FAIL.
216  *
217  * BUGS
218  *   Unimplemented
219  */
220 HRESULT WINAPI ExecuteCabW(HWND hwnd, CABINFOW* pCab, LPVOID pReserved)
221 {
222     FIXME("(%p, %p, %p): stub\n", hwnd, pCab, pReserved);
223     return E_FAIL;
224 }
225
226 /***********************************************************************
227  *      LaunchINFSectionA   (ADVPACK.@)
228  *
229  * See LaunchINFSectionW.
230  */
231 INT WINAPI LaunchINFSectionA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
232 {
233     UNICODE_STRING cmd;
234     HRESULT hr;
235
236     TRACE("(%p, %p, %s, %i)\n", hWnd, hInst, debugstr_a(cmdline), show);
237
238     RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
239
240     hr = LaunchINFSectionW(hWnd, hInst, cmd.Buffer, show);
241
242     RtlFreeUnicodeString(&cmd);
243
244     return hr;
245 }
246
247 /***********************************************************************
248  *      LaunchINFSectionW   (ADVPACK.@)
249  *
250  * Installs an INF section without BACKUP/ROLLBACK capabilities.
251  *
252  * PARAMS
253  *   hWnd    [I] Handle to parent window, NULL for desktop.
254  *   hInst   [I] Instance of the process.
255  *   cmdline [I] Contains parameters in the order INF,section,flags,reboot.
256  *   show    [I] How the window should be shown.
257  *
258  * RETURNS
259  *  Success: S_OK.
260  *  Failure: S_FALSE
261  *
262  * NOTES
263  *  INF - Filename of the INF to launch.
264  *  section - INF section to install.
265  *  flags - see advpub.h.
266  *  reboot - smart reboot behavior
267  *    'A' Always reboot.
268  *    'I' Reboot if needed (default).
269  *    'N' No reboot.
270  * 
271  * BUGS
272  *  Unimplemented.
273  */
274 INT WINAPI LaunchINFSectionW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
275 {
276     FIXME("(%p, %p, %s, %i): stub\n", hWnd, hInst, debugstr_w(cmdline), show);
277     return 0;
278 }
279
280 /***********************************************************************
281  *      LaunchINFSectionExA (ADVPACK.@)
282  *
283  * See LaunchINFSectionExW.
284  */
285 HRESULT WINAPI LaunchINFSectionExA(HWND hWnd, HINSTANCE hInst, LPSTR cmdline, INT show)
286 {
287     UNICODE_STRING cmd;
288     HRESULT hr;
289
290     TRACE("(%p, %p, %s, %i): stub\n", hWnd, hInst, debugstr_a(cmdline), show);
291
292     RtlCreateUnicodeStringFromAsciiz(&cmd, cmdline);
293
294     hr = LaunchINFSectionExW(hWnd, hInst, cmd.Buffer, show);
295
296     RtlFreeUnicodeString(&cmd);
297
298     return hr;
299 }
300
301 /***********************************************************************
302  *      LaunchINFSectionExW (ADVPACK.@)
303  *
304  * Installs an INF section with BACKUP/ROLLBACK capabilities.
305  *
306  * PARAMS
307  *   hWnd    [I] Handle to parent window, NULL for desktop.
308  *   hInst   [I] Instance of the process.
309  *   cmdline [I] Contains parameters in the order INF,section,CAB,flags,reboot.
310  *   show    [I] How the window should be shown.
311  *
312  * RETURNS
313  *  Success: S_OK.
314  *  Failure: E_FAIL.
315  *
316  * NOTES
317  *  INF - Filename of the INF to launch.
318  *  section - INF section to install.
319  *  flags - see advpub.h.
320  *  reboot - smart reboot behavior
321  *    'A' Always reboot.
322  *    'I' Reboot if needed (default).
323  *    'N' No reboot.
324  *
325  * BUGS
326  *  Doesn't handle the reboot flag.
327  */
328 HRESULT WINAPI LaunchINFSectionExW(HWND hWnd, HINSTANCE hInst, LPWSTR cmdline, INT show)
329 {
330     LPWSTR cmdline_copy, cmdline_ptr;
331     LPWSTR flags, ptr;
332     CABINFOW cabinfo;
333     HRESULT hr = S_OK;
334
335     TRACE("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_w(cmdline), show);
336
337     if (!cmdline)
338         return E_INVALIDARG;
339
340     cmdline_copy = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
341     cmdline_ptr = cmdline_copy;
342     lstrcpyW(cmdline_copy, cmdline);
343
344     cabinfo.pszInf = get_parameter(&cmdline_ptr, ',');
345     cabinfo.pszSection = get_parameter(&cmdline_ptr, ',');
346     cabinfo.pszCab = get_parameter(&cmdline_ptr, ',');
347
348     flags = get_parameter(&cmdline_ptr, ',');
349     if (flags)
350         cabinfo.dwFlags = atolW(flags);
351
352     /* get the source path from the cab filename */
353     if (cabinfo.pszCab && *cabinfo.pszCab)
354     {
355         if (!is_full_path(cabinfo.pszCab))
356             goto done;
357
358         lstrcpyW(cabinfo.szSrcPath, cabinfo.pszCab);
359         ptr = strrchrW(cabinfo.szSrcPath, '\\');
360         *(++ptr) = '\0';
361     }
362
363     hr = ExecuteCabW(hWnd, &cabinfo, NULL);
364
365 done:
366     HeapFree(GetProcessHeap(), 0, cmdline_copy);
367
368     return hr;
369 }
370
371 HRESULT launch_exe(LPCWSTR cmd, LPCWSTR dir, HANDLE *phEXE)
372 {
373     STARTUPINFOW si;
374     PROCESS_INFORMATION pi;
375
376     if (phEXE) *phEXE = NULL;
377
378     ZeroMemory(&pi, sizeof(pi));
379     ZeroMemory(&si, sizeof(si));
380     si.cb = sizeof(si);
381
382     if (!CreateProcessW(NULL, (LPWSTR)cmd, NULL, NULL, FALSE,
383                         CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_PROCESS_GROUP,
384                         NULL, dir, &si, &pi))
385     {
386         return HRESULT_FROM_WIN32(GetLastError());
387     }
388
389     CloseHandle(pi.hThread);
390
391     if (phEXE)
392     {
393         *phEXE = pi.hProcess;
394         return S_ASYNCHRONOUS;
395     }
396
397     /* wait for the child process to finish */
398     WaitForSingleObject(pi.hProcess, INFINITE);
399     CloseHandle(pi.hProcess);
400
401     return S_OK;
402 }
403
404 /***********************************************************************
405  *      RunSetupCommandA  (ADVPACK.@)
406  *
407  * See RunSetupCommandW.
408  */
409 HRESULT WINAPI RunSetupCommandA(HWND hWnd, LPCSTR szCmdName,
410                                 LPCSTR szInfSection, LPCSTR szDir,
411                                 LPCSTR lpszTitle, HANDLE *phEXE,
412                                 DWORD dwFlags, LPVOID pvReserved)
413 {
414     UNICODE_STRING cmdname, infsec;
415     UNICODE_STRING dir, title;
416     HRESULT hr;
417
418     TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
419           hWnd, debugstr_a(szCmdName), debugstr_a(szInfSection),
420           debugstr_a(szDir), debugstr_a(lpszTitle),
421           phEXE, dwFlags, pvReserved);
422
423     if (!szCmdName || !szDir)
424         return E_INVALIDARG;
425
426     RtlCreateUnicodeStringFromAsciiz(&cmdname, szCmdName);
427     RtlCreateUnicodeStringFromAsciiz(&infsec, szInfSection);
428     RtlCreateUnicodeStringFromAsciiz(&dir, szDir);
429     RtlCreateUnicodeStringFromAsciiz(&title, lpszTitle);
430
431     hr = RunSetupCommandW(hWnd, cmdname.Buffer, infsec.Buffer, dir.Buffer,
432                           title.Buffer, phEXE, dwFlags, pvReserved);
433
434     RtlFreeUnicodeString(&cmdname);
435     RtlFreeUnicodeString(&infsec);
436     RtlFreeUnicodeString(&dir);
437     RtlFreeUnicodeString(&title);
438
439     return hr;
440 }
441
442 /***********************************************************************
443  *      RunSetupCommandW  (ADVPACK.@)
444  *
445  * Executes an install section in an INF file or a program.
446  *
447  * PARAMS
448  *   hWnd          [I] Handle to parent window, NULL for quiet mode
449  *   szCmdName     [I] Inf or EXE filename to execute
450  *   szInfSection  [I] Inf section to install, NULL for DefaultInstall
451  *   szDir         [I] Path to extracted files
452  *   szTitle       [I] Title of all dialogs
453  *   phEXE         [O] Handle of EXE to wait for
454  *   dwFlags       [I] Flags; see include/advpub.h
455  *   pvReserved    [I] Reserved
456  *
457  * RETURNS
458  *   S_OK                                 Everything OK
459  *   S_ASYNCHRONOUS                       OK, required to wait on phEXE
460  *   ERROR_SUCCESS_REBOOT_REQUIRED        Reboot required
461  *   E_INVALIDARG                         Invalid argument given
462  *   HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION)
463  *                                        Not supported on this Windows version
464  *   E_UNEXPECTED                         Unexpected error
465  *   HRESULT_FROM_WIN32(GetLastError())   Some other error
466  *
467  * BUGS
468  *   INF install unimplemented.
469  */
470 HRESULT WINAPI RunSetupCommandW(HWND hWnd, LPCWSTR szCmdName,
471                                 LPCWSTR szInfSection, LPCWSTR szDir,
472                                 LPCWSTR lpszTitle, HANDLE *phEXE,
473                                 DWORD dwFlags, LPVOID pvReserved)
474 {
475     HINF hinf;
476     HRESULT hr;
477
478     TRACE("(%p, %s, %s, %s, %s, %p, %ld, %p)\n",
479           hWnd, debugstr_w(szCmdName), debugstr_w(szInfSection),
480           debugstr_w(szDir), debugstr_w(lpszTitle),
481           phEXE, dwFlags, pvReserved);
482
483     if (dwFlags)
484         FIXME("Unhandled flags: 0x%08lx\n", dwFlags);
485
486     if (!szCmdName || !szDir)
487         return E_INVALIDARG;
488
489     if (!(dwFlags & RSC_FLAG_INF))
490         return launch_exe(szCmdName, szDir, phEXE);
491
492     hinf = SetupOpenInfFileW(szCmdName, NULL, INF_STYLE_WIN4, NULL);
493     if (hinf == INVALID_HANDLE_VALUE)
494         return ADV_HRESULT(GetLastError());
495
496     hr = spapi_install(hinf, szInfSection, szDir);
497
498     SetupCloseInfFile(hinf);
499     return hr;
500 }