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