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