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