riched20: Removed unneeded NULL check (Coverity).
[wine] / dlls / setupapi / install.c
1 /*
2  * Setupapi install routines
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
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
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "winternl.h"
29 #include "winerror.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winnls.h"
33 #include "winsvc.h"
34 #include "shlobj.h"
35 #include "objidl.h"
36 #include "objbase.h"
37 #include "setupapi.h"
38 #include "setupapi_private.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
43
44 /* info passed to callback functions dealing with files */
45 struct files_callback_info
46 {
47     HSPFILEQ queue;
48     PCWSTR   src_root;
49     UINT     copy_flags;
50     HINF     layout;
51 };
52
53 /* info passed to callback functions dealing with the registry */
54 struct registry_callback_info
55 {
56     HKEY default_root;
57     BOOL delete;
58 };
59
60 /* info passed to callback functions dealing with registering dlls */
61 struct register_dll_info
62 {
63     PSP_FILE_CALLBACK_W callback;
64     PVOID               callback_context;
65     BOOL                unregister;
66 };
67
68 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
69
70 /* Unicode constants */
71 static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
72 static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
73 static const WCHAR RenFiles[]   = {'R','e','n','F','i','l','e','s',0};
74 static const WCHAR Ini2Reg[]    = {'I','n','i','2','R','e','g',0};
75 static const WCHAR LogConf[]    = {'L','o','g','C','o','n','f',0};
76 static const WCHAR AddReg[]     = {'A','d','d','R','e','g',0};
77 static const WCHAR DelReg[]     = {'D','e','l','R','e','g',0};
78 static const WCHAR BitReg[]     = {'B','i','t','R','e','g',0};
79 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
80 static const WCHAR CopyINF[]    = {'C','o','p','y','I','N','F',0};
81 static const WCHAR AddService[] = {'A','d','d','S','e','r','v','i','c','e',0};
82 static const WCHAR DelService[] = {'D','e','l','S','e','r','v','i','c','e',0};
83 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
84 static const WCHAR RegisterDlls[]    = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
85 static const WCHAR UnregisterDlls[]  = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
86 static const WCHAR ProfileItems[]    = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
87 static const WCHAR Name[]            = {'N','a','m','e',0};
88 static const WCHAR CmdLine[]         = {'C','m','d','L','i','n','e',0};
89 static const WCHAR SubDir[]          = {'S','u','b','D','i','r',0};
90 static const WCHAR WineFakeDlls[]    = {'W','i','n','e','F','a','k','e','D','l','l','s',0};
91 static const WCHAR DisplayName[]     = {'D','i','s','p','l','a','y','N','a','m','e',0};
92 static const WCHAR Description[]     = {'D','e','s','c','r','i','p','t','i','o','n',0};
93 static const WCHAR ServiceBinary[]   = {'S','e','r','v','i','c','e','B','i','n','a','r','y',0};
94 static const WCHAR StartName[]       = {'S','t','a','r','t','N','a','m','e',0};
95 static const WCHAR LoadOrderGroup[]  = {'L','o','a','d','O','r','d','e','r','G','r','o','u','p',0};
96 static const WCHAR ServiceType[]     = {'S','e','r','v','i','c','e','T','y','p','e',0};
97 static const WCHAR StartType[]       = {'S','t','a','r','t','T','y','p','e',0};
98 static const WCHAR ErrorControl[]    = {'E','r','r','o','r','C','o','n','t','r','o','l',0};
99
100 static const WCHAR ServicesKey[] = {'S','y','s','t','e','m','\\',
101                         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
102                         'S','e','r','v','i','c','e','s',0};
103
104 /***********************************************************************
105  *            get_field_string
106  *
107  * Retrieve the contents of a field, dynamically growing the buffer if necessary.
108  */
109 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
110                                 WCHAR *static_buffer, DWORD *size )
111 {
112     DWORD required;
113
114     if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
115     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
116     {
117         /* now grow the buffer */
118         if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
119         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
120         *size = required;
121         if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
122     }
123     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
124     return NULL;
125 }
126
127
128 /***********************************************************************
129  *            dup_section_line_field
130  *
131  * Retrieve the contents of a field in a newly-allocated buffer.
132  */
133 static WCHAR *dup_section_line_field( HINF hinf, const WCHAR *section, const WCHAR *line, DWORD index )
134 {
135     INFCONTEXT context;
136     DWORD size;
137     WCHAR *buffer;
138
139     if (!SetupFindFirstLineW( hinf, section, line, &context )) return NULL;
140     if (!SetupGetStringFieldW( &context, index, NULL, 0, &size )) return NULL;
141     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
142     if (!SetupGetStringFieldW( &context, index, buffer, size, NULL )) buffer[0] = 0;
143     return buffer;
144 }
145
146 /***********************************************************************
147  *            copy_files_callback
148  *
149  * Called once for each CopyFiles entry in a given section.
150  */
151 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
152 {
153     struct files_callback_info *info = arg;
154
155     if (field[0] == '@')  /* special case: copy single file */
156         SetupQueueDefaultCopyW( info->queue, info->layout ? info->layout : hinf, info->src_root, NULL, field+1, info->copy_flags );
157     else
158         SetupQueueCopySectionW( info->queue, info->src_root, info->layout ? info->layout : hinf, hinf, field, info->copy_flags );
159     return TRUE;
160 }
161
162
163 /***********************************************************************
164  *            delete_files_callback
165  *
166  * Called once for each DelFiles entry in a given section.
167  */
168 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
169 {
170     struct files_callback_info *info = arg;
171     SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
172     return TRUE;
173 }
174
175
176 /***********************************************************************
177  *            rename_files_callback
178  *
179  * Called once for each RenFiles entry in a given section.
180  */
181 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
182 {
183     struct files_callback_info *info = arg;
184     SetupQueueRenameSectionW( info->queue, hinf, 0, field );
185     return TRUE;
186 }
187
188
189 /***********************************************************************
190  *            get_root_key
191  *
192  * Retrieve the registry root key from its name.
193  */
194 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
195 {
196     static const WCHAR HKCR[] = {'H','K','C','R',0};
197     static const WCHAR HKCU[] = {'H','K','C','U',0};
198     static const WCHAR HKLM[] = {'H','K','L','M',0};
199     static const WCHAR HKU[]  = {'H','K','U',0};
200     static const WCHAR HKR[]  = {'H','K','R',0};
201
202     if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
203     if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
204     if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
205     if (!strcmpiW( name, HKU )) return HKEY_USERS;
206     if (!strcmpiW( name, HKR )) return def_root;
207     return 0;
208 }
209
210
211 /***********************************************************************
212  *            append_multi_sz_value
213  *
214  * Append a multisz string to a multisz registry value.
215  */
216 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
217                                    DWORD str_size )
218 {
219     DWORD size, type, total;
220     WCHAR *buffer, *p;
221
222     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
223     if (type != REG_MULTI_SZ) return;
224
225     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
226     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
227
228     /* compare each string against all the existing ones */
229     total = size;
230     while (*strings)
231     {
232         int len = strlenW(strings) + 1;
233
234         for (p = buffer; *p; p += strlenW(p) + 1)
235             if (!strcmpiW( p, strings )) break;
236
237         if (!*p)  /* not found, need to append it */
238         {
239             memcpy( p, strings, len * sizeof(WCHAR) );
240             p[len] = 0;
241             total += len;
242         }
243         strings += len;
244     }
245     if (total != size)
246     {
247         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
248         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
249     }
250  done:
251     HeapFree( GetProcessHeap(), 0, buffer );
252 }
253
254
255 /***********************************************************************
256  *            delete_multi_sz_value
257  *
258  * Remove a string from a multisz registry value.
259  */
260 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
261 {
262     DWORD size, type;
263     WCHAR *buffer, *src, *dst;
264
265     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
266     if (type != REG_MULTI_SZ) return;
267     /* allocate double the size, one for value before and one for after */
268     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
269     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
270     src = buffer;
271     dst = buffer + size;
272     while (*src)
273     {
274         int len = strlenW(src) + 1;
275         if (strcmpiW( src, string ))
276         {
277             memcpy( dst, src, len * sizeof(WCHAR) );
278             dst += len;
279         }
280         src += len;
281     }
282     *dst++ = 0;
283     if (dst != buffer + 2*size)  /* did we remove something? */
284     {
285         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
286         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
287                         (BYTE *)(buffer + size), dst - (buffer + size) );
288     }
289  done:
290     HeapFree( GetProcessHeap(), 0, buffer );
291 }
292
293
294 /***********************************************************************
295  *            do_reg_operation
296  *
297  * Perform an add/delete registry operation depending on the flags.
298  */
299 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
300 {
301     DWORD type, size;
302
303     if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
304     {
305         if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
306         {
307             if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
308             {
309                 WCHAR *str;
310
311                 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
312                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
313                 SetupGetStringFieldW( context, 5, str, size, NULL );
314                 delete_multi_sz_value( hkey, value, str );
315                 HeapFree( GetProcessHeap(), 0, str );
316             }
317             else RegDeleteValueW( hkey, value );
318         }
319         else NtDeleteKey( hkey );
320         return TRUE;
321     }
322
323     if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
324
325     if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
326     {
327         BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
328         if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
329         if (!exists && (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
330     }
331
332     switch(flags & FLG_ADDREG_TYPE_MASK)
333     {
334     case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
335     case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
336     case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
337     case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
338     case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
339     case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
340     default:                        type = flags >> 16; break;
341     }
342
343     if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
344         (type == REG_DWORD && SetupGetFieldCount(context) == 5))
345     {
346         static const WCHAR empty;
347         WCHAR *str = NULL;
348
349         if (type == REG_MULTI_SZ)
350         {
351             if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
352             if (size)
353             {
354                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
355                 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
356             }
357             if (flags & FLG_ADDREG_APPEND)
358             {
359                 if (!str) return TRUE;
360                 append_multi_sz_value( hkey, value, str, size );
361                 HeapFree( GetProcessHeap(), 0, str );
362                 return TRUE;
363             }
364             /* else fall through to normal string handling */
365         }
366         else
367         {
368             if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
369             if (size)
370             {
371                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
372                 SetupGetStringFieldW( context, 5, str, size, NULL );
373             }
374         }
375
376         if (type == REG_DWORD)
377         {
378             DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
379             TRACE( "setting dword %s to %x\n", debugstr_w(value), dw );
380             RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
381         }
382         else
383         {
384             TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
385             if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
386             else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
387         }
388         HeapFree( GetProcessHeap(), 0, str );
389         return TRUE;
390     }
391     else  /* get the binary data */
392     {
393         BYTE *data = NULL;
394
395         if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
396         if (size)
397         {
398             if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
399             TRACE( "setting binary data %s len %d\n", debugstr_w(value), size );
400             SetupGetBinaryField( context, 5, data, size, NULL );
401         }
402         RegSetValueExW( hkey, value, 0, type, data, size );
403         HeapFree( GetProcessHeap(), 0, data );
404         return TRUE;
405     }
406 }
407
408
409 /***********************************************************************
410  *            registry_callback
411  *
412  * Called once for each AddReg and DelReg entry in a given section.
413  */
414 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
415 {
416     struct registry_callback_info *info = arg;
417     INFCONTEXT context;
418     HKEY root_key, hkey;
419
420     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
421
422     for (; ok; ok = SetupFindNextLine( &context, &context ))
423     {
424         WCHAR buffer[MAX_INF_STRING_LENGTH];
425         INT flags;
426
427         /* get root */
428         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
429             continue;
430         if (!(root_key = get_root_key( buffer, info->default_root )))
431             continue;
432
433         /* get key */
434         if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
435             *buffer = 0;
436
437         /* get flags */
438         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
439
440         if (!info->delete)
441         {
442             if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
443         }
444         else
445         {
446             if (!flags) flags = FLG_ADDREG_DELREG_BIT;
447             else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
448         }
449
450         if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
451         {
452             if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
453         }
454         else if (RegCreateKeyW( root_key, buffer, &hkey ))
455         {
456             ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
457             continue;
458         }
459         TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
460
461         /* get value name */
462         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
463             *buffer = 0;
464
465         /* and now do it */
466         if (!do_reg_operation( hkey, buffer, &context, flags ))
467         {
468             RegCloseKey( hkey );
469             return FALSE;
470         }
471         RegCloseKey( hkey );
472     }
473     return TRUE;
474 }
475
476
477 /***********************************************************************
478  *            do_register_dll
479  *
480  * Register or unregister a dll.
481  */
482 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
483                              INT flags, INT timeout, const WCHAR *args )
484 {
485     HMODULE module;
486     HRESULT res;
487     SP_REGISTER_CONTROL_STATUSW status;
488     IMAGE_NT_HEADERS *nt;
489
490     status.cbSize = sizeof(status);
491     status.FileName = path;
492     status.FailureCode = SPREG_SUCCESS;
493     status.Win32Error = ERROR_SUCCESS;
494
495     if (info->callback)
496     {
497         switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
498                                (UINT_PTR)&status, !info->unregister ))
499         {
500         case FILEOP_ABORT:
501             SetLastError( ERROR_OPERATION_ABORTED );
502             return FALSE;
503         case FILEOP_SKIP:
504             return TRUE;
505         case FILEOP_DOIT:
506             break;
507         }
508     }
509
510     if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
511     {
512         WARN( "could not load %s\n", debugstr_w(path) );
513         status.FailureCode = SPREG_LOADLIBRARY;
514         status.Win32Error = GetLastError();
515         goto done;
516     }
517
518     if ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
519     {
520         /* file is an executable, not a dll */
521         STARTUPINFOW startup;
522         PROCESS_INFORMATION info;
523         WCHAR *cmd_line;
524         BOOL res;
525         static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
526         static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
527
528         FreeLibrary( module );
529         module = NULL;
530         if (!args) args = default_args;
531         cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
532         sprintfW( cmd_line, format, path, args );
533         memset( &startup, 0, sizeof(startup) );
534         startup.cb = sizeof(startup);
535         TRACE( "executing %s\n", debugstr_w(cmd_line) );
536         res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
537         HeapFree( GetProcessHeap(), 0, cmd_line );
538         if (!res)
539         {
540             status.FailureCode = SPREG_LOADLIBRARY;
541             status.Win32Error = GetLastError();
542             goto done;
543         }
544         CloseHandle( info.hThread );
545
546         if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
547         {
548             /* timed out, kill the process */
549             TerminateProcess( info.hProcess, 1 );
550             status.FailureCode = SPREG_TIMEOUT;
551             status.Win32Error = ERROR_TIMEOUT;
552         }
553         CloseHandle( info.hProcess );
554         goto done;
555     }
556
557     if (flags & FLG_REGSVR_DLLREGISTER)
558     {
559         const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
560         HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
561
562         if (!func)
563         {
564             status.FailureCode = SPREG_GETPROCADDR;
565             status.Win32Error = GetLastError();
566             goto done;
567         }
568
569         TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
570         res = func();
571
572         if (FAILED(res))
573         {
574             WARN( "calling %s in %s returned error %x\n", entry_point, debugstr_w(path), res );
575             status.FailureCode = SPREG_REGSVR;
576             status.Win32Error = res;
577             goto done;
578         }
579     }
580
581     if (flags & FLG_REGSVR_DLLINSTALL)
582     {
583         HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
584
585         if (!func)
586         {
587             status.FailureCode = SPREG_GETPROCADDR;
588             status.Win32Error = GetLastError();
589             goto done;
590         }
591
592         TRACE( "calling DllInstall(%d,%s) in %s\n",
593                !info->unregister, debugstr_w(args), debugstr_w(path) );
594         res = func( !info->unregister, args );
595
596         if (FAILED(res))
597         {
598             WARN( "calling DllInstall in %s returned error %x\n", debugstr_w(path), res );
599             status.FailureCode = SPREG_REGSVR;
600             status.Win32Error = res;
601             goto done;
602         }
603     }
604
605 done:
606     if (module) FreeLibrary( module );
607     if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
608                                         (UINT_PTR)&status, !info->unregister );
609     return TRUE;
610 }
611
612
613 /***********************************************************************
614  *            register_dlls_callback
615  *
616  * Called once for each RegisterDlls entry in a given section.
617  */
618 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
619 {
620     struct register_dll_info *info = arg;
621     INFCONTEXT context;
622     BOOL ret = TRUE;
623     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
624
625     for (; ok; ok = SetupFindNextLine( &context, &context ))
626     {
627         WCHAR *path, *args, *p;
628         WCHAR buffer[MAX_INF_STRING_LENGTH];
629         INT flags, timeout;
630
631         /* get directory */
632         if (!(path = PARSER_get_dest_dir( &context ))) continue;
633
634         /* get dll name */
635         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
636             goto done;
637         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
638                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
639         path = p;
640         p += strlenW(p);
641         if (p == path || p[-1] != '\\') *p++ = '\\';
642         strcpyW( p, buffer );
643
644         /* get flags */
645         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
646
647         /* get timeout */
648         if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
649
650         /* get command line */
651         args = NULL;
652         if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
653             args = buffer;
654
655         ret = do_register_dll( info, path, flags, timeout, args );
656
657     done:
658         HeapFree( GetProcessHeap(), 0, path );
659         if (!ret) break;
660     }
661     return ret;
662 }
663
664 /***********************************************************************
665  *            fake_dlls_callback
666  *
667  * Called once for each WineFakeDlls entry in a given section.
668  */
669 static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
670 {
671     INFCONTEXT context;
672     BOOL ret = TRUE;
673     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
674
675     for (; ok; ok = SetupFindNextLine( &context, &context ))
676     {
677         WCHAR *path, *p;
678         WCHAR buffer[MAX_INF_STRING_LENGTH];
679
680         /* get directory */
681         if (!(path = PARSER_get_dest_dir( &context ))) continue;
682
683         /* get dll name */
684         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
685             goto done;
686         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
687                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
688         path = p;
689         p += strlenW(p);
690         if (p == path || p[-1] != '\\') *p++ = '\\';
691         strcpyW( p, buffer );
692
693         /* get source dll */
694         if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
695             p = buffer;  /* otherwise use target base name as default source */
696
697         create_fake_dll( path, p );  /* ignore errors */
698
699     done:
700         HeapFree( GetProcessHeap(), 0, path );
701         if (!ret) break;
702     }
703     return ret;
704 }
705
706 /***********************************************************************
707  *            update_ini_callback
708  *
709  * Called once for each UpdateInis entry in a given section.
710  */
711 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
712 {
713     INFCONTEXT context;
714
715     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
716
717     for (; ok; ok = SetupFindNextLine( &context, &context ))
718     {
719         WCHAR buffer[MAX_INF_STRING_LENGTH];
720         WCHAR  filename[MAX_INF_STRING_LENGTH];
721         WCHAR  section[MAX_INF_STRING_LENGTH];
722         WCHAR  entry[MAX_INF_STRING_LENGTH];
723         WCHAR  string[MAX_INF_STRING_LENGTH];
724         LPWSTR divider;
725
726         if (!SetupGetStringFieldW( &context, 1, filename,
727                                    sizeof(filename)/sizeof(WCHAR), NULL ))
728             continue;
729
730         if (!SetupGetStringFieldW( &context, 2, section,
731                                    sizeof(section)/sizeof(WCHAR), NULL ))
732             continue;
733
734         if (!SetupGetStringFieldW( &context, 4, buffer,
735                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
736             continue;
737
738         divider = strchrW(buffer,'=');
739         if (divider)
740         {
741             *divider = 0;
742             strcpyW(entry,buffer);
743             divider++;
744             strcpyW(string,divider);
745         }
746         else
747         {
748             strcpyW(entry,buffer);
749             string[0]=0;
750         }
751
752         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
753                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
754         WritePrivateProfileStringW(section,entry,string,filename);
755
756     }
757     return TRUE;
758 }
759
760 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
761 {
762     FIXME( "should update ini fields %s\n", debugstr_w(field) );
763     return TRUE;
764 }
765
766 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
767 {
768     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
769     return TRUE;
770 }
771
772 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
773 {
774     FIXME( "should do logconf %s\n", debugstr_w(field) );
775     return TRUE;
776 }
777
778 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
779 {
780     FIXME( "should do bitreg %s\n", debugstr_w(field) );
781     return TRUE;
782 }
783
784 static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
785 {
786     WCHAR lnkpath[MAX_PATH];
787     LPWSTR cmdline=NULL, lnkpath_end;
788     unsigned int name_size;
789     INFCONTEXT name_context, context;
790     int attrs=0;
791
792     static const WCHAR dotlnk[] = {'.','l','n','k',0};
793
794     TRACE( "(%s)\n", debugstr_w(field) );
795
796     if (SetupFindFirstLineW( hinf, field, Name, &name_context ))
797     {
798         SetupGetIntField( &name_context, 2, &attrs );
799         if (attrs & ~FLG_PROFITEM_GROUP) FIXME( "unhandled attributes: %x\n", attrs );
800     }
801     else return TRUE;
802
803     /* calculate filename */
804     SHGetFolderPathW( NULL, CSIDL_COMMON_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, lnkpath );
805     lnkpath_end = lnkpath + strlenW(lnkpath);
806     if (lnkpath_end[-1] != '\\') *lnkpath_end++ = '\\';
807
808     if (!(attrs & FLG_PROFITEM_GROUP) && SetupFindFirstLineW( hinf, field, SubDir, &context ))
809     {
810         unsigned int subdir_size;
811
812         if (!SetupGetStringFieldW( &context, 1, lnkpath_end, (lnkpath+MAX_PATH)-lnkpath_end, &subdir_size ))
813             return TRUE;
814
815         lnkpath_end += subdir_size - 1;
816         if (lnkpath_end[-1] != '\\') *lnkpath_end++ = '\\';
817     }
818
819     if (!SetupGetStringFieldW( &name_context, 1, lnkpath_end, (lnkpath+MAX_PATH)-lnkpath_end, &name_size ))
820         return TRUE;
821
822     lnkpath_end += name_size - 1;
823
824     if (attrs & FLG_PROFITEM_GROUP)
825     {
826         SHPathPrepareForWriteW( NULL, NULL, lnkpath, SHPPFW_DIRCREATE );
827     }
828     else
829     {
830         IShellLinkW* shelllink=NULL;
831         IPersistFile* persistfile=NULL;
832         HRESULT initresult=E_FAIL;
833
834         if (lnkpath+MAX_PATH < lnkpath_end + 5) return TRUE;
835         strcpyW( lnkpath_end, dotlnk );
836
837         TRACE( "link path: %s\n", debugstr_w(lnkpath) );
838
839         /* calculate command line */
840         if (SetupFindFirstLineW( hinf, field, CmdLine, &context ))
841         {
842             unsigned int dir_len=0, subdir_size=0, filename_size=0;
843             int dirid=0;
844             LPCWSTR dir;
845             LPWSTR cmdline_end;
846
847             SetupGetIntField( &context, 1, &dirid );
848             dir = DIRID_get_string( dirid );
849
850             if (dir) dir_len = strlenW(dir);
851
852             SetupGetStringFieldW( &context, 2, NULL, 0, &subdir_size );
853             SetupGetStringFieldW( &context, 3, NULL, 0, &filename_size );
854
855             if (dir_len && filename_size)
856             {
857                 cmdline = cmdline_end = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * (dir_len+subdir_size+filename_size+1) );
858
859                 strcpyW( cmdline_end, dir );
860                 cmdline_end += dir_len;
861                 if (cmdline_end[-1] != '\\') *cmdline_end++ = '\\';
862
863                 if (subdir_size)
864                 {
865                     SetupGetStringFieldW( &context, 2, cmdline_end, subdir_size, NULL );
866                     cmdline_end += subdir_size-1;
867                     if (cmdline_end[-1] != '\\') *cmdline_end++ = '\\';
868                 }
869                 SetupGetStringFieldW( &context, 3, cmdline_end, filename_size, NULL );
870                 TRACE( "cmdline: %s\n", debugstr_w(cmdline));
871             }
872         }
873
874         if (!cmdline) return TRUE;
875
876         initresult = CoInitialize(NULL);
877
878         if (!SUCCEEDED(CoCreateInstance( &CLSID_ShellLink, NULL,
879                                          CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&shelllink)))
880             goto done;
881
882         IShellLinkW_SetPath( shelllink, cmdline );
883         SHPathPrepareForWriteW( NULL, NULL, lnkpath, SHPPFW_DIRCREATE|SHPPFW_IGNOREFILENAME );
884         if (SUCCEEDED(IShellLinkW_QueryInterface( shelllink, &IID_IPersistFile, (LPVOID*)&persistfile)))
885         {
886             TRACE( "writing link: %s\n", debugstr_w(lnkpath) );
887             IPersistFile_Save( persistfile, lnkpath, FALSE );
888             IPersistFile_Release( persistfile );
889         }
890         IShellLinkW_Release( shelllink );
891
892     done:
893         if (SUCCEEDED(initresult)) CoUninitialize();
894         HeapFree( GetProcessHeap(), 0, cmdline );
895     }
896
897     return TRUE;
898 }
899
900 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
901 {
902     FIXME( "should do copy inf %s\n", debugstr_w(field) );
903     return TRUE;
904 }
905
906
907 /***********************************************************************
908  *            iterate_section_fields
909  *
910  * Iterate over all fields of a certain key of a certain section
911  */
912 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
913                                     iterate_fields_func callback, void *arg )
914 {
915     WCHAR static_buffer[200];
916     WCHAR *buffer = static_buffer;
917     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
918     INFCONTEXT context;
919     BOOL ret = FALSE;
920
921     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
922     while (ok)
923     {
924         UINT i, count = SetupGetFieldCount( &context );
925         for (i = 1; i <= count; i++)
926         {
927             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
928                 goto done;
929             if (!callback( hinf, buffer, arg ))
930             {
931                 WARN("callback failed for %s %s err %d\n",
932                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
933                 goto done;
934             }
935         }
936         ok = SetupFindNextMatchLineW( &context, key, &context );
937     }
938     ret = TRUE;
939  done:
940     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
941     return ret;
942 }
943
944
945 /***********************************************************************
946  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
947  */
948 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
949                                               PCSTR section, PCSTR src_root, UINT flags )
950 {
951     UNICODE_STRING sectionW;
952     BOOL ret = FALSE;
953
954     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
955     {
956         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
957         return FALSE;
958     }
959     if (!src_root)
960         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
961                                                 NULL, flags );
962     else
963     {
964         UNICODE_STRING srcW;
965         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
966         {
967             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
968                                                     srcW.Buffer, flags );
969             RtlFreeUnicodeString( &srcW );
970         }
971         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
972     }
973     RtlFreeUnicodeString( &sectionW );
974     return ret;
975 }
976
977
978 /***********************************************************************
979  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
980  */
981 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
982                                               PCWSTR section, PCWSTR src_root, UINT flags )
983 {
984     struct files_callback_info info;
985
986     info.queue      = queue;
987     info.src_root   = src_root;
988     info.copy_flags = flags;
989     info.layout     = hlayout;
990     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
991 }
992
993
994 /***********************************************************************
995  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
996  */
997 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
998                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
999                                          PSP_FILE_CALLBACK_A callback, PVOID context,
1000                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1001 {
1002     UNICODE_STRING sectionW, src_rootW;
1003     struct callback_WtoA_context ctx;
1004     BOOL ret = FALSE;
1005
1006     src_rootW.Buffer = NULL;
1007     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
1008     {
1009         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1010         return FALSE;
1011     }
1012
1013     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1014     {
1015         ctx.orig_context = context;
1016         ctx.orig_handler = callback;
1017         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
1018                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
1019                                            &ctx, devinfo, devinfo_data );
1020         RtlFreeUnicodeString( &sectionW );
1021     }
1022     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1023
1024     RtlFreeUnicodeString( &src_rootW );
1025     return ret;
1026 }
1027
1028
1029 /***********************************************************************
1030  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
1031  */
1032 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
1033                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
1034                                          PSP_FILE_CALLBACK_W callback, PVOID context,
1035                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1036 {
1037     if (flags & SPINST_FILES)
1038     {
1039         struct files_callback_info info;
1040         HSPFILEQ queue;
1041         BOOL ret;
1042
1043         if (!(queue = SetupOpenFileQueue())) return FALSE;
1044         info.queue      = queue;
1045         info.src_root   = src_root;
1046         info.copy_flags = copy_flags;
1047         info.layout     = hinf;
1048         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
1049                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
1050                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
1051                SetupCommitFileQueueW( owner, queue, callback, context ));
1052         SetupCloseFileQueue( queue );
1053         if (!ret) return FALSE;
1054     }
1055     if (flags & SPINST_INIFILES)
1056     {
1057         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
1058             !iterate_section_fields( hinf, section, UpdateIniFields,
1059                                      update_ini_fields_callback, NULL ))
1060             return FALSE;
1061     }
1062     if (flags & SPINST_INI2REG)
1063     {
1064         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
1065             return FALSE;
1066     }
1067     if (flags & SPINST_LOGCONFIG)
1068     {
1069         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
1070             return FALSE;
1071     }
1072     if (flags & SPINST_REGSVR)
1073     {
1074         struct register_dll_info info;
1075
1076         info.unregister = FALSE;
1077         if (flags & SPINST_REGISTERCALLBACKAWARE)
1078         {
1079             info.callback         = callback;
1080             info.callback_context = context;
1081         }
1082         else info.callback = NULL;
1083
1084         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
1085             return FALSE;
1086
1087         if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
1088             return FALSE;
1089     }
1090     if (flags & SPINST_UNREGSVR)
1091     {
1092         struct register_dll_info info;
1093
1094         info.unregister = TRUE;
1095         if (flags & SPINST_REGISTERCALLBACKAWARE)
1096         {
1097             info.callback         = callback;
1098             info.callback_context = context;
1099         }
1100         else info.callback = NULL;
1101
1102         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
1103             return FALSE;
1104     }
1105     if (flags & SPINST_REGISTRY)
1106     {
1107         struct registry_callback_info info;
1108
1109         info.default_root = key_root;
1110         info.delete = TRUE;
1111         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
1112             return FALSE;
1113         info.delete = FALSE;
1114         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
1115             return FALSE;
1116     }
1117     if (flags & SPINST_BITREG)
1118     {
1119         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
1120             return FALSE;
1121     }
1122     if (flags & SPINST_PROFILEITEMS)
1123     {
1124         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
1125             return FALSE;
1126     }
1127     if (flags & SPINST_COPYINF)
1128     {
1129         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
1130             return FALSE;
1131     }
1132
1133     return TRUE;
1134 }
1135
1136
1137 /***********************************************************************
1138  *              InstallHinfSectionW  (SETUPAPI.@)
1139  *
1140  * NOTE: 'cmdline' is <section> <mode> <path> from
1141  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
1142  */
1143 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
1144 {
1145 #ifdef __i386__
1146     static const WCHAR nt_platformW[] = {'.','n','t','x','8','6',0};
1147 #elif defined(__x86_64)
1148     static const WCHAR nt_platformW[] = {'.','n','t','a','m','d','6','4',0};
1149 #else  /* FIXME: other platforms */
1150     static const WCHAR nt_platformW[] = {'.','n','t',0};
1151 #endif
1152     static const WCHAR nt_genericW[] = {'.','n','t',0};
1153     static const WCHAR servicesW[] = {'.','S','e','r','v','i','c','e','s',0};
1154
1155     WCHAR *s, *path, section[MAX_PATH + (sizeof(nt_platformW) + sizeof(servicesW)) / sizeof(WCHAR)];
1156     void *callback_context;
1157     UINT mode;
1158     HINF hinf;
1159
1160     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1161
1162     lstrcpynW( section, cmdline, MAX_PATH );
1163
1164     if (!(s = strchrW( section, ' ' ))) return;
1165     *s++ = 0;
1166     while (*s == ' ') s++;
1167     mode = atoiW( s );
1168
1169     /* quoted paths are not allowed on native, the rest of the command line is taken as the path */
1170     if (!(s = strchrW( s, ' ' ))) return;
1171     while (*s == ' ') s++;
1172     path = s;
1173
1174     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1175     if (hinf == INVALID_HANDLE_VALUE) return;
1176
1177     if (!(GetVersion() & 0x80000000))
1178     {
1179         INFCONTEXT context;
1180
1181         /* check for <section>.ntx86 (or corresponding name for the current platform)
1182          * and then <section>.nt */
1183         s = section + strlenW(section);
1184         memcpy( s, nt_platformW, sizeof(nt_platformW) );
1185         if (!(SetupFindFirstLineW( hinf, section, NULL, &context )))
1186         {
1187             memcpy( s, nt_genericW, sizeof(nt_genericW) );
1188             if (!(SetupFindFirstLineW( hinf, section, NULL, &context ))) *s = 0;
1189         }
1190         if (*s) TRACE( "using section %s instead\n", debugstr_w(section) );
1191     }
1192
1193     callback_context = SetupInitDefaultQueueCallback( hwnd );
1194     SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
1195                                  SetupDefaultQueueCallbackW, callback_context,
1196                                  NULL, NULL );
1197     SetupTermDefaultQueueCallback( callback_context );
1198     strcatW( section, servicesW );
1199     SetupInstallServicesFromInfSectionW( hinf, section, 0 );
1200     SetupCloseInfFile( hinf );
1201
1202     /* FIXME: should check the mode and maybe reboot */
1203     /* there isn't much point in doing that since we */
1204     /* don't yet handle deferred file copies anyway. */
1205 }
1206
1207
1208 /***********************************************************************
1209  *              InstallHinfSectionA  (SETUPAPI.@)
1210  */
1211 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1212 {
1213     UNICODE_STRING cmdlineW;
1214
1215     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1216     {
1217         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1218         RtlFreeUnicodeString( &cmdlineW );
1219     }
1220 }
1221
1222
1223 /***********************************************************************
1224  *            add_service
1225  *
1226  * Create a new service. Helper for SetupInstallServicesFromInfSectionW.
1227  */
1228 static BOOL add_service( SC_HANDLE scm, HINF hinf, const WCHAR *name, const WCHAR *section, DWORD flags )
1229 {
1230     struct registry_callback_info info;
1231     SC_HANDLE service;
1232     INFCONTEXT context;
1233     SERVICE_DESCRIPTIONW descr;
1234     WCHAR *display_name, *start_name, *load_order, *binary_path;
1235     INT service_type = 0, start_type = 0, error_control = 0;
1236     DWORD size;
1237     HKEY hkey;
1238
1239     /* first the mandatory fields */
1240
1241     if (!SetupFindFirstLineW( hinf, section, ServiceType, &context ) ||
1242         !SetupGetIntField( &context, 1, &service_type ))
1243     {
1244         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1245         return FALSE;
1246     }
1247     if (!SetupFindFirstLineW( hinf, section, StartType, &context ) ||
1248         !SetupGetIntField( &context, 1, &start_type ))
1249     {
1250         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1251         return FALSE;
1252     }
1253     if (!SetupFindFirstLineW( hinf, section, ErrorControl, &context ) ||
1254         !SetupGetIntField( &context, 1, &error_control ))
1255     {
1256         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1257         return FALSE;
1258     }
1259     if (!(binary_path = dup_section_line_field( hinf, section, ServiceBinary, 1 )))
1260     {
1261         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1262         return FALSE;
1263     }
1264
1265     /* now the optional fields */
1266
1267     display_name = dup_section_line_field( hinf, section, DisplayName, 1 );
1268     start_name = dup_section_line_field( hinf, section, StartName, 1 );
1269     load_order = dup_section_line_field( hinf, section, LoadOrderGroup, 1 );
1270     descr.lpDescription = dup_section_line_field( hinf, section, Description, 1 );
1271
1272     /* FIXME: Dependencies field */
1273     /* FIXME: Security field */
1274
1275     TRACE( "service %s display %s type %x start %x error %x binary %s order %s startname %s flags %x\n",
1276            debugstr_w(name), debugstr_w(display_name), service_type, start_type, error_control,
1277            debugstr_w(binary_path), debugstr_w(load_order), debugstr_w(start_name), flags );
1278
1279     service = CreateServiceW( scm, name, display_name, SERVICE_ALL_ACCESS,
1280                               service_type, start_type, error_control, binary_path,
1281                               load_order, NULL, NULL, start_name, NULL );
1282     if (service)
1283     {
1284         if (descr.lpDescription) ChangeServiceConfig2W( service, SERVICE_CONFIG_DESCRIPTION, &descr );
1285     }
1286     else
1287     {
1288         if (GetLastError() != ERROR_SERVICE_EXISTS) goto done;
1289         service = OpenServiceW( scm, name, SERVICE_QUERY_CONFIG|SERVICE_CHANGE_CONFIG|SERVICE_START );
1290         if (!service) goto done;
1291
1292         if (flags & (SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE |
1293                      SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP))
1294         {
1295             QUERY_SERVICE_CONFIGW *config = NULL;
1296
1297             if (!QueryServiceConfigW( service, NULL, 0, &size ) &&
1298                 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1299                 config = HeapAlloc( GetProcessHeap(), 0, size );
1300             if (config && QueryServiceConfigW( service, config, size, &size ))
1301             {
1302                 if (flags & SPSVCINST_NOCLOBBER_STARTTYPE) start_type = config->dwStartType;
1303                 if (flags & SPSVCINST_NOCLOBBER_ERRORCONTROL) error_control = config->dwErrorControl;
1304                 if (flags & SPSVCINST_NOCLOBBER_DISPLAYNAME)
1305                 {
1306                     HeapFree( GetProcessHeap(), 0, display_name );
1307                     display_name = strdupW( config->lpDisplayName );
1308                 }
1309                 if (flags & SPSVCINST_NOCLOBBER_LOADORDERGROUP)
1310                 {
1311                     HeapFree( GetProcessHeap(), 0, load_order );
1312                     load_order = strdupW( config->lpLoadOrderGroup );
1313                 }
1314             }
1315             HeapFree( GetProcessHeap(), 0, config );
1316         }
1317         TRACE( "changing %s display %s type %x start %x error %x binary %s loadorder %s startname %s\n",
1318                debugstr_w(name), debugstr_w(display_name), service_type, start_type, error_control,
1319                debugstr_w(binary_path), debugstr_w(load_order), debugstr_w(start_name) );
1320
1321         ChangeServiceConfigW( service, service_type, start_type, error_control, binary_path,
1322                               load_order, NULL, NULL, start_name, NULL, display_name );
1323
1324         if (!(flags & SPSVCINST_NOCLOBBER_DESCRIPTION))
1325             ChangeServiceConfig2W( service, SERVICE_CONFIG_DESCRIPTION, &descr );
1326     }
1327
1328     /* execute the AddReg, DelReg and BitReg entries */
1329
1330     info.default_root = 0;
1331     if (!RegOpenKeyW( HKEY_LOCAL_MACHINE, ServicesKey, &hkey ))
1332     {
1333         RegOpenKeyW( hkey, name, &info.default_root );
1334         RegCloseKey( hkey );
1335     }
1336     if (info.default_root)
1337     {
1338         info.delete = TRUE;
1339         iterate_section_fields( hinf, section, DelReg, registry_callback, &info );
1340         info.delete = FALSE;
1341         iterate_section_fields( hinf, section, AddReg, registry_callback, &info );
1342         RegCloseKey( info.default_root );
1343     }
1344     iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL );
1345
1346     if (flags & SPSVCINST_STARTSERVICE) StartServiceW( service, 0, NULL );
1347     CloseServiceHandle( service );
1348
1349 done:
1350     if (!service) WARN( "failed err %u\n", GetLastError() );
1351     HeapFree( GetProcessHeap(), 0, binary_path );
1352     HeapFree( GetProcessHeap(), 0, display_name );
1353     HeapFree( GetProcessHeap(), 0, start_name );
1354     HeapFree( GetProcessHeap(), 0, load_order );
1355     HeapFree( GetProcessHeap(), 0, descr.lpDescription );
1356     return service != 0;
1357 }
1358
1359
1360 /***********************************************************************
1361  *            del_service
1362  *
1363  * Delete service. Helper for SetupInstallServicesFromInfSectionW.
1364  */
1365 static BOOL del_service( SC_HANDLE scm, HINF hinf, const WCHAR *name, DWORD flags )
1366 {
1367     BOOL ret;
1368     SC_HANDLE service;
1369     SERVICE_STATUS status;
1370
1371     if (!(service = OpenServiceW( scm, name, SERVICE_STOP | DELETE )))
1372     {
1373         if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) return TRUE;
1374         WARN( "cannot open %s err %u\n", debugstr_w(name), GetLastError() );
1375         return FALSE;
1376     }
1377     if (flags & SPSVCINST_STOPSERVICE) ControlService( service, SERVICE_CONTROL_STOP, &status );
1378     TRACE( "deleting %s\n", debugstr_w(name) );
1379     ret = DeleteService( service );
1380     CloseServiceHandle( service );
1381     return ret;
1382 }
1383
1384
1385 /***********************************************************************
1386  *              SetupInstallServicesFromInfSectionW  (SETUPAPI.@)
1387  */
1388 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF hinf, PCWSTR section, DWORD flags )
1389 {
1390     WCHAR service_name[MAX_INF_STRING_LENGTH];
1391     WCHAR service_section[MAX_INF_STRING_LENGTH];
1392     SC_HANDLE scm;
1393     INFCONTEXT context;
1394     INT section_flags;
1395     BOOL ok, ret = FALSE;
1396
1397     if (!(scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS ))) return FALSE;
1398
1399     if (!(ok = SetupFindFirstLineW( hinf, section, AddService, &context )))
1400         SetLastError( ERROR_SECTION_NOT_FOUND );
1401     while (ok)
1402     {
1403         if (!SetupGetStringFieldW( &context, 1, service_name, MAX_INF_STRING_LENGTH, NULL ))
1404             continue;
1405         if (!SetupGetIntField( &context, 2, &section_flags )) section_flags = 0;
1406         if (!SetupGetStringFieldW( &context, 3, service_section, MAX_INF_STRING_LENGTH, NULL ))
1407             continue;
1408         if (!(ret = add_service( scm, hinf, service_name, service_section, section_flags | flags )))
1409             goto done;
1410         ok = SetupFindNextMatchLineW( &context, AddService, &context );
1411     }
1412
1413     if (!(ok = SetupFindFirstLineW( hinf, section, DelService, &context )))
1414         SetLastError( ERROR_SECTION_NOT_FOUND );
1415     while (ok)
1416     {
1417         if (!SetupGetStringFieldW( &context, 1, service_name, MAX_INF_STRING_LENGTH, NULL ))
1418             continue;
1419         if (!SetupGetIntField( &context, 2, &section_flags )) section_flags = 0;
1420         if (!(ret = del_service( scm, hinf, service_name, section_flags | flags ))) goto done;
1421         ok = SetupFindNextMatchLineW( &context, AddService, &context );
1422     }
1423     if (ret) SetLastError( ERROR_SUCCESS );
1424  done:
1425     CloseServiceHandle( scm );
1426     return ret;
1427 }
1428
1429
1430 /***********************************************************************
1431  *              SetupInstallServicesFromInfSectionA  (SETUPAPI.@)
1432  */
1433 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF Inf, PCSTR SectionName, DWORD Flags)
1434 {
1435     UNICODE_STRING SectionNameW;
1436     BOOL ret = FALSE;
1437
1438     if (RtlCreateUnicodeStringFromAsciiz( &SectionNameW, SectionName ))
1439     {
1440         ret = SetupInstallServicesFromInfSectionW( Inf, SectionNameW.Buffer, Flags );
1441         RtlFreeUnicodeString( &SectionNameW );
1442     }
1443     else
1444         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1445
1446     return ret;
1447 }