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