[SETUPAPI]
[reactos.git] / reactos / dll / win32 / setupapi / install.c
1 /*
2 * Setupapi install routines
3 *
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 * 2005-2006 Hervé Poussineau (hpoussin@reactos.org)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "setupapi_private.h"
23
24 #include <winsvc.h>
25 #include <ndk/cmfuncs.h>
26
27 /* Unicode constants */
28 static const WCHAR BackSlash[] = {'\\',0};
29 static const WCHAR GroupOrderListKey[] = {'S','Y','S','T','E','M','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','C','o','n','t','r','o','l','\\','G','r','o','u','p','O','r','d','e','r','L','i','s','t',0};
30 static const WCHAR InfDirectory[] = {'i','n','f','\\',0};
31 static const WCHAR OemFileMask[] = {'o','e','m','*','.','i','n','f',0};
32 static const WCHAR OemFileSpecification[] = {'o','e','m','%','l','u','.','i','n','f',0};
33 static const WCHAR DotLnk[] = {'.','l','n','k',0};
34 static const WCHAR DotServices[] = {'.','S','e','r','v','i','c','e','s',0};
35
36 static const WCHAR DependenciesKey[] = {'D','e','p','e','n','d','e','n','c','i','e','s',0};
37 static const WCHAR DescriptionKey[] = {'D','e','s','c','r','i','p','t','i','o','n',0};
38 static const WCHAR DisplayNameKey[] = {'D','i','s','p','l','a','y','N','a','m','e',0};
39 static const WCHAR ErrorControlKey[] = {'E','r','r','o','r','C','o','n','t','r','o','l',0};
40 static const WCHAR LoadOrderGroupKey[] = {'L','o','a','d','O','r','d','e','r','G','r','o','u','p',0};
41 static const WCHAR SecurityKey[] = {'S','e','c','u','r','i','t','y',0};
42 static const WCHAR ServiceBinaryKey[] = {'S','e','r','v','i','c','e','B','i','n','a','r','y',0};
43 static const WCHAR ServiceTypeKey[] = {'S','e','r','v','i','c','e','T','y','p','e',0};
44 static const WCHAR StartTypeKey[] = {'S','t','a','r','t','T','y','p','e',0};
45
46 static const WCHAR Name[] = {'N','a','m','e',0};
47 static const WCHAR CmdLine[] = {'C','m','d','L','i','n','e',0};
48 static const WCHAR SubDir[] = {'S','u','b','D','i','r',0};
49 static const WCHAR WorkingDir[] = {'W','o','r','k','i','n','g','D','i','r',0};
50 static const WCHAR IconPath[] = {'I','c','o','n','P','a','t','h',0};
51 static const WCHAR IconIndex[] = {'I','c','o','n','I','n','d','e','x',0};
52 static const WCHAR HotKey[] = {'H','o','t','K','e','y',0};
53 static const WCHAR InfoTip[] = {'I','n','f','o','T','i','p',0};
54 static const WCHAR DisplayResource[] = {'D','i','s','p','l','a','y','R','e','s','o','u','r','c','e',0};
55
56 /* info passed to callback functions dealing with files */
57 struct files_callback_info
58 {
59 HSPFILEQ queue;
60 PCWSTR src_root;
61 UINT copy_flags;
62 HINF layout;
63 };
64
65 /* info passed to callback functions dealing with the registry */
66 struct registry_callback_info
67 {
68 HKEY default_root;
69 BOOL delete;
70 };
71
72 /* info passed to callback functions dealing with registering dlls */
73 struct register_dll_info
74 {
75 PSP_FILE_CALLBACK_W callback;
76 PVOID callback_context;
77 BOOL unregister;
78 };
79
80 /* info passed to callback functions dealing with Needs directives */
81 struct needs_callback_info
82 {
83 UINT type;
84
85 HWND owner;
86 UINT flags;
87 HKEY key_root;
88 LPCWSTR src_root;
89 UINT copy_flags;
90 PVOID callback;
91 PVOID context;
92 HDEVINFO devinfo;
93 PSP_DEVINFO_DATA devinfo_data;
94 PVOID reserved1;
95 PVOID reserved2;
96 };
97
98 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
99 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value);
100 typedef HRESULT (WINAPI *COINITIALIZE)(IN LPVOID pvReserved);
101 typedef HRESULT (WINAPI *COCREATEINSTANCE)(IN REFCLSID rclsid, IN LPUNKNOWN pUnkOuter, IN DWORD dwClsContext, IN REFIID riid, OUT LPVOID *ppv);
102 typedef HRESULT (WINAPI *COUNINITIALIZE)(VOID);
103
104 /* Unicode constants */
105 static const WCHAR AddService[] = {'A','d','d','S','e','r','v','i','c','e',0};
106 static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0};
107 static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0};
108 static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0};
109 static const WCHAR Ini2Reg[] = {'I','n','i','2','R','e','g',0};
110 static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0};
111 static const WCHAR AddReg[] = {'A','d','d','R','e','g',0};
112 static const WCHAR DelReg[] = {'D','e','l','R','e','g',0};
113 static const WCHAR BitReg[] = {'B','i','t','R','e','g',0};
114 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
115 static const WCHAR CopyINF[] = {'C','o','p','y','I','N','F',0};
116 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
117 static const WCHAR RegisterDlls[] = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
118 static const WCHAR UnregisterDlls[] = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
119 static const WCHAR ProfileItems[] = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
120 static const WCHAR Include[] = {'I','n','c','l','u','d','e',0};
121 static const WCHAR Needs[] = {'N','e','e','d','s',0};
122 static const WCHAR DotSecurity[] = {'.','S','e','c','u','r','i','t','y',0};
123 #ifdef __WINESRC__
124 static const WCHAR WineFakeDlls[] = {'W','i','n','e','F','a','k','e','D','l','l','s',0};
125 #endif
126
127
128 /***********************************************************************
129 * get_field_string
130 *
131 * Retrieve the contents of a field, dynamically growing the buffer if necessary.
132 */
133 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
134 WCHAR *static_buffer, DWORD *size )
135 {
136 DWORD required;
137
138 if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
139 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
140 {
141 /* now grow the buffer */
142 if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
143 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
144 *size = required;
145 if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
146 }
147 if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
148 return NULL;
149 }
150
151
152 /***********************************************************************
153 * copy_files_callback
154 *
155 * Called once for each CopyFiles entry in a given section.
156 */
157 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
158 {
159 struct files_callback_info *info = arg;
160
161 if (field[0] == '@') /* special case: copy single file */
162 SetupQueueDefaultCopyW( info->queue, info->layout ? info->layout : hinf, info->src_root, NULL, field+1, info->copy_flags );
163 else
164 SetupQueueCopySectionW( info->queue, info->src_root, info->layout ? info->layout : hinf, hinf, field, info->copy_flags );
165 return TRUE;
166 }
167
168
169 /***********************************************************************
170 * delete_files_callback
171 *
172 * Called once for each DelFiles entry in a given section.
173 */
174 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
175 {
176 struct files_callback_info *info = arg;
177 SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
178 return TRUE;
179 }
180
181
182 /***********************************************************************
183 * rename_files_callback
184 *
185 * Called once for each RenFiles entry in a given section.
186 */
187 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
188 {
189 struct files_callback_info *info = arg;
190 SetupQueueRenameSectionW( info->queue, hinf, 0, field );
191 return TRUE;
192 }
193
194
195 /***********************************************************************
196 * get_root_key
197 *
198 * Retrieve the registry root key from its name.
199 */
200 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
201 {
202 static const WCHAR HKCR[] = {'H','K','C','R',0};
203 static const WCHAR HKCU[] = {'H','K','C','U',0};
204 static const WCHAR HKLM[] = {'H','K','L','M',0};
205 static const WCHAR HKU[] = {'H','K','U',0};
206 static const WCHAR HKR[] = {'H','K','R',0};
207
208 if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
209 if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
210 if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
211 if (!strcmpiW( name, HKU )) return HKEY_USERS;
212 if (!strcmpiW( name, HKR )) return def_root;
213 return 0;
214 }
215
216
217 /***********************************************************************
218 * append_multi_sz_value
219 *
220 * Append a multisz string to a multisz registry value.
221 */
222 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
223 DWORD str_size )
224 {
225 DWORD size, type, total;
226 WCHAR *buffer, *p;
227
228 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
229 if (type != REG_MULTI_SZ) return;
230
231 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
232 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
233
234 /* compare each string against all the existing ones */
235 total = size;
236 while (*strings)
237 {
238 int len = strlenW(strings) + 1;
239
240 for (p = buffer; *p; p += strlenW(p) + 1)
241 if (!strcmpiW( p, strings )) break;
242
243 if (!*p) /* not found, need to append it */
244 {
245 memcpy( p, strings, len * sizeof(WCHAR) );
246 p[len] = 0;
247 total += len * sizeof(WCHAR);
248 }
249 strings += len;
250 }
251 if (total != size)
252 {
253 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
254 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total + sizeof(WCHAR) );
255 }
256 done:
257 HeapFree( GetProcessHeap(), 0, buffer );
258 }
259
260
261 /***********************************************************************
262 * delete_multi_sz_value
263 *
264 * Remove a string from a multisz registry value.
265 */
266 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
267 {
268 DWORD size, type;
269 WCHAR *buffer, *src, *dst;
270
271 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
272 if (type != REG_MULTI_SZ) return;
273 /* allocate double the size, one for value before and one for after */
274 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
275 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
276 src = buffer;
277 dst = buffer + size;
278 while (*src)
279 {
280 int len = strlenW(src) + 1;
281 if (strcmpiW( src, string ))
282 {
283 memcpy( dst, src, len * sizeof(WCHAR) );
284 dst += len;
285 }
286 src += len;
287 }
288 *dst++ = 0;
289 if (dst != buffer + 2*size) /* did we remove something? */
290 {
291 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
292 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
293 (BYTE *)(buffer + size), dst - (buffer + size) );
294 }
295 done:
296 HeapFree( GetProcessHeap(), 0, buffer );
297 }
298
299
300 /***********************************************************************
301 * do_reg_operation
302 *
303 * Perform an add/delete registry operation depending on the flags.
304 */
305 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
306 {
307 DWORD type, size;
308
309 if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */
310 {
311 if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
312 {
313 if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
314 {
315 WCHAR *str;
316
317 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
318 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
319 SetupGetStringFieldW( context, 5, str, size, NULL );
320 delete_multi_sz_value( hkey, value, str );
321 HeapFree( GetProcessHeap(), 0, str );
322 }
323 else RegDeleteValueW( hkey, value );
324 }
325 else NtDeleteKey( hkey );
326 return TRUE;
327 }
328
329 if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
330
331 if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
332 {
333 BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
334 if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
335 if (!exists && (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
336 }
337
338 switch(flags & FLG_ADDREG_TYPE_MASK)
339 {
340 case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break;
341 case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break;
342 case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
343 case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break;
344 case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break;
345 case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break;
346 default: type = flags >> 16; break;
347 }
348
349 if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
350 (type == REG_DWORD && SetupGetFieldCount(context) == 5))
351 {
352 static const WCHAR empty;
353 WCHAR *str = NULL;
354
355 if (type == REG_MULTI_SZ)
356 {
357 if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
358 if (size)
359 {
360 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
361 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
362 }
363 if (flags & FLG_ADDREG_APPEND)
364 {
365 if (!str) return TRUE;
366 append_multi_sz_value( hkey, value, str, size );
367 HeapFree( GetProcessHeap(), 0, str );
368 return TRUE;
369 }
370 /* else fall through to normal string handling */
371 }
372 else
373 {
374 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
375 if (size)
376 {
377 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
378 SetupGetStringFieldW( context, 5, str, size, NULL );
379 }
380 }
381
382 if (type == REG_DWORD)
383 {
384 DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
385 TRACE( "setting dword %s to %x\n", debugstr_w(value), dw );
386 RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
387 }
388 else
389 {
390 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
391 if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
392 else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
393 }
394 HeapFree( GetProcessHeap(), 0, str );
395 return TRUE;
396 }
397 else /* get the binary data */
398 {
399 BYTE *data = NULL;
400
401 if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
402 if (size)
403 {
404 if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
405 TRACE( "setting binary data %s len %d\n", debugstr_w(value), size );
406 SetupGetBinaryField( context, 5, data, size, NULL );
407 }
408 RegSetValueExW( hkey, value, 0, type, data, size );
409 HeapFree( GetProcessHeap(), 0, data );
410 return TRUE;
411 }
412 }
413
414
415 /***********************************************************************
416 * registry_callback
417 *
418 * Called once for each AddReg and DelReg entry in a given section.
419 */
420 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
421 {
422 struct registry_callback_info *info = arg;
423 LPWSTR security_key, security_descriptor;
424 INFCONTEXT context, security_context;
425 PSECURITY_DESCRIPTOR sd = NULL;
426 SECURITY_ATTRIBUTES security_attributes = { 0, };
427 HKEY root_key, hkey;
428 DWORD required;
429
430 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
431 if (!ok)
432 return TRUE;
433
434 /* Check for .Security section */
435 security_key = MyMalloc( (strlenW( field ) + strlenW( DotSecurity )) * sizeof(WCHAR) + sizeof(UNICODE_NULL) );
436 if (!security_key)
437 {
438 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
439 return FALSE;
440 }
441 strcpyW( security_key, field );
442 strcatW( security_key, DotSecurity );
443 ok = SetupFindFirstLineW( hinf, security_key, NULL, &security_context );
444 MyFree(security_key);
445 if (ok)
446 {
447 if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, NULL, 0, &required ))
448 return FALSE;
449 security_descriptor = MyMalloc( required * sizeof(WCHAR) );
450 if (!security_descriptor)
451 {
452 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
453 return FALSE;
454 }
455 if (!SetupGetLineTextW( &security_context, NULL, NULL, NULL, security_descriptor, required, NULL ))
456 return FALSE;
457 ok = ConvertStringSecurityDescriptorToSecurityDescriptorW( security_descriptor, SDDL_REVISION_1, &sd, NULL );
458 MyFree( security_descriptor );
459 if (!ok)
460 return FALSE;
461 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
462 security_attributes.lpSecurityDescriptor = sd;
463 }
464
465 for (ok = TRUE; ok; ok = SetupFindNextLine( &context, &context ))
466 {
467 WCHAR buffer[MAX_INF_STRING_LENGTH];
468 INT flags;
469
470 /* get root */
471 if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
472 continue;
473 if (!(root_key = get_root_key( buffer, info->default_root )))
474 continue;
475
476 /* get key */
477 if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
478 *buffer = 0;
479
480 /* get flags */
481 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
482
483 if (!info->delete)
484 {
485 if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */
486 }
487 else
488 {
489 if (!flags) flags = FLG_ADDREG_DELREG_BIT;
490 else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */
491 }
492
493 if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
494 {
495 if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */
496 }
497 else if (RegCreateKeyExW( root_key, buffer, 0, NULL, 0, MAXIMUM_ALLOWED,
498 sd ? &security_attributes : NULL, &hkey, NULL ))
499 {
500 ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
501 continue;
502 }
503 TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
504
505 /* get value name */
506 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
507 *buffer = 0;
508
509 /* and now do it */
510 if (!do_reg_operation( hkey, buffer, &context, flags ))
511 {
512 if (hkey != root_key) RegCloseKey( hkey );
513 if (sd) LocalFree( sd );
514 return FALSE;
515 }
516 if (hkey != root_key) RegCloseKey( hkey );
517 }
518 if (sd) LocalFree( sd );
519 return TRUE;
520 }
521
522
523 /***********************************************************************
524 * do_register_dll
525 *
526 * Register or unregister a dll.
527 */
528 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
529 INT flags, INT timeout, const WCHAR *args )
530 {
531 HMODULE module;
532 HRESULT res;
533 SP_REGISTER_CONTROL_STATUSW status;
534 #ifdef __WINESRC__
535 IMAGE_NT_HEADERS *nt;
536 #endif
537
538 status.cbSize = sizeof(status);
539 status.FileName = path;
540 status.FailureCode = SPREG_SUCCESS;
541 status.Win32Error = ERROR_SUCCESS;
542
543 if (info->callback)
544 {
545 switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
546 (UINT_PTR)&status, !info->unregister ))
547 {
548 case FILEOP_ABORT:
549 SetLastError( ERROR_OPERATION_ABORTED );
550 return FALSE;
551 case FILEOP_SKIP:
552 return TRUE;
553 case FILEOP_DOIT:
554 break;
555 }
556 }
557
558 if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
559 {
560 WARN( "could not load %s\n", debugstr_w(path) );
561 status.FailureCode = SPREG_LOADLIBRARY;
562 status.Win32Error = GetLastError();
563 goto done;
564 }
565
566 #ifdef __WINESRC__
567 if ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
568 {
569 /* file is an executable, not a dll */
570 STARTUPINFOW startup;
571 PROCESS_INFORMATION info;
572 WCHAR *cmd_line;
573 BOOL res;
574 static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
575 static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
576
577 FreeLibrary( module );
578 module = NULL;
579 if (!args) args = default_args;
580 cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
581 sprintfW( cmd_line, format, path, args );
582 memset( &startup, 0, sizeof(startup) );
583 startup.cb = sizeof(startup);
584 TRACE( "executing %s\n", debugstr_w(cmd_line) );
585 res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
586 HeapFree( GetProcessHeap(), 0, cmd_line );
587 if (!res)
588 {
589 status.FailureCode = SPREG_LOADLIBRARY;
590 status.Win32Error = GetLastError();
591 goto done;
592 }
593 CloseHandle( info.hThread );
594
595 if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
596 {
597 /* timed out, kill the process */
598 TerminateProcess( info.hProcess, 1 );
599 status.FailureCode = SPREG_TIMEOUT;
600 status.Win32Error = ERROR_TIMEOUT;
601 }
602 CloseHandle( info.hProcess );
603 goto done;
604 }
605 #endif // __WINESRC__
606
607 if (flags & FLG_REGSVR_DLLREGISTER)
608 {
609 const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
610 HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
611
612 if (!func)
613 {
614 status.FailureCode = SPREG_GETPROCADDR;
615 status.Win32Error = GetLastError();
616 goto done;
617 }
618
619 TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
620 res = func();
621
622 if (FAILED(res))
623 {
624 WARN( "calling %s in %s returned error %x\n", entry_point, debugstr_w(path), res );
625 status.FailureCode = SPREG_REGSVR;
626 status.Win32Error = res;
627 goto done;
628 }
629 }
630
631 if (flags & FLG_REGSVR_DLLINSTALL)
632 {
633 HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
634
635 if (!func)
636 {
637 status.FailureCode = SPREG_GETPROCADDR;
638 status.Win32Error = GetLastError();
639 goto done;
640 }
641
642 TRACE( "calling DllInstall(%d,%s) in %s\n",
643 !info->unregister, debugstr_w(args), debugstr_w(path) );
644 res = func( !info->unregister, args );
645
646 if (FAILED(res))
647 {
648 WARN( "calling DllInstall in %s returned error %x\n", debugstr_w(path), res );
649 status.FailureCode = SPREG_REGSVR;
650 status.Win32Error = res;
651 goto done;
652 }
653 }
654
655 done:
656 if (module) FreeLibrary( module );
657 if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
658 (UINT_PTR)&status, !info->unregister );
659 return TRUE;
660 }
661
662
663 /***********************************************************************
664 * register_dlls_callback
665 *
666 * Called once for each RegisterDlls entry in a given section.
667 */
668 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
669 {
670 struct register_dll_info *info = arg;
671 INFCONTEXT context;
672 BOOL ret = TRUE;
673 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
674
675 for (; ok; ok = SetupFindNextLine( &context, &context ))
676 {
677 WCHAR *path, *args, *p;
678 WCHAR buffer[MAX_INF_STRING_LENGTH];
679 INT flags, timeout;
680
681 /* get directory */
682 if (!(path = PARSER_get_dest_dir( &context ))) continue;
683
684 /* get dll name */
685 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
686 goto done;
687 if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
688 (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
689 path = p;
690 p += strlenW(p);
691 if (p == path || p[-1] != '\\') *p++ = '\\';
692 strcpyW( p, buffer );
693
694 /* get flags */
695 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
696
697 /* get timeout */
698 if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
699
700 /* get command line */
701 args = NULL;
702 if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
703 args = buffer;
704
705 ret = do_register_dll( info, path, flags, timeout, args );
706
707 done:
708 HeapFree( GetProcessHeap(), 0, path );
709 if (!ret) break;
710 }
711 return ret;
712 }
713
714 #ifdef __WINESRC__
715 /***********************************************************************
716 * fake_dlls_callback
717 *
718 * Called once for each WineFakeDlls entry in a given section.
719 */
720 static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
721 {
722 INFCONTEXT context;
723 BOOL ret = TRUE;
724 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
725
726 for (; ok; ok = SetupFindNextLine( &context, &context ))
727 {
728 WCHAR *path, *p;
729 WCHAR buffer[MAX_INF_STRING_LENGTH];
730
731 /* get directory */
732 if (!(path = PARSER_get_dest_dir( &context ))) continue;
733
734 /* get dll name */
735 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
736 goto done;
737 if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
738 (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
739 path = p;
740 p += strlenW(p);
741 if (p == path || p[-1] != '\\') *p++ = '\\';
742 strcpyW( p, buffer );
743
744 /* get source dll */
745 if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
746 p = buffer; /* otherwise use target base name as default source */
747
748 create_fake_dll( path, p ); /* ignore errors */
749
750 done:
751 HeapFree( GetProcessHeap(), 0, path );
752 if (!ret) break;
753 }
754 return ret;
755 }
756 #endif // __WINESRC__
757
758 /***********************************************************************
759 * update_ini_callback
760 *
761 * Called once for each UpdateInis entry in a given section.
762 */
763 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
764 {
765 INFCONTEXT context;
766
767 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
768
769 for (; ok; ok = SetupFindNextLine( &context, &context ))
770 {
771 WCHAR buffer[MAX_INF_STRING_LENGTH];
772 WCHAR filename[MAX_INF_STRING_LENGTH];
773 WCHAR section[MAX_INF_STRING_LENGTH];
774 WCHAR entry[MAX_INF_STRING_LENGTH];
775 WCHAR string[MAX_INF_STRING_LENGTH];
776 LPWSTR divider;
777
778 if (!SetupGetStringFieldW( &context, 1, filename,
779 sizeof(filename)/sizeof(WCHAR), NULL ))
780 continue;
781
782 if (!SetupGetStringFieldW( &context, 2, section,
783 sizeof(section)/sizeof(WCHAR), NULL ))
784 continue;
785
786 if (!SetupGetStringFieldW( &context, 4, buffer,
787 sizeof(buffer)/sizeof(WCHAR), NULL ))
788 continue;
789
790 divider = strchrW(buffer,'=');
791 if (divider)
792 {
793 *divider = 0;
794 strcpyW(entry,buffer);
795 divider++;
796 strcpyW(string,divider);
797 }
798 else
799 {
800 strcpyW(entry,buffer);
801 string[0]=0;
802 }
803
804 TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
805 debugstr_w(string),debugstr_w(section),debugstr_w(filename));
806 WritePrivateProfileStringW(section,entry,string,filename);
807
808 }
809 return TRUE;
810 }
811
812 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
813 {
814 FIXME( "should update ini fields %s\n", debugstr_w(field) );
815 return TRUE;
816 }
817
818 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
819 {
820 FIXME( "should do ini2reg %s\n", debugstr_w(field) );
821 return TRUE;
822 }
823
824 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
825 {
826 FIXME( "should do logconf %s\n", debugstr_w(field) );
827 return TRUE;
828 }
829
830 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
831 {
832 FIXME( "should do bitreg %s\n", debugstr_w(field) );
833 return TRUE;
834 }
835
836 static BOOL Concatenate(int DirId, LPCWSTR SubDirPart, LPCWSTR NamePart, LPWSTR *pFullName)
837 {
838 DWORD dwRequired = 0;
839 LPCWSTR Dir;
840 LPWSTR FullName;
841
842 *pFullName = NULL;
843
844 Dir = DIRID_get_string(DirId);
845 if (Dir)
846 dwRequired += wcslen(Dir) + 1;
847 if (SubDirPart)
848 dwRequired += wcslen(SubDirPart) + 1;
849 if (NamePart)
850 dwRequired += wcslen(NamePart);
851 dwRequired = dwRequired * sizeof(WCHAR) + sizeof(UNICODE_NULL);
852
853 FullName = MyMalloc(dwRequired);
854 if (!FullName)
855 {
856 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
857 return FALSE;
858 }
859 FullName[0] = UNICODE_NULL;
860
861 if (Dir)
862 {
863 wcscat(FullName, Dir);
864 if (FullName[wcslen(FullName) - 1] != '\\')
865 wcscat(FullName, BackSlash);
866 }
867 if (SubDirPart)
868 {
869 wcscat(FullName, SubDirPart);
870 if (FullName[wcslen(FullName) - 1] != '\\')
871 wcscat(FullName, BackSlash);
872 }
873 if (NamePart)
874 wcscat(FullName, NamePart);
875
876 *pFullName = FullName;
877 return TRUE;
878 }
879
880 /***********************************************************************
881 * profile_items_callback
882 *
883 * Called once for each ProfileItems entry in a given section.
884 */
885 static BOOL
886 profile_items_callback(
887 IN HINF hInf,
888 IN PCWSTR SectionName,
889 IN PVOID Arg)
890 {
891 INFCONTEXT Context;
892 LPWSTR LinkSubDir = NULL, LinkName = NULL;
893 INT LinkAttributes = 0;
894 INT LinkFolder = 0;
895 INT FileDirId = 0;
896 INT CSIDL = CSIDL_COMMON_PROGRAMS;
897 LPWSTR FileSubDir = NULL;
898 INT DirId = 0;
899 LPWSTR SubDirPart = NULL, NamePart = NULL;
900 LPWSTR FullLinkName = NULL, FullFileName = NULL, FullWorkingDir = NULL, FullIconName = NULL;
901 INT IconIdx = 0;
902 LPWSTR lpHotKey = NULL, lpInfoTip = NULL;
903 LPWSTR DisplayName = NULL;
904 INT DisplayResId = 0;
905 BOOL ret = FALSE;
906 DWORD Index, Required;
907
908 IShellLinkW *psl;
909 IPersistFile *ppf;
910 HMODULE hOle32 = NULL;
911 COINITIALIZE pCoInitialize;
912 COCREATEINSTANCE pCoCreateInstance;
913 COUNINITIALIZE pCoUninitialize;
914 HRESULT hr;
915
916 TRACE("hInf %p, SectionName %s, Arg %p\n",
917 hInf, debugstr_w(SectionName), Arg);
918
919 /* Read 'Name' entry */
920 if (!SetupFindFirstLineW(hInf, SectionName, Name, &Context))
921 goto cleanup;
922 if (!GetStringField(&Context, 1, &LinkName))
923 goto cleanup;
924 if (SetupGetFieldCount(&Context) >= 2)
925 {
926 if (!SetupGetIntField(&Context, 2, &LinkAttributes))
927 goto cleanup;
928 }
929 if (SetupGetFieldCount(&Context) >= 3)
930 {
931 if (!SetupGetIntField(&Context, 3, &LinkFolder))
932 goto cleanup;
933 }
934
935 /* Read 'CmdLine' entry */
936 if (!SetupFindFirstLineW(hInf, SectionName, CmdLine, &Context))
937 goto cleanup;
938 Index = 1;
939 if (!SetupGetIntField(&Context, Index++, &FileDirId))
940 goto cleanup;
941 if (SetupGetFieldCount(&Context) >= 3)
942 {
943 if (!GetStringField(&Context, Index++, &FileSubDir))
944 goto cleanup;
945 }
946 if (!GetStringField(&Context, Index++, &NamePart))
947 goto cleanup;
948 if (!Concatenate(FileDirId, FileSubDir, NamePart, &FullFileName))
949 goto cleanup;
950 MyFree(NamePart);
951 NamePart = NULL;
952
953 /* Read 'SubDir' entry */
954 if ((LinkAttributes & FLG_PROFITEM_GROUP) == 0 && SetupFindFirstLineW(hInf, SectionName, SubDir, &Context))
955 {
956 if (!GetStringField(&Context, 1, &LinkSubDir))
957 goto cleanup;
958 }
959
960 /* Read 'WorkingDir' entry */
961 if (SetupFindFirstLineW(hInf, SectionName, WorkingDir, &Context))
962 {
963 if (!SetupGetIntField(&Context, 1, &DirId))
964 goto cleanup;
965 if (SetupGetFieldCount(&Context) >= 2)
966 {
967 if (!GetStringField(&Context, 2, &SubDirPart))
968 goto cleanup;
969 }
970 if (!Concatenate(DirId, SubDirPart, NULL, &FullWorkingDir))
971 goto cleanup;
972 MyFree(SubDirPart);
973 SubDirPart = NULL;
974 }
975 else
976 {
977 if (!Concatenate(FileDirId, FileSubDir, NULL, &FullWorkingDir))
978 goto cleanup;
979 }
980
981 /* Read 'IconPath' entry */
982 if (SetupFindFirstLineW(hInf, SectionName, IconPath, &Context))
983 {
984 Index = 1;
985 if (!SetupGetIntField(&Context, Index++, &DirId))
986 goto cleanup;
987 if (SetupGetFieldCount(&Context) >= 3)
988 {
989 if (!GetStringField(&Context, Index++, &SubDirPart))
990 goto cleanup;
991 }
992 if (!GetStringField(&Context, Index, &NamePart))
993 goto cleanup;
994 if (!Concatenate(DirId, SubDirPart, NamePart, &FullIconName))
995 goto cleanup;
996 MyFree(SubDirPart);
997 MyFree(NamePart);
998 SubDirPart = NamePart = NULL;
999 }
1000 else
1001 {
1002 FullIconName = pSetupDuplicateString(FullFileName);
1003 if (!FullIconName)
1004 goto cleanup;
1005 }
1006
1007 /* Read 'IconIndex' entry */
1008 if (SetupFindFirstLineW(hInf, SectionName, IconIndex, &Context))
1009 {
1010 if (!SetupGetIntField(&Context, 1, &IconIdx))
1011 goto cleanup;
1012 }
1013
1014 /* Read 'HotKey' and 'InfoTip' entries */
1015 GetLineText(hInf, SectionName, HotKey, &lpHotKey);
1016 GetLineText(hInf, SectionName, InfoTip, &lpInfoTip);
1017
1018 /* Read 'DisplayResource' entry */
1019 if (SetupFindFirstLineW(hInf, SectionName, DisplayResource, &Context))
1020 {
1021 if (!GetStringField(&Context, 1, &DisplayName))
1022 goto cleanup;
1023 if (!SetupGetIntField(&Context, 2, &DisplayResId))
1024 goto cleanup;
1025 }
1026
1027 /* Some debug */
1028 TRACE("Link is %s\\%s, attributes 0x%x\n", debugstr_w(LinkSubDir), debugstr_w(LinkName), LinkAttributes);
1029 TRACE("File is %s\n", debugstr_w(FullFileName));
1030 TRACE("Working dir %s\n", debugstr_w(FullWorkingDir));
1031 TRACE("Icon is %s, %d\n", debugstr_w(FullIconName), IconIdx);
1032 TRACE("Hotkey %s\n", debugstr_w(lpHotKey));
1033 TRACE("InfoTip %s\n", debugstr_w(lpInfoTip));
1034 TRACE("Display %s, %d\n", DisplayName, DisplayResId);
1035
1036 /* Load ole32.dll */
1037 hOle32 = LoadLibraryA("ole32.dll");
1038 if (!hOle32)
1039 goto cleanup;
1040 pCoInitialize = (COINITIALIZE)GetProcAddress(hOle32, "CoInitialize");
1041 if (!pCoInitialize)
1042 goto cleanup;
1043 pCoCreateInstance = (COCREATEINSTANCE)GetProcAddress(hOle32, "CoCreateInstance");
1044 if (!pCoCreateInstance)
1045 goto cleanup;
1046 pCoUninitialize = (COUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize");
1047 if (!pCoUninitialize)
1048 goto cleanup;
1049
1050 /* Create shortcut */
1051 hr = pCoInitialize(NULL);
1052 if (!SUCCEEDED(hr))
1053 {
1054 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
1055 SetLastError(HRESULT_CODE(hr));
1056 else
1057 SetLastError(E_FAIL);
1058 goto cleanup;
1059 }
1060 hr = pCoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&psl);
1061 if (SUCCEEDED(hr))
1062 {
1063 /* Fill link properties */
1064 hr = IShellLinkW_SetPath(psl, FullFileName);
1065 if (SUCCEEDED(hr))
1066 hr = IShellLinkW_SetArguments(psl, L"");
1067 if (SUCCEEDED(hr))
1068 hr = IShellLinkW_SetWorkingDirectory(psl, FullWorkingDir);
1069 if (SUCCEEDED(hr))
1070 hr = IShellLinkW_SetIconLocation(psl, FullIconName, IconIdx);
1071 if (SUCCEEDED(hr) && lpHotKey)
1072 FIXME("Need to store hotkey %s in shell link\n", debugstr_w(lpHotKey));
1073 if (SUCCEEDED(hr) && lpInfoTip)
1074 hr = IShellLinkW_SetDescription(psl, lpInfoTip);
1075 if (SUCCEEDED(hr) && DisplayName)
1076 FIXME("Need to store display name %s, %d in shell link\n", debugstr_w(DisplayName), DisplayResId);
1077 if (SUCCEEDED(hr))
1078 {
1079 hr = IShellLinkW_QueryInterface(psl, &IID_IPersistFile, (LPVOID*)&ppf);
1080 if (SUCCEEDED(hr))
1081 {
1082 Required = (MAX_PATH + 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 {
1794 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1795 goto cleanup;
1796 }
1797 if (!GetIntField(hInf, ServiceSection, StartTypeKey, &StartType))
1798 {
1799 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1800 goto cleanup;
1801 }
1802 if (!GetIntField(hInf, ServiceSection, ErrorControlKey, &ErrorControl))
1803 {
1804 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1805 goto cleanup;
1806 }
1807 useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1808
1809 hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1810 if (hSCManager == NULL)
1811 goto cleanup;
1812
1813 if (!GetLineText(hInf, ServiceSection, ServiceBinaryKey, &ServiceBinary))
1814 {
1815 SetLastError( ERROR_BAD_SERVICE_INSTALLSECT );
1816 goto cleanup;
1817 }
1818
1819 /* Adjust binary path according to the service type */
1820 FixupServiceBinaryPath(ServiceType, &ServiceBinary);
1821
1822 /* Don't check return value, as these fields are optional and
1823 * GetLineText initialize output parameter even on failure */
1824 GetLineText(hInf, ServiceSection, LoadOrderGroupKey, &LoadOrderGroup);
1825 GetLineText(hInf, ServiceSection, DisplayNameKey, &DisplayName);
1826 GetLineText(hInf, ServiceSection, DescriptionKey, &Description);
1827 GetLineText(hInf, ServiceSection, DependenciesKey, &Dependencies);
1828
1829 /* If there is no group, we must not request a tag */
1830 if (!LoadOrderGroup || !*LoadOrderGroup)
1831 useTag = FALSE;
1832
1833 hService = OpenServiceW(
1834 hSCManager,
1835 ServiceName,
1836 DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | WRITE_DAC);
1837 if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1838 goto cleanup;
1839
1840 if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
1841 {
1842 ret = DeleteService(hService);
1843 if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
1844 goto cleanup;
1845 }
1846
1847 if (hService == NULL)
1848 {
1849 /* Create new service */
1850 hService = CreateServiceW(
1851 hSCManager,
1852 ServiceName,
1853 DisplayName,
1854 WRITE_DAC,
1855 ServiceType,
1856 StartType,
1857 ErrorControl,
1858 ServiceBinary,
1859 LoadOrderGroup,
1860 useTag ? &tagId : NULL,
1861 Dependencies,
1862 NULL, NULL);
1863 if (hService == NULL)
1864 goto cleanup;
1865 }
1866 else
1867 {
1868 DWORD bufferSize;
1869 /* Read current configuration */
1870 if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1871 {
1872 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1873 goto cleanup;
1874 ServiceConfig = MyMalloc(bufferSize);
1875 if (!ServiceConfig)
1876 {
1877 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1878 goto cleanup;
1879 }
1880 if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1881 goto cleanup;
1882 }
1883 tagId = ServiceConfig->dwTagId;
1884
1885 /* Update configuration */
1886 ret = ChangeServiceConfigW(
1887 hService,
1888 ServiceType,
1889 (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1890 (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1891 ServiceBinary,
1892 (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1893 useTag ? &tagId : NULL,
1894 (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1895 NULL, NULL,
1896 (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1897 if (!ret)
1898 goto cleanup;
1899 }
1900
1901 /* Set security */
1902 if (GetLineText(hInf, ServiceSection, SecurityKey, &SecurityDescriptor))
1903 {
1904 ret = ConvertStringSecurityDescriptorToSecurityDescriptorW(SecurityDescriptor, SDDL_REVISION_1, &sd, NULL);
1905 if (!ret)
1906 goto cleanup;
1907 ret = SetServiceObjectSecurity(hService, DACL_SECURITY_INFORMATION, sd);
1908 if (!ret)
1909 goto cleanup;
1910 }
1911
1912 /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1913
1914 if (useTag)
1915 {
1916 /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1917 LPCWSTR lpLoadOrderGroup;
1918 DWORD bufferSize;
1919
1920 lpLoadOrderGroup = LoadOrderGroup;
1921 if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1922 lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1923
1924 rc = RegOpenKeyW(
1925 list ? list->HKLM : HKEY_LOCAL_MACHINE,
1926 GroupOrderListKey,
1927 &hGroupOrderListKey);
1928 if (rc != ERROR_SUCCESS)
1929 {
1930 SetLastError(rc);
1931 goto cleanup;
1932 }
1933 rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1934 if (rc == ERROR_FILE_NOT_FOUND)
1935 bufferSize = sizeof(DWORD);
1936 else if (rc != ERROR_SUCCESS)
1937 {
1938 SetLastError(rc);
1939 goto cleanup;
1940 }
1941 else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1942 {
1943 SetLastError(ERROR_GEN_FAILURE);
1944 goto cleanup;
1945 }
1946 /* Allocate buffer to store existing data + the new tag */
1947 GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
1948 if (!GroupOrder)
1949 {
1950 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1951 goto cleanup;
1952 }
1953 if (rc == ERROR_SUCCESS)
1954 {
1955 /* Read existing data */
1956 rc = RegQueryValueExW(
1957 hGroupOrderListKey,
1958 lpLoadOrderGroup,
1959 NULL,
1960 NULL,
1961 (BYTE*)GroupOrder,
1962 &bufferSize);
1963 if (rc != ERROR_SUCCESS)
1964 {
1965 SetLastError(rc);
1966 goto cleanup;
1967 }
1968 if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1969 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1970 }
1971 else
1972 {
1973 GroupOrder[0] = 0;
1974 }
1975 GroupOrder[0]++;
1976 if (ServiceFlags & SPSVCINST_TAGTOFRONT)
1977 GroupOrder[1] = tagId;
1978 else
1979 GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
1980
1981 rc = RegSetValueExW(
1982 hGroupOrderListKey,
1983 lpLoadOrderGroup,
1984 0,
1985 REG_BINARY,
1986 (BYTE*)GroupOrder,
1987 bufferSize + sizeof(DWORD));
1988 if (rc != ERROR_SUCCESS)
1989 {
1990 SetLastError(rc);
1991 goto cleanup;
1992 }
1993 }
1994
1995 /* Handle AddReg and DelReg */
1996 rc = RegOpenKeyExW(
1997 list ? list->HKLM : HKEY_LOCAL_MACHINE,
1998 REGSTR_PATH_SERVICES,
1999 0,
2000 READ_CONTROL,
2001 &hServicesKey);
2002 if (rc != ERROR_SUCCESS)
2003 {
2004 SetLastError(rc);
2005 goto cleanup;
2006 }
2007 rc = RegOpenKeyExW(
2008 hServicesKey,
2009 ServiceName,
2010 0,
2011 KEY_READ | KEY_WRITE,
2012 &hServiceKey);
2013 RegCloseKey(hServicesKey);
2014 if (rc != ERROR_SUCCESS)
2015 {
2016 SetLastError(rc);
2017 goto cleanup;
2018 }
2019
2020 ret = SetupInstallFromInfSectionW(
2021 NULL,
2022 hInf,
2023 ServiceSection,
2024 SPINST_REGISTRY,
2025 hServiceKey,
2026 NULL,
2027 0,
2028 NULL,
2029 NULL,
2030 NULL,
2031 NULL);
2032 RegCloseKey(hServiceKey);
2033
2034 cleanup:
2035 if (hSCManager != NULL)
2036 CloseServiceHandle(hSCManager);
2037 if (hService != NULL)
2038 CloseServiceHandle(hService);
2039 if (hGroupOrderListKey != NULL)
2040 RegCloseKey(hGroupOrderListKey);
2041 if (sd != NULL)
2042 LocalFree(sd);
2043 MyFree(ServiceConfig);
2044 MyFree(ServiceBinary);
2045 MyFree(LoadOrderGroup);
2046 MyFree(DisplayName);
2047 MyFree(Description);
2048 MyFree(Dependencies);
2049 MyFree(SecurityDescriptor);
2050 MyFree(GroupOrder);
2051
2052 TRACE("Returning %d\n", ret);
2053 return ret;
2054 }
2055
2056
2057 /***********************************************************************
2058 * SetupInstallServicesFromInfSectionExW (SETUPAPI.@)
2059 */
2060 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
2061 {
2062 struct DeviceInfoSet *list = NULL;
2063 BOOL ret = FALSE;
2064
2065 TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
2066 flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
2067
2068 if (!sectionname)
2069 SetLastError(ERROR_INVALID_PARAMETER);
2070 else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
2071 {
2072 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));
2073 SetLastError(ERROR_INVALID_FLAGS);
2074 }
2075 else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
2076 SetLastError(ERROR_INVALID_HANDLE);
2077 else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEVICE_INFO_SET_MAGIC)
2078 SetLastError(ERROR_INVALID_HANDLE);
2079 else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
2080 SetLastError(ERROR_INVALID_USER_BUFFER);
2081 else if (reserved1 != NULL || reserved2 != NULL)
2082 SetLastError(ERROR_INVALID_PARAMETER);
2083 else
2084 {
2085 struct needs_callback_info needs_info;
2086 LPWSTR ServiceName = NULL;
2087 LPWSTR ServiceSection = NULL;
2088 INT ServiceFlags;
2089 INFCONTEXT ContextService;
2090 BOOL bNeedReboot = FALSE;
2091
2092 /* Parse 'Include' and 'Needs' directives */
2093 iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
2094 needs_info.type = 1;
2095 needs_info.flags = flags;
2096 needs_info.devinfo = DeviceInfoSet;
2097 needs_info.devinfo_data = DeviceInfoData;
2098 needs_info.reserved1 = reserved1;
2099 needs_info.reserved2 = reserved2;
2100 iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
2101
2102 if (flags & SPSVCINST_STOPSERVICE)
2103 {
2104 FIXME("Stopping the device not implemented\n");
2105 /* This may lead to require a reboot */
2106 /* bNeedReboot = TRUE; */
2107 #if 0
2108 SERVICE_STATUS ServiceStatus;
2109 ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
2110 if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
2111 goto done;
2112 if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
2113 {
2114 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
2115 goto done;
2116 }
2117 #endif
2118 flags &= ~SPSVCINST_STOPSERVICE;
2119 }
2120
2121 if (!(ret = SetupFindFirstLineW( hinf, sectionname, NULL, &ContextService )))
2122 {
2123 SetLastError( ERROR_SECTION_NOT_FOUND );
2124 goto done;
2125 }
2126
2127 ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
2128 while (ret)
2129 {
2130 if (!GetStringField(&ContextService, 1, &ServiceName))
2131 goto done;
2132
2133 ret = SetupGetIntField(
2134 &ContextService,
2135 2, /* Field index */
2136 &ServiceFlags);
2137 if (!ret)
2138 {
2139 /* The field may be empty. Ignore the error */
2140 ServiceFlags = 0;
2141 }
2142
2143 if (!GetStringField(&ContextService, 3, &ServiceSection))
2144 goto done;
2145
2146 ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
2147 if (!ret)
2148 goto done;
2149
2150 if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
2151 {
2152 ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
2153 if (!ret)
2154 goto done;
2155 }
2156
2157 HeapFree(GetProcessHeap(), 0, ServiceName);
2158 HeapFree(GetProcessHeap(), 0, ServiceSection);
2159 ServiceName = ServiceSection = NULL;
2160 ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
2161 }
2162
2163 if (bNeedReboot)
2164 SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
2165 else
2166 SetLastError(ERROR_SUCCESS);
2167 ret = TRUE;
2168 }
2169 done:
2170 TRACE("Returning %d\n", ret);
2171 return ret;
2172 }
2173
2174
2175 /***********************************************************************
2176 * SetupCopyOEMInfA (SETUPAPI.@)
2177 */
2178 BOOL WINAPI SetupCopyOEMInfA(
2179 IN PCSTR SourceInfFileName,
2180 IN PCSTR OEMSourceMediaLocation,
2181 IN DWORD OEMSourceMediaType,
2182 IN DWORD CopyStyle,
2183 OUT PSTR DestinationInfFileName OPTIONAL,
2184 IN DWORD DestinationInfFileNameSize,
2185 OUT PDWORD RequiredSize OPTIONAL,
2186 OUT PSTR* DestinationInfFileNameComponent OPTIONAL)
2187 {
2188 PWSTR SourceInfFileNameW = NULL;
2189 PWSTR OEMSourceMediaLocationW = NULL;
2190 PWSTR DestinationInfFileNameW = NULL;
2191 PWSTR DestinationInfFileNameComponentW = NULL;
2192 BOOL ret = FALSE;
2193 DWORD size;
2194
2195 TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2196 SourceInfFileName, OEMSourceMediaLocation, OEMSourceMediaType,
2197 CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2198 RequiredSize, DestinationInfFileNameComponent);
2199
2200 if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2201 SetLastError(ERROR_INVALID_PARAMETER);
2202 else if (!(SourceInfFileNameW = pSetupMultiByteToUnicode(SourceInfFileName, CP_ACP)))
2203 SetLastError(ERROR_INVALID_PARAMETER);
2204 else if (OEMSourceMediaType != SPOST_NONE && !(OEMSourceMediaLocationW = pSetupMultiByteToUnicode(OEMSourceMediaLocation, CP_ACP)))
2205 SetLastError(ERROR_INVALID_PARAMETER);
2206 else
2207 {
2208 if (DestinationInfFileNameSize != 0)
2209 {
2210 DestinationInfFileNameW = MyMalloc(DestinationInfFileNameSize * sizeof(WCHAR));
2211 if (!DestinationInfFileNameW)
2212 {
2213 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2214 goto cleanup;
2215 }
2216 }
2217
2218 ret = SetupCopyOEMInfW(
2219 SourceInfFileNameW,
2220 OEMSourceMediaLocationW,
2221 OEMSourceMediaType,
2222 CopyStyle,
2223 DestinationInfFileNameW,
2224 DestinationInfFileNameSize,
2225 &size,
2226 DestinationInfFileNameComponent ? &DestinationInfFileNameComponentW : NULL);
2227 if (!ret)
2228 {
2229 if (RequiredSize) *RequiredSize = size;
2230 goto cleanup;
2231 }
2232
2233 if (DestinationInfFileNameSize != 0)
2234 {
2235 if (WideCharToMultiByte(CP_ACP, 0, DestinationInfFileNameW, -1,
2236 DestinationInfFileName, DestinationInfFileNameSize, NULL, NULL) == 0)
2237 {
2238 DestinationInfFileName[0] = '\0';
2239 goto cleanup;
2240 }
2241 }
2242 if (DestinationInfFileNameComponent)
2243 {
2244 if (DestinationInfFileNameComponentW)
2245 *DestinationInfFileNameComponent = &DestinationInfFileName[DestinationInfFileNameComponentW - DestinationInfFileNameW];
2246 else
2247 *DestinationInfFileNameComponent = NULL;
2248 }
2249 ret = TRUE;
2250 }
2251
2252 cleanup:
2253 MyFree(SourceInfFileNameW);
2254 MyFree(OEMSourceMediaLocationW);
2255 MyFree(DestinationInfFileNameW);
2256 TRACE("Returning %d\n", ret);
2257 if (ret) SetLastError(ERROR_SUCCESS);
2258 return ret;
2259 }
2260
2261 static int compare_files( HANDLE file1, HANDLE file2 )
2262 {
2263 char buffer1[2048];
2264 char buffer2[2048];
2265 DWORD size1;
2266 DWORD size2;
2267
2268 while( ReadFile(file1, buffer1, sizeof(buffer1), &size1, NULL) &&
2269 ReadFile(file2, buffer2, sizeof(buffer2), &size2, NULL) )
2270 {
2271 int ret;
2272 if (size1 != size2)
2273 return size1 > size2 ? 1 : -1;
2274 if (!size1)
2275 return 0;
2276 ret = memcmp( buffer1, buffer2, size1 );
2277 if (ret)
2278 return ret;
2279 }
2280
2281 return 0;
2282 }
2283
2284 /***********************************************************************
2285 * SetupCopyOEMInfW (SETUPAPI.@)
2286 */
2287 BOOL WINAPI SetupCopyOEMInfW(
2288 IN PCWSTR SourceInfFileName,
2289 IN PCWSTR OEMSourceMediaLocation,
2290 IN DWORD OEMSourceMediaType,
2291 IN DWORD CopyStyle,
2292 OUT PWSTR DestinationInfFileName OPTIONAL,
2293 IN DWORD DestinationInfFileNameSize,
2294 OUT PDWORD RequiredSize OPTIONAL,
2295 OUT PWSTR* DestinationInfFileNameComponent OPTIONAL)
2296 {
2297 BOOL ret = FALSE;
2298
2299 TRACE("%s %s 0x%lx 0x%lx %p 0%lu %p %p\n",
2300 debugstr_w(SourceInfFileName), debugstr_w(OEMSourceMediaLocation), OEMSourceMediaType,
2301 CopyStyle, DestinationInfFileName, DestinationInfFileNameSize,
2302 RequiredSize, DestinationInfFileNameComponent);
2303
2304 if (!SourceInfFileName)
2305 SetLastError(ERROR_INVALID_PARAMETER);
2306 else if (OEMSourceMediaType != SPOST_NONE && OEMSourceMediaType != SPOST_PATH && OEMSourceMediaType != SPOST_URL)
2307 SetLastError(ERROR_INVALID_PARAMETER);
2308 else if (CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY))
2309 {
2310 TRACE("Unknown flags: 0x%08lx\n", CopyStyle & ~(SP_COPY_DELETESOURCE | SP_COPY_REPLACEONLY | SP_COPY_NOOVERWRITE | SP_COPY_OEMINF_CATALOG_ONLY));
2311 SetLastError(ERROR_INVALID_FLAGS);
2312 }
2313 else if (!DestinationInfFileName && DestinationInfFileNameSize > 0)
2314 SetLastError(ERROR_INVALID_PARAMETER);
2315 else if (CopyStyle & SP_COPY_OEMINF_CATALOG_ONLY)
2316 {
2317 FIXME("CopyStyle 0x%x not supported\n", SP_COPY_OEMINF_CATALOG_ONLY);
2318 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2319 }
2320 else
2321 {
2322 HANDLE hSearch = INVALID_HANDLE_VALUE;
2323 WIN32_FIND_DATAW FindFileData;
2324 BOOL AlreadyExists;
2325 DWORD NextFreeNumber = 0;
2326 SIZE_T len;
2327 LPWSTR pFullFileName = NULL;
2328 LPWSTR pFileName; /* Pointer into pFullFileName buffer */
2329 HANDLE hSourceFile = INVALID_HANDLE_VALUE;
2330
2331 if (OEMSourceMediaType == SPOST_PATH || OEMSourceMediaType == SPOST_URL)
2332 FIXME("OEMSourceMediaType 0x%lx ignored\n", OEMSourceMediaType);
2333
2334 /* Check if source file exists, and open it */
2335 if (strchrW(SourceInfFileName, '\\' ) || strchrW(SourceInfFileName, '/' ))
2336 {
2337 WCHAR *path;
2338
2339 if (!(len = GetFullPathNameW(SourceInfFileName, 0, NULL, NULL)))
2340 return FALSE;
2341 if (!(path = MyMalloc(len * sizeof(WCHAR))))
2342 {
2343 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2344 return FALSE;
2345 }
2346 GetFullPathNameW(SourceInfFileName, len, path, NULL);
2347 hSourceFile = CreateFileW(
2348 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2349 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2350 NULL, OPEN_EXISTING, 0, NULL);
2351 MyFree(path);
2352 }
2353 else /* try Windows directory */
2354 {
2355 WCHAR *path, *p;
2356 static const WCHAR Inf[] = {'\\','i','n','f','\\',0};
2357 static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
2358
2359 len = GetWindowsDirectoryW(NULL, 0) + strlenW(SourceInfFileName) + 12;
2360 if (!(path = MyMalloc(len * sizeof(WCHAR))))
2361 {
2362 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2363 return FALSE;
2364 }
2365 GetWindowsDirectoryW(path, len);
2366 p = path + strlenW(path);
2367 strcpyW(p, Inf);
2368 strcatW(p, SourceInfFileName);
2369 hSourceFile = CreateFileW(
2370 path, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2371 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2372 NULL, OPEN_EXISTING, 0, NULL);
2373 if (hSourceFile == INVALID_HANDLE_VALUE)
2374 {
2375 strcpyW(p, System32);
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 }
2382 MyFree(path);
2383 }
2384 if (hSourceFile == INVALID_HANDLE_VALUE)
2385 {
2386 SetLastError(ERROR_FILE_NOT_FOUND);
2387 goto cleanup;
2388 }
2389
2390 /* Prepare .inf file specification */
2391 len = MAX_PATH + 1 + strlenW(InfDirectory) + 13;
2392 pFullFileName = MyMalloc(len * sizeof(WCHAR));
2393 if (!pFullFileName)
2394 {
2395 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2396 goto cleanup;
2397 }
2398 len = GetSystemWindowsDirectoryW(pFullFileName, MAX_PATH);
2399 if (len == 0 || len > MAX_PATH)
2400 goto cleanup;
2401 if (pFullFileName[strlenW(pFullFileName) - 1] != '\\')
2402 strcatW(pFullFileName, BackSlash);
2403 strcatW(pFullFileName, InfDirectory);
2404 pFileName = &pFullFileName[strlenW(pFullFileName)];
2405
2406 /* Search if the specified .inf file already exists in %WINDIR%\Inf */
2407 AlreadyExists = FALSE;
2408 strcpyW(pFileName, OemFileMask);
2409 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2410 if (hSearch != INVALID_HANDLE_VALUE)
2411 {
2412 LARGE_INTEGER SourceFileSize;
2413
2414 if (GetFileSizeEx(hSourceFile, &SourceFileSize))
2415 {
2416 do
2417 {
2418 LARGE_INTEGER DestFileSize;
2419 HANDLE hDestFile;
2420
2421 strcpyW(pFileName, FindFileData.cFileName);
2422 hDestFile = CreateFileW(
2423 pFullFileName, FILE_READ_DATA | FILE_READ_ATTRIBUTES,
2424 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2425 NULL, OPEN_EXISTING, 0, NULL);
2426 if (hDestFile != INVALID_HANDLE_VALUE)
2427 {
2428 if (GetFileSizeEx(hDestFile, &DestFileSize)
2429 && DestFileSize.QuadPart == SourceFileSize.QuadPart
2430 && !compare_files(hSourceFile, hDestFile))
2431 {
2432 TRACE("%s already exists as %s\n",
2433 debugstr_w(SourceInfFileName), debugstr_w(pFileName));
2434 AlreadyExists = TRUE;
2435 }
2436 }
2437 } while (!AlreadyExists && FindNextFileW(hSearch, &FindFileData));
2438 }
2439 FindClose(hSearch);
2440 hSearch = INVALID_HANDLE_VALUE;
2441 }
2442
2443 if (!AlreadyExists && CopyStyle & SP_COPY_REPLACEONLY)
2444 {
2445 /* FIXME: set DestinationInfFileName, RequiredSize, DestinationInfFileNameComponent */
2446 SetLastError(ERROR_FILE_NOT_FOUND);
2447 goto cleanup;
2448 }
2449 else if (AlreadyExists && (CopyStyle & SP_COPY_NOOVERWRITE))
2450 {
2451 DWORD Size = strlenW(pFileName) + 1;
2452
2453 if (RequiredSize)
2454 *RequiredSize = Size;
2455 if (DestinationInfFileNameSize == 0)
2456 SetLastError(ERROR_FILE_EXISTS);
2457 else if (DestinationInfFileNameSize < Size)
2458 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2459 else
2460 {
2461 SetLastError(ERROR_FILE_EXISTS);
2462 strcpyW(DestinationInfFileName, pFileName);
2463 }
2464 goto cleanup;
2465 }
2466
2467 /* Search the number to give to OEM??.INF */
2468 strcpyW(pFileName, OemFileMask);
2469 hSearch = FindFirstFileW(pFullFileName, &FindFileData);
2470 if (hSearch == INVALID_HANDLE_VALUE)
2471 {
2472 if (GetLastError() != ERROR_FILE_NOT_FOUND)
2473 goto cleanup;
2474 }
2475 else
2476 {
2477 do
2478 {
2479 DWORD CurrentNumber;
2480 if (swscanf(FindFileData.cFileName, OemFileSpecification, &CurrentNumber) == 1
2481 && CurrentNumber <= 99999)
2482 {
2483 if (CurrentNumber >= NextFreeNumber)
2484 NextFreeNumber = CurrentNumber + 1;
2485 }
2486 } while (FindNextFileW(hSearch, &FindFileData));
2487 }
2488
2489 if (NextFreeNumber > 99999)
2490 {
2491 ERR("Too much custom .inf files\n");
2492 SetLastError(ERROR_GEN_FAILURE);
2493 goto cleanup;
2494 }
2495
2496 /* Create the full path: %WINDIR%\Inf\OEM{XXXXX}.inf */
2497 sprintfW(pFileName, OemFileSpecification, NextFreeNumber);
2498 TRACE("Next available file is %s\n", debugstr_w(pFileName));
2499
2500 if (!CopyFileW(SourceInfFileName, pFullFileName, TRUE))
2501 {
2502 TRACE("CopyFileW() failed with error 0x%lx\n", GetLastError());
2503 goto cleanup;
2504 }
2505
2506 len = strlenW(pFullFileName) + 1;
2507 if (RequiredSize)
2508 *RequiredSize = len;
2509 if (DestinationInfFileName)
2510 {
2511 if (DestinationInfFileNameSize >= len)
2512 {
2513 strcpyW(DestinationInfFileName, pFullFileName);
2514 if (DestinationInfFileNameComponent)
2515 *DestinationInfFileNameComponent = &DestinationInfFileName[pFileName - pFullFileName];
2516 }
2517 else
2518 {
2519 SetLastError(ERROR_INSUFFICIENT_BUFFER);
2520 goto cleanup;
2521 }
2522 }
2523
2524 if (CopyStyle & SP_COPY_DELETESOURCE)
2525 {
2526 if (!DeleteFileW(SourceInfFileName))
2527 {
2528 TRACE("DeleteFileW() failed with error 0x%lx\n", GetLastError());
2529 goto cleanup;
2530 }
2531 }
2532
2533 ret = TRUE;
2534
2535 cleanup:
2536 if (hSourceFile != INVALID_HANDLE_VALUE)
2537 CloseHandle(hSourceFile);
2538 if (hSearch != INVALID_HANDLE_VALUE)
2539 FindClose(hSearch);
2540 MyFree(pFullFileName);
2541 }
2542
2543 TRACE("Returning %d\n", ret);
2544 if (ret) SetLastError(ERROR_SUCCESS);
2545 return ret;
2546 }