include: Add rstinf.idl.
[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     cleanup_fake_dlls();
704     return ret;
705 }
706
707 /***********************************************************************
708  *            update_ini_callback
709  *
710  * Called once for each UpdateInis entry in a given section.
711  */
712 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
713 {
714     INFCONTEXT context;
715
716     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
717
718     for (; ok; ok = SetupFindNextLine( &context, &context ))
719     {
720         WCHAR buffer[MAX_INF_STRING_LENGTH];
721         WCHAR  filename[MAX_INF_STRING_LENGTH];
722         WCHAR  section[MAX_INF_STRING_LENGTH];
723         WCHAR  entry[MAX_INF_STRING_LENGTH];
724         WCHAR  string[MAX_INF_STRING_LENGTH];
725         LPWSTR divider;
726
727         if (!SetupGetStringFieldW( &context, 1, filename,
728                                    sizeof(filename)/sizeof(WCHAR), NULL ))
729             continue;
730
731         if (!SetupGetStringFieldW( &context, 2, section,
732                                    sizeof(section)/sizeof(WCHAR), NULL ))
733             continue;
734
735         if (!SetupGetStringFieldW( &context, 4, buffer,
736                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
737             continue;
738
739         divider = strchrW(buffer,'=');
740         if (divider)
741         {
742             *divider = 0;
743             strcpyW(entry,buffer);
744             divider++;
745             strcpyW(string,divider);
746         }
747         else
748         {
749             strcpyW(entry,buffer);
750             string[0]=0;
751         }
752
753         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
754                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
755         WritePrivateProfileStringW(section,entry,string,filename);
756
757     }
758     return TRUE;
759 }
760
761 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
762 {
763     FIXME( "should update ini fields %s\n", debugstr_w(field) );
764     return TRUE;
765 }
766
767 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
768 {
769     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
770     return TRUE;
771 }
772
773 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
774 {
775     FIXME( "should do logconf %s\n", debugstr_w(field) );
776     return TRUE;
777 }
778
779 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
780 {
781     FIXME( "should do bitreg %s\n", debugstr_w(field) );
782     return TRUE;
783 }
784
785 static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
786 {
787     WCHAR lnkpath[MAX_PATH];
788     LPWSTR cmdline=NULL, lnkpath_end;
789     unsigned int name_size;
790     INFCONTEXT name_context, context;
791     int attrs=0;
792
793     static const WCHAR dotlnk[] = {'.','l','n','k',0};
794
795     TRACE( "(%s)\n", debugstr_w(field) );
796
797     if (SetupFindFirstLineW( hinf, field, Name, &name_context ))
798     {
799         SetupGetIntField( &name_context, 2, &attrs );
800         if (attrs & ~FLG_PROFITEM_GROUP) FIXME( "unhandled attributes: %x\n", attrs );
801     }
802     else return TRUE;
803
804     /* calculate filename */
805     SHGetFolderPathW( NULL, CSIDL_COMMON_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, lnkpath );
806     lnkpath_end = lnkpath + strlenW(lnkpath);
807     if (lnkpath_end[-1] != '\\') *lnkpath_end++ = '\\';
808
809     if (!(attrs & FLG_PROFITEM_GROUP) && SetupFindFirstLineW( hinf, field, SubDir, &context ))
810     {
811         unsigned int subdir_size;
812
813         if (!SetupGetStringFieldW( &context, 1, lnkpath_end, (lnkpath+MAX_PATH)-lnkpath_end, &subdir_size ))
814             return TRUE;
815
816         lnkpath_end += subdir_size - 1;
817         if (lnkpath_end[-1] != '\\') *lnkpath_end++ = '\\';
818     }
819
820     if (!SetupGetStringFieldW( &name_context, 1, lnkpath_end, (lnkpath+MAX_PATH)-lnkpath_end, &name_size ))
821         return TRUE;
822
823     lnkpath_end += name_size - 1;
824
825     if (attrs & FLG_PROFITEM_GROUP)
826     {
827         SHPathPrepareForWriteW( NULL, NULL, lnkpath, SHPPFW_DIRCREATE );
828     }
829     else
830     {
831         IShellLinkW* shelllink=NULL;
832         IPersistFile* persistfile=NULL;
833         HRESULT initresult=E_FAIL;
834
835         if (lnkpath+MAX_PATH < lnkpath_end + 5) return TRUE;
836         strcpyW( lnkpath_end, dotlnk );
837
838         TRACE( "link path: %s\n", debugstr_w(lnkpath) );
839
840         /* calculate command line */
841         if (SetupFindFirstLineW( hinf, field, CmdLine, &context ))
842         {
843             unsigned int dir_len=0, subdir_size=0, filename_size=0;
844             int dirid=0;
845             LPCWSTR dir;
846             LPWSTR cmdline_end;
847
848             SetupGetIntField( &context, 1, &dirid );
849             dir = DIRID_get_string( dirid );
850
851             if (dir) dir_len = strlenW(dir);
852
853             SetupGetStringFieldW( &context, 2, NULL, 0, &subdir_size );
854             SetupGetStringFieldW( &context, 3, NULL, 0, &filename_size );
855
856             if (dir_len && filename_size)
857             {
858                 cmdline = cmdline_end = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * (dir_len+subdir_size+filename_size+1) );
859
860                 strcpyW( cmdline_end, dir );
861                 cmdline_end += dir_len;
862                 if (cmdline_end[-1] != '\\') *cmdline_end++ = '\\';
863
864                 if (subdir_size)
865                 {
866                     SetupGetStringFieldW( &context, 2, cmdline_end, subdir_size, NULL );
867                     cmdline_end += subdir_size-1;
868                     if (cmdline_end[-1] != '\\') *cmdline_end++ = '\\';
869                 }
870                 SetupGetStringFieldW( &context, 3, cmdline_end, filename_size, NULL );
871                 TRACE( "cmdline: %s\n", debugstr_w(cmdline));
872             }
873         }
874
875         if (!cmdline) return TRUE;
876
877         initresult = CoInitialize(NULL);
878
879         if (FAILED(CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
880                                      &IID_IShellLinkW, (LPVOID*)&shelllink )))
881             goto done;
882
883         IShellLinkW_SetPath( shelllink, cmdline );
884         SHPathPrepareForWriteW( NULL, NULL, lnkpath, SHPPFW_DIRCREATE|SHPPFW_IGNOREFILENAME );
885         if (SUCCEEDED(IShellLinkW_QueryInterface( shelllink, &IID_IPersistFile, (LPVOID*)&persistfile)))
886         {
887             TRACE( "writing link: %s\n", debugstr_w(lnkpath) );
888             IPersistFile_Save( persistfile, lnkpath, FALSE );
889             IPersistFile_Release( persistfile );
890         }
891         IShellLinkW_Release( shelllink );
892
893     done:
894         if (SUCCEEDED(initresult)) CoUninitialize();
895         HeapFree( GetProcessHeap(), 0, cmdline );
896     }
897
898     return TRUE;
899 }
900
901 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
902 {
903     FIXME( "should do copy inf %s\n", debugstr_w(field) );
904     return TRUE;
905 }
906
907
908 /***********************************************************************
909  *            iterate_section_fields
910  *
911  * Iterate over all fields of a certain key of a certain section
912  */
913 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
914                                     iterate_fields_func callback, void *arg )
915 {
916     WCHAR static_buffer[200];
917     WCHAR *buffer = static_buffer;
918     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
919     INFCONTEXT context;
920     BOOL ret = FALSE;
921
922     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
923     while (ok)
924     {
925         UINT i, count = SetupGetFieldCount( &context );
926         for (i = 1; i <= count; i++)
927         {
928             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
929                 goto done;
930             if (!callback( hinf, buffer, arg ))
931             {
932                 WARN("callback failed for %s %s err %d\n",
933                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
934                 goto done;
935             }
936         }
937         ok = SetupFindNextMatchLineW( &context, key, &context );
938     }
939     ret = TRUE;
940  done:
941     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
942     return ret;
943 }
944
945
946 /***********************************************************************
947  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
948  */
949 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
950                                               PCSTR section, PCSTR src_root, UINT flags )
951 {
952     UNICODE_STRING sectionW;
953     BOOL ret = FALSE;
954
955     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
956     {
957         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
958         return FALSE;
959     }
960     if (!src_root)
961         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
962                                                 NULL, flags );
963     else
964     {
965         UNICODE_STRING srcW;
966         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
967         {
968             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
969                                                     srcW.Buffer, flags );
970             RtlFreeUnicodeString( &srcW );
971         }
972         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
973     }
974     RtlFreeUnicodeString( &sectionW );
975     return ret;
976 }
977
978
979 /***********************************************************************
980  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
981  */
982 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
983                                               PCWSTR section, PCWSTR src_root, UINT flags )
984 {
985     struct files_callback_info info;
986
987     info.queue      = queue;
988     info.src_root   = src_root;
989     info.copy_flags = flags;
990     info.layout     = hlayout;
991     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
992 }
993
994
995 /***********************************************************************
996  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
997  */
998 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
999                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
1000                                          PSP_FILE_CALLBACK_A callback, PVOID context,
1001                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1002 {
1003     UNICODE_STRING sectionW, src_rootW;
1004     struct callback_WtoA_context ctx;
1005     BOOL ret = FALSE;
1006
1007     src_rootW.Buffer = NULL;
1008     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
1009     {
1010         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1011         return FALSE;
1012     }
1013
1014     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1015     {
1016         ctx.orig_context = context;
1017         ctx.orig_handler = callback;
1018         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
1019                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
1020                                            &ctx, devinfo, devinfo_data );
1021         RtlFreeUnicodeString( &sectionW );
1022     }
1023     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1024
1025     RtlFreeUnicodeString( &src_rootW );
1026     return ret;
1027 }
1028
1029
1030 /***********************************************************************
1031  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
1032  */
1033 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
1034                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
1035                                          PSP_FILE_CALLBACK_W callback, PVOID context,
1036                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1037 {
1038     if (flags & SPINST_FILES)
1039     {
1040         struct files_callback_info info;
1041         HSPFILEQ queue;
1042         BOOL ret;
1043
1044         if (!(queue = SetupOpenFileQueue())) return FALSE;
1045         info.queue      = queue;
1046         info.src_root   = src_root;
1047         info.copy_flags = copy_flags;
1048         info.layout     = hinf;
1049         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
1050                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
1051                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
1052                SetupCommitFileQueueW( owner, queue, callback, context ));
1053         SetupCloseFileQueue( queue );
1054         if (!ret) return FALSE;
1055     }
1056     if (flags & SPINST_INIFILES)
1057     {
1058         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
1059             !iterate_section_fields( hinf, section, UpdateIniFields,
1060                                      update_ini_fields_callback, NULL ))
1061             return FALSE;
1062     }
1063     if (flags & SPINST_INI2REG)
1064     {
1065         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
1066             return FALSE;
1067     }
1068     if (flags & SPINST_LOGCONFIG)
1069     {
1070         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
1071             return FALSE;
1072     }
1073     if (flags & SPINST_REGSVR)
1074     {
1075         struct register_dll_info info;
1076
1077         info.unregister = FALSE;
1078         if (flags & SPINST_REGISTERCALLBACKAWARE)
1079         {
1080             info.callback         = callback;
1081             info.callback_context = context;
1082         }
1083         else info.callback = NULL;
1084
1085         if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
1086             return FALSE;
1087
1088         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
1089             return FALSE;
1090     }
1091     if (flags & SPINST_UNREGSVR)
1092     {
1093         struct register_dll_info info;
1094
1095         info.unregister = TRUE;
1096         if (flags & SPINST_REGISTERCALLBACKAWARE)
1097         {
1098             info.callback         = callback;
1099             info.callback_context = context;
1100         }
1101         else info.callback = NULL;
1102
1103         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
1104             return FALSE;
1105     }
1106     if (flags & SPINST_REGISTRY)
1107     {
1108         struct registry_callback_info info;
1109
1110         info.default_root = key_root;
1111         info.delete = TRUE;
1112         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
1113             return FALSE;
1114         info.delete = FALSE;
1115         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
1116             return FALSE;
1117     }
1118     if (flags & SPINST_BITREG)
1119     {
1120         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
1121             return FALSE;
1122     }
1123     if (flags & SPINST_PROFILEITEMS)
1124     {
1125         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
1126             return FALSE;
1127     }
1128     if (flags & SPINST_COPYINF)
1129     {
1130         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
1131             return FALSE;
1132     }
1133
1134     return TRUE;
1135 }
1136
1137
1138 /***********************************************************************
1139  *              InstallHinfSectionW  (SETUPAPI.@)
1140  *
1141  * NOTE: 'cmdline' is <section> <mode> <path> from
1142  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
1143  */
1144 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
1145 {
1146 #ifdef __i386__
1147     static const WCHAR nt_platformW[] = {'.','n','t','x','8','6',0};
1148 #elif defined(__x86_64)
1149     static const WCHAR nt_platformW[] = {'.','n','t','a','m','d','6','4',0};
1150 #else  /* FIXME: other platforms */
1151     static const WCHAR nt_platformW[] = {'.','n','t',0};
1152 #endif
1153     static const WCHAR nt_genericW[] = {'.','n','t',0};
1154     static const WCHAR servicesW[] = {'.','S','e','r','v','i','c','e','s',0};
1155
1156     WCHAR *s, *path, section[MAX_PATH + (sizeof(nt_platformW) + sizeof(servicesW)) / sizeof(WCHAR)];
1157     void *callback_context;
1158     UINT mode;
1159     HINF hinf;
1160
1161     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1162
1163     lstrcpynW( section, cmdline, MAX_PATH );
1164
1165     if (!(s = strchrW( section, ' ' ))) return;
1166     *s++ = 0;
1167     while (*s == ' ') s++;
1168     mode = atoiW( s );
1169
1170     /* quoted paths are not allowed on native, the rest of the command line is taken as the path */
1171     if (!(s = strchrW( s, ' ' ))) return;
1172     while (*s == ' ') s++;
1173     path = s;
1174
1175     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1176     if (hinf == INVALID_HANDLE_VALUE) return;
1177
1178     if (!(GetVersion() & 0x80000000))
1179     {
1180         INFCONTEXT context;
1181
1182         /* check for <section>.ntx86 (or corresponding name for the current platform)
1183          * and then <section>.nt */
1184         s = section + strlenW(section);
1185         memcpy( s, nt_platformW, sizeof(nt_platformW) );
1186         if (!(SetupFindFirstLineW( hinf, section, NULL, &context )))
1187         {
1188             memcpy( s, nt_genericW, sizeof(nt_genericW) );
1189             if (!(SetupFindFirstLineW( hinf, section, NULL, &context ))) *s = 0;
1190         }
1191         if (*s) TRACE( "using section %s instead\n", debugstr_w(section) );
1192     }
1193
1194     callback_context = SetupInitDefaultQueueCallback( hwnd );
1195     SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
1196                                  SetupDefaultQueueCallbackW, callback_context,
1197                                  NULL, NULL );
1198     SetupTermDefaultQueueCallback( callback_context );
1199     strcatW( section, servicesW );
1200     SetupInstallServicesFromInfSectionW( hinf, section, 0 );
1201     SetupCloseInfFile( hinf );
1202
1203     /* FIXME: should check the mode and maybe reboot */
1204     /* there isn't much point in doing that since we */
1205     /* don't yet handle deferred file copies anyway. */
1206 }
1207
1208
1209 /***********************************************************************
1210  *              InstallHinfSectionA  (SETUPAPI.@)
1211  */
1212 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1213 {
1214     UNICODE_STRING cmdlineW;
1215
1216     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1217     {
1218         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1219         RtlFreeUnicodeString( &cmdlineW );
1220     }
1221 }
1222
1223
1224 /***********************************************************************
1225  *            add_service
1226  *
1227  * Create a new service. Helper for SetupInstallServicesFromInfSectionW.
1228  */
1229 static BOOL add_service( SC_HANDLE scm, HINF hinf, const WCHAR *name, const WCHAR *section, DWORD flags )
1230 {
1231     struct registry_callback_info info;
1232     SC_HANDLE service;
1233     INFCONTEXT context;
1234     SERVICE_DESCRIPTIONW descr;
1235     WCHAR *display_name, *start_name, *load_order, *binary_path;
1236     INT service_type = 0, start_type = 0, error_control = 0;
1237     DWORD size;
1238     HKEY hkey;
1239
1240     /* first the mandatory fields */
1241
1242     if (!SetupFindFirstLineW( hinf, section, ServiceType, &context ) ||
1243         !SetupGetIntField( &context, 1, &service_type ))
1244     {
1245         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1246         return FALSE;
1247     }
1248     if (!SetupFindFirstLineW( hinf, section, StartType, &context ) ||
1249         !SetupGetIntField( &context, 1, &start_type ))
1250     {
1251         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1252         return FALSE;
1253     }
1254     if (!SetupFindFirstLineW( hinf, section, ErrorControl, &context ) ||
1255         !SetupGetIntField( &context, 1, &error_control ))
1256     {
1257         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1258         return FALSE;
1259     }
1260     if (!(binary_path = dup_section_line_field( hinf, section, ServiceBinary, 1 )))
1261     {
1262         SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1263         return FALSE;
1264     }
1265
1266     /* now the optional fields */
1267
1268     display_name = dup_section_line_field( hinf, section, DisplayName, 1 );
1269     start_name = dup_section_line_field( hinf, section, StartName, 1 );
1270     load_order = dup_section_line_field( hinf, section, LoadOrderGroup, 1 );
1271     descr.lpDescription = dup_section_line_field( hinf, section, Description, 1 );
1272
1273     /* FIXME: Dependencies field */
1274     /* FIXME: Security field */
1275
1276     TRACE( "service %s display %s type %x start %x error %x binary %s order %s startname %s flags %x\n",
1277            debugstr_w(name), debugstr_w(display_name), service_type, start_type, error_control,
1278            debugstr_w(binary_path), debugstr_w(load_order), debugstr_w(start_name), flags );
1279
1280     service = CreateServiceW( scm, name, display_name, SERVICE_ALL_ACCESS,
1281                               service_type, start_type, error_control, binary_path,
1282                               load_order, NULL, NULL, start_name, NULL );
1283     if (service)
1284     {
1285         if (descr.lpDescription) ChangeServiceConfig2W( service, SERVICE_CONFIG_DESCRIPTION, &descr );
1286     }
1287     else
1288     {
1289         if (GetLastError() != ERROR_SERVICE_EXISTS) goto done;
1290         service = OpenServiceW( scm, name, SERVICE_QUERY_CONFIG|SERVICE_CHANGE_CONFIG|SERVICE_START );
1291         if (!service) goto done;
1292
1293         if (flags & (SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE |
1294                      SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP))
1295         {
1296             QUERY_SERVICE_CONFIGW *config = NULL;
1297
1298             if (!QueryServiceConfigW( service, NULL, 0, &size ) &&
1299                 GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1300                 config = HeapAlloc( GetProcessHeap(), 0, size );
1301             if (config && QueryServiceConfigW( service, config, size, &size ))
1302             {
1303                 if (flags & SPSVCINST_NOCLOBBER_STARTTYPE) start_type = config->dwStartType;
1304                 if (flags & SPSVCINST_NOCLOBBER_ERRORCONTROL) error_control = config->dwErrorControl;
1305                 if (flags & SPSVCINST_NOCLOBBER_DISPLAYNAME)
1306                 {
1307                     HeapFree( GetProcessHeap(), 0, display_name );
1308                     display_name = strdupW( config->lpDisplayName );
1309                 }
1310                 if (flags & SPSVCINST_NOCLOBBER_LOADORDERGROUP)
1311                 {
1312                     HeapFree( GetProcessHeap(), 0, load_order );
1313                     load_order = strdupW( config->lpLoadOrderGroup );
1314                 }
1315             }
1316             HeapFree( GetProcessHeap(), 0, config );
1317         }
1318         TRACE( "changing %s display %s type %x start %x error %x binary %s loadorder %s startname %s\n",
1319                debugstr_w(name), debugstr_w(display_name), service_type, start_type, error_control,
1320                debugstr_w(binary_path), debugstr_w(load_order), debugstr_w(start_name) );
1321
1322         ChangeServiceConfigW( service, service_type, start_type, error_control, binary_path,
1323                               load_order, NULL, NULL, start_name, NULL, display_name );
1324
1325         if (!(flags & SPSVCINST_NOCLOBBER_DESCRIPTION))
1326             ChangeServiceConfig2W( service, SERVICE_CONFIG_DESCRIPTION, &descr );
1327     }
1328
1329     /* execute the AddReg, DelReg and BitReg entries */
1330
1331     info.default_root = 0;
1332     if (!RegOpenKeyW( HKEY_LOCAL_MACHINE, ServicesKey, &hkey ))
1333     {
1334         RegOpenKeyW( hkey, name, &info.default_root );
1335         RegCloseKey( hkey );
1336     }
1337     if (info.default_root)
1338     {
1339         info.delete = TRUE;
1340         iterate_section_fields( hinf, section, DelReg, registry_callback, &info );
1341         info.delete = FALSE;
1342         iterate_section_fields( hinf, section, AddReg, registry_callback, &info );
1343         RegCloseKey( info.default_root );
1344     }
1345     iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL );
1346
1347     if (flags & SPSVCINST_STARTSERVICE) StartServiceW( service, 0, NULL );
1348     CloseServiceHandle( service );
1349
1350 done:
1351     if (!service) WARN( "failed err %u\n", GetLastError() );
1352     HeapFree( GetProcessHeap(), 0, binary_path );
1353     HeapFree( GetProcessHeap(), 0, display_name );
1354     HeapFree( GetProcessHeap(), 0, start_name );
1355     HeapFree( GetProcessHeap(), 0, load_order );
1356     HeapFree( GetProcessHeap(), 0, descr.lpDescription );
1357     return service != 0;
1358 }
1359
1360
1361 /***********************************************************************
1362  *            del_service
1363  *
1364  * Delete service. Helper for SetupInstallServicesFromInfSectionW.
1365  */
1366 static BOOL del_service( SC_HANDLE scm, HINF hinf, const WCHAR *name, DWORD flags )
1367 {
1368     BOOL ret;
1369     SC_HANDLE service;
1370     SERVICE_STATUS status;
1371
1372     if (!(service = OpenServiceW( scm, name, SERVICE_STOP | DELETE )))
1373     {
1374         if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) return TRUE;
1375         WARN( "cannot open %s err %u\n", debugstr_w(name), GetLastError() );
1376         return FALSE;
1377     }
1378     if (flags & SPSVCINST_STOPSERVICE) ControlService( service, SERVICE_CONTROL_STOP, &status );
1379     TRACE( "deleting %s\n", debugstr_w(name) );
1380     ret = DeleteService( service );
1381     CloseServiceHandle( service );
1382     return ret;
1383 }
1384
1385
1386 /***********************************************************************
1387  *              SetupInstallServicesFromInfSectionW  (SETUPAPI.@)
1388  */
1389 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF hinf, PCWSTR section, DWORD flags )
1390 {
1391     WCHAR service_name[MAX_INF_STRING_LENGTH];
1392     WCHAR service_section[MAX_INF_STRING_LENGTH];
1393     SC_HANDLE scm;
1394     INFCONTEXT context;
1395     INT section_flags;
1396     BOOL ok, ret = FALSE;
1397
1398     if (!(scm = OpenSCManagerW( NULL, NULL, SC_MANAGER_ALL_ACCESS ))) return FALSE;
1399
1400     if (!(ok = SetupFindFirstLineW( hinf, section, AddService, &context )))
1401         SetLastError( ERROR_SECTION_NOT_FOUND );
1402     while (ok)
1403     {
1404         if (!SetupGetStringFieldW( &context, 1, service_name, MAX_INF_STRING_LENGTH, NULL ))
1405             continue;
1406         if (!SetupGetIntField( &context, 2, &section_flags )) section_flags = 0;
1407         if (!SetupGetStringFieldW( &context, 3, service_section, MAX_INF_STRING_LENGTH, NULL ))
1408             continue;
1409         if (!(ret = add_service( scm, hinf, service_name, service_section, section_flags | flags )))
1410             goto done;
1411         ok = SetupFindNextMatchLineW( &context, AddService, &context );
1412     }
1413
1414     if (!(ok = SetupFindFirstLineW( hinf, section, DelService, &context )))
1415         SetLastError( ERROR_SECTION_NOT_FOUND );
1416     while (ok)
1417     {
1418         if (!SetupGetStringFieldW( &context, 1, service_name, MAX_INF_STRING_LENGTH, NULL ))
1419             continue;
1420         if (!SetupGetIntField( &context, 2, &section_flags )) section_flags = 0;
1421         if (!(ret = del_service( scm, hinf, service_name, section_flags | flags ))) goto done;
1422         ok = SetupFindNextMatchLineW( &context, AddService, &context );
1423     }
1424     if (ret) SetLastError( ERROR_SUCCESS );
1425  done:
1426     CloseServiceHandle( scm );
1427     return ret;
1428 }
1429
1430
1431 /***********************************************************************
1432  *              SetupInstallServicesFromInfSectionA  (SETUPAPI.@)
1433  */
1434 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF Inf, PCSTR SectionName, DWORD Flags)
1435 {
1436     UNICODE_STRING SectionNameW;
1437     BOOL ret = FALSE;
1438
1439     if (RtlCreateUnicodeStringFromAsciiz( &SectionNameW, SectionName ))
1440     {
1441         ret = SetupInstallServicesFromInfSectionW( Inf, SectionNameW.Buffer, Flags );
1442         RtlFreeUnicodeString( &SectionNameW );
1443     }
1444     else
1445         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1446
1447     return ret;
1448 }