xref: /reactos/dll/win32/setupapi/install.c (revision 4f0b8d3d)
1 /*
2  * Setupapi install routines
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
5  *           2005-2006 Herv� Poussineau (hpoussin@reactos.org)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "setupapi_private.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
25 
26 /* Unicode constants */
27 static const WCHAR BackSlash[] = {'\\',0};
28 static const WCHAR GroupOrderListKey[] = {'S','Y','S','T','E','M','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','G','r','o','u','p','O','r','d','e','r','L','i','s','t',0};
29 static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
30 static const WCHAR OemFileMask[] = {'o','e','m','*','.','i','n','f',0};
31 static const WCHAR OemFileSpecification[] = {'o','e','m','%','l','u','.','i','n','f',0};
32 static const WCHAR DotLnk[] = {'.','l','n','k',0};
33 static const WCHAR DotServices[]  = {'.','S','e','r','v','i','c','e','s',0};
34 
35 static const WCHAR DependenciesKey[] = {'D','e','p','e','n','d','e','n','c','i','e','s',0};
36 static const WCHAR DescriptionKey[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
37 static const WCHAR DisplayNameKey[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
38 static const WCHAR ErrorControlKey[] = {'E','r','r','o','r','C','o','n','t','r','o','l',0};
39 static const WCHAR LoadOrderGroupKey[] = {'L','o','a','d','O','r','d','e','r','G','r','o','u','p',0};
40 static const WCHAR SecurityKey[] = {'S','e','c','u','r','i','t','y',0};
41 static const WCHAR ServiceBinaryKey[] = {'S','e','r','v','i','c','e','B','i','n','a','r','y',0};
42 static const WCHAR ServiceTypeKey[] = {'S','e','r','v','i','c','e','T','y','p','e',0};
43 static const WCHAR StartTypeKey[] = {'S','t','a','r','t','T','y','p','e',0};
44 
45 static const WCHAR Name[] = {'N','a','m','e',0};
46 static const WCHAR CmdLine[] = {'C','m','d','L','i','n','e',0};
47 static const WCHAR SubDir[] = {'S','u','b','D','i','r',0};
48 static const WCHAR WorkingDir[] = {'W','o','r','k','i','n','g','D','i','r',0};
49 static const WCHAR IconPath[] = {'I','c','o','n','P','a','t','h',0};
50 static const WCHAR IconIndex[] = {'I','c','o','n','I','n','d','e','x',0};
51 static const WCHAR HotKey[] = {'H','o','t','K','e','y',0};
52 static const WCHAR InfoTip[] = {'I','n','f','o','T','i','p',0};
53 static const WCHAR DisplayResource[] = {'D','i','s','p','l','a','y','R','e','s','o','u','r','c','e',0};
54 
55 /* info passed to callback functions dealing with files */
56 struct files_callback_info
57 {
58     HSPFILEQ queue;
59     PCWSTR   src_root;
60     UINT     copy_flags;
61     HINF     layout;
62 };
63 
64 /* info passed to callback functions dealing with the registry */
65 struct registry_callback_info
66 {
67     HKEY default_root;
68     BOOL delete;
69 };
70 
71 /* info passed to callback functions dealing with registering dlls */
72 struct register_dll_info
73 {
74     PSP_FILE_CALLBACK_W callback;
75     PVOID               callback_context;
76     BOOL                unregister;
77 };
78 
79 /* info passed to callback functions dealing with Needs directives */
80 struct needs_callback_info
81 {
82     UINT type;
83 
84     HWND             owner;
85     UINT             flags;
86     HKEY             key_root;
87     LPCWSTR          src_root;
88     UINT             copy_flags;
89     PVOID            callback;
90     PVOID            context;
91     HDEVINFO         devinfo;
92     PSP_DEVINFO_DATA devinfo_data;
93     PVOID            reserved1;
94     PVOID            reserved2;
95 };
96 
97 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
98 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value);
99 typedef HRESULT (WINAPI *COINITIALIZE)(IN LPVOID pvReserved);
100 typedef HRESULT (WINAPI *COCREATEINSTANCE)(IN REFCLSID rclsid, IN LPUNKNOWN pUnkOuter, IN DWORD dwClsContext, IN REFIID riid, OUT LPVOID *ppv);
101 typedef HRESULT (WINAPI *COUNINITIALIZE)(VOID);
102 
103 /* Unicode constants */
104 static const WCHAR AddService[] = {'A','d','d','S','e','r','v','i','c','e',0};
105 static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
106 static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
107 static const WCHAR RenFiles[]   = {'R','e','n','F','i','l','e','s',0};
108 static const WCHAR Ini2Reg[]    = {'I','n','i','2','R','e','g',0};
109 static const WCHAR LogConf[]    = {'L','o','g','C','o','n','f',0};
110 static const WCHAR AddReg[]     = {'A','d','d','R','e','g',0};
111 static const WCHAR DelReg[]     = {'D','e','l','R','e','g',0};
112 static const WCHAR BitReg[]     = {'B','i','t','R','e','g',0};
113 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
114 static const WCHAR CopyINF[]    = {'C','o','p','y','I','N','F',0};
115 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
116 static const WCHAR RegisterDlls[]    = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
117 static const WCHAR UnregisterDlls[]  = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
118 static const WCHAR ProfileItems[]    = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
119 static const WCHAR Include[]         = {'I','n','c','l','u','d','e',0};
120 static const WCHAR Needs[]           = {'N','e','e','d','s',0};
121 static const WCHAR DotSecurity[]     = {'.','S','e','c','u','r','i','t','y',0};
122 #ifdef __WINESRC__
123 static const WCHAR WineFakeDlls[]    = {'W','i','n','e','F','a','k','e','D','l','l','s',0};
124 #endif
125 
126 
127 /***********************************************************************
128  *            get_field_string
129  *
130  * Retrieve the contents of a field, dynamically growing the buffer if necessary.
131  */
132 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
133                                 WCHAR *static_buffer, DWORD *size )
134 {
135     DWORD required;
136 
137     if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
138     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
139     {
140         /* now grow the buffer */
141         if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
142         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
143         *size = required;
144         if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
145     }
146     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
147     return NULL;
148 }
149 
150 
151 /***********************************************************************
152  *            copy_files_callback
153  *
154  * Called once for each CopyFiles entry in a given section.
155  */
156 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
157 {
158     struct files_callback_info *info = arg;
159 
160     if (field[0] == '@')  /* special case: copy single file */
161         SetupQueueDefaultCopyW( info->queue, info->layout ? info->layout : hinf, info->src_root, NULL, field+1, info->copy_flags );
162     else
163         SetupQueueCopySectionW( info->queue, info->src_root, info->layout ? info->layout : hinf, hinf, field, info->copy_flags );
164     return TRUE;
165 }
166 
167 
168 /***********************************************************************
169  *            delete_files_callback
170  *
171  * Called once for each DelFiles entry in a given section.
172  */
173 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
174 {
175     struct files_callback_info *info = arg;
176     SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
177     return TRUE;
178 }
179 
180 
181 /***********************************************************************
182  *            rename_files_callback
183  *
184  * Called once for each RenFiles entry in a given section.
185  */
186 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
187 {
188     struct files_callback_info *info = arg;
189     SetupQueueRenameSectionW( info->queue, hinf, 0, field );
190     return TRUE;
191 }
192 
193 
194 /***********************************************************************
195  *            get_root_key
196  *
197  * Retrieve the registry root key from its name.
198  */
199 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
200 {
201     static const WCHAR HKCR[] = {'H','K','C','R',0};
202     static const WCHAR HKCU[] = {'H','K','C','U',0};
203     static const WCHAR HKLM[] = {'H','K','L','M',0};
204     static const WCHAR HKU[]  = {'H','K','U',0};
205     static const WCHAR HKR[]  = {'H','K','R',0};
206 
207     if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
208     if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
209     if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
210     if (!strcmpiW( name, HKU )) return HKEY_USERS;
211     if (!strcmpiW( name, HKR )) return def_root;
212     return 0;
213 }
214 
215 
216 /***********************************************************************
217  *            append_multi_sz_value
218  *
219  * Append a multisz string to a multisz registry value.
220  */
221 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
222                                    DWORD str_size )
223 {
224     DWORD size, type, total;
225     WCHAR *buffer, *p;
226 
227     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
228     if (type != REG_MULTI_SZ) return;
229 
230     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
231     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
232 
233     /* compare each string against all the existing ones */
234     total = size;
235     while (*strings)
236     {
237         int len = strlenW(strings) + 1;
238 
239         for (p = buffer; *p; p += strlenW(p) + 1)
240             if (!strcmpiW( p, strings )) break;
241 
242         if (!*p)  /* not found, need to append it */
243         {
244             memcpy( p, strings, len * sizeof(WCHAR) );
245             p[len] = 0;
246             total += len;
247         }
248         strings += len;
249     }
250     if (total != size)
251     {
252         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
253         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total + sizeof(WCHAR) );
254     }
255  done:
256     HeapFree( GetProcessHeap(), 0, buffer );
257 }
258 
259 
260 /***********************************************************************
261  *            delete_multi_sz_value
262  *
263  * Remove a string from a multisz registry value.
264  */
265 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
266 {
267     DWORD size, type;
268     WCHAR *buffer, *src, *dst;
269 
270     if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
271     if (type != REG_MULTI_SZ) return;
272     /* allocate double the size, one for value before and one for after */
273     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
274     if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
275     src = buffer;
276     dst = buffer + size;
277     while (*src)
278     {
279         int len = strlenW(src) + 1;
280         if (strcmpiW( src, string ))
281         {
282             memcpy( dst, src, len * sizeof(WCHAR) );
283             dst += len;
284         }
285         src += len;
286     }
287     *dst++ = 0;
288     if (dst != buffer + 2*size)  /* did we remove something? */
289     {
290         TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
291         RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
292                         (BYTE *)(buffer + size), dst - (buffer + size) );
293     }
294  done:
295     HeapFree( GetProcessHeap(), 0, buffer );
296 }
297 
298 
299 /***********************************************************************
300  *            do_reg_operation
301  *
302  * Perform an add/delete registry operation depending on the flags.
303  */
304 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
305 {
306     DWORD type, size;
307 
308     if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
309     {
310         if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
311         {
312             if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
313             {
314                 WCHAR *str;
315 
316                 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
317                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
318                 SetupGetStringFieldW( context, 5, str, size, NULL );
319                 delete_multi_sz_value( hkey, value, str );
320                 HeapFree( GetProcessHeap(), 0, str );
321             }
322             else RegDeleteValueW( hkey, value );
323         }
324         else NtDeleteKey( hkey );
325         return TRUE;
326     }
327 
328     if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
329 
330     if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
331     {
332         BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
333         if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
334         if (!exists && (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
335     }
336 
337     switch(flags & FLG_ADDREG_TYPE_MASK)
338     {
339     case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
340     case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
341     case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
342     case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
343     case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
344     case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
345     default:                        type = flags >> 16; break;
346     }
347 
348     if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
349         (type == REG_DWORD && SetupGetFieldCount(context) == 5))
350     {
351         static const WCHAR empty;
352         WCHAR *str = NULL;
353 
354         if (type == REG_MULTI_SZ)
355         {
356             if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
357             if (size)
358             {
359                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
360                 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
361             }
362             if (flags & FLG_ADDREG_APPEND)
363             {
364                 if (!str) return TRUE;
365                 append_multi_sz_value( hkey, value, str, size );
366                 HeapFree( GetProcessHeap(), 0, str );
367                 return TRUE;
368             }
369             /* else fall through to normal string handling */
370         }
371         else
372         {
373             if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
374             if (size)
375             {
376                 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
377                 SetupGetStringFieldW( context, 5, str, size, NULL );
378             }
379         }
380 
381         if (type == REG_DWORD)
382         {
383             DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
384             TRACE( "setting dword %s to %x\n", debugstr_w(value), dw );
385             RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
386         }
387         else
388         {
389             TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
390             if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
391             else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
392         }
393         HeapFree( GetProcessHeap(), 0, str );
394         return TRUE;
395     }
396     else  /* get the binary data */
397     {
398         BYTE *data = NULL;
399 
400         if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
401         if (size)
402         {
403             if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
404             TRACE( "setting binary data %s len %d\n", debugstr_w(value), size );
405             SetupGetBinaryField( context, 5, data, size, NULL );
406         }
407         RegSetValueExW( hkey, value, 0, type, data, size );
408         HeapFree( GetProcessHeap(), 0, data );
409         return TRUE;
410     }
411 }
412 
413 
414 /***********************************************************************
415  *            registry_callback
416  *
417  * Called once for each AddReg and DelReg entry in a given section.
418  */
419 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
420 {
421     struct registry_callback_info *info = arg;
422     LPWSTR security_key, security_descriptor;
423     INFCONTEXT context, security_context;
424     PSECURITY_DESCRIPTOR sd = NULL;
425     SECURITY_ATTRIBUTES security_attributes = { 0, };
426     HKEY root_key, hkey;
427     DWORD required;
428 
429     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
430     if (!ok)
431         return TRUE;
432 
433     /* Check for .Security section */
434     security_key = MyMalloc( (strlenW( field ) + strlenW( DotSecurity )) * sizeof(WCHAR) + sizeof(UNICODE_NULL) );
435     if (!security_key)
436     {
437         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
438         return FALSE;
439     }
440     strcpyW( security_key, field );
441     strcatW( security_key, DotSecurity );
442     ok = SetupFindFirstLineW( hinf, security_key, NULL, &security_context );
443     MyFree(security_key);
444     if (ok)
445     {
446         if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, NULL, 0, &required ))
447             return FALSE;
448         security_descriptor = MyMalloc( required * sizeof(WCHAR) );
449         if (!security_descriptor)
450         {
451             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
452             return FALSE;
453         }
454         if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, security_descriptor, required, NULL ))
455             return FALSE;
456         ok = ConvertStringSecurityDescriptorToSecurityDescriptorW( security_descriptor, SDDL_REVISION_1, &sd, NULL );
457         MyFree( security_descriptor );
458         if (!ok)
459             return FALSE;
460         security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
461         security_attributes.lpSecurityDescriptor = sd;
462     }
463 
464     for (ok = TRUE; ok; ok = SetupFindNextLine( &context, &context ))
465     {
466         WCHAR buffer[MAX_INF_STRING_LENGTH];
467         INT flags;
468 
469         /* get root */
470         if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
471             continue;
472         if (!(root_key = get_root_key( buffer, info->default_root )))
473             continue;
474 
475         /* get key */
476         if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
477             *buffer = 0;
478 
479         /* get flags */
480         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
481 
482         if (!info->delete)
483         {
484             if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
485         }
486         else
487         {
488             if (!flags) flags = FLG_ADDREG_DELREG_BIT;
489             else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
490         }
491 
492         if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
493         {
494             if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
495         }
496         else if (RegCreateKeyExW( root_key, buffer, 0, NULL, 0, MAXIMUM_ALLOWED,
497             sd ? &security_attributes : NULL, &hkey, NULL ))
498         {
499             ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
500             continue;
501         }
502         TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
503 
504         /* get value name */
505         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
506             *buffer = 0;
507 
508         /* and now do it */
509         if (!do_reg_operation( hkey, buffer, &context, flags ))
510         {
511             if (hkey != root_key) RegCloseKey( hkey );
512             if (sd) LocalFree( sd );
513             return FALSE;
514         }
515         if (hkey != root_key) RegCloseKey( hkey );
516     }
517     if (sd) LocalFree( sd );
518     return TRUE;
519 }
520 
521 
522 /***********************************************************************
523  *            do_register_dll
524  *
525  * Register or unregister a dll.
526  */
527 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
528                              INT flags, INT timeout, const WCHAR *args )
529 {
530     HMODULE module;
531     HRESULT res;
532     SP_REGISTER_CONTROL_STATUSW status;
533 #ifdef __WINESRC__
534     IMAGE_NT_HEADERS *nt;
535 #endif
536 
537     status.cbSize = sizeof(status);
538     status.FileName = path;
539     status.FailureCode = SPREG_SUCCESS;
540     status.Win32Error = ERROR_SUCCESS;
541 
542     if (info->callback)
543     {
544         switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
545                                (UINT_PTR)&status, !info->unregister ))
546         {
547         case FILEOP_ABORT:
548             SetLastError( ERROR_OPERATION_ABORTED );
549             return FALSE;
550         case FILEOP_SKIP:
551             return TRUE;
552         case FILEOP_DOIT:
553             break;
554         }
555     }
556 
557     if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
558     {
559         WARN( "could not load %s\n", debugstr_w(path) );
560         status.FailureCode = SPREG_LOADLIBRARY;
561         status.Win32Error = GetLastError();
562         goto done;
563     }
564 
565 #ifdef __WINESRC__
566     if ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
567     {
568         /* file is an executable, not a dll */
569         STARTUPINFOW startup;
570         PROCESS_INFORMATION info;
571         WCHAR *cmd_line;
572         BOOL res;
573         static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
574         static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
575 
576         FreeLibrary( module );
577         module = NULL;
578         if (!args) args = default_args;
579         cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
580         sprintfW( cmd_line, format, path, args );
581         memset( &startup, 0, sizeof(startup) );
582         startup.cb = sizeof(startup);
583         TRACE( "executing %s\n", debugstr_w(cmd_line) );
584         res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
585         HeapFree( GetProcessHeap(), 0, cmd_line );
586         if (!res)
587         {
588             status.FailureCode = SPREG_LOADLIBRARY;
589             status.Win32Error = GetLastError();
590             goto done;
591         }
592         CloseHandle( info.hThread );
593 
594         if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
595         {
596             /* timed out, kill the process */
597             TerminateProcess( info.hProcess, 1 );
598             status.FailureCode = SPREG_TIMEOUT;
599             status.Win32Error = ERROR_TIMEOUT;
600         }
601         CloseHandle( info.hProcess );
602         goto done;
603     }
604 #endif // __WINESRC__
605 
606     if (flags & FLG_REGSVR_DLLREGISTER)
607     {
608         const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
609         HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
610 
611         if (!func)
612         {
613             status.FailureCode = SPREG_GETPROCADDR;
614             status.Win32Error = GetLastError();
615             goto done;
616         }
617 
618         TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
619         res = func();
620 
621         if (FAILED(res))
622         {
623             WARN( "calling %s in %s returned error %x\n", entry_point, debugstr_w(path), res );
624             status.FailureCode = SPREG_REGSVR;
625             status.Win32Error = res;
626             goto done;
627         }
628     }
629 
630     if (flags & FLG_REGSVR_DLLINSTALL)
631     {
632         HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
633 
634         if (!func)
635         {
636             status.FailureCode = SPREG_GETPROCADDR;
637             status.Win32Error = GetLastError();
638             goto done;
639         }
640 
641         TRACE( "calling DllInstall(%d,%s) in %s\n",
642                !info->unregister, debugstr_w(args), debugstr_w(path) );
643         res = func( !info->unregister, args );
644 
645         if (FAILED(res))
646         {
647             WARN( "calling DllInstall in %s returned error %x\n", debugstr_w(path), res );
648             status.FailureCode = SPREG_REGSVR;
649             status.Win32Error = res;
650             goto done;
651         }
652     }
653 
654 done:
655     if (module) FreeLibrary( module );
656     if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
657                                         (UINT_PTR)&status, !info->unregister );
658     return TRUE;
659 }
660 
661 
662 /***********************************************************************
663  *            register_dlls_callback
664  *
665  * Called once for each RegisterDlls entry in a given section.
666  */
667 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
668 {
669     struct register_dll_info *info = arg;
670     INFCONTEXT context;
671     BOOL ret = TRUE;
672     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
673 
674     for (; ok; ok = SetupFindNextLine( &context, &context ))
675     {
676         WCHAR *path, *args, *p;
677         WCHAR buffer[MAX_INF_STRING_LENGTH];
678         INT flags, timeout;
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 flags */
694         if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
695 
696         /* get timeout */
697         if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
698 
699         /* get command line */
700         args = NULL;
701         if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
702             args = buffer;
703 
704         ret = do_register_dll( info, path, flags, timeout, args );
705 
706     done:
707         HeapFree( GetProcessHeap(), 0, path );
708         if (!ret) break;
709     }
710     return ret;
711 }
712 
713 #ifdef __WINESRC__
714 /***********************************************************************
715  *            fake_dlls_callback
716  *
717  * Called once for each WineFakeDlls entry in a given section.
718  */
719 static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
720 {
721     INFCONTEXT context;
722     BOOL ret = TRUE;
723     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
724 
725     for (; ok; ok = SetupFindNextLine( &context, &context ))
726     {
727         WCHAR *path, *p;
728         WCHAR buffer[MAX_INF_STRING_LENGTH];
729 
730         /* get directory */
731         if (!(path = PARSER_get_dest_dir( &context ))) continue;
732 
733         /* get dll name */
734         if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
735             goto done;
736         if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
737                                (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
738         path = p;
739         p += strlenW(p);
740         if (p == path || p[-1] != '\\') *p++ = '\\';
741         strcpyW( p, buffer );
742 
743         /* get source dll */
744         if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
745             p = buffer;  /* otherwise use target base name as default source */
746 
747         create_fake_dll( path, p );  /* ignore errors */
748 
749     done:
750         HeapFree( GetProcessHeap(), 0, path );
751         if (!ret) break;
752     }
753     return ret;
754 }
755 #endif // __WINESRC__
756 
757 /***********************************************************************
758  *            update_ini_callback
759  *
760  * Called once for each UpdateInis entry in a given section.
761  */
762 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
763 {
764     INFCONTEXT context;
765 
766     BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
767 
768     for (; ok; ok = SetupFindNextLine( &context, &context ))
769     {
770         WCHAR buffer[MAX_INF_STRING_LENGTH];
771         WCHAR  filename[MAX_INF_STRING_LENGTH];
772         WCHAR  section[MAX_INF_STRING_LENGTH];
773         WCHAR  entry[MAX_INF_STRING_LENGTH];
774         WCHAR  string[MAX_INF_STRING_LENGTH];
775         LPWSTR divider;
776 
777         if (!SetupGetStringFieldW( &context, 1, filename,
778                                    sizeof(filename)/sizeof(WCHAR), NULL ))
779             continue;
780 
781         if (!SetupGetStringFieldW( &context, 2, section,
782                                    sizeof(section)/sizeof(WCHAR), NULL ))
783             continue;
784 
785         if (!SetupGetStringFieldW( &context, 4, buffer,
786                                    sizeof(buffer)/sizeof(WCHAR), NULL ))
787             continue;
788 
789         divider = strchrW(buffer,'=');
790         if (divider)
791         {
792             *divider = 0;
793             strcpyW(entry,buffer);
794             divider++;
795             strcpyW(string,divider);
796         }
797         else
798         {
799             strcpyW(entry,buffer);
800             string[0]=0;
801         }
802 
803         TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
804                debugstr_w(string),debugstr_w(section),debugstr_w(filename));
805         WritePrivateProfileStringW(section,entry,string,filename);
806 
807     }
808     return TRUE;
809 }
810 
811 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
812 {
813     FIXME( "should update ini fields %s\n", debugstr_w(field) );
814     return TRUE;
815 }
816 
817 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
818 {
819     FIXME( "should do ini2reg %s\n", debugstr_w(field) );
820     return TRUE;
821 }
822 
823 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
824 {
825     FIXME( "should do logconf %s\n", debugstr_w(field) );
826     return TRUE;
827 }
828 
829 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
830 {
831     FIXME( "should do bitreg %s\n", debugstr_w(field) );
832     return TRUE;
833 }
834 
835 static BOOL Concatenate(int DirId, LPCWSTR SubDirPart, LPCWSTR NamePart, LPWSTR *pFullName)
836 {
837     DWORD dwRequired = 0;
838     LPCWSTR Dir;
839     LPWSTR FullName;
840 
841     *pFullName = NULL;
842 
843     Dir = DIRID_get_string(DirId);
844     if (Dir)
845         dwRequired += wcslen(Dir) + 1;
846     if (SubDirPart)
847         dwRequired += wcslen(SubDirPart) + 1;
848     if (NamePart)
849         dwRequired += wcslen(NamePart);
850     dwRequired = dwRequired * sizeof(WCHAR) + sizeof(UNICODE_NULL);
851 
852     FullName = MyMalloc(dwRequired);
853     if (!FullName)
854     {
855         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
856         return FALSE;
857     }
858     FullName[0] = UNICODE_NULL;
859 
860     if (Dir)
861     {
862         wcscat(FullName, Dir);
863         if (FullName[wcslen(FullName) - 1] != '\\')
864             wcscat(FullName, BackSlash);
865     }
866     if (SubDirPart)
867     {
868         wcscat(FullName, SubDirPart);
869         if (FullName[wcslen(FullName) - 1] != '\\')
870             wcscat(FullName, BackSlash);
871     }
872     if (NamePart)
873         wcscat(FullName, NamePart);
874 
875     *pFullName = FullName;
876     return TRUE;
877 }
878 
879 /***********************************************************************
880  *            profile_items_callback
881  *
882  * Called once for each ProfileItems entry in a given section.
883  */
884 static BOOL
885 profile_items_callback(
886     IN HINF hInf,
887     IN PCWSTR SectionName,
888     IN PVOID Arg)
889 {
890     INFCONTEXT Context;
891     LPWSTR LinkSubDir = NULL, LinkName = NULL;
892     INT LinkAttributes = 0;
893     INT LinkFolder = 0;
894     INT FileDirId = 0;
895     INT CSIDL = CSIDL_COMMON_PROGRAMS;
896     LPWSTR FileSubDir = NULL;
897     INT DirId = 0;
898     LPWSTR SubDirPart = NULL, NamePart = NULL;
899     LPWSTR FullLinkName = NULL, FullFileName = NULL, FullWorkingDir = NULL, FullIconName = NULL;
900     INT IconIdx = 0;
901     LPWSTR lpHotKey = NULL, lpInfoTip = NULL;
902     LPWSTR DisplayName = NULL;
903     INT DisplayResId = 0;
904     BOOL ret = FALSE;
905     DWORD Index, Required;
906 
907     IShellLinkW *psl;
908     IPersistFile *ppf;
909     HMODULE hOle32 = NULL;
910     COINITIALIZE pCoInitialize;
911     COCREATEINSTANCE pCoCreateInstance;
912     COUNINITIALIZE pCoUninitialize;
913     HRESULT hr;
914 
915     TRACE("hInf %p, SectionName %s, Arg %p\n",
916         hInf, debugstr_w(SectionName), Arg);
917 
918     /* Read 'Name' entry */
919     if (!SetupFindFirstLineW(hInf, SectionName, Name, &Context))
920         goto cleanup;
921     if (!GetStringField(&Context, 1, &LinkName))
922         goto cleanup;
923     if (SetupGetFieldCount(&Context) >= 2)
924     {
925         if (!SetupGetIntField(&Context, 2, &LinkAttributes))
926             goto cleanup;
927     }
928     if (SetupGetFieldCount(&Context) >= 3)
929     {
930         if (!SetupGetIntField(&Context, 3, &LinkFolder))
931             goto cleanup;
932     }
933 
934     /* Read 'CmdLine' entry */
935     if (!SetupFindFirstLineW(hInf, SectionName, CmdLine, &Context))
936         goto cleanup;
937     Index = 1;
938     if (!SetupGetIntField(&Context, Index++, &FileDirId))
939         goto cleanup;
940     if (SetupGetFieldCount(&Context) >= 3)
941     {
942         if (!GetStringField(&Context, Index++, &FileSubDir))
943             goto cleanup;
944     }
945     if (!GetStringField(&Context, Index++, &NamePart))
946         goto cleanup;
947     if (!Concatenate(FileDirId, FileSubDir, NamePart, &FullFileName))
948         goto cleanup;
949     MyFree(NamePart);
950     NamePart = NULL;
951 
952     /* Read 'SubDir' entry */
953     if ((LinkAttributes & FLG_PROFITEM_GROUP) == 0 && SetupFindFirstLineW(hInf, SectionName, SubDir, &Context))
954     {
955         if (!GetStringField(&Context, 1, &LinkSubDir))
956             goto cleanup;
957     }
958 
959     /* Read 'WorkingDir' entry */
960     if (SetupFindFirstLineW(hInf, SectionName, WorkingDir, &Context))
961     {
962         if (!SetupGetIntField(&Context, 1, &DirId))
963             goto cleanup;
964         if (SetupGetFieldCount(&Context) >= 2)
965         {
966             if (!GetStringField(&Context, 2, &SubDirPart))
967                 goto cleanup;
968         }
969         if (!Concatenate(DirId, SubDirPart, NULL, &FullWorkingDir))
970             goto cleanup;
971         MyFree(SubDirPart);
972         SubDirPart = NULL;
973     }
974     else
975     {
976         if (!Concatenate(FileDirId, FileSubDir, NULL, &FullWorkingDir))
977             goto cleanup;
978     }
979 
980     /* Read 'IconPath' entry */
981     if (SetupFindFirstLineW(hInf, SectionName, IconPath, &Context))
982     {
983         Index = 1;
984         if (!SetupGetIntField(&Context, Index++, &DirId))
985             goto cleanup;
986         if (SetupGetFieldCount(&Context) >= 3)
987         {
988             if (!GetStringField(&Context, Index++, &SubDirPart))
989                 goto cleanup;
990         }
991         if (!GetStringField(&Context, Index, &NamePart))
992             goto cleanup;
993         if (!Concatenate(DirId, SubDirPart, NamePart, &FullIconName))
994             goto cleanup;
995         MyFree(SubDirPart);
996         MyFree(NamePart);
997         SubDirPart = NamePart = NULL;
998     }
999     else
1000     {
1001         FullIconName = pSetupDuplicateString(FullFileName);
1002         if (!FullIconName)
1003             goto cleanup;
1004     }
1005 
1006     /* Read 'IconIndex' entry */
1007     if (SetupFindFirstLineW(hInf, SectionName, IconIndex, &Context))
1008     {
1009         if (!SetupGetIntField(&Context, 1, &IconIdx))
1010             goto cleanup;
1011     }
1012 
1013     /* Read 'HotKey' and 'InfoTip' entries */
1014     GetLineText(hInf, SectionName, HotKey, &lpHotKey);
1015     GetLineText(hInf, SectionName, InfoTip, &lpInfoTip);
1016 
1017     /* Read 'DisplayResource' entry */
1018     if (SetupFindFirstLineW(hInf, SectionName, DisplayResource, &Context))
1019     {
1020         if (!GetStringField(&Context, 1, &DisplayName))
1021             goto cleanup;
1022         if (!SetupGetIntField(&Context, 2, &DisplayResId))
1023             goto cleanup;
1024     }
1025 
1026     /* Some debug */
1027     TRACE("Link is %s\\%s, attributes 0x%x\n", debugstr_w(LinkSubDir), debugstr_w(LinkName), LinkAttributes);
1028     TRACE("File is %s\n", debugstr_w(FullFileName));
1029     TRACE("Working dir %s\n", debugstr_w(FullWorkingDir));
1030     TRACE("Icon is %s, %d\n", debugstr_w(FullIconName), IconIdx);
1031     TRACE("Hotkey %s\n", debugstr_w(lpHotKey));
1032     TRACE("InfoTip %s\n", debugstr_w(lpInfoTip));
1033     TRACE("Display %s, %d\n", DisplayName, DisplayResId);
1034 
1035     /* Load ole32.dll */
1036     hOle32 = LoadLibraryA("ole32.dll");
1037     if (!hOle32)
1038         goto cleanup;
1039     pCoInitialize = (COINITIALIZE)GetProcAddress(hOle32, "CoInitialize");
1040     if (!pCoInitialize)
1041         goto cleanup;
1042     pCoCreateInstance = (COCREATEINSTANCE)GetProcAddress(hOle32, "CoCreateInstance");
1043     if (!pCoCreateInstance)
1044         goto cleanup;
1045     pCoUninitialize = (COUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize");
1046     if (!pCoUninitialize)
1047         goto cleanup;
1048 
1049     /* Create shortcut */
1050     hr = pCoInitialize(NULL);
1051     if (!SUCCEEDED(hr))
1052     {
1053         if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
1054             SetLastError(HRESULT_CODE(hr));
1055         else
1056             SetLastError(E_FAIL);
1057         goto cleanup;
1058     }
1059     hr = pCoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&psl);
1060     if (SUCCEEDED(hr))
1061     {
1062         /* Fill link properties */
1063         hr = IShellLinkW_SetPath(psl, FullFileName);
1064         if (SUCCEEDED(hr))
1065             hr = IShellLinkW_SetArguments(psl, L"");
1066         if (SUCCEEDED(hr))
1067             hr = IShellLinkW_SetWorkingDirectory(psl, FullWorkingDir);
1068         if (SUCCEEDED(hr))
1069             hr = IShellLinkW_SetIconLocation(psl, FullIconName, IconIdx);
1070         if (SUCCEEDED(hr) && lpHotKey)
1071             FIXME("Need to store hotkey %s in shell link\n", debugstr_w(lpHotKey));
1072         if (SUCCEEDED(hr) && lpInfoTip)
1073             hr = IShellLinkW_SetDescription(psl, lpInfoTip);
1074         if (SUCCEEDED(hr) && DisplayName)
1075             FIXME("Need to store display name %s, %d in shell link\n", debugstr_w(DisplayName), DisplayResId);
1076         if (SUCCEEDED(hr))
1077         {
1078             hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1079             if (SUCCEEDED(hr))
1080             {
1081                 Required = (MAX_PATH + wcslen(LinkSubDir) + 1 + wcslen(LinkName)) * sizeof(WCHAR);
1082                 FullLinkName = MyMalloc(Required);
1083                 if (!FullLinkName)
1084                     hr = E_OUTOFMEMORY;
1085                 else
1086                 {
1087                     if (LinkAttributes & (FLG_PROFITEM_DELETE | FLG_PROFITEM_GROUP))
1088                         FIXME("Need to handle FLG_PROFITEM_DELETE and FLG_PROFITEM_GROUP\n");
1089                     if (LinkAttributes & FLG_PROFITEM_CSIDL)
1090                         CSIDL = LinkFolder;
1091                     else if (LinkAttributes & FLG_PROFITEM_CURRENTUSER)
1092                         CSIDL = CSIDL_PROGRAMS;
1093 
1094                     if (SHGetSpecialFolderPathW(
1095                         NULL,
1096                         FullLinkName,
1097                         CSIDL,
1098                         TRUE))
1099                     {
1100                         if (FullLinkName[wcslen(FullLinkName) - 1] != '\\')
1101                             wcscat(FullLinkName, BackSlash);
1102                         if (LinkSubDir)
1103                         {
1104                             wcscat(FullLinkName, LinkSubDir);
1105                             if (FullLinkName[wcslen(FullLinkName) - 1] != '\\')
1106                                 wcscat(FullLinkName, BackSlash);
1107                         }
1108                         wcscat(FullLinkName, LinkName);
1109                         wcscat(FullLinkName, DotLnk);
1110                         hr = IPersistFile_Save(ppf, FullLinkName, TRUE);
1111                     }
1112                     else
1113                         hr = HRESULT_FROM_WIN32(GetLastError());
1114                 }
1115                 IPersistFile_Release(ppf);
1116             }
1117         }
1118         IShellLinkW_Release(psl);
1119     }
1120     pCoUninitialize();
1121     if (SUCCEEDED(hr))
1122         ret = TRUE;
1123     else
1124     {
1125         if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
1126             SetLastError(HRESULT_CODE(hr));
1127         else
1128             SetLastError(E_FAIL);
1129     }
1130 
1131 cleanup:
1132     MyFree(LinkSubDir);
1133     MyFree(LinkName);
1134     MyFree(FileSubDir);
1135     MyFree(SubDirPart);
1136     MyFree(NamePart);
1137     MyFree(FullFileName);
1138     MyFree(FullWorkingDir);
1139     MyFree(FullIconName);
1140     MyFree(FullLinkName);
1141     MyFree(lpHotKey);
1142     MyFree(lpInfoTip);
1143     MyFree(DisplayName);
1144     if (hOle32)
1145         FreeLibrary(hOle32);
1146 
1147     TRACE("Returning %d\n", ret);
1148     return ret;
1149 }
1150 
1151 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
1152 {
1153     FIXME( "should do copy inf %s\n", debugstr_w(field) );
1154     return TRUE;
1155 }
1156 
1157 
1158 /***********************************************************************
1159  *            iterate_section_fields
1160  *
1161  * Iterate over all fields of a certain key of a certain section
1162  */
1163 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
1164                                     iterate_fields_func callback, void *arg )
1165 {
1166     WCHAR static_buffer[200];
1167     WCHAR *buffer = static_buffer;
1168     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
1169     INFCONTEXT context;
1170     BOOL ret = FALSE;
1171 
1172     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
1173     while (ok)
1174     {
1175         UINT i, count = SetupGetFieldCount( &context );
1176         for (i = 1; i <= count; i++)
1177         {
1178             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
1179                 goto done;
1180             if (!callback( hinf, buffer, arg ))
1181             {
1182                 WARN("callback failed for %s %s err %d\n",
1183                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
1184                 goto done;
1185             }
1186         }
1187         ok = SetupFindNextMatchLineW( &context, key, &context );
1188     }
1189     ret = TRUE;
1190  done:
1191     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
1192     return ret;
1193 }
1194 
1195 
1196 /***********************************************************************
1197  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
1198  */
1199 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
1200                                               PCSTR section, PCSTR src_root, UINT flags )
1201 {
1202     UNICODE_STRING sectionW;
1203     BOOL ret = FALSE;
1204 
1205     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1206     {
1207         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1208         return FALSE;
1209     }
1210     if (!src_root)
1211         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
1212                                                 NULL, flags );
1213     else
1214     {
1215         UNICODE_STRING srcW;
1216         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
1217         {
1218             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
1219                                                     srcW.Buffer, flags );
1220             RtlFreeUnicodeString( &srcW );
1221         }
1222         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1223     }
1224     RtlFreeUnicodeString( &sectionW );
1225     return ret;
1226 }
1227 
1228 
1229 /***********************************************************************
1230  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
1231  */
1232 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
1233                                               PCWSTR section, PCWSTR src_root, UINT flags )
1234 {
1235     struct files_callback_info info;
1236 
1237     info.queue      = queue;
1238     info.src_root   = src_root;
1239     info.copy_flags = flags;
1240     info.layout     = hlayout;
1241     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
1242 }
1243 
1244 
1245 /***********************************************************************
1246  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
1247  */
1248 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
1249                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
1250                                          PSP_FILE_CALLBACK_A callback, PVOID context,
1251                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1252 {
1253     UNICODE_STRING sectionW, src_rootW;
1254     struct callback_WtoA_context ctx;
1255     BOOL ret = FALSE;
1256 
1257     src_rootW.Buffer = NULL;
1258     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
1259     {
1260         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1261         return FALSE;
1262     }
1263 
1264     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1265     {
1266         ctx.orig_context = context;
1267         ctx.orig_handler = callback;
1268         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
1269                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
1270                                            &ctx, devinfo, devinfo_data );
1271         RtlFreeUnicodeString( &sectionW );
1272     }
1273     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1274 
1275     RtlFreeUnicodeString( &src_rootW );
1276     return ret;
1277 }
1278 
1279 
1280 /***********************************************************************
1281  *            include_callback
1282  *
1283  * Called once for each Include entry in a given section.
1284  */
1285 static BOOL include_callback( HINF hinf, PCWSTR field, void *arg )
1286 {
1287     return SetupOpenAppendInfFileW( field, hinf, NULL );
1288 }
1289 
1290 
1291 /***********************************************************************
1292  *            needs_callback
1293  *
1294  * Called once for each Needs entry in a given section.
1295  */
1296 static BOOL needs_callback( HINF hinf, PCWSTR field, void *arg )
1297 {
1298     struct needs_callback_info *info = arg;
1299 
1300     switch (info->type)
1301     {
1302         case 0:
1303             return SetupInstallFromInfSectionW(info->owner, *(HINF*)hinf, field, info->flags,
1304                info->key_root, info->src_root, info->copy_flags, info->callback,
1305                info->context, info->devinfo, info->devinfo_data);
1306         case 1:
1307             return SetupInstallServicesFromInfSectionExW(*(HINF*)hinf, field, info->flags,
1308                 info->devinfo, info->devinfo_data, info->reserved1, info->reserved2);
1309         default:
1310             ERR("Unknown info type %u\n", info->type);
1311             return FALSE;
1312     }
1313 }
1314 
1315 
1316 /***********************************************************************
1317  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
1318  */
1319 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
1320                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
1321                                          PSP_FILE_CALLBACK_W callback, PVOID context,
1322                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1323 {
1324     struct needs_callback_info needs_info;
1325 
1326     /* Parse 'Include' and 'Needs' directives */
1327     iterate_section_fields( hinf, section, Include, include_callback, NULL);
1328     needs_info.type = 0;
1329     needs_info.owner = owner;
1330     needs_info.flags = flags;
1331     needs_info.key_root = key_root;
1332     needs_info.src_root = src_root;
1333     needs_info.copy_flags = copy_flags;
1334     needs_info.callback = callback;
1335     needs_info.context = context;
1336     needs_info.devinfo = devinfo;
1337     needs_info.devinfo_data = devinfo_data;
1338     iterate_section_fields( hinf, section, Needs, needs_callback, &needs_info);
1339 
1340     if (flags & SPINST_FILES)
1341     {
1342         SP_DEVINSTALL_PARAMS_W install_params;
1343         struct files_callback_info info;
1344         HSPFILEQ queue = NULL;
1345         BOOL use_custom_queue;
1346         BOOL ret;
1347 
1348         install_params.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
1349         use_custom_queue = SetupDiGetDeviceInstallParamsW(devinfo, devinfo_data, &install_params) && (install_params.Flags & DI_NOVCP);
1350         if (!use_custom_queue && ((queue = SetupOpenFileQueue()) == (HSPFILEQ)INVALID_HANDLE_VALUE ))
1351             return FALSE;
1352         info.queue      = use_custom_queue ? install_params.FileQueue : queue;
1353         info.src_root   = src_root;
1354         info.copy_flags = copy_flags;
1355         info.layout     = hinf;
1356         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
1357                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
1358                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ));
1359         if (!use_custom_queue)
1360         {
1361             if (ret)
1362                 ret = SetupCommitFileQueueW( owner, queue, callback, context );
1363             SetupCloseFileQueue( queue );
1364         }
1365         if (!ret) return FALSE;
1366     }
1367     if (flags & SPINST_INIFILES)
1368     {
1369         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
1370             !iterate_section_fields( hinf, section, UpdateIniFields,
1371                                      update_ini_fields_callback, NULL ))
1372             return FALSE;
1373     }
1374     if (flags & SPINST_INI2REG)
1375     {
1376         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
1377             return FALSE;
1378     }
1379     if (flags & SPINST_LOGCONFIG)
1380     {
1381         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
1382             return FALSE;
1383     }
1384     if (flags & SPINST_REGSVR)
1385     {
1386         struct register_dll_info info;
1387 
1388         info.unregister = FALSE;
1389         if (flags & SPINST_REGISTERCALLBACKAWARE)
1390         {
1391             info.callback         = callback;
1392             info.callback_context = context;
1393         }
1394         else info.callback = NULL;
1395 
1396         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
1397             return FALSE;
1398 
1399 #ifdef __WINESRC__
1400         if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
1401             return FALSE;
1402 #endif // __WINESRC__
1403     }
1404     if (flags & SPINST_UNREGSVR)
1405     {
1406         struct register_dll_info info;
1407 
1408         info.unregister = TRUE;
1409         if (flags & SPINST_REGISTERCALLBACKAWARE)
1410         {
1411             info.callback         = callback;
1412             info.callback_context = context;
1413         }
1414         else info.callback = NULL;
1415 
1416         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
1417             return FALSE;
1418     }
1419     if (flags & SPINST_REGISTRY)
1420     {
1421         struct registry_callback_info info;
1422 
1423         info.default_root = key_root;
1424         info.delete = TRUE;
1425         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
1426             return FALSE;
1427         info.delete = FALSE;
1428         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
1429             return FALSE;
1430     }
1431     if (flags & SPINST_BITREG)
1432     {
1433         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
1434             return FALSE;
1435     }
1436     if (flags & SPINST_PROFILEITEMS)
1437     {
1438         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
1439             return FALSE;
1440     }
1441     if (flags & SPINST_COPYINF)
1442     {
1443         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
1444             return FALSE;
1445     }
1446 
1447     return TRUE;
1448 }
1449 
1450 
1451 /***********************************************************************
1452  *		InstallHinfSectionW  (SETUPAPI.@)
1453  *
1454  * NOTE: 'cmdline' is <section> <mode> <path> from
1455  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
1456  */
1457 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
1458 {
1459     WCHAR *s, *path, section[MAX_PATH];
1460     void *callback_context = NULL;
1461     DWORD SectionNameLength;
1462     UINT mode;
1463     HINF hinf = INVALID_HANDLE_VALUE;
1464     BOOL bRebootRequired = FALSE;
1465     BOOL ret;
1466 
1467     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1468 
1469     lstrcpynW( section, cmdline, MAX_PATH );
1470 
1471     if (!(s = strchrW( section, ' ' ))) return;
1472     *s++ = 0;
1473     while (*s == ' ') s++;
1474     mode = atoiW( s );
1475 
1476     /* quoted paths are not allowed on native, the rest of the command line is taken as the path */
1477     if (!(s = strchrW( s, ' ' ))) return;
1478     while (*s == ' ') s++;
1479     path = s;
1480 
1481     if (mode & 0x80)
1482     {
1483         FIXME("default path of the installation not changed\n");
1484         mode &= ~0x80;
1485     }
1486 
1487     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1488     if (hinf == INVALID_HANDLE_VALUE)
1489     {
1490         WARN("SetupOpenInfFileW(%s) failed (Error %u)\n", path, GetLastError());
1491         goto cleanup;
1492     }
1493 
1494     ret = SetupDiGetActualSectionToInstallW(
1495        hinf, section, section, sizeof(section)/sizeof(section[0]), &SectionNameLength, NULL );
1496     if (!ret)
1497     {
1498         WARN("SetupDiGetActualSectionToInstallW() failed (Error %u)\n", GetLastError());
1499         goto cleanup;
1500     }
1501     if (SectionNameLength > MAX_PATH - strlenW(DotServices))
1502     {
1503         WARN("Section name '%s' too long\n", section);
1504         goto cleanup;
1505     }
1506 
1507     /* Copy files and add registry entries */
1508     callback_context = SetupInitDefaultQueueCallback( hwnd );
1509     ret = SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL,
1510                                        SP_COPY_NEWER | SP_COPY_IN_USE_NEEDS_REBOOT,
1511                                        SetupDefaultQueueCallbackW, callback_context,
1512                                        NULL, NULL );
1513     if (!ret)
1514     {
1515         WARN("SetupInstallFromInfSectionW() failed (Error %u)\n", GetLastError());
1516         goto cleanup;
1517     }
1518     /* FIXME: need to check if some files were in use and need reboot
1519      * bReboot = ...;
1520      */
1521 
1522     /* Install services */
1523     wcscat(section, DotServices);
1524     ret = SetupInstallServicesFromInfSectionW( hinf, section, 0 );
1525     if (!ret && GetLastError() == ERROR_SECTION_NOT_FOUND)
1526         ret = TRUE;
1527     if (!ret)
1528     {
1529         WARN("SetupInstallServicesFromInfSectionW() failed (Error %u)\n", GetLastError());
1530         goto cleanup;
1531     }
1532     else if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
1533     {
1534         bRebootRequired = TRUE;
1535     }
1536 
1537     /* Check if we need to reboot */
1538     switch (mode)
1539     {
1540         case 0:
1541             /* Never reboot */
1542             break;
1543         case 1:
1544             /* Always reboot */
1545             ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
1546                 SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED);
1547             break;
1548         case 2:
1549             /* Query user before rebooting */
1550             SetupPromptReboot(NULL, hwnd, FALSE);
1551             break;
1552         case 3:
1553             /* Reboot if necessary */
1554             if (bRebootRequired)
1555             {
1556                 ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
1557                     SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED);
1558             }
1559             break;
1560         case 4:
1561             /* If necessary, query user before rebooting */
1562             if (bRebootRequired)
1563             {
1564                 SetupPromptReboot(NULL, hwnd, FALSE);
1565             }
1566             break;
1567         default:
1568             break;
1569     }
1570 
1571 cleanup:
1572     if ( callback_context )
1573         SetupTermDefaultQueueCallback( callback_context );
1574     if ( hinf != INVALID_HANDLE_VALUE )
1575         SetupCloseInfFile( hinf );
1576 }
1577 
1578 
1579 /***********************************************************************
1580  *		InstallHinfSectionA  (SETUPAPI.@)
1581  */
1582 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1583 {
1584     UNICODE_STRING cmdlineW;
1585 
1586     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1587     {
1588         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1589         RtlFreeUnicodeString( &cmdlineW );
1590     }
1591 }
1592 
1593 /***********************************************************************
1594  *              SetupInstallServicesFromInfSectionW  (SETUPAPI.@)
1595  */
1596 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF Inf, PCWSTR SectionName, DWORD Flags)
1597 {
1598     return SetupInstallServicesFromInfSectionExW( Inf, SectionName, Flags,
1599                                                   NULL, NULL, NULL, NULL );
1600 }
1601 
1602 /***********************************************************************
1603  *              SetupInstallServicesFromInfSectionA  (SETUPAPI.@)
1604  */
1605 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF Inf, PCSTR SectionName, DWORD Flags)
1606 {
1607     return SetupInstallServicesFromInfSectionExA( Inf, SectionName, Flags,
1608                                                   NULL, NULL, NULL, NULL );
1609 }
1610 
1611 /***********************************************************************
1612  *		SetupInstallServicesFromInfSectionExA  (SETUPAPI.@)
1613  */
1614 BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
1615 {
1616     UNICODE_STRING sectionnameW;
1617     BOOL ret = FALSE;
1618 
1619     if (RtlCreateUnicodeStringFromAsciiz( &sectionnameW, sectionname ))
1620     {
1621         ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
1622         RtlFreeUnicodeString( &sectionnameW );
1623     }
1624     else
1625         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1626 
1627     return ret;
1628 }
1629 
1630 
1631 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
1632 {
1633     DWORD required;
1634     PWSTR buf = NULL;
1635 
1636     *value = NULL;
1637 
1638     if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
1639         && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
1640         return FALSE;
1641 
1642     buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
1643     if ( ! buf )
1644     {
1645         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1646         return FALSE;
1647     }
1648 
1649     if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
1650     {
1651         HeapFree( GetProcessHeap(), 0, buf );
1652         return FALSE;
1653     }
1654 
1655     *value = buf;
1656     return TRUE;
1657 }
1658 
1659 
1660 static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
1661 {
1662     LPWSTR buffer, end;
1663     INT res;
1664 
1665     if (! GetLineText( hinf, section_name, key_name, &buffer ) )
1666         return FALSE;
1667 
1668     res = wcstol( buffer, &end, 0 );
1669     if (end != buffer && !*end)
1670     {
1671         HeapFree(GetProcessHeap(), 0, buffer);
1672         *value = res;
1673         return TRUE;
1674     }
1675     else
1676     {
1677         HeapFree(GetProcessHeap(), 0, buffer);
1678         SetLastError( ERROR_INVALID_DATA );
1679         return FALSE;
1680     }
1681 }
1682 
1683 
1684 BOOL GetStringField( PINFCONTEXT context, DWORD index, PWSTR *value)
1685 {
1686     DWORD RequiredSize;
1687     BOOL ret;
1688 
1689     ret = SetupGetStringFieldW(
1690         context,
1691         index,
1692         NULL, 0,
1693         &RequiredSize);
1694     if (!ret)
1695         return FALSE;
1696     else if (RequiredSize == 0)
1697     {
1698         *value = NULL;
1699         return TRUE;
1700     }
1701 
1702     /* We got the needed size for the buffer */
1703     *value = MyMalloc(RequiredSize * sizeof(WCHAR));
1704     if (!*value)
1705     {
1706         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1707         return FALSE;
1708     }
1709     ret = SetupGetStringFieldW(
1710         context,
1711         index,
1712         *value, RequiredSize, NULL);
1713     if (!ret)
1714         MyFree(*value);
1715 
1716     return ret;
1717 }
1718 
1719 static VOID FixupServiceBinaryPath(
1720     IN DWORD ServiceType,
1721     IN OUT LPWSTR *ServiceBinary)
1722 {
1723     LPWSTR Buffer;
1724     WCHAR ReactosDir[MAX_PATH];
1725     DWORD RosDirLength, ServiceLength, Win32Length;
1726 
1727     GetWindowsDirectoryW(ReactosDir, MAX_PATH);
1728     RosDirLength = strlenW(ReactosDir);
1729     ServiceLength = strlenW(*ServiceBinary);
1730 
1731     /* Check and fix two things:
1732        1. Get rid of C:\ReactOS and use relative
1733           path instead.
1734        2. Add %SystemRoot% for Win32 services */
1735 
1736     if (ServiceLength < RosDirLength)
1737         return;
1738 
1739     if (!wcsnicmp(*ServiceBinary, ReactosDir, RosDirLength))
1740     {
1741         /* Yes, the first part is the C:\ReactOS\, just skip it */
1742         MoveMemory(*ServiceBinary, *ServiceBinary + RosDirLength + 1,
1743             (ServiceLength - RosDirLength) * sizeof(WCHAR));
1744 
1745         /* Handle Win32-services differently */
1746         if (ServiceType & SERVICE_WIN32)
1747         {
1748             Win32Length = (ServiceLength -
1749                 RosDirLength - 1 + 13) * sizeof(WCHAR);
1750             /* -1 to not count the separator after C:\ReactOS
1751                wcslen(L"%SystemRoot%\\") = 13*sizeof(wchar_t) */
1752             Buffer = MyMalloc(Win32Length);
1753 
1754             wcscpy(Buffer, L"%SystemRoot%\\");
1755             wcscat(Buffer, *ServiceBinary);
1756             MyFree(*ServiceBinary);
1757 
1758             *ServiceBinary = Buffer;
1759         }
1760     }
1761 }
1762 
1763 static BOOL InstallOneService(
1764     struct DeviceInfoSet *list,
1765     IN HINF hInf,
1766     IN LPCWSTR ServiceSection,
1767     IN LPCWSTR ServiceName,
1768     IN UINT ServiceFlags)
1769 {
1770     SC_HANDLE hSCManager = NULL;
1771     SC_HANDLE hService = NULL;
1772     LPDWORD GroupOrder = NULL;
1773     LPQUERY_SERVICE_CONFIGW ServiceConfig = NULL;
1774     HKEY hServicesKey, hServiceKey;
1775     LONG rc;
1776     BOOL ret = FALSE;
1777 
1778     HKEY hGroupOrderListKey = NULL;
1779     LPWSTR ServiceBinary = NULL;
1780     LPWSTR LoadOrderGroup = NULL;
1781     LPWSTR DisplayName = NULL;
1782     LPWSTR Description = NULL;
1783     LPWSTR Dependencies = NULL;
1784     LPWSTR SecurityDescriptor = NULL;
1785     PSECURITY_DESCRIPTOR sd = NULL;
1786     INT ServiceType, StartType, ErrorControl;
1787     DWORD dwRegType;
1788     DWORD tagId = (DWORD)-1;
1789     BOOL useTag;
1790 
1791     if (!GetIntField(hInf, ServiceSection, ServiceTypeKey, &ServiceType))
1792         goto cleanup;
1793     if (!GetIntField(hInf, ServiceSection, StartTypeKey, &StartType))
1794         goto cleanup;
1795     if (!GetIntField(hInf, ServiceSection, ErrorControlKey, &ErrorControl))
1796         goto cleanup;
1797     useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1798 
1799     hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1800     if (hSCManager == NULL)
1801         goto cleanup;
1802 
1803     if (!GetLineText(hInf, ServiceSection, ServiceBinaryKey, &ServiceBinary))
1804         goto cleanup;
1805 
1806     /* Adjust binary path according to the service type */
1807     FixupServiceBinaryPath(ServiceType, &ServiceBinary);
1808 
1809     /* Don't check return value, as these fields are optional and
1810      * GetLineText initialize output parameter even on failure */
1811     GetLineText(hInf, ServiceSection, LoadOrderGroupKey, &LoadOrderGroup);
1812     GetLineText(hInf, ServiceSection, DisplayNameKey, &DisplayName);
1813     GetLineText(hInf, ServiceSection, DescriptionKey, &Description);
1814     GetLineText(hInf, ServiceSection, DependenciesKey, &Dependencies);
1815 
1816     /* If there is no group, we must not request a tag */
1817     if (!LoadOrderGroup || !*LoadOrderGroup)
1818         useTag = FALSE;
1819 
1820     hService = OpenServiceW(
1821         hSCManager,
1822         ServiceName,
1823         DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | WRITE_DAC);
1824     if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1825         goto cleanup;
1826 
1827     if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
1828     {
1829         ret = DeleteService(hService);
1830         if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
1831             goto cleanup;
1832     }
1833 
1834     if (hService == NULL)
1835     {
1836         /* Create new service */
1837         hService = CreateServiceW(
1838             hSCManager,
1839             ServiceName,
1840             DisplayName,
1841             WRITE_DAC,
1842             ServiceType,
1843             StartType,
1844             ErrorControl,
1845             ServiceBinary,
1846             LoadOrderGroup,
1847             useTag ? &tagId : NULL,
1848             Dependencies,
1849             NULL, NULL);
1850         if (hService == NULL)
1851             goto cleanup;
1852     }
1853     else
1854     {
1855         DWORD bufferSize;
1856         /* Read current configuration */
1857         if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1858         {
1859             if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1860                 goto cleanup;
1861             ServiceConfig = MyMalloc(bufferSize);
1862             if (!ServiceConfig)
1863             {
1864                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1865                 goto cleanup;
1866             }
1867             if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1868                 goto cleanup;
1869         }
1870         tagId = ServiceConfig->dwTagId;
1871 
1872         /* Update configuration */
1873         ret = ChangeServiceConfigW(
1874             hService,
1875             ServiceType,
1876             (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1877             (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1878             ServiceBinary,
1879             (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1880             useTag ? &tagId : NULL,
1881             (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1882             NULL, NULL,
1883             (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1884         if (!ret)
1885             goto cleanup;
1886     }
1887 
1888     /* Set security */
1889     if (GetLineText(hInf, ServiceSection, SecurityKey, &SecurityDescriptor))
1890     {
1891         ret = ConvertStringSecurityDescriptorToSecurityDescriptorW(SecurityDescriptor, SDDL_REVISION_1, &sd, NULL);
1892         if (!ret)
1893             goto cleanup;
1894         ret = SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd);
1895         if (!ret)
1896             goto cleanup;
1897     }
1898 
1899     /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1900 
1901     if (useTag)
1902     {
1903         /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1904         LPCWSTR lpLoadOrderGroup;
1905         DWORD bufferSize;
1906 
1907         lpLoadOrderGroup = LoadOrderGroup;
1908         if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1909             lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1910 
1911         rc = RegOpenKeyW(
1912             list ? list->HKLM : HKEY_LOCAL_MACHINE,
1913             GroupOrderListKey,
1914             &hGroupOrderListKey);
1915         if (rc != ERROR_SUCCESS)
1916         {
1917             SetLastError(rc);
1918             goto cleanup;
1919         }
1920         rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1921         if (rc == ERROR_FILE_NOT_FOUND)
1922             bufferSize = sizeof(DWORD);
1923         else if (rc != ERROR_SUCCESS)
1924         {
1925             SetLastError(rc);
1926             goto cleanup;
1927         }
1928         else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1929         {
1930             SetLastError(ERROR_GEN_FAILURE);
1931             goto cleanup;
1932         }
1933         /* Allocate buffer to store existing data + the new tag */
1934         GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
1935         if (!GroupOrder)
1936         {
1937             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1938             goto cleanup;
1939         }
1940         if (rc == ERROR_SUCCESS)
1941         {
1942             /* Read existing data */
1943             rc = RegQueryValueExW(
1944                 hGroupOrderListKey,
1945                 lpLoadOrderGroup,
1946                 NULL,
1947                 NULL,
1948                 (BYTE*)GroupOrder,
1949                 &bufferSize);
1950             if (rc != ERROR_SUCCESS)
1951             {
1952                 SetLastError(rc);
1953                 goto cleanup;
1954             }
1955             if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1956                 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1957         }
1958         else
1959         {
1960             GroupOrder[0] = 0;
1961         }
1962         GroupOrder[0]++;
1963         if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1964             GroupOrder[1] = tagId;
1965         else
1966             GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
1967 
1968         rc = RegSetValueExW(
1969             hGroupOrderListKey,
1970             lpLoadOrderGroup,
1971             0,
1972             REG_BINARY,
1973             (BYTE*)GroupOrder,
1974             bufferSize + sizeof(DWORD));
1975         if (rc != ERROR_SUCCESS)
1976         {
1977             SetLastError(rc);
1978             goto cleanup;
1979         }
1980     }
1981 
1982     /* Handle AddReg and DelReg */
1983     rc = RegOpenKeyExW(
1984         list ? list->HKLM : HKEY_LOCAL_MACHINE,
1985         REGSTR_PATH_SERVICES,
1986         0,
1987         0,
1988         &hServicesKey);
1989     if (rc != ERROR_SUCCESS)
1990     {
1991         SetLastError(rc);
1992         goto cleanup;
1993     }
1994     rc = RegOpenKeyExW(
1995         hServicesKey,
1996         ServiceName,
1997         0,
1998         KEY_READ | KEY_WRITE,
1999         &hServiceKey);
2000     RegCloseKey(hServicesKey);
2001     if (rc != ERROR_SUCCESS)
2002     {
2003         SetLastError(rc);
2004         goto cleanup;
2005     }
2006 
2007     ret = SetupInstallFromInfSectionW(
2008         NULL,
2009         hInf,
2010         ServiceSection,
2011         SPINST_REGISTRY,
2012         hServiceKey,
2013         NULL,
2014         0,
2015         NULL,
2016         NULL,
2017         NULL,
2018         NULL);
2019     RegCloseKey(hServiceKey);
2020 
2021 cleanup:
2022     if (hSCManager != NULL)
2023         CloseServiceHandle(hSCManager);
2024     if (hService != NULL)
2025         CloseServiceHandle(hService);
2026     if (hGroupOrderListKey != NULL)
2027         RegCloseKey(hGroupOrderListKey);
2028     if (sd != NULL)
2029         LocalFree(sd);
2030     MyFree(ServiceConfig);
2031     MyFree(ServiceBinary);
2032     MyFree(LoadOrderGroup);
2033     MyFree(DisplayName);
2034     MyFree(Description);
2035     MyFree(Dependencies);
2036     MyFree(SecurityDescriptor);
2037     MyFree(GroupOrder);
2038 
2039     TRACE("Returning %d\n", ret);
2040     return ret;
2041 }
2042 
2043 
2044 /***********************************************************************
2045  *		SetupInstallServicesFromInfSectionExW  (SETUPAPI.@)
2046  */
2047 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
2048 {
2049     struct DeviceInfoSet *list = NULL;
2050     BOOL ret = FALSE;
2051 
2052     TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
2053         flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
2054 
2055     if (!sectionname)
2056         SetLastError(ERROR_INVALID_PARAMETER);
2057     else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
2058     {
2059         TRACE("Unknown flags: 0x%08lx\n", flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE));
2060         SetLastError(ERROR_INVALID_FLAGS);
2061     }
2062     else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
2063         SetLastError(ERROR_INVALID_HANDLE);
2064     else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
2065         SetLastError(ERROR_INVALID_HANDLE);
2066     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
2067         SetLastError(ERROR_INVALID_USER_BUFFER);
2068     else if (reserved1 != NULL || reserved2 != NULL)
2069         SetLastError(ERROR_INVALID_PARAMETER);
2070     else
2071     {
2072         struct needs_callback_info needs_info;
2073         LPWSTR ServiceName = NULL;
2074         LPWSTR ServiceSection = NULL;
2075         INT ServiceFlags;
2076         INFCONTEXT ContextService;
2077         BOOL bNeedReboot = FALSE;
2078 
2079         /* Parse 'Include' and 'Needs' directives */
2080         iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
2081         needs_info.type = 1;
2082         needs_info.flags = flags;
2083         needs_info.devinfo = DeviceInfoSet;
2084         needs_info.devinfo_data = DeviceInfoData;
2085         needs_info.reserved1 = reserved1;
2086         needs_info.reserved2 = reserved2;
2087         iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
2088 
2089         if (flags & SPSVCINST_STOPSERVICE)
2090         {
2091             FIXME("Stopping the device not implemented\n");
2092             /* This may lead to require a reboot */
2093             /* bNeedReboot = TRUE; */
2094 #if 0
2095             SERVICE_STATUS ServiceStatus;
2096             ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
2097             if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
2098                 goto cleanup;
2099             if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
2100             {
2101                 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
2102                 goto cleanup;
2103             }
2104 #endif
2105             flags &= ~SPSVCINST_STOPSERVICE;
2106         }
2107 
2108         ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
2109         while (ret)
2110         {
2111             if (!GetStringField(&ContextService, 1, &ServiceName))
2112                 goto nextservice;
2113 
2114             ret = SetupGetIntField(
2115                 &ContextService,
2116                 2, /* Field index */
2117                 &ServiceFlags);
2118             if (!ret)
2119             {
2120                 /* The field may be empty. Ignore the error */
2121                 ServiceFlags = 0;
2122             }
2123 
2124             if (!GetStringField(&ContextService, 3, &ServiceSection))
2125                 goto nextservice;
2126 
2127             ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
2128             if (!ret)
2129                 goto nextservice;
2130 
2131             if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
2132             {
2133                 ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
2134                 if (!ret)
2135                     goto nextservice;
2136             }
2137 
2138 nextservice:
2139             HeapFree(GetProcessHeap(), 0, ServiceName);
2140             HeapFree(GetProcessHeap(), 0, ServiceSection);
2141             ServiceName = ServiceSection = NULL;
2142             ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
2143         }
2144 
2145         if (bNeedReboot)
2146             SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
2147         else
2148             SetLastError(ERROR_SUCCESS);
2149         ret = TRUE;
2150     }
2151 
2152     TRACE("Returning %d\n", ret);
2153     return ret;
2154 }
2155 
2156 
2157 /***********************************************************************
2158  *		SetupCopyOEMInfA  (SETUPAPI.@)
2159  */
2160 BOOL WINAPI SetupCopyOEMInfA(
2161         IN PCSTR SourceInfFileName,
2162         IN PCSTR OEMSourceMediaLocation,
2163         IN DWORD OEMSourceMediaType,
2164         IN DWORD CopyStyle,
2165         OUT PSTR DestinationInfFileName OPTIONAL,
2166         IN DWORD DestinationInfFileNameSize,
2167         OUT PDWORD RequiredSize OPTIONAL,
2168         OUT PSTR* DestinationInfFileNameComponent OPTIONAL)
2169 {
2170     PWSTR SourceInfFileNameW = NULL;
2171     PWSTR OEMSourceMediaLocationW = NULL;
2172     PWSTR DestinationInfFileNameW = NULL;
2173     PWSTR DestinationInfFileNameComponentW = NULL;
2174     BOOL ret = FALSE;
2175 
2176     TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2177         SourceInfFileName, OEMSourceMediaLocation, OEMSourceMediaType,
2178         CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2179         RequiredSize, DestinationInfFileNameComponent);
2180 
2181     if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2182         SetLastError(ERROR_INVALID_PARAMETER);
2183     else if (!(SourceInfFileNameW = pSetupMultiByteToUnicode(SourceInfFileName, CP_ACP)))
2184         SetLastError(ERROR_INVALID_PARAMETER);
2185     else if (OEMSourceMediaType != SPOST_NONE && !(OEMSourceMediaLocationW = pSetupMultiByteToUnicode(OEMSourceMediaLocation, CP_ACP)))
2186         SetLastError(ERROR_INVALID_PARAMETER);
2187     else
2188     {
2189         if (DestinationInfFileNameSize != 0)
2190         {
2191             DestinationInfFileNameW = MyMalloc(DestinationInfFileNameSize * sizeof(WCHAR));
2192             if (!DestinationInfFileNameW)
2193             {
2194                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2195                 goto cleanup;
2196             }
2197         }
2198 
2199         ret = SetupCopyOEMInfW(
2200             SourceInfFileNameW,
2201             OEMSourceMediaLocationW,
2202             OEMSourceMediaType,
2203             CopyStyle,
2204             DestinationInfFileNameW,
2205             DestinationInfFileNameSize,
2206             RequiredSize,
2207             DestinationInfFileNameComponent ? &DestinationInfFileNameComponentW : NULL);
2208         if (!ret)
2209             goto cleanup;
2210 
2211         if (DestinationInfFileNameSize != 0)
2212         {
2213             if (WideCharToMultiByte(CP_ACP, 0, DestinationInfFileNameW, -1,
2214                 DestinationInfFileName, DestinationInfFileNameSize, NULL, NULL) == 0)
2215             {
2216                 DestinationInfFileName[0] = '\0';
2217                 goto cleanup;
2218             }
2219         }
2220         if (DestinationInfFileNameComponent)
2221         {
2222             if (DestinationInfFileNameComponentW)
2223                 *DestinationInfFileNameComponent = &DestinationInfFileName[DestinationInfFileNameComponentW - DestinationInfFileNameW];
2224             else
2225                 *DestinationInfFileNameComponent = NULL;
2226         }
2227         ret = TRUE;
2228     }
2229 
2230 cleanup:
2231     MyFree(SourceInfFileNameW);
2232     MyFree(OEMSourceMediaLocationW);
2233     MyFree(DestinationInfFileNameW);
2234     TRACE("Returning %d\n", ret);
2235     if (ret) SetLastError(ERROR_SUCCESS);
2236     return ret;
2237 }
2238 
2239 static int compare_files( HANDLE file1, HANDLE file2 )
2240 {
2241     char buffer1[2048];
2242     char buffer2[2048];
2243     DWORD size1;
2244     DWORD size2;
2245 
2246     while( ReadFile(file1, buffer1, sizeof(buffer1), &size1, NULL) &&
2247            ReadFile(file2, buffer2, sizeof(buffer2), &size2, NULL) )
2248     {
2249         int ret;
2250         if (size1 != size2)
2251             return size1 > size2 ? 1 : -1;
2252         if (!size1)
2253             return 0;
2254         ret = memcmp( buffer1, buffer2, size1 );
2255         if (ret)
2256             return ret;
2257     }
2258 
2259     return 0;
2260 }
2261 
2262 /***********************************************************************
2263  *		SetupCopyOEMInfW  (SETUPAPI.@)
2264  */
2265 BOOL WINAPI SetupCopyOEMInfW(
2266         IN PCWSTR SourceInfFileName,
2267         IN PCWSTR OEMSourceMediaLocation,
2268         IN DWORD OEMSourceMediaType,
2269         IN DWORD CopyStyle,
2270         OUT PWSTR DestinationInfFileName OPTIONAL,
2271         IN DWORD DestinationInfFileNameSize,
2272         OUT PDWORD RequiredSize OPTIONAL,
2273         OUT PWSTR* DestinationInfFileNameComponent OPTIONAL)
2274 {
2275     BOOL ret = FALSE;
2276 
2277     TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2278         debugstr_w(SourceInfFileName), debugstr_w(OEMSourceMediaLocation), OEMSourceMediaType,
2279         CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2280         RequiredSize, DestinationInfFileNameComponent);
2281 
2282     if (!SourceInfFileName)
2283         SetLastError(ERROR_INVALID_PARAMETER);
2284     else if (OEMSourceMediaType != SPOST_NONE && OEMSourceMediaType != SPOST_PATH && OEMSourceMediaType != SPOST_URL)
2285         SetLastError(ERROR_INVALID_PARAMETER);
2286     else if (OEMSourceMediaType != SPOST_NONE && !OEMSourceMediaLocation)
2287         SetLastError(ERROR_INVALID_PARAMETER);
2288     else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
2289     {
2290         TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
2291         SetLastError(ERROR_INVALID_FLAGS);
2292     }
2293     else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2294         SetLastError(ERROR_INVALID_PARAMETER);
2295     else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
2296     {
2297         FIXME("CopyStyle 0x%x not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
2298         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2299     }
2300     else
2301     {
2302         HANDLE hSearch = INVALID_HANDLE_VALUE;
2303         WIN32_FIND_DATAW FindFileData;
2304         BOOL AlreadyExists;
2305         DWORD NextFreeNumber = 0;
2306         SIZE_T len;
2307         LPWSTR pFullFileName = NULL;
2308         LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2309         HANDLE hSourceFile = INVALID_HANDLE_VALUE;
2310 
2311         if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
2312             FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
2313 
2314         /* Check if source file exists, and open it */
2315         if (strchrW(SourceInfFileName, '\\' ) || strchrW(SourceInfFileName, '/' ))
2316         {
2317             WCHAR *path;
2318 
2319             if (!(len = GetFullPathNameW(SourceInfFileName, 0, NULL, NULL)))
2320                 return FALSE;
2321             if (!(path = MyMalloc(len * sizeof(WCHAR))))
2322             {
2323                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2324                 return FALSE;
2325             }
2326             GetFullPathNameW(SourceInfFileName, len, path, NULL);
2327             hSourceFile = CreateFileW(
2328                 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2329                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2330                 NULL, OPEN_EXISTING, 0, NULL);
2331             MyFree(path);
2332         }
2333         else  /* try Windows directory */
2334         {
2335             WCHAR *path, *p;
2336             static const WCHAR Inf[]      = {'\\','i','n','f','\\',0};
2337             static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
2338 
2339             len = GetWindowsDirectoryW(NULL, 0) + strlenW(SourceInfFileName) + 12;
2340             if (!(path = MyMalloc(len * sizeof(WCHAR))))
2341             {
2342                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2343                 return FALSE;
2344             }
2345             GetWindowsDirectoryW(path, len);
2346             p = path + strlenW(path);
2347             strcpyW(p, Inf);
2348             strcatW(p, SourceInfFileName);
2349             hSourceFile = CreateFileW(
2350                 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2351                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2352                 NULL, OPEN_EXISTING, 0, NULL);
2353             if (hSourceFile == INVALID_HANDLE_VALUE)
2354             {
2355                 strcpyW(p, System32);
2356                 strcatW(p, SourceInfFileName);
2357                 hSourceFile = CreateFileW(
2358                     path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2359                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2360                     NULL, OPEN_EXISTING, 0, NULL);
2361             }
2362             MyFree(path);
2363         }
2364         if (hSourceFile == INVALID_HANDLE_VALUE)
2365         {
2366             SetLastError(ERROR_FILE_NOT_FOUND);
2367             goto cleanup;
2368         }
2369 
2370         /* Prepare .inf file specification */
2371         len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
2372         pFullFileName = MyMalloc(len * sizeof(WCHAR));
2373         if (!pFullFileName)
2374         {
2375             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2376             goto cleanup;
2377         }
2378         len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2379         if (len == 0 || len > MAX_PATH)
2380             goto cleanup;
2381         if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2382             strcatW(pFullFileName, BackSlash);
2383         strcatW(pFullFileName, InfDirectory);
2384         pFileName = &pFullFileName[strlenW(pFullFileName)];
2385 
2386         /* Search if the specified .inf file already exists in %WINDIR%\Inf */
2387         AlreadyExists = FALSE;
2388         strcpyW(pFileName, OemFileMask);
2389         hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2390         if (hSearch != INVALID_HANDLE_VALUE)
2391         {
2392             LARGE_INTEGER SourceFileSize;
2393 
2394             if (GetFileSizeEx(hSourceFile, &SourceFileSize))
2395             {
2396                 do
2397                 {
2398                     LARGE_INTEGER DestFileSize;
2399                     HANDLE hDestFile;
2400 
2401                     strcpyW(pFileName, FindFileData.cFileName);
2402                     hDestFile = CreateFileW(
2403                         pFullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2404                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2405                         NULL, OPEN_EXISTING, 0, NULL);
2406                     if (hDestFile != INVALID_HANDLE_VALUE)
2407                     {
2408                         if (GetFileSizeEx(hDestFile, &DestFileSize)
2409                          && DestFileSize.QuadPart == SourceFileSize.QuadPart
2410                          && !compare_files(hSourceFile, hDestFile))
2411                         {
2412                             TRACE("%s already exists as %s\n",
2413                                 debugstr_w(SourceInfFileName), debugstr_w(pFileName));
2414                             AlreadyExists = TRUE;
2415                         }
2416                     }
2417                 } while (!AlreadyExists && FindNextFileW(hSearch, &FindFileData));
2418             }
2419             FindClose(hSearch);
2420             hSearch = INVALID_HANDLE_VALUE;
2421         }
2422 
2423         if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
2424         {
2425             /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
2426             SetLastError(ERROR_FILE_NOT_FOUND);
2427             goto cleanup;
2428         }
2429         else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
2430         {
2431             DWORD Size = strlenW(pFileName) + 1;
2432 
2433             if (RequiredSize)
2434                 *RequiredSize = Size;
2435             if (DestinationInfFileNameSize == 0)
2436                 SetLastError(ERROR_FILE_EXISTS);
2437             else if (DestinationInfFileNameSize < Size)
2438                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2439             else
2440             {
2441                 SetLastError(ERROR_FILE_EXISTS);
2442                 strcpyW(DestinationInfFileName, pFileName);
2443             }
2444             goto cleanup;
2445         }
2446 
2447         /* Search the number to give to OEM??.INF */
2448         strcpyW(pFileName, OemFileMask);
2449         hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2450         if (hSearch == INVALID_HANDLE_VALUE)
2451         {
2452             if (GetLastError() != ERROR_FILE_NOT_FOUND)
2453                 goto cleanup;
2454         }
2455         else
2456         {
2457             do
2458             {
2459                 DWORD CurrentNumber;
2460                 if (swscanf(FindFileData.cFileName, OemFileSpecification, &CurrentNumber) == 1
2461                     && CurrentNumber <= 99999)
2462                 {
2463                     if (CurrentNumber >= NextFreeNumber)
2464                         NextFreeNumber = CurrentNumber + 1;
2465                 }
2466             } while (FindNextFileW(hSearch, &FindFileData));
2467         }
2468 
2469         if (NextFreeNumber > 99999)
2470         {
2471             ERR("Too much custom .inf files\n");
2472             SetLastError(ERROR_GEN_FAILURE);
2473             goto cleanup;
2474         }
2475 
2476         /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
2477         sprintfW(pFileName, OemFileSpecification, NextFreeNumber);
2478         TRACE("Next available file is %s\n", debugstr_w(pFileName));
2479 
2480         if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
2481         {
2482             TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
2483             goto cleanup;
2484         }
2485 
2486         len = strlenW(pFullFileName) + 1;
2487         if (RequiredSize)
2488             *RequiredSize = len;
2489         if (DestinationInfFileName)
2490         {
2491             if (DestinationInfFileNameSize >= len)
2492             {
2493                 strcpyW(DestinationInfFileName, pFullFileName);
2494                 if (DestinationInfFileNameComponent)
2495                     *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
2496             }
2497             else
2498             {
2499                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2500                 goto cleanup;
2501             }
2502         }
2503 
2504         if (CopyStyle & SP_COPY_DELETESOURCE)
2505         {
2506             if (!DeleteFileW(SourceInfFileName))
2507             {
2508                 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
2509                 goto cleanup;
2510             }
2511         }
2512 
2513         ret = TRUE;
2514 
2515 cleanup:
2516         if (hSourceFile != INVALID_HANDLE_VALUE)
2517             CloseHandle(hSourceFile);
2518         if (hSearch != INVALID_HANDLE_VALUE)
2519             FindClose(hSearch);
2520         MyFree(pFullFileName);
2521     }
2522 
2523     TRACE("Returning %d\n", ret);
2524     if (ret) SetLastError(ERROR_SUCCESS);
2525     return ret;
2526 }
2527