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