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