34c22daf94fefb3dc96b10022384d6cee417f078
[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 // TODO: Localize the error string.
1581 if (!ret && !(GlobalSetupFlags & PSPGF_NONINTERACTIVE))
1582 {
1583 MessageBoxW(hwnd, section, L"setupapi.dll: An error happened...", MB_ICONERROR | MB_OK);
1584 }
1585 }
1586
1587
1588 /***********************************************************************
1589 * InstallHinfSectionA (SETUPAPI.@)
1590 */
1591 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
1592 {
1593 UNICODE_STRING cmdlineW;
1594
1595 if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1596 {
1597 InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1598 RtlFreeUnicodeString( &cmdlineW );
1599 }
1600 }
1601
1602 /***********************************************************************
1603 * SetupInstallServicesFromInfSectionW (SETUPAPI.@)
1604 */
1605 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF Inf, PCWSTR SectionName, DWORD Flags)
1606 {
1607 return SetupInstallServicesFromInfSectionExW( Inf, SectionName, Flags,
1608 NULL, NULL, NULL, NULL );
1609 }
1610
1611 /***********************************************************************
1612 * SetupInstallServicesFromInfSectionA (SETUPAPI.@)
1613 */
1614 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF Inf, PCSTR SectionName, DWORD Flags)
1615 {
1616 return SetupInstallServicesFromInfSectionExA( Inf, SectionName, Flags,
1617 NULL, NULL, NULL, NULL );
1618 }
1619
1620 /***********************************************************************
1621 * SetupInstallServicesFromInfSectionExA (SETUPAPI.@)
1622 */
1623 BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
1624 {
1625 UNICODE_STRING sectionnameW;
1626 BOOL ret = FALSE;
1627
1628 if (RtlCreateUnicodeStringFromAsciiz( &sectionnameW, sectionname ))
1629 {
1630 ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
1631 RtlFreeUnicodeString( &sectionnameW );
1632 }
1633 else
1634 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1635
1636 return ret;
1637 }
1638
1639
1640 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
1641 {
1642 DWORD required;
1643 PWSTR buf = NULL;
1644
1645 *value = NULL;
1646
1647 if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
1648 && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
1649 return FALSE;
1650
1651 buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
1652 if ( ! buf )
1653 {
1654 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1655 return FALSE;
1656 }
1657
1658 if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
1659 {
1660 HeapFree( GetProcessHeap(), 0, buf );
1661 return FALSE;
1662 }
1663
1664 *value = buf;
1665 return TRUE;
1666 }
1667
1668
1669 static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
1670 {
1671 LPWSTR buffer, end;
1672 INT res;
1673
1674 if (! GetLineText( hinf, section_name, key_name, &buffer ) )
1675 return FALSE;
1676
1677 res = wcstol( buffer, &end, 0 );
1678 if (end != buffer && !*end)
1679 {
1680 HeapFree(GetProcessHeap(), 0, buffer);
1681 *value = res;
1682 return TRUE;
1683 }
1684 else
1685 {
1686 HeapFree(GetProcessHeap(), 0, buffer);
1687 SetLastError( ERROR_INVALID_DATA );
1688 return FALSE;
1689 }
1690 }
1691
1692
1693 BOOL GetStringField( PINFCONTEXT context, DWORD index, PWSTR *value)
1694 {
1695 DWORD RequiredSize;
1696 BOOL ret;
1697
1698 ret = SetupGetStringFieldW(
1699 context,
1700 index,
1701 NULL, 0,
1702 &RequiredSize);
1703 if (!ret)
1704 return FALSE;
1705 else if (RequiredSize == 0)
1706 {
1707 *value = NULL;
1708 return TRUE;
1709 }
1710
1711 /* We got the needed size for the buffer */
1712 *value = MyMalloc(RequiredSize * sizeof(WCHAR));
1713 if (!*value)
1714 {
1715 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1716 return FALSE;
1717 }
1718 ret = SetupGetStringFieldW(
1719 context,
1720 index,
1721 *value, RequiredSize, NULL);
1722 if (!ret)
1723 MyFree(*value);
1724
1725 return ret;
1726 }
1727
1728 static VOID FixupServiceBinaryPath(
1729 IN DWORD ServiceType,
1730 IN OUT LPWSTR *ServiceBinary)
1731 {
1732 LPWSTR Buffer;
1733 WCHAR ReactOSDir[MAX_PATH];
1734 DWORD RosDirLength, ServiceLength, Win32Length;
1735
1736 GetWindowsDirectoryW(ReactOSDir, MAX_PATH);
1737 RosDirLength = strlenW(ReactOSDir);
1738 ServiceLength = strlenW(*ServiceBinary);
1739
1740 /* Check and fix two things:
1741 1. Get rid of C:\ReactOS and use relative
1742 path instead.
1743 2. Add %SystemRoot% for Win32 services */
1744
1745 if (ServiceLength < RosDirLength)
1746 return;
1747
1748 if (!wcsnicmp(*ServiceBinary, ReactOSDir, RosDirLength))
1749 {
1750 /* Yes, the first part is the C:\ReactOS\, just skip it */
1751 MoveMemory(*ServiceBinary, *ServiceBinary + RosDirLength + 1,
1752 (ServiceLength - RosDirLength) * sizeof(WCHAR));
1753
1754 /* Handle Win32-services differently */
1755 if (ServiceType & SERVICE_WIN32)
1756 {
1757 Win32Length = (ServiceLength - RosDirLength) * sizeof(WCHAR)
1758 - sizeof(L'\\') + sizeof(L"%SystemRoot%\\");
1759 Buffer = MyMalloc(Win32Length);
1760
1761 wcscpy(Buffer, L"%SystemRoot%\\");
1762 wcscat(Buffer, *ServiceBinary);
1763 MyFree(*ServiceBinary);
1764
1765 *ServiceBinary = Buffer;
1766 }
1767 }
1768 }
1769
1770 static BOOL InstallOneService(
1771 struct DeviceInfoSet *list,
1772 IN HINF hInf,
1773 IN LPCWSTR ServiceSection,
1774 IN LPCWSTR ServiceName,
1775 IN UINT ServiceFlags)
1776 {
1777 SC_HANDLE hSCManager = NULL;
1778 SC_HANDLE hService = NULL;
1779 LPDWORD GroupOrder = NULL;
1780 LPQUERY_SERVICE_CONFIGW ServiceConfig = NULL;
1781 HKEY hServicesKey, hServiceKey;
1782 LONG rc;
1783 BOOL ret = FALSE;
1784
1785 HKEY hGroupOrderListKey = NULL;
1786 LPWSTR ServiceBinary = NULL;
1787 LPWSTR LoadOrderGroup = NULL;
1788 LPWSTR DisplayName = NULL;
1789 LPWSTR Description = NULL;
1790 LPWSTR Dependencies = NULL;
1791 LPWSTR SecurityDescriptor = NULL;
1792 PSECURITY_DESCRIPTOR sd = NULL;
1793 INT ServiceType, StartType, ErrorControl;
1794 DWORD dwRegType;
1795 DWORD tagId = (DWORD)-1;
1796 BOOL useTag;
1797
1798 if (!GetIntField(hInf, ServiceSection, ServiceTypeKey, &ServiceType))
1799 {
1800 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1801 goto cleanup;
1802 }
1803 if (!GetIntField(hInf, ServiceSection, StartTypeKey, &StartType))
1804 {
1805 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1806 goto cleanup;
1807 }
1808 if (!GetIntField(hInf, ServiceSection, ErrorControlKey, &ErrorControl))
1809 {
1810 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1811 goto cleanup;
1812 }
1813 useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1814
1815 hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1816 if (hSCManager == NULL)
1817 goto cleanup;
1818
1819 if (!GetLineText(hInf, ServiceSection, ServiceBinaryKey, &ServiceBinary))
1820 {
1821 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1822 goto cleanup;
1823 }
1824
1825 /* Adjust binary path according to the service type */
1826 FixupServiceBinaryPath(ServiceType, &ServiceBinary);
1827
1828 /* Don't check return value, as these fields are optional and
1829 * GetLineText initialize output parameter even on failure */
1830 GetLineText(hInf, ServiceSection, LoadOrderGroupKey, &LoadOrderGroup);
1831 GetLineText(hInf, ServiceSection, DisplayNameKey, &DisplayName);
1832 GetLineText(hInf, ServiceSection, DescriptionKey, &Description);
1833 GetLineText(hInf, ServiceSection, DependenciesKey, &Dependencies);
1834
1835 /* If there is no group, we must not request a tag */
1836 if (!LoadOrderGroup || !*LoadOrderGroup)
1837 useTag = FALSE;
1838
1839 hService = OpenServiceW(
1840 hSCManager,
1841 ServiceName,
1842 DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | WRITE_DAC);
1843 if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1844 goto cleanup;
1845
1846 if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
1847 {
1848 ret = DeleteService(hService);
1849 if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
1850 goto cleanup;
1851 }
1852
1853 if (hService == NULL)
1854 {
1855 /* Create new service */
1856 hService = CreateServiceW(
1857 hSCManager,
1858 ServiceName,
1859 DisplayName,
1860 WRITE_DAC,
1861 ServiceType,
1862 StartType,
1863 ErrorControl,
1864 ServiceBinary,
1865 LoadOrderGroup,
1866 useTag ? &tagId : NULL,
1867 Dependencies,
1868 NULL, NULL);
1869 if (hService == NULL)
1870 goto cleanup;
1871 }
1872 else
1873 {
1874 DWORD bufferSize;
1875 /* Read current configuration */
1876 if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1877 {
1878 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1879 goto cleanup;
1880 ServiceConfig = MyMalloc(bufferSize);
1881 if (!ServiceConfig)
1882 {
1883 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1884 goto cleanup;
1885 }
1886 if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1887 goto cleanup;
1888 }
1889 tagId = ServiceConfig->dwTagId;
1890
1891 /* Update configuration */
1892 ret = ChangeServiceConfigW(
1893 hService,
1894 ServiceType,
1895 (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1896 (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1897 ServiceBinary,
1898 (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1899 useTag ? &tagId : NULL,
1900 (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1901 NULL, NULL,
1902 (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1903 if (!ret)
1904 goto cleanup;
1905 }
1906
1907 /* Set security */
1908 if (GetLineText(hInf, ServiceSection, SecurityKey, &SecurityDescriptor))
1909 {
1910 ret = ConvertStringSecurityDescriptorToSecurityDescriptorW(SecurityDescriptor, SDDL_REVISION_1, &sd, NULL);
1911 if (!ret)
1912 goto cleanup;
1913 ret = SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd);
1914 if (!ret)
1915 goto cleanup;
1916 }
1917
1918 /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1919
1920 if (useTag)
1921 {
1922 /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1923 LPCWSTR lpLoadOrderGroup;
1924 DWORD bufferSize;
1925
1926 lpLoadOrderGroup = LoadOrderGroup;
1927 if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1928 lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1929
1930 rc = RegOpenKeyW(
1931 list ? list->HKLM : HKEY_LOCAL_MACHINE,
1932 GroupOrderListKey,
1933 &hGroupOrderListKey);
1934 if (rc != ERROR_SUCCESS)
1935 {
1936 SetLastError(rc);
1937 goto cleanup;
1938 }
1939 rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1940 if (rc == ERROR_FILE_NOT_FOUND)
1941 bufferSize = sizeof(DWORD);
1942 else if (rc != ERROR_SUCCESS)
1943 {
1944 SetLastError(rc);
1945 goto cleanup;
1946 }
1947 else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1948 {
1949 SetLastError(ERROR_GEN_FAILURE);
1950 goto cleanup;
1951 }
1952 /* Allocate buffer to store existing data + the new tag */
1953 GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
1954 if (!GroupOrder)
1955 {
1956 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1957 goto cleanup;
1958 }
1959 if (rc == ERROR_SUCCESS)
1960 {
1961 /* Read existing data */
1962 rc = RegQueryValueExW(
1963 hGroupOrderListKey,
1964 lpLoadOrderGroup,
1965 NULL,
1966 NULL,
1967 (BYTE*)GroupOrder,
1968 &bufferSize);
1969 if (rc != ERROR_SUCCESS)
1970 {
1971 SetLastError(rc);
1972 goto cleanup;
1973 }
1974 if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1975 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1976 }
1977 else
1978 {
1979 GroupOrder[0] = 0;
1980 }
1981 GroupOrder[0]++;
1982 if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1983 GroupOrder[1] = tagId;
1984 else
1985 GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
1986
1987 rc = RegSetValueExW(
1988 hGroupOrderListKey,
1989 lpLoadOrderGroup,
1990 0,
1991 REG_BINARY,
1992 (BYTE*)GroupOrder,
1993 bufferSize + sizeof(DWORD));
1994 if (rc != ERROR_SUCCESS)
1995 {
1996 SetLastError(rc);
1997 goto cleanup;
1998 }
1999 }
2000
2001 /* Handle AddReg and DelReg */
2002 rc = RegOpenKeyExW(
2003 list ? list->HKLM : HKEY_LOCAL_MACHINE,
2004 REGSTR_PATH_SERVICES,
2005 0,
2006 READ_CONTROL,
2007 &hServicesKey);
2008 if (rc != ERROR_SUCCESS)
2009 {
2010 SetLastError(rc);
2011 goto cleanup;
2012 }
2013 rc = RegOpenKeyExW(
2014 hServicesKey,
2015 ServiceName,
2016 0,
2017 KEY_READ | KEY_WRITE,
2018 &hServiceKey);
2019 RegCloseKey(hServicesKey);
2020 if (rc != ERROR_SUCCESS)
2021 {
2022 SetLastError(rc);
2023 goto cleanup;
2024 }
2025
2026 ret = SetupInstallFromInfSectionW(
2027 NULL,
2028 hInf,
2029 ServiceSection,
2030 SPINST_REGISTRY,
2031 hServiceKey,
2032 NULL,
2033 0,
2034 NULL,
2035 NULL,
2036 NULL,
2037 NULL);
2038 RegCloseKey(hServiceKey);
2039
2040 cleanup:
2041 if (hSCManager != NULL)
2042 CloseServiceHandle(hSCManager);
2043 if (hService != NULL)
2044 CloseServiceHandle(hService);
2045 if (hGroupOrderListKey != NULL)
2046 RegCloseKey(hGroupOrderListKey);
2047 if (sd != NULL)
2048 LocalFree(sd);
2049 MyFree(ServiceConfig);
2050 MyFree(ServiceBinary);
2051 MyFree(LoadOrderGroup);
2052 MyFree(DisplayName);
2053 MyFree(Description);
2054 MyFree(Dependencies);
2055 MyFree(SecurityDescriptor);
2056 MyFree(GroupOrder);
2057
2058 TRACE("Returning %d\n", ret);
2059 return ret;
2060 }
2061
2062
2063 /***********************************************************************
2064 * SetupInstallServicesFromInfSectionExW (SETUPAPI.@)
2065 */
2066 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
2067 {
2068 struct DeviceInfoSet *list = NULL;
2069 BOOL ret = FALSE;
2070
2071 TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
2072 flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
2073
2074 if (!sectionname)
2075 SetLastError(ERROR_INVALID_PARAMETER);
2076 else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
2077 {
2078 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));
2079 SetLastError(ERROR_INVALID_FLAGS);
2080 }
2081 else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
2082 SetLastError(ERROR_INVALID_HANDLE);
2083 else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
2084 SetLastError(ERROR_INVALID_HANDLE);
2085 else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
2086 SetLastError(ERROR_INVALID_USER_BUFFER);
2087 else if (reserved1 != NULL || reserved2 != NULL)
2088 SetLastError(ERROR_INVALID_PARAMETER);
2089 else
2090 {
2091 struct needs_callback_info needs_info;
2092 LPWSTR ServiceName = NULL;
2093 LPWSTR ServiceSection = NULL;
2094 INT ServiceFlags;
2095 INFCONTEXT ContextService;
2096 BOOL bNeedReboot = FALSE;
2097
2098 /* Parse 'Include' and 'Needs' directives */
2099 iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
2100 needs_info.type = 1;
2101 needs_info.flags = flags;
2102 needs_info.devinfo = DeviceInfoSet;
2103 needs_info.devinfo_data = DeviceInfoData;
2104 needs_info.reserved1 = reserved1;
2105 needs_info.reserved2 = reserved2;
2106 iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
2107
2108 if (flags & SPSVCINST_STOPSERVICE)
2109 {
2110 FIXME("Stopping the device not implemented\n");
2111 /* This may lead to require a reboot */
2112 /* bNeedReboot = TRUE; */
2113 #if 0
2114 SERVICE_STATUS ServiceStatus;
2115 ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
2116 if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
2117 goto done;
2118 if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
2119 {
2120 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
2121 goto done;
2122 }
2123 #endif
2124 flags &= ~SPSVCINST_STOPSERVICE;
2125 }
2126
2127 if (!(ret = SetupFindFirstLineW( hinf, sectionname, NULL, &ContextService )))
2128 {
2129 SetLastError( ERROR_SECTION_NOT_FOUND );
2130 goto done;
2131 }
2132
2133 ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
2134 while (ret)
2135 {
2136 if (!GetStringField(&ContextService, 1, &ServiceName))
2137 goto done;
2138
2139 ret = SetupGetIntField(
2140 &ContextService,
2141 2, /* Field index */
2142 &ServiceFlags);
2143 if (!ret)
2144 {
2145 /* The field may be empty. Ignore the error */
2146 ServiceFlags = 0;
2147 }
2148
2149 if (!GetStringField(&ContextService, 3, &ServiceSection))
2150 goto done;
2151
2152 ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
2153 if (!ret)
2154 goto done;
2155
2156 if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
2157 {
2158 ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
2159 if (!ret)
2160 goto done;
2161 }
2162
2163 HeapFree(GetProcessHeap(), 0, ServiceName);
2164 HeapFree(GetProcessHeap(), 0, ServiceSection);
2165 ServiceName = ServiceSection = NULL;
2166 ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
2167 }
2168
2169 if (bNeedReboot)
2170 SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
2171 else
2172 SetLastError(ERROR_SUCCESS);
2173 ret = TRUE;
2174 }
2175 done:
2176 TRACE("Returning %d\n", ret);
2177 return ret;
2178 }
2179
2180
2181 /***********************************************************************
2182 * SetupCopyOEMInfA (SETUPAPI.@)
2183 */
2184 BOOL WINAPI SetupCopyOEMInfA(
2185 IN PCSTR SourceInfFileName,
2186 IN PCSTR OEMSourceMediaLocation,
2187 IN DWORD OEMSourceMediaType,
2188 IN DWORD CopyStyle,
2189 OUT PSTR DestinationInfFileName OPTIONAL,
2190 IN DWORD DestinationInfFileNameSize,
2191 OUT PDWORD RequiredSize OPTIONAL,
2192 OUT PSTR* DestinationInfFileNameComponent OPTIONAL)
2193 {
2194 PWSTR SourceInfFileNameW = NULL;
2195 PWSTR OEMSourceMediaLocationW = NULL;
2196 PWSTR DestinationInfFileNameW = NULL;
2197 PWSTR DestinationInfFileNameComponentW = NULL;
2198 BOOL ret = FALSE;
2199 DWORD size;
2200
2201 TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2202 SourceInfFileName, OEMSourceMediaLocation, OEMSourceMediaType,
2203 CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2204 RequiredSize, DestinationInfFileNameComponent);
2205
2206 if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2207 SetLastError(ERROR_INVALID_PARAMETER);
2208 else if (!(SourceInfFileNameW = pSetupMultiByteToUnicode(SourceInfFileName, CP_ACP)))
2209 SetLastError(ERROR_INVALID_PARAMETER);
2210 else if (OEMSourceMediaType != SPOST_NONE && !(OEMSourceMediaLocationW = pSetupMultiByteToUnicode(OEMSourceMediaLocation, CP_ACP)))
2211 SetLastError(ERROR_INVALID_PARAMETER);
2212 else
2213 {
2214 if (DestinationInfFileNameSize != 0)
2215 {
2216 DestinationInfFileNameW = MyMalloc(DestinationInfFileNameSize * sizeof(WCHAR));
2217 if (!DestinationInfFileNameW)
2218 {
2219 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2220 goto cleanup;
2221 }
2222 }
2223
2224 ret = SetupCopyOEMInfW(
2225 SourceInfFileNameW,
2226 OEMSourceMediaLocationW,
2227 OEMSourceMediaType,
2228 CopyStyle,
2229 DestinationInfFileNameW,
2230 DestinationInfFileNameSize,
2231 &size,
2232 DestinationInfFileNameComponent ? &DestinationInfFileNameComponentW : NULL);
2233 if (!ret)
2234 {
2235 if (RequiredSize) *RequiredSize = size;
2236 goto cleanup;
2237 }
2238
2239 if (DestinationInfFileNameSize != 0)
2240 {
2241 if (WideCharToMultiByte(CP_ACP, 0, DestinationInfFileNameW, -1,
2242 DestinationInfFileName, DestinationInfFileNameSize, NULL, NULL) == 0)
2243 {
2244 DestinationInfFileName[0] = '\0';
2245 goto cleanup;
2246 }
2247 }
2248 if (DestinationInfFileNameComponent)
2249 {
2250 if (DestinationInfFileNameComponentW)
2251 *DestinationInfFileNameComponent = &DestinationInfFileName[DestinationInfFileNameComponentW - DestinationInfFileNameW];
2252 else
2253 *DestinationInfFileNameComponent = NULL;
2254 }
2255 ret = TRUE;
2256 }
2257
2258 cleanup:
2259 MyFree(SourceInfFileNameW);
2260 MyFree(OEMSourceMediaLocationW);
2261 MyFree(DestinationInfFileNameW);
2262 TRACE("Returning %d\n", ret);
2263 if (ret) SetLastError(ERROR_SUCCESS);
2264 return ret;
2265 }
2266
2267 static int compare_files( HANDLE file1, HANDLE file2 )
2268 {
2269 char buffer1[2048];
2270 char buffer2[2048];
2271 DWORD size1;
2272 DWORD size2;
2273
2274 while( ReadFile(file1, buffer1, sizeof(buffer1), &size1, NULL) &&
2275 ReadFile(file2, buffer2, sizeof(buffer2), &size2, NULL) )
2276 {
2277 int ret;
2278 if (size1 != size2)
2279 return size1 > size2 ? 1 : -1;
2280 if (!size1)
2281 return 0;
2282 ret = memcmp( buffer1, buffer2, size1 );
2283 if (ret)
2284 return ret;
2285 }
2286
2287 return 0;
2288 }
2289
2290 /***********************************************************************
2291 * SetupCopyOEMInfW (SETUPAPI.@)
2292 */
2293 BOOL WINAPI SetupCopyOEMInfW(
2294 IN PCWSTR SourceInfFileName,
2295 IN PCWSTR OEMSourceMediaLocation,
2296 IN DWORD OEMSourceMediaType,
2297 IN DWORD CopyStyle,
2298 OUT PWSTR DestinationInfFileName OPTIONAL,
2299 IN DWORD DestinationInfFileNameSize,
2300 OUT PDWORD RequiredSize OPTIONAL,
2301 OUT PWSTR* DestinationInfFileNameComponent OPTIONAL)
2302 {
2303 BOOL ret = FALSE;
2304
2305 TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2306 debugstr_w(SourceInfFileName), debugstr_w(OEMSourceMediaLocation), OEMSourceMediaType,
2307 CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2308 RequiredSize, DestinationInfFileNameComponent);
2309
2310 if (!SourceInfFileName)
2311 SetLastError(ERROR_INVALID_PARAMETER);
2312 else if (OEMSourceMediaType != SPOST_NONE && OEMSourceMediaType != SPOST_PATH && OEMSourceMediaType != SPOST_URL)
2313 SetLastError(ERROR_INVALID_PARAMETER);
2314 else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
2315 {
2316 TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
2317 SetLastError(ERROR_INVALID_FLAGS);
2318 }
2319 else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2320 SetLastError(ERROR_INVALID_PARAMETER);
2321 else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
2322 {
2323 FIXME("CopyStyle 0x%x not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
2324 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2325 }
2326 else
2327 {
2328 HANDLE hSearch = INVALID_HANDLE_VALUE;
2329 WIN32_FIND_DATAW FindFileData;
2330 BOOL AlreadyExists;
2331 DWORD NextFreeNumber = 0;
2332 SIZE_T len;
2333 LPWSTR pFullFileName = NULL;
2334 LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2335 HANDLE hSourceFile = INVALID_HANDLE_VALUE;
2336
2337 if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
2338 FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
2339
2340 /* Check if source file exists, and open it */
2341 if (strchrW(SourceInfFileName, '\\' ) || strchrW(SourceInfFileName, '/' ))
2342 {
2343 WCHAR *path;
2344
2345 if (!(len = GetFullPathNameW(SourceInfFileName, 0, NULL, NULL)))
2346 return FALSE;
2347 if (!(path = MyMalloc(len * sizeof(WCHAR))))
2348 {
2349 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2350 return FALSE;
2351 }
2352 GetFullPathNameW(SourceInfFileName, len, path, NULL);
2353 hSourceFile = CreateFileW(
2354 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2355 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2356 NULL, OPEN_EXISTING, 0, NULL);
2357 MyFree(path);
2358 }
2359 else /* try Windows directory */
2360 {
2361 WCHAR *path, *p;
2362 static const WCHAR Inf[] = {'\\','i','n','f','\\',0};
2363 static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
2364
2365 len = GetWindowsDirectoryW(NULL, 0) + strlenW(SourceInfFileName) + 12;
2366 if (!(path = MyMalloc(len * sizeof(WCHAR))))
2367 {
2368 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2369 return FALSE;
2370 }
2371 GetWindowsDirectoryW(path, len);
2372 p = path + strlenW(path);
2373 strcpyW(p, Inf);
2374 strcatW(p, SourceInfFileName);
2375 hSourceFile = CreateFileW(
2376 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2377 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2378 NULL, OPEN_EXISTING, 0, NULL);
2379 if (hSourceFile == INVALID_HANDLE_VALUE)
2380 {
2381 strcpyW(p, System32);
2382 strcatW(p, SourceInfFileName);
2383 hSourceFile = CreateFileW(
2384 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2385 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2386 NULL, OPEN_EXISTING, 0, NULL);
2387 }
2388 MyFree(path);
2389 }
2390 if (hSourceFile == INVALID_HANDLE_VALUE)
2391 {
2392 SetLastError(ERROR_FILE_NOT_FOUND);
2393 goto cleanup;
2394 }
2395
2396 /* Prepare .inf file specification */
2397 len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
2398 pFullFileName = MyMalloc(len * sizeof(WCHAR));
2399 if (!pFullFileName)
2400 {
2401 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2402 goto cleanup;
2403 }
2404 len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2405 if (len == 0 || len > MAX_PATH)
2406 goto cleanup;
2407 if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2408 strcatW(pFullFileName, BackSlash);
2409 strcatW(pFullFileName, InfDirectory);
2410 pFileName = &pFullFileName[strlenW(pFullFileName)];
2411
2412 /* Search if the specified .inf file already exists in %WINDIR%\Inf */
2413 AlreadyExists = FALSE;
2414 strcpyW(pFileName, OemFileMask);
2415 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2416 if (hSearch != INVALID_HANDLE_VALUE)
2417 {
2418 LARGE_INTEGER SourceFileSize;
2419
2420 if (GetFileSizeEx(hSourceFile, &SourceFileSize))
2421 {
2422 do
2423 {
2424 LARGE_INTEGER DestFileSize;
2425 HANDLE hDestFile;
2426
2427 strcpyW(pFileName, FindFileData.cFileName);
2428 hDestFile = CreateFileW(
2429 pFullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2430 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2431 NULL, OPEN_EXISTING, 0, NULL);
2432 if (hDestFile != INVALID_HANDLE_VALUE)
2433 {
2434 if (GetFileSizeEx(hDestFile, &DestFileSize)
2435 && DestFileSize.QuadPart == SourceFileSize.QuadPart
2436 && !compare_files(hSourceFile, hDestFile))
2437 {
2438 TRACE("%s already exists as %s\n",
2439 debugstr_w(SourceInfFileName), debugstr_w(pFileName));
2440 AlreadyExists = TRUE;
2441 }
2442 }
2443 } while (!AlreadyExists && FindNextFileW(hSearch, &FindFileData));
2444 }
2445 FindClose(hSearch);
2446 hSearch = INVALID_HANDLE_VALUE;
2447 }
2448
2449 if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
2450 {
2451 /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
2452 SetLastError(ERROR_FILE_NOT_FOUND);
2453 goto cleanup;
2454 }
2455 else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
2456 {
2457 DWORD Size = strlenW(pFileName) + 1;
2458
2459 if (RequiredSize)
2460 *RequiredSize = Size;
2461 if (DestinationInfFileNameSize == 0)
2462 SetLastError(ERROR_FILE_EXISTS);
2463 else if (DestinationInfFileNameSize < Size)
2464 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2465 else
2466 {
2467 SetLastError(ERROR_FILE_EXISTS);
2468 strcpyW(DestinationInfFileName, pFileName);
2469 }
2470 goto cleanup;
2471 }
2472
2473 /* Search the number to give to OEM??.INF */
2474 strcpyW(pFileName, OemFileMask);
2475 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2476 if (hSearch == INVALID_HANDLE_VALUE)
2477 {
2478 if (GetLastError() != ERROR_FILE_NOT_FOUND)
2479 goto cleanup;
2480 }
2481 else
2482 {
2483 do
2484 {
2485 DWORD CurrentNumber;
2486 if (swscanf(FindFileData.cFileName, OemFileSpecification, &CurrentNumber) == 1
2487 && CurrentNumber <= 99999)
2488 {
2489 if (CurrentNumber >= NextFreeNumber)
2490 NextFreeNumber = CurrentNumber + 1;
2491 }
2492 } while (FindNextFileW(hSearch, &FindFileData));
2493 }
2494
2495 if (NextFreeNumber > 99999)
2496 {
2497 ERR("Too much custom .inf files\n");
2498 SetLastError(ERROR_GEN_FAILURE);
2499 goto cleanup;
2500 }
2501
2502 /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
2503 sprintfW(pFileName, OemFileSpecification, NextFreeNumber);
2504 TRACE("Next available file is %s\n", debugstr_w(pFileName));
2505
2506 if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
2507 {
2508 TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
2509 goto cleanup;
2510 }
2511
2512 len = strlenW(pFullFileName) + 1;
2513 if (RequiredSize)
2514 *RequiredSize = len;
2515 if (DestinationInfFileName)
2516 {
2517 if (DestinationInfFileNameSize >= len)
2518 {
2519 strcpyW(DestinationInfFileName, pFullFileName);
2520 if (DestinationInfFileNameComponent)
2521 *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
2522 }
2523 else
2524 {
2525 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2526 goto cleanup;
2527 }
2528 }
2529
2530 if (CopyStyle & SP_COPY_DELETESOURCE)
2531 {
2532 if (!DeleteFileW(SourceInfFileName))
2533 {
2534 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
2535 goto cleanup;
2536 }
2537 }
2538
2539 ret = TRUE;
2540
2541 cleanup:
2542 if (hSourceFile != INVALID_HANDLE_VALUE)
2543 CloseHandle(hSourceFile);
2544 if (hSearch != INVALID_HANDLE_VALUE)
2545 FindClose(hSearch);
2546 MyFree(pFullFileName);
2547 }
2548
2549 TRACE("Returning %d\n", ret);
2550 if (ret) SetLastError(ERROR_SUCCESS);
2551 return ret;
2552 }