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