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