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