xref: /reactos/dll/win32/setupapi/install.c (revision c2d0d784)
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         if (SUCCEEDED(hr))
1064             hr = IShellLinkW_SetPath(psl, FullFileName);
1065         if (SUCCEEDED(hr))
1066             hr = IShellLinkW_SetArguments(psl, L"");
1067         if (SUCCEEDED(hr))
1068             hr = IShellLinkW_SetWorkingDirectory(psl, FullWorkingDir);
1069         if (SUCCEEDED(hr))
1070             hr = IShellLinkW_SetIconLocation(psl, FullIconName, IconIdx);
1071         if (SUCCEEDED(hr) && lpHotKey)
1072             FIXME("Need to store hotkey %s in shell link\n", debugstr_w(lpHotKey));
1073         if (SUCCEEDED(hr) && lpInfoTip)
1074             hr = IShellLinkW_SetDescription(psl, lpInfoTip);
1075         if (SUCCEEDED(hr) && DisplayName)
1076             FIXME("Need to store display name %s, %d in shell link\n", debugstr_w(DisplayName), DisplayResId);
1077         if (SUCCEEDED(hr))
1078         {
1079             hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1080             if (SUCCEEDED(hr))
1081             {
1082                 Required = (MAX_PATH + wcslen(LinkSubDir) + 1 + wcslen(LinkName)) * sizeof(WCHAR);
1083                 FullLinkName = MyMalloc(Required);
1084                 if (!FullLinkName)
1085                     hr = E_OUTOFMEMORY;
1086                 else
1087                 {
1088                     if (LinkAttributes & (FLG_PROFITEM_DELETE | FLG_PROFITEM_GROUP))
1089                         FIXME("Need to handle FLG_PROFITEM_DELETE and FLG_PROFITEM_GROUP\n");
1090                     if (LinkAttributes & FLG_PROFITEM_CSIDL)
1091                         CSIDL = LinkFolder;
1092                     else if (LinkAttributes & FLG_PROFITEM_CURRENTUSER)
1093                         CSIDL = CSIDL_PROGRAMS;
1094 
1095                     if (SHGetSpecialFolderPathW(
1096                         NULL,
1097                         FullLinkName,
1098                         CSIDL,
1099                         TRUE))
1100                     {
1101                         if (FullLinkName[wcslen(FullLinkName) - 1] != '\\')
1102                             wcscat(FullLinkName, BackSlash);
1103                         if (LinkSubDir)
1104                         {
1105                             wcscat(FullLinkName, LinkSubDir);
1106                             if (FullLinkName[wcslen(FullLinkName) - 1] != '\\')
1107                                 wcscat(FullLinkName, BackSlash);
1108                         }
1109                         wcscat(FullLinkName, LinkName);
1110                         wcscat(FullLinkName, DotLnk);
1111                         hr = IPersistFile_Save(ppf, FullLinkName, TRUE);
1112                     }
1113                     else
1114                         hr = HRESULT_FROM_WIN32(GetLastError());
1115                 }
1116                 IPersistFile_Release(ppf);
1117             }
1118         }
1119         IShellLinkW_Release(psl);
1120     }
1121     pCoUninitialize();
1122     if (SUCCEEDED(hr))
1123         ret = TRUE;
1124     else
1125     {
1126         if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
1127             SetLastError(HRESULT_CODE(hr));
1128         else
1129             SetLastError(E_FAIL);
1130     }
1131 
1132 cleanup:
1133     MyFree(LinkSubDir);
1134     MyFree(LinkName);
1135     MyFree(FileSubDir);
1136     MyFree(SubDirPart);
1137     MyFree(NamePart);
1138     MyFree(FullFileName);
1139     MyFree(FullWorkingDir);
1140     MyFree(FullIconName);
1141     MyFree(FullLinkName);
1142     MyFree(lpHotKey);
1143     MyFree(lpInfoTip);
1144     MyFree(DisplayName);
1145     if (hOle32)
1146         FreeLibrary(hOle32);
1147 
1148     TRACE("Returning %d\n", ret);
1149     return ret;
1150 }
1151 
1152 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
1153 {
1154     FIXME( "should do copy inf %s\n", debugstr_w(field) );
1155     return TRUE;
1156 }
1157 
1158 
1159 /***********************************************************************
1160  *            iterate_section_fields
1161  *
1162  * Iterate over all fields of a certain key of a certain section
1163  */
1164 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
1165                                     iterate_fields_func callback, void *arg )
1166 {
1167     WCHAR static_buffer[200];
1168     WCHAR *buffer = static_buffer;
1169     DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
1170     INFCONTEXT context;
1171     BOOL ret = FALSE;
1172 
1173     BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
1174     while (ok)
1175     {
1176         UINT i, count = SetupGetFieldCount( &context );
1177         for (i = 1; i <= count; i++)
1178         {
1179             if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
1180                 goto done;
1181             if (!callback( hinf, buffer, arg ))
1182             {
1183                 WARN("callback failed for %s %s err %d\n",
1184                      debugstr_w(section), debugstr_w(buffer), GetLastError() );
1185                 goto done;
1186             }
1187         }
1188         ok = SetupFindNextMatchLineW( &context, key, &context );
1189     }
1190     ret = TRUE;
1191  done:
1192     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
1193     return ret;
1194 }
1195 
1196 
1197 /***********************************************************************
1198  *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
1199  */
1200 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
1201                                               PCSTR section, PCSTR src_root, UINT flags )
1202 {
1203     UNICODE_STRING sectionW;
1204     BOOL ret = FALSE;
1205 
1206     if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1207     {
1208         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1209         return FALSE;
1210     }
1211     if (!src_root)
1212         ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
1213                                                 NULL, flags );
1214     else
1215     {
1216         UNICODE_STRING srcW;
1217         if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
1218         {
1219             ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
1220                                                     srcW.Buffer, flags );
1221             RtlFreeUnicodeString( &srcW );
1222         }
1223         else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1224     }
1225     RtlFreeUnicodeString( &sectionW );
1226     return ret;
1227 }
1228 
1229 
1230 /***********************************************************************
1231  *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
1232  */
1233 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
1234                                               PCWSTR section, PCWSTR src_root, UINT flags )
1235 {
1236     struct files_callback_info info;
1237 
1238     info.queue      = queue;
1239     info.src_root   = src_root;
1240     info.copy_flags = flags;
1241     info.layout     = hlayout;
1242     return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
1243 }
1244 
1245 
1246 /***********************************************************************
1247  *            SetupInstallFromInfSectionA   (SETUPAPI.@)
1248  */
1249 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
1250                                          HKEY key_root, PCSTR src_root, UINT copy_flags,
1251                                          PSP_FILE_CALLBACK_A callback, PVOID context,
1252                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1253 {
1254     UNICODE_STRING sectionW, src_rootW;
1255     struct callback_WtoA_context ctx;
1256     BOOL ret = FALSE;
1257 
1258     src_rootW.Buffer = NULL;
1259     if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
1260     {
1261         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1262         return FALSE;
1263     }
1264 
1265     if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
1266     {
1267         ctx.orig_context = context;
1268         ctx.orig_handler = callback;
1269         ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
1270                                            src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
1271                                            &ctx, devinfo, devinfo_data );
1272         RtlFreeUnicodeString( &sectionW );
1273     }
1274     else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1275 
1276     RtlFreeUnicodeString( &src_rootW );
1277     return ret;
1278 }
1279 
1280 
1281 /***********************************************************************
1282  *            include_callback
1283  *
1284  * Called once for each Include entry in a given section.
1285  */
1286 static BOOL include_callback( HINF hinf, PCWSTR field, void *arg )
1287 {
1288     return SetupOpenAppendInfFileW( field, hinf, NULL );
1289 }
1290 
1291 
1292 /***********************************************************************
1293  *            needs_callback
1294  *
1295  * Called once for each Needs entry in a given section.
1296  */
1297 static BOOL needs_callback( HINF hinf, PCWSTR field, void *arg )
1298 {
1299     struct needs_callback_info *info = arg;
1300 
1301     switch (info->type)
1302     {
1303         case 0:
1304             return SetupInstallFromInfSectionW(info->owner, *(HINF*)hinf, field, info->flags,
1305                info->key_root, info->src_root, info->copy_flags, info->callback,
1306                info->context, info->devinfo, info->devinfo_data);
1307         case 1:
1308             return SetupInstallServicesFromInfSectionExW(*(HINF*)hinf, field, info->flags,
1309                 info->devinfo, info->devinfo_data, info->reserved1, info->reserved2);
1310         default:
1311             ERR("Unknown info type %u\n", info->type);
1312             return FALSE;
1313     }
1314 }
1315 
1316 
1317 /***********************************************************************
1318  *            SetupInstallFromInfSectionW   (SETUPAPI.@)
1319  */
1320 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
1321                                          HKEY key_root, PCWSTR src_root, UINT copy_flags,
1322                                          PSP_FILE_CALLBACK_W callback, PVOID context,
1323                                          HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
1324 {
1325     struct needs_callback_info needs_info;
1326 
1327     /* Parse 'Include' and 'Needs' directives */
1328     iterate_section_fields( hinf, section, Include, include_callback, NULL);
1329     needs_info.type = 0;
1330     needs_info.owner = owner;
1331     needs_info.flags = flags;
1332     needs_info.key_root = key_root;
1333     needs_info.src_root = src_root;
1334     needs_info.copy_flags = copy_flags;
1335     needs_info.callback = callback;
1336     needs_info.context = context;
1337     needs_info.devinfo = devinfo;
1338     needs_info.devinfo_data = devinfo_data;
1339     iterate_section_fields( hinf, section, Needs, needs_callback, &needs_info);
1340 
1341     if (flags & SPINST_FILES)
1342     {
1343         SP_DEVINSTALL_PARAMS_W install_params;
1344         struct files_callback_info info;
1345         HSPFILEQ queue = NULL;
1346         BOOL use_custom_queue;
1347         BOOL ret;
1348 
1349         install_params.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
1350         use_custom_queue = SetupDiGetDeviceInstallParamsW(devinfo, devinfo_data, &install_params) && (install_params.Flags & DI_NOVCP);
1351         if (!use_custom_queue && ((queue = SetupOpenFileQueue()) == (HSPFILEQ)INVALID_HANDLE_VALUE ))
1352             return FALSE;
1353         info.queue      = use_custom_queue ? install_params.FileQueue : queue;
1354         info.src_root   = src_root;
1355         info.copy_flags = copy_flags;
1356         info.layout     = hinf;
1357         ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
1358                iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
1359                iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ));
1360         if (!use_custom_queue)
1361         {
1362             if (ret)
1363                 ret = SetupCommitFileQueueW( owner, queue, callback, context );
1364             SetupCloseFileQueue( queue );
1365         }
1366         if (!ret) return FALSE;
1367     }
1368     if (flags & SPINST_INIFILES)
1369     {
1370         if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
1371             !iterate_section_fields( hinf, section, UpdateIniFields,
1372                                      update_ini_fields_callback, NULL ))
1373             return FALSE;
1374     }
1375     if (flags & SPINST_INI2REG)
1376     {
1377         if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
1378             return FALSE;
1379     }
1380     if (flags & SPINST_LOGCONFIG)
1381     {
1382         if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
1383             return FALSE;
1384     }
1385     if (flags & SPINST_REGSVR)
1386     {
1387         struct register_dll_info info;
1388 
1389         info.unregister = FALSE;
1390         if (flags & SPINST_REGISTERCALLBACKAWARE)
1391         {
1392             info.callback         = callback;
1393             info.callback_context = context;
1394         }
1395         else info.callback = NULL;
1396 
1397         if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
1398             return FALSE;
1399 
1400 #ifdef __WINESRC__
1401         if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
1402             return FALSE;
1403 #endif // __WINESRC__
1404     }
1405     if (flags & SPINST_UNREGSVR)
1406     {
1407         struct register_dll_info info;
1408 
1409         info.unregister = TRUE;
1410         if (flags & SPINST_REGISTERCALLBACKAWARE)
1411         {
1412             info.callback         = callback;
1413             info.callback_context = context;
1414         }
1415         else info.callback = NULL;
1416 
1417         if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
1418             return FALSE;
1419     }
1420     if (flags & SPINST_REGISTRY)
1421     {
1422         struct registry_callback_info info;
1423 
1424         info.default_root = key_root;
1425         info.delete = TRUE;
1426         if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
1427             return FALSE;
1428         info.delete = FALSE;
1429         if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
1430             return FALSE;
1431     }
1432     if (flags & SPINST_BITREG)
1433     {
1434         if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
1435             return FALSE;
1436     }
1437     if (flags & SPINST_PROFILEITEMS)
1438     {
1439         if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
1440             return FALSE;
1441     }
1442     if (flags & SPINST_COPYINF)
1443     {
1444         if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
1445             return FALSE;
1446     }
1447 
1448     return TRUE;
1449 }
1450 
1451 
1452 /***********************************************************************
1453  *		InstallHinfSectionW  (SETUPAPI.@)
1454  *
1455  * NOTE: 'cmdline' is <section> <mode> <path> from
1456  *   RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
1457  */
1458 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
1459 {
1460     WCHAR *s, *path, section[MAX_PATH];
1461     void *callback_context = NULL;
1462     DWORD SectionNameLength;
1463     UINT mode;
1464     HINF hinf = INVALID_HANDLE_VALUE;
1465     BOOL bRebootRequired = FALSE;
1466     BOOL ret;
1467 
1468     TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1469 
1470     lstrcpynW( section, cmdline, MAX_PATH );
1471 
1472     if (!(s = strchrW( section, ' ' ))) return;
1473     *s++ = 0;
1474     while (*s == ' ') s++;
1475     mode = atoiW( s );
1476 
1477     /* quoted paths are not allowed on native, the rest of the command line is taken as the path */
1478     if (!(s = strchrW( s, ' ' ))) return;
1479     while (*s == ' ') s++;
1480     path = s;
1481 
1482     if (mode & 0x80)
1483     {
1484         FIXME("default path of the installation not changed\n");
1485         mode &= ~0x80;
1486     }
1487 
1488     hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1489     if (hinf == INVALID_HANDLE_VALUE)
1490     {
1491         WARN("SetupOpenInfFileW(%s) failed (Error %u)\n", path, GetLastError());
1492         goto cleanup;
1493     }
1494 
1495     ret = SetupDiGetActualSectionToInstallW(
1496        hinf, section, section, sizeof(section)/sizeof(section[0]), &SectionNameLength, NULL );
1497     if (!ret)
1498     {
1499         WARN("SetupDiGetActualSectionToInstallW() failed (Error %u)\n", GetLastError());
1500         goto cleanup;
1501     }
1502     if (SectionNameLength > MAX_PATH - strlenW(DotServices))
1503     {
1504         WARN("Section name '%s' too long\n", section);
1505         goto cleanup;
1506     }
1507 
1508     /* Copy files and add registry entries */
1509     callback_context = SetupInitDefaultQueueCallback( hwnd );
1510     ret = SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL,
1511                                        SP_COPY_NEWER | SP_COPY_IN_USE_NEEDS_REBOOT,
1512                                        SetupDefaultQueueCallbackW, callback_context,
1513                                        NULL, NULL );
1514     if (!ret)
1515     {
1516         WARN("SetupInstallFromInfSectionW() failed (Error %u)\n", GetLastError());
1517         goto cleanup;
1518     }
1519     /* FIXME: need to check if some files were in use and need reboot
1520      * bReboot = ...;
1521      */
1522 
1523     /* Install services */
1524     wcscat(section, DotServices);
1525     ret = SetupInstallServicesFromInfSectionW( hinf, section, 0 );
1526     if (!ret && GetLastError() == ERROR_SECTION_NOT_FOUND)
1527         ret = TRUE;
1528     if (!ret)
1529     {
1530         WARN("SetupInstallServicesFromInfSectionW() failed (Error %u)\n", GetLastError());
1531         goto cleanup;
1532     }
1533     else if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
1534     {
1535         bRebootRequired = TRUE;
1536     }
1537 
1538     /* Check if we need to reboot */
1539     switch (mode)
1540     {
1541         case 0:
1542             /* Never reboot */
1543             break;
1544         case 1:
1545             /* Always reboot */
1546             ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
1547                 SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED);
1548             break;
1549         case 2:
1550             /* Query user before rebooting */
1551             SetupPromptReboot(NULL, hwnd, FALSE);
1552             break;
1553         case 3:
1554             /* Reboot if necessary */
1555             if (bRebootRequired)
1556             {
1557                 ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
1558                     SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED);
1559             }
1560             break;
1561         case 4:
1562             /* If necessary, query user before rebooting */
1563             if (bRebootRequired)
1564             {
1565                 SetupPromptReboot(NULL, hwnd, FALSE);
1566             }
1567             break;
1568         default:
1569             break;
1570     }
1571 
1572 cleanup:
1573     if ( callback_context )
1574         SetupTermDefaultQueueCallback( callback_context );
1575     if ( hinf != INVALID_HANDLE_VALUE )
1576         SetupCloseInfFile( hinf );
1577 }
1578 
1579 
1580 /***********************************************************************
1581  *		InstallHinfSectionA  (SETUPAPI.@)
1582  */
1583 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1584 {
1585     UNICODE_STRING cmdlineW;
1586 
1587     if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1588     {
1589         InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1590         RtlFreeUnicodeString( &cmdlineW );
1591     }
1592 }
1593 
1594 /***********************************************************************
1595  *              SetupInstallServicesFromInfSectionW  (SETUPAPI.@)
1596  */
1597 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF Inf, PCWSTR SectionName, DWORD Flags)
1598 {
1599     return SetupInstallServicesFromInfSectionExW( Inf, SectionName, Flags,
1600                                                   NULL, NULL, NULL, NULL );
1601 }
1602 
1603 /***********************************************************************
1604  *              SetupInstallServicesFromInfSectionA  (SETUPAPI.@)
1605  */
1606 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF Inf, PCSTR SectionName, DWORD Flags)
1607 {
1608     return SetupInstallServicesFromInfSectionExA( Inf, SectionName, Flags,
1609                                                   NULL, NULL, NULL, NULL );
1610 }
1611 
1612 /***********************************************************************
1613  *		SetupInstallServicesFromInfSectionExA  (SETUPAPI.@)
1614  */
1615 BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
1616 {
1617     UNICODE_STRING sectionnameW;
1618     BOOL ret = FALSE;
1619 
1620     if (RtlCreateUnicodeStringFromAsciiz( &sectionnameW, sectionname ))
1621     {
1622         ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
1623         RtlFreeUnicodeString( &sectionnameW );
1624     }
1625     else
1626         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1627 
1628     return ret;
1629 }
1630 
1631 
1632 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
1633 {
1634     DWORD required;
1635     PWSTR buf = NULL;
1636 
1637     *value = NULL;
1638 
1639     if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
1640         && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
1641         return FALSE;
1642 
1643     buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
1644     if ( ! buf )
1645     {
1646         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1647         return FALSE;
1648     }
1649 
1650     if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
1651     {
1652         HeapFree( GetProcessHeap(), 0, buf );
1653         return FALSE;
1654     }
1655 
1656     *value = buf;
1657     return TRUE;
1658 }
1659 
1660 
1661 static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
1662 {
1663     LPWSTR buffer, end;
1664     INT res;
1665 
1666     if (! GetLineText( hinf, section_name, key_name, &buffer ) )
1667         return FALSE;
1668 
1669     res = wcstol( buffer, &end, 0 );
1670     if (end != buffer && !*end)
1671     {
1672         HeapFree(GetProcessHeap(), 0, buffer);
1673         *value = res;
1674         return TRUE;
1675     }
1676     else
1677     {
1678         HeapFree(GetProcessHeap(), 0, buffer);
1679         SetLastError( ERROR_INVALID_DATA );
1680         return FALSE;
1681     }
1682 }
1683 
1684 
1685 BOOL GetStringField( PINFCONTEXT context, DWORD index, PWSTR *value)
1686 {
1687     DWORD RequiredSize;
1688     BOOL ret;
1689 
1690     ret = SetupGetStringFieldW(
1691         context,
1692         index,
1693         NULL, 0,
1694         &RequiredSize);
1695     if (!ret)
1696         return FALSE;
1697     else if (RequiredSize == 0)
1698     {
1699         *value = NULL;
1700         return TRUE;
1701     }
1702 
1703     /* We got the needed size for the buffer */
1704     *value = MyMalloc(RequiredSize * sizeof(WCHAR));
1705     if (!*value)
1706     {
1707         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1708         return FALSE;
1709     }
1710     ret = SetupGetStringFieldW(
1711         context,
1712         index,
1713         *value, RequiredSize, NULL);
1714     if (!ret)
1715         MyFree(*value);
1716 
1717     return ret;
1718 }
1719 
1720 static VOID FixupServiceBinaryPath(
1721     IN DWORD ServiceType,
1722     IN OUT LPWSTR *ServiceBinary)
1723 {
1724     LPWSTR Buffer;
1725     WCHAR ReactosDir[MAX_PATH];
1726     DWORD RosDirLength, ServiceLength, Win32Length;
1727 
1728     GetWindowsDirectoryW(ReactosDir, MAX_PATH);
1729     RosDirLength = strlenW(ReactosDir);
1730     ServiceLength = strlenW(*ServiceBinary);
1731 
1732     /* Check and fix two things:
1733        1. Get rid of C:\ReactOS and use relative
1734           path instead.
1735        2. Add %SystemRoot% for Win32 services */
1736 
1737     if (ServiceLength < RosDirLength)
1738         return;
1739 
1740     if (!wcsnicmp(*ServiceBinary, ReactosDir, RosDirLength))
1741     {
1742         /* Yes, the first part is the C:\ReactOS\, just skip it */
1743         MoveMemory(*ServiceBinary, *ServiceBinary + RosDirLength + 1,
1744             (ServiceLength - RosDirLength) * sizeof(WCHAR));
1745 
1746         /* Handle Win32-services differently */
1747         if (ServiceType & SERVICE_WIN32)
1748         {
1749             Win32Length = (ServiceLength -
1750                 RosDirLength - 1 + 13) * sizeof(WCHAR);
1751             /* -1 to not count the separator after C:\ReactOS
1752                wcslen(L"%SystemRoot%\\") = 13*sizeof(wchar_t) */
1753             Buffer = MyMalloc(Win32Length);
1754 
1755             wcscpy(Buffer, L"%SystemRoot%\\");
1756             wcscat(Buffer, *ServiceBinary);
1757             MyFree(*ServiceBinary);
1758 
1759             *ServiceBinary = Buffer;
1760         }
1761     }
1762 }
1763 
1764 static BOOL InstallOneService(
1765     struct DeviceInfoSet *list,
1766     IN HINF hInf,
1767     IN LPCWSTR ServiceSection,
1768     IN LPCWSTR ServiceName,
1769     IN UINT ServiceFlags)
1770 {
1771     SC_HANDLE hSCManager = NULL;
1772     SC_HANDLE hService = NULL;
1773     LPDWORD GroupOrder = NULL;
1774     LPQUERY_SERVICE_CONFIGW ServiceConfig = NULL;
1775     HKEY hServicesKey, hServiceKey;
1776     LONG rc;
1777     BOOL ret = FALSE;
1778 
1779     HKEY hGroupOrderListKey = NULL;
1780     LPWSTR ServiceBinary = NULL;
1781     LPWSTR LoadOrderGroup = NULL;
1782     LPWSTR DisplayName = NULL;
1783     LPWSTR Description = NULL;
1784     LPWSTR Dependencies = NULL;
1785     LPWSTR SecurityDescriptor = NULL;
1786     PSECURITY_DESCRIPTOR sd = NULL;
1787     INT ServiceType, StartType, ErrorControl;
1788     DWORD dwRegType;
1789     DWORD tagId = (DWORD)-1;
1790     BOOL useTag;
1791 
1792     if (!GetIntField(hInf, ServiceSection, ServiceTypeKey, &ServiceType))
1793         goto cleanup;
1794     if (!GetIntField(hInf, ServiceSection, StartTypeKey, &StartType))
1795         goto cleanup;
1796     if (!GetIntField(hInf, ServiceSection, ErrorControlKey, &ErrorControl))
1797         goto cleanup;
1798     useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1799 
1800     hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1801     if (hSCManager == NULL)
1802         goto cleanup;
1803 
1804     if (!GetLineText(hInf, ServiceSection, ServiceBinaryKey, &ServiceBinary))
1805         goto cleanup;
1806 
1807     /* Adjust binary path according to the service type */
1808     FixupServiceBinaryPath(ServiceType, &ServiceBinary);
1809 
1810     /* Don't check return value, as these fields are optional and
1811      * GetLineText initialize output parameter even on failure */
1812     GetLineText(hInf, ServiceSection, LoadOrderGroupKey, &LoadOrderGroup);
1813     GetLineText(hInf, ServiceSection, DisplayNameKey, &DisplayName);
1814     GetLineText(hInf, ServiceSection, DescriptionKey, &Description);
1815     GetLineText(hInf, ServiceSection, DependenciesKey, &Dependencies);
1816 
1817     /* If there is no group, we must not request a tag */
1818     if (!LoadOrderGroup || !*LoadOrderGroup)
1819         useTag = FALSE;
1820 
1821     hService = OpenServiceW(
1822         hSCManager,
1823         ServiceName,
1824         DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | WRITE_DAC);
1825     if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1826         goto cleanup;
1827 
1828     if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
1829     {
1830         ret = DeleteService(hService);
1831         if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
1832             goto cleanup;
1833     }
1834 
1835     if (hService == NULL)
1836     {
1837         /* Create new service */
1838         hService = CreateServiceW(
1839             hSCManager,
1840             ServiceName,
1841             DisplayName,
1842             WRITE_DAC,
1843             ServiceType,
1844             StartType,
1845             ErrorControl,
1846             ServiceBinary,
1847             LoadOrderGroup,
1848             useTag ? &tagId : NULL,
1849             Dependencies,
1850             NULL, NULL);
1851         if (hService == NULL)
1852             goto cleanup;
1853     }
1854     else
1855     {
1856         DWORD bufferSize;
1857         /* Read current configuration */
1858         if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1859         {
1860             if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1861                 goto cleanup;
1862             ServiceConfig = MyMalloc(bufferSize);
1863             if (!ServiceConfig)
1864             {
1865                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1866                 goto cleanup;
1867             }
1868             if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1869                 goto cleanup;
1870         }
1871         tagId = ServiceConfig->dwTagId;
1872 
1873         /* Update configuration */
1874         ret = ChangeServiceConfigW(
1875             hService,
1876             ServiceType,
1877             (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1878             (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1879             ServiceBinary,
1880             (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1881             useTag ? &tagId : NULL,
1882             (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1883             NULL, NULL,
1884             (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1885         if (!ret)
1886             goto cleanup;
1887     }
1888 
1889     /* Set security */
1890     if (GetLineText(hInf, ServiceSection, SecurityKey, &SecurityDescriptor))
1891     {
1892         ret = ConvertStringSecurityDescriptorToSecurityDescriptorW(SecurityDescriptor, SDDL_REVISION_1, &sd, NULL);
1893         if (!ret)
1894             goto cleanup;
1895         ret = SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd);
1896         if (!ret)
1897             goto cleanup;
1898     }
1899 
1900     /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1901 
1902     if (useTag)
1903     {
1904         /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1905         LPCWSTR lpLoadOrderGroup;
1906         DWORD bufferSize;
1907 
1908         lpLoadOrderGroup = LoadOrderGroup;
1909         if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1910             lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1911 
1912         rc = RegOpenKeyW(
1913             list ? list->HKLM : HKEY_LOCAL_MACHINE,
1914             GroupOrderListKey,
1915             &hGroupOrderListKey);
1916         if (rc != ERROR_SUCCESS)
1917         {
1918             SetLastError(rc);
1919             goto cleanup;
1920         }
1921         rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1922         if (rc == ERROR_FILE_NOT_FOUND)
1923             bufferSize = sizeof(DWORD);
1924         else if (rc != ERROR_SUCCESS)
1925         {
1926             SetLastError(rc);
1927             goto cleanup;
1928         }
1929         else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1930         {
1931             SetLastError(ERROR_GEN_FAILURE);
1932             goto cleanup;
1933         }
1934         /* Allocate buffer to store existing data + the new tag */
1935         GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
1936         if (!GroupOrder)
1937         {
1938             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1939             goto cleanup;
1940         }
1941         if (rc == ERROR_SUCCESS)
1942         {
1943             /* Read existing data */
1944             rc = RegQueryValueExW(
1945                 hGroupOrderListKey,
1946                 lpLoadOrderGroup,
1947                 NULL,
1948                 NULL,
1949                 (BYTE*)GroupOrder,
1950                 &bufferSize);
1951             if (rc != ERROR_SUCCESS)
1952             {
1953                 SetLastError(rc);
1954                 goto cleanup;
1955             }
1956             if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1957                 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1958         }
1959         else
1960         {
1961             GroupOrder[0] = 0;
1962         }
1963         GroupOrder[0]++;
1964         if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1965             GroupOrder[1] = tagId;
1966         else
1967             GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
1968 
1969         rc = RegSetValueExW(
1970             hGroupOrderListKey,
1971             lpLoadOrderGroup,
1972             0,
1973             REG_BINARY,
1974             (BYTE*)GroupOrder,
1975             bufferSize + sizeof(DWORD));
1976         if (rc != ERROR_SUCCESS)
1977         {
1978             SetLastError(rc);
1979             goto cleanup;
1980         }
1981     }
1982 
1983     /* Handle AddReg and DelReg */
1984     rc = RegOpenKeyExW(
1985         list ? list->HKLM : HKEY_LOCAL_MACHINE,
1986         REGSTR_PATH_SERVICES,
1987         0,
1988         0,
1989         &hServicesKey);
1990     if (rc != ERROR_SUCCESS)
1991     {
1992         SetLastError(rc);
1993         goto cleanup;
1994     }
1995     rc = RegOpenKeyExW(
1996         hServicesKey,
1997         ServiceName,
1998         0,
1999         KEY_READ | KEY_WRITE,
2000         &hServiceKey);
2001     RegCloseKey(hServicesKey);
2002     if (rc != ERROR_SUCCESS)
2003     {
2004         SetLastError(rc);
2005         goto cleanup;
2006     }
2007 
2008     ret = SetupInstallFromInfSectionW(
2009         NULL,
2010         hInf,
2011         ServiceSection,
2012         SPINST_REGISTRY,
2013         hServiceKey,
2014         NULL,
2015         0,
2016         NULL,
2017         NULL,
2018         NULL,
2019         NULL);
2020     RegCloseKey(hServiceKey);
2021 
2022 cleanup:
2023     if (hSCManager != NULL)
2024         CloseServiceHandle(hSCManager);
2025     if (hService != NULL)
2026         CloseServiceHandle(hService);
2027     if (hGroupOrderListKey != NULL)
2028         RegCloseKey(hGroupOrderListKey);
2029     if (sd != NULL)
2030         LocalFree(sd);
2031     MyFree(ServiceConfig);
2032     MyFree(ServiceBinary);
2033     MyFree(LoadOrderGroup);
2034     MyFree(DisplayName);
2035     MyFree(Description);
2036     MyFree(Dependencies);
2037     MyFree(SecurityDescriptor);
2038     MyFree(GroupOrder);
2039 
2040     TRACE("Returning %d\n", ret);
2041     return ret;
2042 }
2043 
2044 
2045 /***********************************************************************
2046  *		SetupInstallServicesFromInfSectionExW  (SETUPAPI.@)
2047  */
2048 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
2049 {
2050     struct DeviceInfoSet *list = NULL;
2051     BOOL ret = FALSE;
2052 
2053     TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
2054         flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
2055 
2056     if (!sectionname)
2057         SetLastError(ERROR_INVALID_PARAMETER);
2058     else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
2059     {
2060         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));
2061         SetLastError(ERROR_INVALID_FLAGS);
2062     }
2063     else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
2064         SetLastError(ERROR_INVALID_HANDLE);
2065     else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
2066         SetLastError(ERROR_INVALID_HANDLE);
2067     else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
2068         SetLastError(ERROR_INVALID_USER_BUFFER);
2069     else if (reserved1 != NULL || reserved2 != NULL)
2070         SetLastError(ERROR_INVALID_PARAMETER);
2071     else
2072     {
2073         struct needs_callback_info needs_info;
2074         LPWSTR ServiceName = NULL;
2075         LPWSTR ServiceSection = NULL;
2076         INT ServiceFlags;
2077         INFCONTEXT ContextService;
2078         BOOL bNeedReboot = FALSE;
2079 
2080         /* Parse 'Include' and 'Needs' directives */
2081         iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
2082         needs_info.type = 1;
2083         needs_info.flags = flags;
2084         needs_info.devinfo = DeviceInfoSet;
2085         needs_info.devinfo_data = DeviceInfoData;
2086         needs_info.reserved1 = reserved1;
2087         needs_info.reserved2 = reserved2;
2088         iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
2089 
2090         if (flags & SPSVCINST_STOPSERVICE)
2091         {
2092             FIXME("Stopping the device not implemented\n");
2093             /* This may lead to require a reboot */
2094             /* bNeedReboot = TRUE; */
2095 #if 0
2096             SERVICE_STATUS ServiceStatus;
2097             ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
2098             if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
2099                 goto cleanup;
2100             if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
2101             {
2102                 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
2103                 goto cleanup;
2104             }
2105 #endif
2106             flags &= ~SPSVCINST_STOPSERVICE;
2107         }
2108 
2109         ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
2110         while (ret)
2111         {
2112             if (!GetStringField(&ContextService, 1, &ServiceName))
2113                 goto nextservice;
2114 
2115             ret = SetupGetIntField(
2116                 &ContextService,
2117                 2, /* Field index */
2118                 &ServiceFlags);
2119             if (!ret)
2120             {
2121                 /* The field may be empty. Ignore the error */
2122                 ServiceFlags = 0;
2123             }
2124 
2125             if (!GetStringField(&ContextService, 3, &ServiceSection))
2126                 goto nextservice;
2127 
2128             ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
2129             if (!ret)
2130                 goto nextservice;
2131 
2132             if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
2133             {
2134                 ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
2135                 if (!ret)
2136                     goto nextservice;
2137             }
2138 
2139 nextservice:
2140             HeapFree(GetProcessHeap(), 0, ServiceName);
2141             HeapFree(GetProcessHeap(), 0, ServiceSection);
2142             ServiceName = ServiceSection = NULL;
2143             ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
2144         }
2145 
2146         if (bNeedReboot)
2147             SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
2148         else
2149             SetLastError(ERROR_SUCCESS);
2150         ret = TRUE;
2151     }
2152 
2153     TRACE("Returning %d\n", ret);
2154     return ret;
2155 }
2156 
2157 
2158 /***********************************************************************
2159  *		SetupCopyOEMInfA  (SETUPAPI.@)
2160  */
2161 BOOL WINAPI SetupCopyOEMInfA(
2162         IN PCSTR SourceInfFileName,
2163         IN PCSTR OEMSourceMediaLocation,
2164         IN DWORD OEMSourceMediaType,
2165         IN DWORD CopyStyle,
2166         OUT PSTR DestinationInfFileName OPTIONAL,
2167         IN DWORD DestinationInfFileNameSize,
2168         OUT PDWORD RequiredSize OPTIONAL,
2169         OUT PSTR* DestinationInfFileNameComponent OPTIONAL)
2170 {
2171     PWSTR SourceInfFileNameW = NULL;
2172     PWSTR OEMSourceMediaLocationW = NULL;
2173     PWSTR DestinationInfFileNameW = NULL;
2174     PWSTR DestinationInfFileNameComponentW = NULL;
2175     BOOL ret = FALSE;
2176 
2177     TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2178         SourceInfFileName, OEMSourceMediaLocation, OEMSourceMediaType,
2179         CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2180         RequiredSize, DestinationInfFileNameComponent);
2181 
2182     if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2183         SetLastError(ERROR_INVALID_PARAMETER);
2184     else if (!(SourceInfFileNameW = pSetupMultiByteToUnicode(SourceInfFileName, CP_ACP)))
2185         SetLastError(ERROR_INVALID_PARAMETER);
2186     else if (OEMSourceMediaType != SPOST_NONE && !(OEMSourceMediaLocationW = pSetupMultiByteToUnicode(OEMSourceMediaLocation, CP_ACP)))
2187         SetLastError(ERROR_INVALID_PARAMETER);
2188     else
2189     {
2190         if (DestinationInfFileNameSize != 0)
2191         {
2192             DestinationInfFileNameW = MyMalloc(DestinationInfFileNameSize * sizeof(WCHAR));
2193             if (!DestinationInfFileNameW)
2194             {
2195                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2196                 goto cleanup;
2197             }
2198         }
2199 
2200         ret = SetupCopyOEMInfW(
2201             SourceInfFileNameW,
2202             OEMSourceMediaLocationW,
2203             OEMSourceMediaType,
2204             CopyStyle,
2205             DestinationInfFileNameW,
2206             DestinationInfFileNameSize,
2207             RequiredSize,
2208             DestinationInfFileNameComponent ? &DestinationInfFileNameComponentW : NULL);
2209         if (!ret)
2210             goto cleanup;
2211 
2212         if (DestinationInfFileNameSize != 0)
2213         {
2214             if (WideCharToMultiByte(CP_ACP, 0, DestinationInfFileNameW, -1,
2215                 DestinationInfFileName, DestinationInfFileNameSize, NULL, NULL) == 0)
2216             {
2217                 DestinationInfFileName[0] = '\0';
2218                 goto cleanup;
2219             }
2220         }
2221         if (DestinationInfFileNameComponent)
2222         {
2223             if (DestinationInfFileNameComponentW)
2224                 *DestinationInfFileNameComponent = &DestinationInfFileName[DestinationInfFileNameComponentW - DestinationInfFileNameW];
2225             else
2226                 *DestinationInfFileNameComponent = NULL;
2227         }
2228         ret = TRUE;
2229     }
2230 
2231 cleanup:
2232     MyFree(SourceInfFileNameW);
2233     MyFree(OEMSourceMediaLocationW);
2234     MyFree(DestinationInfFileNameW);
2235     TRACE("Returning %d\n", ret);
2236     if (ret) SetLastError(ERROR_SUCCESS);
2237     return ret;
2238 }
2239 
2240 static int compare_files( HANDLE file1, HANDLE file2 )
2241 {
2242     char buffer1[2048];
2243     char buffer2[2048];
2244     DWORD size1;
2245     DWORD size2;
2246 
2247     while( ReadFile(file1, buffer1, sizeof(buffer1), &size1, NULL) &&
2248            ReadFile(file2, buffer2, sizeof(buffer2), &size2, NULL) )
2249     {
2250         int ret;
2251         if (size1 != size2)
2252             return size1 > size2 ? 1 : -1;
2253         if (!size1)
2254             return 0;
2255         ret = memcmp( buffer1, buffer2, size1 );
2256         if (ret)
2257             return ret;
2258     }
2259 
2260     return 0;
2261 }
2262 
2263 /***********************************************************************
2264  *		SetupCopyOEMInfW  (SETUPAPI.@)
2265  */
2266 BOOL WINAPI SetupCopyOEMInfW(
2267         IN PCWSTR SourceInfFileName,
2268         IN PCWSTR OEMSourceMediaLocation,
2269         IN DWORD OEMSourceMediaType,
2270         IN DWORD CopyStyle,
2271         OUT PWSTR DestinationInfFileName OPTIONAL,
2272         IN DWORD DestinationInfFileNameSize,
2273         OUT PDWORD RequiredSize OPTIONAL,
2274         OUT PWSTR* DestinationInfFileNameComponent OPTIONAL)
2275 {
2276     BOOL ret = FALSE;
2277 
2278     TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2279         debugstr_w(SourceInfFileName), debugstr_w(OEMSourceMediaLocation), OEMSourceMediaType,
2280         CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2281         RequiredSize, DestinationInfFileNameComponent);
2282 
2283     if (!SourceInfFileName)
2284         SetLastError(ERROR_INVALID_PARAMETER);
2285     else if (OEMSourceMediaType != SPOST_NONE && OEMSourceMediaType != SPOST_PATH && OEMSourceMediaType != SPOST_URL)
2286         SetLastError(ERROR_INVALID_PARAMETER);
2287     else if (OEMSourceMediaType != SPOST_NONE && !OEMSourceMediaLocation)
2288         SetLastError(ERROR_INVALID_PARAMETER);
2289     else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
2290     {
2291         TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
2292         SetLastError(ERROR_INVALID_FLAGS);
2293     }
2294     else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2295         SetLastError(ERROR_INVALID_PARAMETER);
2296     else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
2297     {
2298         FIXME("CopyStyle 0x%x not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
2299         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2300     }
2301     else
2302     {
2303         HANDLE hSearch = INVALID_HANDLE_VALUE;
2304         WIN32_FIND_DATAW FindFileData;
2305         BOOL AlreadyExists;
2306         DWORD NextFreeNumber = 0;
2307         SIZE_T len;
2308         LPWSTR pFullFileName = NULL;
2309         LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2310         HANDLE hSourceFile = INVALID_HANDLE_VALUE;
2311 
2312         if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
2313             FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
2314 
2315         /* Check if source file exists, and open it */
2316         if (strchrW(SourceInfFileName, '\\' ) || strchrW(SourceInfFileName, '/' ))
2317         {
2318             WCHAR *path;
2319 
2320             if (!(len = GetFullPathNameW(SourceInfFileName, 0, NULL, NULL)))
2321                 return FALSE;
2322             if (!(path = MyMalloc(len * sizeof(WCHAR))))
2323             {
2324                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2325                 return FALSE;
2326             }
2327             GetFullPathNameW(SourceInfFileName, len, path, NULL);
2328             hSourceFile = CreateFileW(
2329                 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2330                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2331                 NULL, OPEN_EXISTING, 0, NULL);
2332             MyFree(path);
2333         }
2334         else  /* try Windows directory */
2335         {
2336             WCHAR *path, *p;
2337             static const WCHAR Inf[]      = {'\\','i','n','f','\\',0};
2338             static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
2339 
2340             len = GetWindowsDirectoryW(NULL, 0) + strlenW(SourceInfFileName) + 12;
2341             if (!(path = MyMalloc(len * sizeof(WCHAR))))
2342             {
2343                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2344                 return FALSE;
2345             }
2346             GetWindowsDirectoryW(path, len);
2347             p = path + strlenW(path);
2348             strcpyW(p, Inf);
2349             strcatW(p, SourceInfFileName);
2350             hSourceFile = CreateFileW(
2351                 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2352                 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2353                 NULL, OPEN_EXISTING, 0, NULL);
2354             if (hSourceFile == INVALID_HANDLE_VALUE)
2355             {
2356                 strcpyW(p, System32);
2357                 strcatW(p, SourceInfFileName);
2358                 hSourceFile = CreateFileW(
2359                     path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2360                     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2361                     NULL, OPEN_EXISTING, 0, NULL);
2362             }
2363             MyFree(path);
2364         }
2365         if (hSourceFile == INVALID_HANDLE_VALUE)
2366         {
2367             SetLastError(ERROR_FILE_NOT_FOUND);
2368             goto cleanup;
2369         }
2370 
2371         /* Prepare .inf file specification */
2372         len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
2373         pFullFileName = MyMalloc(len * sizeof(WCHAR));
2374         if (!pFullFileName)
2375         {
2376             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2377             goto cleanup;
2378         }
2379         len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2380         if (len == 0 || len > MAX_PATH)
2381             goto cleanup;
2382         if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2383             strcatW(pFullFileName, BackSlash);
2384         strcatW(pFullFileName, InfDirectory);
2385         pFileName = &pFullFileName[strlenW(pFullFileName)];
2386 
2387         /* Search if the specified .inf file already exists in %WINDIR%\Inf */
2388         AlreadyExists = FALSE;
2389         strcpyW(pFileName, OemFileMask);
2390         hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2391         if (hSearch != INVALID_HANDLE_VALUE)
2392         {
2393             LARGE_INTEGER SourceFileSize;
2394 
2395             if (GetFileSizeEx(hSourceFile, &SourceFileSize))
2396             {
2397                 do
2398                 {
2399                     LARGE_INTEGER DestFileSize;
2400                     HANDLE hDestFile;
2401 
2402                     strcpyW(pFileName, FindFileData.cFileName);
2403                     hDestFile = CreateFileW(
2404                         pFullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2405                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2406                         NULL, OPEN_EXISTING, 0, NULL);
2407                     if (hDestFile != INVALID_HANDLE_VALUE)
2408                     {
2409                         if (GetFileSizeEx(hDestFile, &DestFileSize)
2410                          && DestFileSize.QuadPart == SourceFileSize.QuadPart
2411                          && !compare_files(hSourceFile, hDestFile))
2412                         {
2413                             TRACE("%s already exists as %s\n",
2414                                 debugstr_w(SourceInfFileName), debugstr_w(pFileName));
2415                             AlreadyExists = TRUE;
2416                         }
2417                     }
2418                 } while (!AlreadyExists && FindNextFileW(hSearch, &FindFileData));
2419             }
2420             FindClose(hSearch);
2421             hSearch = INVALID_HANDLE_VALUE;
2422         }
2423 
2424         if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
2425         {
2426             /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
2427             SetLastError(ERROR_FILE_NOT_FOUND);
2428             goto cleanup;
2429         }
2430         else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
2431         {
2432             DWORD Size = strlenW(pFileName) + 1;
2433 
2434             if (RequiredSize)
2435                 *RequiredSize = Size;
2436             if (DestinationInfFileNameSize == 0)
2437                 SetLastError(ERROR_FILE_EXISTS);
2438             else if (DestinationInfFileNameSize < Size)
2439                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2440             else
2441             {
2442                 SetLastError(ERROR_FILE_EXISTS);
2443                 strcpyW(DestinationInfFileName, pFileName);
2444             }
2445             goto cleanup;
2446         }
2447 
2448         /* Search the number to give to OEM??.INF */
2449         strcpyW(pFileName, OemFileMask);
2450         hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2451         if (hSearch == INVALID_HANDLE_VALUE)
2452         {
2453             if (GetLastError() != ERROR_FILE_NOT_FOUND)
2454                 goto cleanup;
2455         }
2456         else
2457         {
2458             do
2459             {
2460                 DWORD CurrentNumber;
2461                 if (swscanf(FindFileData.cFileName, OemFileSpecification, &CurrentNumber) == 1
2462                     && CurrentNumber <= 99999)
2463                 {
2464                     if (CurrentNumber >= NextFreeNumber)
2465                         NextFreeNumber = CurrentNumber + 1;
2466                 }
2467             } while (FindNextFileW(hSearch, &FindFileData));
2468         }
2469 
2470         if (NextFreeNumber > 99999)
2471         {
2472             ERR("Too much custom .inf files\n");
2473             SetLastError(ERROR_GEN_FAILURE);
2474             goto cleanup;
2475         }
2476 
2477         /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
2478         sprintfW(pFileName, OemFileSpecification, NextFreeNumber);
2479         TRACE("Next available file is %s\n", debugstr_w(pFileName));
2480 
2481         if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
2482         {
2483             TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
2484             goto cleanup;
2485         }
2486 
2487         len = strlenW(pFullFileName) + 1;
2488         if (RequiredSize)
2489             *RequiredSize = len;
2490         if (DestinationInfFileName)
2491         {
2492             if (DestinationInfFileNameSize >= len)
2493             {
2494                 strcpyW(DestinationInfFileName, pFullFileName);
2495                 if (DestinationInfFileNameComponent)
2496                     *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
2497             }
2498             else
2499             {
2500                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2501                 goto cleanup;
2502             }
2503         }
2504 
2505         if (CopyStyle & SP_COPY_DELETESOURCE)
2506         {
2507             if (!DeleteFileW(SourceInfFileName))
2508             {
2509                 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
2510                 goto cleanup;
2511             }
2512         }
2513 
2514         ret = TRUE;
2515 
2516 cleanup:
2517         if (hSourceFile != INVALID_HANDLE_VALUE)
2518             CloseHandle(hSourceFile);
2519         if (hSearch != INVALID_HANDLE_VALUE)
2520             FindClose(hSearch);
2521         MyFree(pFullFileName);
2522     }
2523 
2524     TRACE("Returning %d\n", ret);
2525     if (ret) SetLastError(ERROR_SUCCESS);
2526     return ret;
2527 }
2528