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