- Merge the remaining portion of the wlan-bringup branch
[reactos.git] / reactos / dll / win32 / setupapi / install.c
1 /*
2 * Setupapi install routines
3 *
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 * 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "setupapi_private.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
25
26 /* Unicode constants */
27 static const WCHAR BackSlash[] = {'\\',0};
28 static const WCHAR 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 if (SUCCEEDED(hr))
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 (OEMSourceMediaType != SPOST_NONE && !OEMSourceMediaLocation)
2288 SetLastError(ERROR_INVALID_PARAMETER);
2289 else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
2290 {
2291 TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
2292 SetLastError(ERROR_INVALID_FLAGS);
2293 }
2294 else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2295 SetLastError(ERROR_INVALID_PARAMETER);
2296 else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
2297 {
2298 FIXME("CopyStyle 0x%x not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
2299 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2300 }
2301 else
2302 {
2303 HANDLE hSearch = INVALID_HANDLE_VALUE;
2304 WIN32_FIND_DATAW FindFileData;
2305 BOOL AlreadyExists;
2306 DWORD NextFreeNumber = 0;
2307 SIZE_T len;
2308 LPWSTR pFullFileName = NULL;
2309 LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2310 HANDLE hSourceFile = INVALID_HANDLE_VALUE;
2311
2312 if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
2313 FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
2314
2315 /* Check if source file exists, and open it */
2316 if (strchrW(SourceInfFileName, '\\' ) || strchrW(SourceInfFileName, '/' ))
2317 {
2318 WCHAR *path;
2319
2320 if (!(len = GetFullPathNameW(SourceInfFileName, 0, NULL, NULL)))
2321 return FALSE;
2322 if (!(path = MyMalloc(len * sizeof(WCHAR))))
2323 {
2324 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2325 return FALSE;
2326 }
2327 GetFullPathNameW(SourceInfFileName, len, path, NULL);
2328 hSourceFile = CreateFileW(
2329 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2330 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2331 NULL, OPEN_EXISTING, 0, NULL);
2332 MyFree(path);
2333 }
2334 else /* try Windows directory */
2335 {
2336 WCHAR *path, *p;
2337 static const WCHAR Inf[] = {'\\','i','n','f','\\',0};
2338 static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
2339
2340 len = GetWindowsDirectoryW(NULL, 0) + strlenW(SourceInfFileName) + 12;
2341 if (!(path = MyMalloc(len * sizeof(WCHAR))))
2342 {
2343 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2344 return FALSE;
2345 }
2346 GetWindowsDirectoryW(path, len);
2347 p = path + strlenW(path);
2348 strcpyW(p, Inf);
2349 strcatW(p, SourceInfFileName);
2350 hSourceFile = CreateFileW(
2351 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2352 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2353 NULL, OPEN_EXISTING, 0, NULL);
2354 if (hSourceFile == INVALID_HANDLE_VALUE)
2355 {
2356 strcpyW(p, System32);
2357 strcatW(p, SourceInfFileName);
2358 hSourceFile = CreateFileW(
2359 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2360 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2361 NULL, OPEN_EXISTING, 0, NULL);
2362 }
2363 MyFree(path);
2364 }
2365 if (hSourceFile == INVALID_HANDLE_VALUE)
2366 {
2367 SetLastError(ERROR_FILE_NOT_FOUND);
2368 goto cleanup;
2369 }
2370
2371 /* Prepare .inf file specification */
2372 len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
2373 pFullFileName = MyMalloc(len * sizeof(WCHAR));
2374 if (!pFullFileName)
2375 {
2376 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2377 goto cleanup;
2378 }
2379 len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2380 if (len == 0 || len > MAX_PATH)
2381 goto cleanup;
2382 if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2383 strcatW(pFullFileName, BackSlash);
2384 strcatW(pFullFileName, InfDirectory);
2385 pFileName = &pFullFileName[strlenW(pFullFileName)];
2386
2387 /* Search if the specified .inf file already exists in %WINDIR%\Inf */
2388 AlreadyExists = FALSE;
2389 strcpyW(pFileName, OemFileMask);
2390 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2391 if (hSearch != INVALID_HANDLE_VALUE)
2392 {
2393 LARGE_INTEGER SourceFileSize;
2394
2395 if (GetFileSizeEx(hSourceFile, &SourceFileSize))
2396 {
2397 do
2398 {
2399 LARGE_INTEGER DestFileSize;
2400 HANDLE hDestFile;
2401
2402 strcpyW(pFileName, FindFileData.cFileName);
2403 hDestFile = CreateFileW(
2404 pFullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2405 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2406 NULL, OPEN_EXISTING, 0, NULL);
2407 if (hDestFile != INVALID_HANDLE_VALUE)
2408 {
2409 if (GetFileSizeEx(hDestFile, &DestFileSize)
2410 && DestFileSize.QuadPart == SourceFileSize.QuadPart
2411 && !compare_files(hSourceFile, hDestFile))
2412 {
2413 TRACE("%s already exists as %s\n",
2414 debugstr_w(SourceInfFileName), debugstr_w(pFileName));
2415 AlreadyExists = TRUE;
2416 }
2417 }
2418 } while (!AlreadyExists && FindNextFileW(hSearch, &FindFileData));
2419 }
2420 FindClose(hSearch);
2421 hSearch = INVALID_HANDLE_VALUE;
2422 }
2423
2424 if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
2425 {
2426 /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
2427 SetLastError(ERROR_FILE_NOT_FOUND);
2428 goto cleanup;
2429 }
2430 else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
2431 {
2432 DWORD Size = strlenW(pFileName) + 1;
2433
2434 if (RequiredSize)
2435 *RequiredSize = Size;
2436 if (DestinationInfFileNameSize == 0)
2437 SetLastError(ERROR_FILE_EXISTS);
2438 else if (DestinationInfFileNameSize < Size)
2439 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2440 else
2441 {
2442 SetLastError(ERROR_FILE_EXISTS);
2443 strcpyW(DestinationInfFileName, pFileName);
2444 }
2445 goto cleanup;
2446 }
2447
2448 /* Search the number to give to OEM??.INF */
2449 strcpyW(pFileName, OemFileMask);
2450 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2451 if (hSearch == INVALID_HANDLE_VALUE)
2452 {
2453 if (GetLastError() != ERROR_FILE_NOT_FOUND)
2454 goto cleanup;
2455 }
2456 else
2457 {
2458 do
2459 {
2460 DWORD CurrentNumber;
2461 if (swscanf(FindFileData.cFileName, OemFileSpecification, &CurrentNumber) == 1
2462 && CurrentNumber <= 99999)
2463 {
2464 if (CurrentNumber >= NextFreeNumber)
2465 NextFreeNumber = CurrentNumber + 1;
2466 }
2467 } while (FindNextFileW(hSearch, &FindFileData));
2468 }
2469
2470 if (NextFreeNumber > 99999)
2471 {
2472 ERR("Too much custom .inf files\n");
2473 SetLastError(ERROR_GEN_FAILURE);
2474 goto cleanup;
2475 }
2476
2477 /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
2478 sprintfW(pFileName, OemFileSpecification, NextFreeNumber);
2479 TRACE("Next available file is %s\n", debugstr_w(pFileName));
2480
2481 if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
2482 {
2483 TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
2484 goto cleanup;
2485 }
2486
2487 len = strlenW(pFullFileName) + 1;
2488 if (RequiredSize)
2489 *RequiredSize = len;
2490 if (DestinationInfFileName)
2491 {
2492 if (DestinationInfFileNameSize >= len)
2493 {
2494 strcpyW(DestinationInfFileName, pFullFileName);
2495 if (DestinationInfFileNameComponent)
2496 *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
2497 }
2498 else
2499 {
2500 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2501 goto cleanup;
2502 }
2503 }
2504
2505 if (CopyStyle & SP_COPY_DELETESOURCE)
2506 {
2507 if (!DeleteFileW(SourceInfFileName))
2508 {
2509 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
2510 goto cleanup;
2511 }
2512 }
2513
2514 ret = TRUE;
2515
2516 cleanup:
2517 if (hSourceFile != INVALID_HANDLE_VALUE)
2518 CloseHandle(hSourceFile);
2519 if (hSearch != INVALID_HANDLE_VALUE)
2520 FindClose(hSearch);
2521 MyFree(pFullFileName);
2522 }
2523
2524 TRACE("Returning %d\n", ret);
2525 if (ret) SetLastError(ERROR_SUCCESS);
2526 return ret;
2527 }