- Implement SetupCopyOEMInfW
[reactos.git] / reactos / dll / win32 / setupapi / install.c
1 /*
2 * Setupapi install routines
3 *
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 * 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "setupapi_private.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
25
26 /* Unicode constants */
27 static const WCHAR BackSlash[] = {'\\',0};
28 static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
29
30 /* info passed to callback functions dealing with files */
31 struct files_callback_info
32 {
33 HSPFILEQ queue;
34 PCWSTR src_root;
35 UINT copy_flags;
36 HINF layout;
37 };
38
39 /* info passed to callback functions dealing with the registry */
40 struct registry_callback_info
41 {
42 HKEY default_root;
43 BOOL delete;
44 };
45
46 /* info passed to callback functions dealing with registering dlls */
47 struct register_dll_info
48 {
49 PSP_FILE_CALLBACK_W callback;
50 PVOID callback_context;
51 BOOL unregister;
52 };
53
54 /* info passed to callback functions dealing with Needs directives */
55 struct needs_callback_info
56 {
57 UINT type;
58
59 HWND owner;
60 UINT flags;
61 HKEY key_root;
62 LPCWSTR src_root;
63 UINT copy_flags;
64 PVOID callback;
65 PVOID context;
66 HDEVINFO devinfo;
67 PSP_DEVINFO_DATA devinfo_data;
68 PVOID reserved1;
69 PVOID reserved2;
70 };
71
72 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
73
74 /* Unicode constants */
75 static const WCHAR AddService[] = {'A','d','d','S','e','r','v','i','c','e',0};
76 static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0};
77 static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0};
78 static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0};
79 static const WCHAR Ini2Reg[] = {'I','n','i','2','R','e','g',0};
80 static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0};
81 static const WCHAR AddReg[] = {'A','d','d','R','e','g',0};
82 static const WCHAR DelReg[] = {'D','e','l','R','e','g',0};
83 static const WCHAR BitReg[] = {'B','i','t','R','e','g',0};
84 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
85 static const WCHAR CopyINF[] = {'C','o','p','y','I','N','F',0};
86 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
87 static const WCHAR RegisterDlls[] = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
88 static const WCHAR UnregisterDlls[] = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
89 static const WCHAR ProfileItems[] = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
90 static const WCHAR Include[] = {'I','n','c','l','u','d','e',0};
91 static const WCHAR Needs[] = {'N','e','e','d','s',0};
92
93
94 /***********************************************************************
95 * get_field_string
96 *
97 * Retrieve the contents of a field, dynamically growing the buffer if necessary.
98 */
99 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
100 WCHAR *static_buffer, DWORD *size )
101 {
102 DWORD required;
103
104 if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
105 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
106 {
107 /* now grow the buffer */
108 if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
109 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
110 *size = required;
111 if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
112 }
113 if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
114 return NULL;
115 }
116
117
118 /***********************************************************************
119 * copy_files_callback
120 *
121 * Called once for each CopyFiles entry in a given section.
122 */
123 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
124 {
125 struct files_callback_info *info = arg;
126
127 if (field[0] == '@') /* special case: copy single file */
128 SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, &field[1], info->copy_flags );
129 else
130 SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags );
131 return TRUE;
132 }
133
134
135 /***********************************************************************
136 * delete_files_callback
137 *
138 * Called once for each DelFiles entry in a given section.
139 */
140 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
141 {
142 struct files_callback_info *info = arg;
143 SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
144 return TRUE;
145 }
146
147
148 /***********************************************************************
149 * rename_files_callback
150 *
151 * Called once for each RenFiles entry in a given section.
152 */
153 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
154 {
155 struct files_callback_info *info = arg;
156 SetupQueueRenameSectionW( info->queue, hinf, 0, field );
157 return TRUE;
158 }
159
160
161 /***********************************************************************
162 * get_root_key
163 *
164 * Retrieve the registry root key from its name.
165 */
166 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
167 {
168 static const WCHAR HKCR[] = {'H','K','C','R',0};
169 static const WCHAR HKCU[] = {'H','K','C','U',0};
170 static const WCHAR HKLM[] = {'H','K','L','M',0};
171 static const WCHAR HKU[] = {'H','K','U',0};
172 static const WCHAR HKR[] = {'H','K','R',0};
173
174 if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
175 if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
176 if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
177 if (!strcmpiW( name, HKU )) return HKEY_USERS;
178 if (!strcmpiW( name, HKR )) return def_root;
179 return 0;
180 }
181
182
183 /***********************************************************************
184 * append_multi_sz_value
185 *
186 * Append a multisz string to a multisz registry value.
187 */
188 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
189 DWORD str_size )
190 {
191 DWORD size, type, total;
192 WCHAR *buffer, *p;
193
194 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
195 if (type != REG_MULTI_SZ) return;
196
197 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
198 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
199
200 /* compare each string against all the existing ones */
201 total = size;
202 while (*strings)
203 {
204 int len = strlenW(strings) + 1;
205
206 for (p = buffer; *p; p += strlenW(p) + 1)
207 if (!strcmpiW( p, strings )) break;
208
209 if (!*p) /* not found, need to append it */
210 {
211 memcpy( p, strings, len * sizeof(WCHAR) );
212 p[len] = 0;
213 total += len;
214 }
215 strings += len;
216 }
217 if (total != size)
218 {
219 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
220 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
221 }
222 done:
223 HeapFree( GetProcessHeap(), 0, buffer );
224 }
225
226
227 /***********************************************************************
228 * delete_multi_sz_value
229 *
230 * Remove a string from a multisz registry value.
231 */
232 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
233 {
234 DWORD size, type;
235 WCHAR *buffer, *src, *dst;
236
237 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
238 if (type != REG_MULTI_SZ) return;
239 /* allocate double the size, one for value before and one for after */
240 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
241 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
242 src = buffer;
243 dst = buffer + size;
244 while (*src)
245 {
246 int len = strlenW(src) + 1;
247 if (strcmpiW( src, string ))
248 {
249 memcpy( dst, src, len * sizeof(WCHAR) );
250 dst += len;
251 }
252 src += len;
253 }
254 *dst++ = 0;
255 if (dst != buffer + 2*size) /* did we remove something? */
256 {
257 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
258 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
259 (BYTE *)(buffer + size), dst - (buffer + size) );
260 }
261 done:
262 HeapFree( GetProcessHeap(), 0, buffer );
263 }
264
265
266 /***********************************************************************
267 * do_reg_operation
268 *
269 * Perform an add/delete registry operation depending on the flags.
270 */
271 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
272 {
273 DWORD type, size;
274
275 if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */
276 {
277 if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
278 {
279 if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
280 {
281 WCHAR *str;
282
283 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
284 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
285 SetupGetStringFieldW( context, 5, str, size, NULL );
286 delete_multi_sz_value( hkey, value, str );
287 HeapFree( GetProcessHeap(), 0, str );
288 }
289 else RegDeleteValueW( hkey, value );
290 }
291 else RegDeleteKeyW( hkey, NULL );
292 return TRUE;
293 }
294
295 if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
296
297 if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
298 {
299 BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
300 if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
301 if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
302 }
303
304 switch(flags & FLG_ADDREG_TYPE_MASK)
305 {
306 case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break;
307 case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break;
308 case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
309 case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break;
310 case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break;
311 case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break;
312 default: type = flags >> 16; break;
313 }
314
315 if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
316 (type == REG_DWORD && SetupGetFieldCount(context) == 5))
317 {
318 static const WCHAR empty;
319 WCHAR *str = NULL;
320
321 if (type == REG_MULTI_SZ)
322 {
323 if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
324 if (size)
325 {
326 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
327 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
328 }
329 if (flags & FLG_ADDREG_APPEND)
330 {
331 if (!str) return TRUE;
332 append_multi_sz_value( hkey, value, str, size );
333 HeapFree( GetProcessHeap(), 0, str );
334 return TRUE;
335 }
336 /* else fall through to normal string handling */
337 }
338 else
339 {
340 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
341 if (size)
342 {
343 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
344 SetupGetStringFieldW( context, 5, str, size, NULL );
345 }
346 }
347
348 if (type == REG_DWORD)
349 {
350 DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
351 TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw );
352 RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
353 }
354 else
355 {
356 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
357 if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
358 else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
359 }
360 HeapFree( GetProcessHeap(), 0, str );
361 return TRUE;
362 }
363 else /* get the binary data */
364 {
365 BYTE *data = NULL;
366
367 if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
368 if (size)
369 {
370 if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
371 TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size );
372 SetupGetBinaryField( context, 5, data, size, NULL );
373 }
374 RegSetValueExW( hkey, value, 0, type, data, size );
375 HeapFree( GetProcessHeap(), 0, data );
376 return TRUE;
377 }
378 }
379
380
381 /***********************************************************************
382 * registry_callback
383 *
384 * Called once for each AddReg and DelReg entry in a given section.
385 */
386 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
387 {
388 struct registry_callback_info *info = arg;
389 INFCONTEXT context;
390 HKEY root_key, hkey;
391
392 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
393
394 for (; ok; ok = SetupFindNextLine( &context, &context ))
395 {
396 WCHAR buffer[MAX_INF_STRING_LENGTH];
397 INT flags;
398
399 /* get root */
400 if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
401 continue;
402 if (!(root_key = get_root_key( buffer, info->default_root )))
403 continue;
404
405 /* get key */
406 if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
407 *buffer = 0;
408
409 /* get flags */
410 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
411
412 if (!info->delete)
413 {
414 if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */
415 }
416 else
417 {
418 if (!flags) flags = FLG_ADDREG_DELREG_BIT;
419 else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */
420 }
421
422 if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
423 {
424 if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */
425 }
426 else if (RegCreateKeyW( root_key, buffer, &hkey ))
427 {
428 ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
429 continue;
430 }
431 TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
432
433 /* get value name */
434 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
435 *buffer = 0;
436
437 /* and now do it */
438 if (!do_reg_operation( hkey, buffer, &context, flags ))
439 {
440 if (hkey != root_key) RegCloseKey( hkey );
441 return FALSE;
442 }
443 if (hkey != root_key) RegCloseKey( hkey );
444 }
445 return TRUE;
446 }
447
448
449 /***********************************************************************
450 * do_register_dll
451 *
452 * Register or unregister a dll.
453 */
454 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
455 INT flags, INT timeout, const WCHAR *args )
456 {
457 HMODULE module;
458 HRESULT res;
459 SP_REGISTER_CONTROL_STATUSW status;
460 #ifdef __WINESRC__
461 IMAGE_NT_HEADERS *nt;
462 #endif
463
464 status.cbSize = sizeof(status);
465 status.FileName = path;
466 status.FailureCode = SPREG_SUCCESS;
467 status.Win32Error = ERROR_SUCCESS;
468
469 if (info->callback)
470 {
471 switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
472 (UINT_PTR)&status, !info->unregister ))
473 {
474 case FILEOP_ABORT:
475 SetLastError( ERROR_OPERATION_ABORTED );
476 return FALSE;
477 case FILEOP_SKIP:
478 return TRUE;
479 case FILEOP_DOIT:
480 break;
481 }
482 }
483
484 if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
485 {
486 WARN( "could not load %s\n", debugstr_w(path) );
487 status.FailureCode = SPREG_LOADLIBRARY;
488 status.Win32Error = GetLastError();
489 goto done;
490 }
491
492 #ifdef __WINESRC__
493 if ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
494 {
495 /* file is an executable, not a dll */
496 STARTUPINFOW startup;
497 PROCESS_INFORMATION info;
498 WCHAR *cmd_line;
499 BOOL res;
500 static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
501 static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
502
503 FreeLibrary( module );
504 module = NULL;
505 if (!args) args = default_args;
506 cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
507 sprintfW( cmd_line, format, path, args );
508 memset( &startup, 0, sizeof(startup) );
509 startup.cb = sizeof(startup);
510 TRACE( "executing %s\n", debugstr_w(cmd_line) );
511 res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
512 HeapFree( GetProcessHeap(), 0, cmd_line );
513 if (!res)
514 {
515 status.FailureCode = SPREG_LOADLIBRARY;
516 status.Win32Error = GetLastError();
517 goto done;
518 }
519 CloseHandle( info.hThread );
520
521 if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
522 {
523 /* timed out, kill the process */
524 TerminateProcess( info.hProcess, 1 );
525 status.FailureCode = SPREG_TIMEOUT;
526 status.Win32Error = ERROR_TIMEOUT;
527 }
528 CloseHandle( info.hProcess );
529 goto done;
530 }
531 #endif // __WINESRC__
532
533 if (flags & FLG_REGSVR_DLLREGISTER)
534 {
535 const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
536 HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
537
538 if (!func)
539 {
540 status.FailureCode = SPREG_GETPROCADDR;
541 status.Win32Error = GetLastError();
542 goto done;
543 }
544
545 TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
546 res = func();
547
548 if (FAILED(res))
549 {
550 WARN( "calling %s in %s returned error %lx\n", entry_point, debugstr_w(path), res );
551 status.FailureCode = SPREG_REGSVR;
552 status.Win32Error = res;
553 goto done;
554 }
555 }
556
557 if (flags & FLG_REGSVR_DLLINSTALL)
558 {
559 HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
560
561 if (!func)
562 {
563 status.FailureCode = SPREG_GETPROCADDR;
564 status.Win32Error = GetLastError();
565 goto done;
566 }
567
568 TRACE( "calling DllInstall(%d,%s) in %s\n",
569 !info->unregister, debugstr_w(args), debugstr_w(path) );
570 res = func( !info->unregister, args );
571
572 if (FAILED(res))
573 {
574 WARN( "calling DllInstall in %s returned error %lx\n", debugstr_w(path), res );
575 status.FailureCode = SPREG_REGSVR;
576 status.Win32Error = res;
577 goto done;
578 }
579 }
580
581 done:
582 if (module) FreeLibrary( module );
583 if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
584 (UINT_PTR)&status, !info->unregister );
585 return TRUE;
586 }
587
588
589 /***********************************************************************
590 * register_dlls_callback
591 *
592 * Called once for each RegisterDlls entry in a given section.
593 */
594 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
595 {
596 struct register_dll_info *info = arg;
597 INFCONTEXT context;
598 BOOL ret = TRUE;
599 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
600
601 for (; ok; ok = SetupFindNextLine( &context, &context ))
602 {
603 WCHAR *path, *args, *p;
604 WCHAR buffer[MAX_INF_STRING_LENGTH];
605 INT flags, timeout;
606
607 /* get directory */
608 if (!(path = PARSER_get_dest_dir( &context ))) continue;
609
610 /* get dll name */
611 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
612 goto done;
613 if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
614 (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
615 path = p;
616 p += strlenW(p);
617 if (p == path || p[-1] != '\\') *p++ = '\\';
618 strcpyW( p, buffer );
619
620 /* get flags */
621 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
622
623 /* get timeout */
624 if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
625
626 /* get command line */
627 args = NULL;
628 if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
629 args = buffer;
630
631 ret = do_register_dll( info, path, flags, timeout, args );
632
633 done:
634 HeapFree( GetProcessHeap(), 0, path );
635 if (!ret) break;
636 }
637 return ret;
638 }
639
640 #ifdef __WINESRC__
641 /***********************************************************************
642 * fake_dlls_callback
643 *
644 * Called once for each WineFakeDlls entry in a given section.
645 */
646 static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
647 {
648 INFCONTEXT context;
649 BOOL ret = TRUE;
650 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
651
652 for (; ok; ok = SetupFindNextLine( &context, &context ))
653 {
654 WCHAR *path, *p;
655 WCHAR buffer[MAX_INF_STRING_LENGTH];
656
657 /* get directory */
658 if (!(path = PARSER_get_dest_dir( &context ))) continue;
659
660 /* get dll name */
661 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
662 goto done;
663 if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
664 (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
665 path = p;
666 p += strlenW(p);
667 if (p == path || p[-1] != '\\') *p++ = '\\';
668 strcpyW( p, buffer );
669
670 /* get source dll */
671 if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
672 p = buffer; /* otherwise use target base name as default source */
673
674 create_fake_dll( path, p ); /* ignore errors */
675
676 done:
677 HeapFree( GetProcessHeap(), 0, path );
678 if (!ret) break;
679 }
680 return ret;
681 }
682 #endif // __WINESRC__
683
684 /***********************************************************************
685 * update_ini_callback
686 *
687 * Called once for each UpdateInis entry in a given section.
688 */
689 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
690 {
691 INFCONTEXT context;
692
693 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
694
695 for (; ok; ok = SetupFindNextLine( &context, &context ))
696 {
697 WCHAR buffer[MAX_INF_STRING_LENGTH];
698 WCHAR filename[MAX_INF_STRING_LENGTH];
699 WCHAR section[MAX_INF_STRING_LENGTH];
700 WCHAR entry[MAX_INF_STRING_LENGTH];
701 WCHAR string[MAX_INF_STRING_LENGTH];
702 LPWSTR divider;
703
704 if (!SetupGetStringFieldW( &context, 1, filename,
705 sizeof(filename)/sizeof(WCHAR), NULL ))
706 continue;
707
708 if (!SetupGetStringFieldW( &context, 2, section,
709 sizeof(section)/sizeof(WCHAR), NULL ))
710 continue;
711
712 if (!SetupGetStringFieldW( &context, 4, buffer,
713 sizeof(buffer)/sizeof(WCHAR), NULL ))
714 continue;
715
716 divider = strchrW(buffer,'=');
717 if (divider)
718 {
719 *divider = 0;
720 strcpyW(entry,buffer);
721 divider++;
722 strcpyW(string,divider);
723 }
724 else
725 {
726 strcpyW(entry,buffer);
727 string[0]=0;
728 }
729
730 TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
731 debugstr_w(string),debugstr_w(section),debugstr_w(filename));
732 WritePrivateProfileStringW(section,entry,string,filename);
733
734 }
735 return TRUE;
736 }
737
738 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
739 {
740 FIXME( "should update ini fields %s\n", debugstr_w(field) );
741 return TRUE;
742 }
743
744 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
745 {
746 FIXME( "should do ini2reg %s\n", debugstr_w(field) );
747 return TRUE;
748 }
749
750 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
751 {
752 FIXME( "should do logconf %s\n", debugstr_w(field) );
753 return TRUE;
754 }
755
756 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
757 {
758 FIXME( "should do bitreg %s\n", debugstr_w(field) );
759 return TRUE;
760 }
761
762 static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
763 {
764 FIXME( "should do profile items %s\n", debugstr_w(field) );
765 return TRUE;
766 }
767
768 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
769 {
770 FIXME( "should do copy inf %s\n", debugstr_w(field) );
771 return TRUE;
772 }
773
774
775 /***********************************************************************
776 * iterate_section_fields
777 *
778 * Iterate over all fields of a certain key of a certain section
779 */
780 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
781 iterate_fields_func callback, void *arg )
782 {
783 WCHAR static_buffer[200];
784 WCHAR *buffer = static_buffer;
785 DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
786 INFCONTEXT context;
787 BOOL ret = FALSE;
788
789 BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
790 while (ok)
791 {
792 UINT i, count = SetupGetFieldCount( &context );
793 for (i = 1; i <= count; i++)
794 {
795 if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
796 goto done;
797 if (!callback( hinf, buffer, arg ))
798 {
799 WARN("callback failed for %s %s err %ld\n",
800 debugstr_w(section), debugstr_w(buffer), GetLastError() );
801 goto done;
802 }
803 }
804 ok = SetupFindNextMatchLineW( &context, key, &context );
805 }
806 ret = TRUE;
807 done:
808 if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
809 return ret;
810 }
811
812
813 /***********************************************************************
814 * SetupInstallFilesFromInfSectionA (SETUPAPI.@)
815 */
816 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
817 PCSTR section, PCSTR src_root, UINT flags )
818 {
819 UNICODE_STRING sectionW;
820 BOOL ret = FALSE;
821
822 if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
823 {
824 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
825 return FALSE;
826 }
827 if (!src_root)
828 ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
829 NULL, flags );
830 else
831 {
832 UNICODE_STRING srcW;
833 if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
834 {
835 ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
836 srcW.Buffer, flags );
837 RtlFreeUnicodeString( &srcW );
838 }
839 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
840 }
841 RtlFreeUnicodeString( &sectionW );
842 return ret;
843 }
844
845
846 /***********************************************************************
847 * SetupInstallFilesFromInfSectionW (SETUPAPI.@)
848 */
849 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
850 PCWSTR section, PCWSTR src_root, UINT flags )
851 {
852 struct files_callback_info info;
853
854 info.queue = queue;
855 info.src_root = src_root;
856 info.copy_flags = flags;
857 info.layout = hlayout;
858 return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
859 }
860
861
862 /***********************************************************************
863 * SetupInstallFromInfSectionA (SETUPAPI.@)
864 */
865 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
866 HKEY key_root, PCSTR src_root, UINT copy_flags,
867 PSP_FILE_CALLBACK_A callback, PVOID context,
868 HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
869 {
870 UNICODE_STRING sectionW, src_rootW;
871 struct callback_WtoA_context ctx;
872 BOOL ret = FALSE;
873
874 src_rootW.Buffer = NULL;
875 if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
876 {
877 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
878 return FALSE;
879 }
880
881 if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
882 {
883 ctx.orig_context = context;
884 ctx.orig_handler = callback;
885 ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
886 src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
887 &ctx, devinfo, devinfo_data );
888 RtlFreeUnicodeString( &sectionW );
889 }
890 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
891
892 RtlFreeUnicodeString( &src_rootW );
893 return ret;
894 }
895
896
897 /***********************************************************************
898 * include_callback
899 *
900 * Called once for each Include entry in a given section.
901 */
902 static BOOL include_callback( HINF hinf, PCWSTR field, void *arg )
903 {
904 return SetupOpenAppendInfFileW( field, hinf, NULL );
905 }
906
907
908 /***********************************************************************
909 * needs_callback
910 *
911 * Called once for each Needs entry in a given section.
912 */
913 static BOOL needs_callback( HINF hinf, PCWSTR field, void *arg )
914 {
915 struct needs_callback_info *info = arg;
916
917 switch (info->type)
918 {
919 case 0:
920 return SetupInstallFromInfSectionW(info->owner, *(HINF*)hinf, field, info->flags,
921 info->key_root, info->src_root, info->copy_flags, info->callback,
922 info->context, info->devinfo, info->devinfo_data);
923 case 1:
924 return SetupInstallServicesFromInfSectionExW(*(HINF*)hinf, field, info->flags,
925 info->devinfo, info->devinfo_data, info->reserved1, info->reserved2);
926 default:
927 ERR("Unknown info type %ld\n", info->type);
928 return FALSE;
929 }
930 }
931
932
933 /***********************************************************************
934 * SetupInstallFromInfSectionW (SETUPAPI.@)
935 */
936 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
937 HKEY key_root, PCWSTR src_root, UINT copy_flags,
938 PSP_FILE_CALLBACK_W callback, PVOID context,
939 HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
940 {
941 struct needs_callback_info needs_info;
942
943 /* Parse 'Include' and 'Needs' directives */
944 iterate_section_fields( hinf, section, Include, include_callback, NULL);
945 needs_info.type = 0;
946 needs_info.owner = owner;
947 needs_info.flags = flags;
948 needs_info.key_root = key_root;
949 needs_info.src_root = src_root;
950 needs_info.copy_flags = copy_flags;
951 needs_info.callback = callback;
952 needs_info.context = context;
953 needs_info.devinfo = devinfo;
954 needs_info.devinfo_data = devinfo_data;
955 iterate_section_fields( hinf, section, Needs, needs_callback, &needs_info);
956
957 if (flags & SPINST_FILES)
958 {
959 SP_DEVINSTALL_PARAMS_W install_params;
960 struct files_callback_info info;
961 HSPFILEQ queue = NULL;
962 BOOL use_custom_queue;
963 BOOL ret;
964
965 install_params.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
966 use_custom_queue = SetupDiGetDeviceInstallParamsW(devinfo, devinfo_data, &install_params) && (install_params.Flags & DI_NOVCP);
967 if (!use_custom_queue && ((queue = SetupOpenFileQueue()) == (HSPFILEQ)INVALID_HANDLE_VALUE ))
968 return FALSE;
969 info.queue = use_custom_queue ? install_params.FileQueue : queue;
970 info.src_root = src_root;
971 info.copy_flags = copy_flags;
972 info.layout = hinf;
973 ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
974 iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
975 iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ));
976 if (!use_custom_queue)
977 {
978 if (ret)
979 ret = SetupCommitFileQueueW( owner, queue, callback, context );
980 SetupCloseFileQueue( queue );
981 }
982 if (!ret) return FALSE;
983 }
984 if (flags & SPINST_INIFILES)
985 {
986 if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
987 !iterate_section_fields( hinf, section, UpdateIniFields,
988 update_ini_fields_callback, NULL ))
989 return FALSE;
990 }
991 if (flags & SPINST_INI2REG)
992 {
993 if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
994 return FALSE;
995 }
996 if (flags & SPINST_LOGCONFIG)
997 {
998 if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
999 return FALSE;
1000 }
1001 if (flags & SPINST_REGSVR)
1002 {
1003 struct register_dll_info info;
1004
1005 info.unregister = FALSE;
1006 if (flags & SPINST_REGISTERCALLBACKAWARE)
1007 {
1008 info.callback = callback;
1009 info.callback_context = context;
1010 }
1011 else info.callback = NULL;
1012
1013 if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
1014 return FALSE;
1015
1016 #ifdef __WINESRC__
1017 if (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
1018 return FALSE;
1019 #endif // __WINESRC__
1020 }
1021 if (flags & SPINST_UNREGSVR)
1022 {
1023 struct register_dll_info info;
1024
1025 info.unregister = TRUE;
1026 if (flags & SPINST_REGISTERCALLBACKAWARE)
1027 {
1028 info.callback = callback;
1029 info.callback_context = context;
1030 }
1031 else info.callback = NULL;
1032
1033 if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
1034 return FALSE;
1035 }
1036 if (flags & SPINST_REGISTRY)
1037 {
1038 struct registry_callback_info info;
1039
1040 info.default_root = key_root;
1041 info.delete = TRUE;
1042 if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
1043 return FALSE;
1044 info.delete = FALSE;
1045 if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
1046 return FALSE;
1047 }
1048 if (flags & SPINST_BITREG)
1049 {
1050 if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
1051 return FALSE;
1052 }
1053 if (flags & SPINST_PROFILEITEMS)
1054 {
1055 if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
1056 return FALSE;
1057 }
1058 if (flags & SPINST_COPYINF)
1059 {
1060 if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
1061 return FALSE;
1062 }
1063
1064 return TRUE;
1065 }
1066
1067
1068 /***********************************************************************
1069 * InstallHinfSectionW (SETUPAPI.@)
1070 *
1071 * NOTE: 'cmdline' is <section> <mode> <path> from
1072 * RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
1073 */
1074 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
1075 {
1076 WCHAR *p, *path, section[MAX_PATH];
1077 void *callback_context;
1078 UINT mode;
1079 HINF hinf;
1080
1081 TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
1082
1083 lstrcpynW( section, cmdline, sizeof(section)/sizeof(WCHAR) );
1084
1085 if (!(p = strchrW( section, ' ' ))) return;
1086 *p++ = 0;
1087 while (*p == ' ') p++;
1088 mode = atoiW( p );
1089
1090 if (!(p = strchrW( p, ' ' ))) return;
1091 path = p + 1;
1092 while (*path == ' ') path++;
1093
1094 hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
1095 if (hinf == INVALID_HANDLE_VALUE) return;
1096
1097 callback_context = SetupInitDefaultQueueCallback( hwnd );
1098 SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
1099 SetupDefaultQueueCallbackW, callback_context,
1100 NULL, NULL );
1101 SetupTermDefaultQueueCallback( callback_context );
1102 SetupCloseInfFile( hinf );
1103
1104 /* FIXME: should check the mode and maybe reboot */
1105 /* there isn't much point in doing that since we */
1106 /* don't yet handle deferred file copies anyway. */
1107 }
1108
1109
1110 /***********************************************************************
1111 * InstallHinfSectionA (SETUPAPI.@)
1112 */
1113 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1114 {
1115 UNICODE_STRING cmdlineW;
1116
1117 if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1118 {
1119 InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1120 RtlFreeUnicodeString( &cmdlineW );
1121 }
1122 }
1123
1124
1125 /***********************************************************************
1126 * SetupInstallServicesFromInfSectionA (SETUPAPI.@)
1127 */
1128 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF hinf, PCSTR sectionname, DWORD flags )
1129 {
1130 return SetupInstallServicesFromInfSectionExA( hinf, sectionname, flags,
1131 NULL, NULL, NULL, NULL );
1132 }
1133
1134
1135 /***********************************************************************
1136 * SetupInstallServicesFromInfSectionW (SETUPAPI.@)
1137 */
1138 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF hinf, PCWSTR sectionname, DWORD flags )
1139 {
1140 return SetupInstallServicesFromInfSectionExW( hinf, sectionname, flags,
1141 NULL, NULL, NULL, NULL );
1142 }
1143
1144
1145 /***********************************************************************
1146 * SetupInstallServicesFromInfSectionExA (SETUPAPI.@)
1147 */
1148 BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
1149 {
1150 UNICODE_STRING sectionnameW;
1151 BOOL ret = FALSE;
1152
1153 if (RtlCreateUnicodeStringFromAsciiz( &sectionnameW, sectionname ))
1154 {
1155 ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
1156 RtlFreeUnicodeString( &sectionnameW );
1157 }
1158 else
1159 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1160
1161 return ret;
1162 }
1163
1164
1165 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
1166 {
1167 DWORD required;
1168 PWSTR buf = NULL;
1169
1170 *value = NULL;
1171
1172 if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
1173 && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
1174 return FALSE;
1175
1176 buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
1177 if ( ! buf )
1178 {
1179 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1180 return FALSE;
1181 }
1182
1183 if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
1184 {
1185 HeapFree( GetProcessHeap(), 0, buf );
1186 return FALSE;
1187 }
1188
1189 *value = buf;
1190 return TRUE;
1191 }
1192
1193
1194 static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
1195 {
1196 LPWSTR buffer, end;
1197 INT res;
1198
1199 if (! GetLineText( hinf, section_name, key_name, &buffer ) )
1200 return FALSE;
1201
1202 res = wcstol( buffer, &end, 0 );
1203 if (end != buffer && !*end)
1204 {
1205 HeapFree(GetProcessHeap(), 0, buffer);
1206 *value = res;
1207 return TRUE;
1208 }
1209 else
1210 {
1211 HeapFree(GetProcessHeap(), 0, buffer);
1212 SetLastError( ERROR_INVALID_DATA );
1213 return FALSE;
1214 }
1215 }
1216
1217
1218 BOOL GetStringField( PINFCONTEXT context, DWORD index, PWSTR *value)
1219 {
1220 DWORD RequiredSize;
1221 BOOL ret;
1222
1223 ret = SetupGetStringFieldW(
1224 context,
1225 index,
1226 NULL, 0,
1227 &RequiredSize);
1228 if (!ret)
1229 return FALSE;
1230 else if (RequiredSize == 0)
1231 {
1232 *value = NULL;
1233 return TRUE;
1234 }
1235
1236 /* We got the needed size for the buffer */
1237 *value = MyMalloc(RequiredSize * sizeof(WCHAR));
1238 if (!*value)
1239 {
1240 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1241 return FALSE;
1242 }
1243 ret = SetupGetStringFieldW(
1244 context,
1245 index,
1246 *value, RequiredSize, NULL);
1247 if (!ret)
1248 MyFree(*value);
1249
1250 return ret;
1251 }
1252
1253
1254 static BOOL InstallOneService(
1255 struct DeviceInfoSet *list,
1256 IN HINF hInf,
1257 IN LPCWSTR ServiceSection,
1258 IN LPCWSTR ServiceName,
1259 IN UINT ServiceFlags)
1260 {
1261 SC_HANDLE hSCManager = NULL;
1262 SC_HANDLE hService = NULL;
1263 LPDWORD GroupOrder = NULL;
1264 LPQUERY_SERVICE_CONFIG ServiceConfig = NULL;
1265 BOOL ret = FALSE;
1266
1267 HKEY hGroupOrderListKey = NULL;
1268 LPWSTR ServiceBinary = NULL;
1269 LPWSTR LoadOrderGroup = NULL;
1270 LPWSTR DisplayName = NULL;
1271 LPWSTR Description = NULL;
1272 LPWSTR Dependencies = NULL;
1273 INT ServiceType, StartType, ErrorControl;
1274 DWORD dwRegType;
1275 DWORD tagId = (DWORD)-1;
1276 BOOL useTag;
1277
1278 if (!GetIntField(hInf, ServiceSection, L"ServiceType", &ServiceType))
1279 goto cleanup;
1280 if (!GetIntField(hInf, ServiceSection, L"StartType", &StartType))
1281 goto cleanup;
1282 if (!GetIntField(hInf, ServiceSection, L"ErrorControl", &ErrorControl))
1283 goto cleanup;
1284 useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1285
1286 hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1287 if (hSCManager == NULL)
1288 goto cleanup;
1289
1290 if (!GetLineText(hInf, ServiceSection, L"ServiceBinary", &ServiceBinary))
1291 goto cleanup;
1292
1293 /* Don't check return value, as these fields are optional and
1294 * GetLineText initialize output parameter even on failure */
1295 GetLineText(hInf, ServiceSection, L"LoadOrderGroup", &LoadOrderGroup);
1296 GetLineText(hInf, ServiceSection, L"DisplayName", &DisplayName);
1297 GetLineText(hInf, ServiceSection, L"Description", &Description);
1298 GetLineText(hInf, ServiceSection, L"Dependencies", &Dependencies);
1299
1300 hService = OpenServiceW(
1301 hSCManager,
1302 ServiceName,
1303 GENERIC_READ | GENERIC_WRITE);
1304 if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1305 goto cleanup;
1306
1307 if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
1308 {
1309 ret = DeleteService(hService);
1310 if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
1311 goto cleanup;
1312 }
1313
1314 if (hService == NULL)
1315 {
1316 /* Create new service */
1317 hService = CreateServiceW(
1318 hSCManager,
1319 ServiceName,
1320 DisplayName,
1321 0,
1322 ServiceType,
1323 StartType,
1324 ErrorControl,
1325 ServiceBinary,
1326 LoadOrderGroup,
1327 useTag ? &tagId : NULL,
1328 Dependencies,
1329 NULL, NULL);
1330 if (hService == NULL)
1331 goto cleanup;
1332 }
1333 else
1334 {
1335 DWORD bufferSize;
1336 /* Read current configuration */
1337 if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1338 {
1339 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1340 goto cleanup;
1341 ServiceConfig = MyMalloc(bufferSize);
1342 if (!ServiceConfig)
1343 {
1344 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1345 goto cleanup;
1346 }
1347 if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1348 goto cleanup;
1349 }
1350 tagId = ServiceConfig->dwTagId;
1351
1352 /* Update configuration */
1353 ret = ChangeServiceConfigW(
1354 hService,
1355 ServiceType,
1356 (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1357 (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1358 ServiceBinary,
1359 (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1360 useTag ? &tagId : NULL,
1361 (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1362 NULL, NULL,
1363 (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1364 if (!ret)
1365 goto cleanup;
1366 }
1367
1368 /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1369
1370 if (useTag)
1371 {
1372 /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1373 LONG rc;
1374 LPCWSTR lpLoadOrderGroup;
1375 DWORD bufferSize;
1376
1377 lpLoadOrderGroup = LoadOrderGroup;
1378 if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1379 lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1380
1381 rc = RegOpenKey(
1382 list ? list->HKLM : HKEY_LOCAL_MACHINE,
1383 L"SYSTEM\\CurrentControlSet\\Control\\GroupOrderList",
1384 &hGroupOrderListKey);
1385 if (rc != ERROR_SUCCESS)
1386 {
1387 SetLastError(rc);
1388 goto cleanup;
1389 }
1390 rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1391 if (rc == ERROR_FILE_NOT_FOUND)
1392 bufferSize = sizeof(DWORD);
1393 else if (rc != ERROR_SUCCESS)
1394 {
1395 SetLastError(rc);
1396 goto cleanup;
1397 }
1398 else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1399 {
1400 SetLastError(ERROR_GEN_FAILURE);
1401 goto cleanup;
1402 }
1403 /* Allocate buffer to store existing data + the new tag */
1404 GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
1405 if (!GroupOrder)
1406 {
1407 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1408 goto cleanup;
1409 }
1410 if (rc == ERROR_SUCCESS)
1411 {
1412 /* Read existing data */
1413 rc = RegQueryValueExW(
1414 hGroupOrderListKey,
1415 lpLoadOrderGroup,
1416 NULL,
1417 NULL,
1418 (BYTE*)GroupOrder,
1419 &bufferSize);
1420 if (rc != ERROR_SUCCESS)
1421 {
1422 SetLastError(rc);
1423 goto cleanup;
1424 }
1425 if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1426 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1427 }
1428 else
1429 {
1430 GroupOrder[0] = 0;
1431 }
1432 GroupOrder[0]++;
1433 if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1434 GroupOrder[1] = tagId;
1435 else
1436 GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
1437
1438 rc = RegSetValueExW(
1439 hGroupOrderListKey,
1440 lpLoadOrderGroup,
1441 0,
1442 REG_BINARY,
1443 (BYTE*)GroupOrder,
1444 bufferSize + sizeof(DWORD));
1445 if (rc != ERROR_SUCCESS)
1446 {
1447 SetLastError(rc);
1448 goto cleanup;
1449 }
1450 }
1451
1452 ret = TRUE;
1453
1454 cleanup:
1455 if (hSCManager != NULL)
1456 CloseServiceHandle(hSCManager);
1457 if (hService != NULL)
1458 CloseServiceHandle(hService);
1459 if (hGroupOrderListKey != NULL)
1460 RegCloseKey(hGroupOrderListKey);
1461 MyFree(ServiceConfig);
1462 MyFree(ServiceBinary);
1463 MyFree(LoadOrderGroup);
1464 MyFree(DisplayName);
1465 MyFree(Description);
1466 MyFree(Dependencies);
1467 MyFree(GroupOrder);
1468
1469 TRACE("Returning %d\n", ret);
1470 return ret;
1471 }
1472
1473
1474 /***********************************************************************
1475 * SetupInstallServicesFromInfSectionExW (SETUPAPI.@)
1476 */
1477 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
1478 {
1479 struct DeviceInfoSet *list = NULL;
1480 BOOL ret = FALSE;
1481
1482 TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
1483 flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
1484
1485 if (!sectionname)
1486 SetLastError(ERROR_INVALID_PARAMETER);
1487 else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
1488 {
1489 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));
1490 SetLastError(ERROR_INVALID_FLAGS);
1491 }
1492 else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
1493 SetLastError(ERROR_INVALID_HANDLE);
1494 else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
1495 SetLastError(ERROR_INVALID_HANDLE);
1496 else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
1497 SetLastError(ERROR_INVALID_USER_BUFFER);
1498 else if (reserved1 != NULL || reserved2 != NULL)
1499 SetLastError(ERROR_INVALID_PARAMETER);
1500 else
1501 {
1502 struct needs_callback_info needs_info;
1503 LPWSTR ServiceName = NULL;
1504 LPWSTR ServiceSection = NULL;
1505 INT ServiceFlags;
1506 INFCONTEXT ContextService;
1507 BOOL bNeedReboot = FALSE;
1508
1509 /* Parse 'Include' and 'Needs' directives */
1510 iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
1511 needs_info.type = 1;
1512 needs_info.flags = flags;
1513 needs_info.devinfo = DeviceInfoSet;
1514 needs_info.devinfo_data = DeviceInfoData;
1515 needs_info.reserved1 = reserved1;
1516 needs_info.reserved2 = reserved2;
1517 iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
1518
1519 if (flags & SPSVCINST_STOPSERVICE)
1520 {
1521 FIXME("Stopping the device not implemented\n");
1522 /* This may lead to require a reboot */
1523 /* bNeedReboot = TRUE; */
1524 #if 0
1525 SERVICE_STATUS ServiceStatus;
1526 ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
1527 if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
1528 goto cleanup;
1529 if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
1530 {
1531 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
1532 goto cleanup;
1533 }
1534 #endif
1535 flags &= ~SPSVCINST_STOPSERVICE;
1536 }
1537
1538 ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
1539 while (ret)
1540 {
1541 if (!GetStringField(&ContextService, 1, &ServiceName))
1542 goto nextservice;
1543
1544 ret = SetupGetIntField(
1545 &ContextService,
1546 2, /* Field index */
1547 &ServiceFlags);
1548 if (!ret)
1549 {
1550 /* The field may be empty. Ignore the error */
1551 ServiceFlags = 0;
1552 }
1553
1554 if (!GetStringField(&ContextService, 3, &ServiceSection))
1555 goto nextservice;
1556
1557 ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
1558 if (!ret)
1559 goto nextservice;
1560
1561 if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
1562 {
1563 ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
1564 if (!ret)
1565 goto nextservice;
1566 }
1567
1568 nextservice:
1569 HeapFree(GetProcessHeap(), 0, ServiceName);
1570 HeapFree(GetProcessHeap(), 0, ServiceSection);
1571 ServiceName = ServiceSection = NULL;
1572 ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
1573 }
1574
1575 if (bNeedReboot)
1576 SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
1577 else
1578 SetLastError(ERROR_SUCCESS);
1579 ret = TRUE;
1580 }
1581
1582 TRACE("Returning %d\n", ret);
1583 return ret;
1584 }
1585
1586
1587 /***********************************************************************
1588 * SetupCopyOEMInfA (SETUPAPI.@)
1589 */
1590 BOOL WINAPI SetupCopyOEMInfA(
1591 IN PCSTR SourceInfFileName,
1592 IN PCSTR OEMSourceMediaLocation,
1593 IN DWORD OEMSourceMediaType,
1594 IN DWORD CopyStyle,
1595 OUT PSTR DestinationInfFileName OPTIONAL,
1596 IN DWORD DestinationInfFileNameSize,
1597 OUT PDWORD RequiredSize OPTIONAL,
1598 OUT PSTR* DestinationInfFileNameComponent OPTIONAL)
1599 {
1600 PWSTR SourceInfFileNameW = NULL;
1601 PWSTR OEMSourceMediaLocationW = NULL;
1602 PWSTR DestinationInfFileNameW = NULL;
1603 PWSTR DestinationInfFileNameComponentW = NULL;
1604 BOOL ret = FALSE;
1605
1606 TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
1607 SourceInfFileName, OEMSourceMediaLocation, OEMSourceMediaType,
1608 CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
1609 RequiredSize, DestinationInfFileNameComponent);
1610
1611 if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
1612 SetLastError(ERROR_INVALID_PARAMETER);
1613 else if (!(SourceInfFileNameW = MultiByteToUnicode(SourceInfFileName, CP_ACP)))
1614 SetLastError(ERROR_INVALID_PARAMETER);
1615 else if (!(OEMSourceMediaLocationW = MultiByteToUnicode(OEMSourceMediaLocation, CP_ACP)))
1616 SetLastError(ERROR_INVALID_PARAMETER);
1617 else
1618 {
1619 if (DestinationInfFileNameSize != 0)
1620 {
1621 DestinationInfFileNameW = MyMalloc(DestinationInfFileNameSize * sizeof(WCHAR));
1622 if (!DestinationInfFileNameW)
1623 {
1624 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1625 goto cleanup;
1626 }
1627 }
1628
1629 ret = SetupCopyOEMInfW(
1630 SourceInfFileNameW,
1631 OEMSourceMediaLocationW,
1632 OEMSourceMediaType,
1633 CopyStyle,
1634 DestinationInfFileNameW,
1635 DestinationInfFileNameSize,
1636 RequiredSize,
1637 DestinationInfFileNameComponent ? &DestinationInfFileNameComponentW : NULL);
1638 if (!ret)
1639 goto cleanup;
1640
1641 if (DestinationInfFileNameSize != 0)
1642 {
1643 if (WideCharToMultiByte(CP_ACP, 0, DestinationInfFileNameW, -1,
1644 DestinationInfFileName, DestinationInfFileNameSize, NULL, NULL) == 0)
1645 {
1646 DestinationInfFileName[0] = '\0';
1647 goto cleanup;
1648 }
1649 }
1650 if (DestinationInfFileNameComponent)
1651 {
1652 if (DestinationInfFileNameComponentW)
1653 *DestinationInfFileNameComponent = &DestinationInfFileName[DestinationInfFileNameComponentW - DestinationInfFileNameW];
1654 else
1655 *DestinationInfFileNameComponent = NULL;
1656 }
1657 ret = TRUE;
1658 }
1659
1660 cleanup:
1661 MyFree(SourceInfFileNameW);
1662 MyFree(OEMSourceMediaLocationW);
1663 MyFree(DestinationInfFileNameW);
1664
1665 TRACE("Returning %d\n", ret);
1666 return ret;
1667 }
1668
1669 /***********************************************************************
1670 * SetupCopyOEMInfW (SETUPAPI.@)
1671 */
1672 BOOL WINAPI SetupCopyOEMInfW(
1673 IN PCWSTR SourceInfFileName,
1674 IN PCWSTR OEMSourceMediaLocation,
1675 IN DWORD OEMSourceMediaType,
1676 IN DWORD CopyStyle,
1677 OUT PWSTR DestinationInfFileName OPTIONAL,
1678 IN DWORD DestinationInfFileNameSize,
1679 OUT PDWORD RequiredSize OPTIONAL,
1680 OUT PWSTR* DestinationInfFileNameComponent OPTIONAL)
1681 {
1682 BOOL ret = FALSE;
1683
1684 TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
1685 debugstr_w(SourceInfFileName), debugstr_w(OEMSourceMediaLocation), OEMSourceMediaType,
1686 CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
1687 RequiredSize, DestinationInfFileNameComponent);
1688
1689 if (!SourceInfFileName)
1690 SetLastError(ERROR_INVALID_PARAMETER);
1691 else if (OEMSourceMediaType != SPOST_NONE && OEMSourceMediaType != SPOST_PATH && OEMSourceMediaType != SPOST_URL)
1692 SetLastError(ERROR_INVALID_PARAMETER);
1693 else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
1694 {
1695 TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
1696 SetLastError(ERROR_INVALID_FLAGS);
1697 }
1698 else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
1699 SetLastError(ERROR_INVALID_PARAMETER);
1700 else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
1701 {
1702 FIXME("CopyStyle 0x%lx not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
1703 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1704 }
1705 else
1706 {
1707 HANDLE hSearch = INVALID_HANDLE_VALUE;
1708 WIN32_FIND_DATAW FindFileData;
1709 BOOL AlreadyExists;
1710 DWORD NextFreeNumber = 0;
1711 SIZE_T len;
1712 LPWSTR pFullFileName = NULL;
1713 LPWSTR pFileName; /* Pointer into pFullFileName buffer */
1714
1715 if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
1716 FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
1717
1718 /* Search if the specified .inf file already exists in %WINDIR%\Inf */
1719 AlreadyExists = FALSE; /* FIXME */
1720
1721 if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
1722 {
1723 /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
1724 SetLastError(ERROR_FILE_NOT_FOUND);
1725 goto cleanup;
1726 }
1727 else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
1728 {
1729 //SetLastError(ERROR_FILE_EXISTS);
1730 /* FIXME: set return fields */
1731 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1732 FIXME("File already exists. Need to return its name!\n");
1733 goto cleanup;
1734 }
1735
1736 /* Search the number to give to OEM??.INF */
1737 len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
1738 pFullFileName = MyMalloc(len * sizeof(WCHAR));
1739 if (!pFullFileName)
1740 {
1741 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1742 goto cleanup;
1743 }
1744 len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
1745 if (len == 0 || len > MAX_PATH)
1746 goto cleanup;
1747 if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
1748 strcatW(pFullFileName, BackSlash);
1749 strcatW(pFullFileName, InfDirectory);
1750 pFileName = &pFullFileName[strlenW(pFullFileName)];
1751 sprintfW(pFileName, L"oem*.inf", NextFreeNumber);
1752 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
1753 if (hSearch == INVALID_HANDLE_VALUE)
1754 {
1755 if (GetLastError() != ERROR_FILE_NOT_FOUND)
1756 goto cleanup;
1757 }
1758 else
1759 {
1760 do
1761 {
1762 DWORD CurrentNumber;
1763 if (swscanf(FindFileData.cFileName, L"oem%lu.inf", &CurrentNumber) == 1
1764 && CurrentNumber <= 99999)
1765 {
1766 NextFreeNumber = CurrentNumber + 1;
1767 }
1768 } while (FindNextFile(hSearch, &FindFileData));
1769 }
1770
1771 if (NextFreeNumber > 99999)
1772 {
1773 ERR("Too much custom .inf files\n");
1774 SetLastError(ERROR_GEN_FAILURE);
1775 goto cleanup;
1776 }
1777
1778 /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
1779 sprintfW(pFileName, L"oem%lu.inf", NextFreeNumber);
1780 TRACE("Next available file is %s\n", debugstr_w(pFileName));
1781
1782 if (RequiredSize)
1783 *RequiredSize = len;
1784 if (DestinationInfFileName)
1785 {
1786 if (DestinationInfFileNameSize < len)
1787 {
1788 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1789 goto cleanup;
1790 }
1791 strcpyW(DestinationInfFileName, pFullFileName);
1792 if (DestinationInfFileNameComponent)
1793 *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
1794 }
1795
1796 if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
1797 {
1798 TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
1799 goto cleanup;
1800 }
1801
1802 if (CopyStyle & SP_COPY_DELETESOURCE)
1803 {
1804 if (!DeleteFileW(SourceInfFileName))
1805 {
1806 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
1807 goto cleanup;
1808 }
1809 }
1810
1811 ret = TRUE;
1812
1813 cleanup:
1814 if (hSearch != INVALID_HANDLE_VALUE)
1815 FindClose(hSearch);
1816 MyFree(pFullFileName);
1817 }
1818
1819 TRACE("Returning %d\n", ret);
1820 return ret;
1821 }