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