Implement SetupDiGetDeviceInfoListClass
[reactos.git] / reactos / lib / setupapi / install.c
1 /*
2 * Setupapi install routines
3 *
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
5 * 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "setupapi_private.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
25
26 /* info passed to callback functions dealing with files */
27 struct files_callback_info
28 {
29 HSPFILEQ queue;
30 PCWSTR src_root;
31 UINT copy_flags;
32 HINF layout;
33 };
34
35 /* info passed to callback functions dealing with the registry */
36 struct registry_callback_info
37 {
38 HKEY default_root;
39 BOOL delete;
40 };
41
42 /* info passed to callback functions dealing with registering dlls */
43 struct register_dll_info
44 {
45 PSP_FILE_CALLBACK_W callback;
46 PVOID callback_context;
47 BOOL unregister;
48 };
49
50 typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
51
52 /* Unicode constants */
53 static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0};
54 static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0};
55 static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0};
56 static const WCHAR Ini2Reg[] = {'I','n','i','2','R','e','g',0};
57 static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0};
58 static const WCHAR AddReg[] = {'A','d','d','R','e','g',0};
59 static const WCHAR DelReg[] = {'D','e','l','R','e','g',0};
60 static const WCHAR BitReg[] = {'B','i','t','R','e','g',0};
61 static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
62 static const WCHAR CopyINF[] = {'C','o','p','y','I','N','F',0};
63 static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
64 static const WCHAR RegisterDlls[] = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
65 static const WCHAR UnregisterDlls[] = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
66 static const WCHAR ProfileItems[] = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
67 static const WCHAR Include[] = {'I','n','c','l','u','d','e',0};
68 static const WCHAR Needs[] = {'N','e','e','d','s',0};
69
70
71 /***********************************************************************
72 * get_field_string
73 *
74 * Retrieve the contents of a field, dynamically growing the buffer if necessary.
75 */
76 static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
77 WCHAR *static_buffer, DWORD *size )
78 {
79 DWORD required;
80
81 if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
82 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
83 {
84 /* now grow the buffer */
85 if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
86 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
87 *size = required;
88 if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
89 }
90 if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
91 return NULL;
92 }
93
94
95 /***********************************************************************
96 * copy_files_callback
97 *
98 * Called once for each CopyFiles entry in a given section.
99 */
100 static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
101 {
102 struct files_callback_info *info = arg;
103
104 if (field[0] == '@') /* special case: copy single file */
105 SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags );
106 else
107 SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags );
108 return TRUE;
109 }
110
111
112 /***********************************************************************
113 * delete_files_callback
114 *
115 * Called once for each DelFiles entry in a given section.
116 */
117 static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
118 {
119 struct files_callback_info *info = arg;
120 SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
121 return TRUE;
122 }
123
124
125 /***********************************************************************
126 * rename_files_callback
127 *
128 * Called once for each RenFiles entry in a given section.
129 */
130 static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
131 {
132 struct files_callback_info *info = arg;
133 SetupQueueRenameSectionW( info->queue, hinf, 0, field );
134 return TRUE;
135 }
136
137
138 /***********************************************************************
139 * get_root_key
140 *
141 * Retrieve the registry root key from its name.
142 */
143 static HKEY get_root_key( const WCHAR *name, HKEY def_root )
144 {
145 static const WCHAR HKCR[] = {'H','K','C','R',0};
146 static const WCHAR HKCU[] = {'H','K','C','U',0};
147 static const WCHAR HKLM[] = {'H','K','L','M',0};
148 static const WCHAR HKU[] = {'H','K','U',0};
149 static const WCHAR HKR[] = {'H','K','R',0};
150
151 if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
152 if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
153 if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
154 if (!strcmpiW( name, HKU )) return HKEY_USERS;
155 if (!strcmpiW( name, HKR )) return def_root;
156 return 0;
157 }
158
159
160 /***********************************************************************
161 * append_multi_sz_value
162 *
163 * Append a multisz string to a multisz registry value.
164 */
165 static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
166 DWORD str_size )
167 {
168 DWORD size, type, total;
169 WCHAR *buffer, *p;
170
171 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
172 if (type != REG_MULTI_SZ) return;
173
174 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
175 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
176
177 /* compare each string against all the existing ones */
178 total = size;
179 while (*strings)
180 {
181 int len = strlenW(strings) + 1;
182
183 for (p = buffer; *p; p += strlenW(p) + 1)
184 if (!strcmpiW( p, strings )) break;
185
186 if (!*p) /* not found, need to append it */
187 {
188 memcpy( p, strings, len * sizeof(WCHAR) );
189 p[len] = 0;
190 total += len;
191 }
192 strings += len;
193 }
194 if (total != size)
195 {
196 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
197 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
198 }
199 done:
200 HeapFree( GetProcessHeap(), 0, buffer );
201 }
202
203
204 /***********************************************************************
205 * delete_multi_sz_value
206 *
207 * Remove a string from a multisz registry value.
208 */
209 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
210 {
211 DWORD size, type;
212 WCHAR *buffer, *src, *dst;
213
214 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
215 if (type != REG_MULTI_SZ) return;
216 /* allocate double the size, one for value before and one for after */
217 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
218 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
219 src = buffer;
220 dst = buffer + size;
221 while (*src)
222 {
223 int len = strlenW(src) + 1;
224 if (strcmpiW( src, string ))
225 {
226 memcpy( dst, src, len * sizeof(WCHAR) );
227 dst += len;
228 }
229 src += len;
230 }
231 *dst++ = 0;
232 if (dst != buffer + 2*size) /* did we remove something? */
233 {
234 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
235 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
236 (BYTE *)(buffer + size), dst - (buffer + size) );
237 }
238 done:
239 HeapFree( GetProcessHeap(), 0, buffer );
240 }
241
242
243 /***********************************************************************
244 * do_reg_operation
245 *
246 * Perform an add/delete registry operation depending on the flags.
247 */
248 static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
249 {
250 DWORD type, size;
251
252 if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */
253 {
254 if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
255 {
256 if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
257 {
258 WCHAR *str;
259
260 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
261 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
262 SetupGetStringFieldW( context, 5, str, size, NULL );
263 delete_multi_sz_value( hkey, value, str );
264 HeapFree( GetProcessHeap(), 0, str );
265 }
266 else RegDeleteValueW( hkey, value );
267 }
268 else RegDeleteKeyW( hkey, NULL );
269 return TRUE;
270 }
271
272 if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
273
274 if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
275 {
276 BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
277 if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
278 if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
279 }
280
281 switch(flags & FLG_ADDREG_TYPE_MASK)
282 {
283 case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break;
284 case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break;
285 case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
286 case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break;
287 case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break;
288 case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break;
289 default: type = flags >> 16; break;
290 }
291
292 if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
293 (type == REG_DWORD && SetupGetFieldCount(context) == 5))
294 {
295 static const WCHAR empty;
296 WCHAR *str = NULL;
297
298 if (type == REG_MULTI_SZ)
299 {
300 if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
301 if (size)
302 {
303 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
304 SetupGetMultiSzFieldW( context, 5, str, size, NULL );
305 }
306 if (flags & FLG_ADDREG_APPEND)
307 {
308 if (!str) return TRUE;
309 append_multi_sz_value( hkey, value, str, size );
310 HeapFree( GetProcessHeap(), 0, str );
311 return TRUE;
312 }
313 /* else fall through to normal string handling */
314 }
315 else
316 {
317 if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
318 if (size)
319 {
320 if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
321 SetupGetStringFieldW( context, 5, str, size, NULL );
322 }
323 }
324
325 if (type == REG_DWORD)
326 {
327 DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
328 TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw );
329 RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
330 }
331 else
332 {
333 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
334 if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
335 else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
336 }
337 HeapFree( GetProcessHeap(), 0, str );
338 return TRUE;
339 }
340 else /* get the binary data */
341 {
342 BYTE *data = NULL;
343
344 if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
345 if (size)
346 {
347 if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
348 TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size );
349 SetupGetBinaryField( context, 5, data, size, NULL );
350 }
351 RegSetValueExW( hkey, value, 0, type, data, size );
352 HeapFree( GetProcessHeap(), 0, data );
353 return TRUE;
354 }
355 }
356
357
358 /***********************************************************************
359 * registry_callback
360 *
361 * Called once for each AddReg and DelReg entry in a given section.
362 */
363 static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
364 {
365 struct registry_callback_info *info = arg;
366 INFCONTEXT context;
367 HKEY root_key, hkey;
368
369 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
370
371 for (; ok; ok = SetupFindNextLine( &context, &context ))
372 {
373 WCHAR buffer[MAX_INF_STRING_LENGTH];
374 INT flags;
375
376 /* get root */
377 if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
378 continue;
379 if (!(root_key = get_root_key( buffer, info->default_root )))
380 continue;
381
382 /* get key */
383 if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
384 *buffer = 0;
385
386 /* get flags */
387 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
388
389 if (!info->delete)
390 {
391 if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */
392 }
393 else
394 {
395 if (!flags) flags = FLG_ADDREG_DELREG_BIT;
396 else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */
397 }
398
399 if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
400 {
401 if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */
402 }
403 else if (RegCreateKeyW( root_key, buffer, &hkey ))
404 {
405 ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
406 continue;
407 }
408 TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
409
410 /* get value name */
411 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
412 *buffer = 0;
413
414 /* and now do it */
415 if (!do_reg_operation( hkey, buffer, &context, flags ))
416 {
417 RegCloseKey( hkey );
418 return FALSE;
419 }
420 RegCloseKey( hkey );
421 }
422 return TRUE;
423 }
424
425
426 /***********************************************************************
427 * do_register_dll
428 *
429 * Register or unregister a dll.
430 */
431 static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
432 INT flags, INT timeout, const WCHAR *args )
433 {
434 HMODULE module;
435 HRESULT res;
436 SP_REGISTER_CONTROL_STATUSW status;
437
438 status.cbSize = sizeof(status);
439 status.FileName = path;
440 status.FailureCode = SPREG_SUCCESS;
441 status.Win32Error = ERROR_SUCCESS;
442
443 if (info->callback)
444 {
445 switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
446 (UINT_PTR)&status, !info->unregister ))
447 {
448 case FILEOP_ABORT:
449 SetLastError( ERROR_OPERATION_ABORTED );
450 return FALSE;
451 case FILEOP_SKIP:
452 return TRUE;
453 case FILEOP_DOIT:
454 break;
455 }
456 }
457
458 if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
459 {
460 WARN( "could not load %s\n", debugstr_w(path) );
461 status.FailureCode = SPREG_LOADLIBRARY;
462 status.Win32Error = GetLastError();
463 goto done;
464 }
465
466 if (flags & FLG_REGSVR_DLLREGISTER)
467 {
468 const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
469 HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
470
471 if (!func)
472 {
473 status.FailureCode = SPREG_GETPROCADDR;
474 status.Win32Error = GetLastError();
475 goto done;
476 }
477
478 TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
479 res = func();
480
481 if (FAILED(res))
482 {
483 WARN( "calling %s in %s returned error %lx\n", entry_point, debugstr_w(path), res );
484 status.FailureCode = SPREG_REGSVR;
485 status.Win32Error = res;
486 goto done;
487 }
488 }
489
490 if (flags & FLG_REGSVR_DLLINSTALL)
491 {
492 HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
493
494 if (!func)
495 {
496 status.FailureCode = SPREG_GETPROCADDR;
497 status.Win32Error = GetLastError();
498 goto done;
499 }
500
501 TRACE( "calling DllInstall(%d,%s) in %s\n",
502 !info->unregister, debugstr_w(args), debugstr_w(path) );
503 res = func( !info->unregister, args );
504
505 if (FAILED(res))
506 {
507 WARN( "calling DllInstall in %s returned error %lx\n", debugstr_w(path), res );
508 status.FailureCode = SPREG_REGSVR;
509 status.Win32Error = res;
510 goto done;
511 }
512 }
513
514 done:
515 if (module) FreeLibrary( module );
516 if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
517 (UINT_PTR)&status, !info->unregister );
518 return TRUE;
519 }
520
521
522 /***********************************************************************
523 * register_dlls_callback
524 *
525 * Called once for each RegisterDlls entry in a given section.
526 */
527 static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
528 {
529 struct register_dll_info *info = arg;
530 INFCONTEXT context;
531 BOOL ret = TRUE;
532 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
533
534 for (; ok; ok = SetupFindNextLine( &context, &context ))
535 {
536 WCHAR *path, *args, *p;
537 WCHAR buffer[MAX_INF_STRING_LENGTH];
538 INT flags, timeout;
539
540 /* get directory */
541 if (!(path = PARSER_get_dest_dir( &context ))) continue;
542
543 /* get dll name */
544 if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
545 goto done;
546 if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
547 (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
548 path = p;
549 p += strlenW(p);
550 if (p == path || p[-1] != '\\') *p++ = '\\';
551 strcpyW( p, buffer );
552
553 /* get flags */
554 if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
555
556 /* get timeout */
557 if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
558
559 /* get command line */
560 args = NULL;
561 if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
562 args = buffer;
563
564 ret = do_register_dll( info, path, flags, timeout, args );
565
566 done:
567 HeapFree( GetProcessHeap(), 0, path );
568 if (!ret) break;
569 }
570 return ret;
571 }
572
573 /***********************************************************************
574 * update_ini_callback
575 *
576 * Called once for each UpdateInis entry in a given section.
577 */
578 static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
579 {
580 INFCONTEXT context;
581
582 BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
583
584 for (; ok; ok = SetupFindNextLine( &context, &context ))
585 {
586 WCHAR buffer[MAX_INF_STRING_LENGTH];
587 WCHAR filename[MAX_INF_STRING_LENGTH];
588 WCHAR section[MAX_INF_STRING_LENGTH];
589 WCHAR entry[MAX_INF_STRING_LENGTH];
590 WCHAR string[MAX_INF_STRING_LENGTH];
591 LPWSTR divider;
592
593 if (!SetupGetStringFieldW( &context, 1, filename,
594 sizeof(filename)/sizeof(WCHAR), NULL ))
595 continue;
596
597 if (!SetupGetStringFieldW( &context, 2, section,
598 sizeof(section)/sizeof(WCHAR), NULL ))
599 continue;
600
601 if (!SetupGetStringFieldW( &context, 4, buffer,
602 sizeof(buffer)/sizeof(WCHAR), NULL ))
603 continue;
604
605 divider = strchrW(buffer,'=');
606 if (divider)
607 {
608 *divider = 0;
609 strcpyW(entry,buffer);
610 divider++;
611 strcpyW(string,divider);
612 }
613 else
614 {
615 strcpyW(entry,buffer);
616 string[0]=0;
617 }
618
619 TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
620 debugstr_w(string),debugstr_w(section),debugstr_w(filename));
621 WritePrivateProfileStringW(section,entry,string,filename);
622
623 }
624 return TRUE;
625 }
626
627 static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
628 {
629 FIXME( "should update ini fields %s\n", debugstr_w(field) );
630 return TRUE;
631 }
632
633 static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
634 {
635 FIXME( "should do ini2reg %s\n", debugstr_w(field) );
636 return TRUE;
637 }
638
639 static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
640 {
641 FIXME( "should do logconf %s\n", debugstr_w(field) );
642 return TRUE;
643 }
644
645 static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
646 {
647 FIXME( "should do bitreg %s\n", debugstr_w(field) );
648 return TRUE;
649 }
650
651 static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
652 {
653 FIXME( "should do profile items %s\n", debugstr_w(field) );
654 return TRUE;
655 }
656
657 static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
658 {
659 FIXME( "should do copy inf %s\n", debugstr_w(field) );
660 return TRUE;
661 }
662
663
664 /***********************************************************************
665 * iterate_section_fields
666 *
667 * Iterate over all fields of a certain key of a certain section
668 */
669 static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
670 iterate_fields_func callback, void *arg )
671 {
672 WCHAR static_buffer[200];
673 WCHAR *buffer = static_buffer;
674 DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
675 INFCONTEXT context;
676 BOOL ret = FALSE;
677
678 BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
679 while (ok)
680 {
681 UINT i, count = SetupGetFieldCount( &context );
682 for (i = 1; i <= count; i++)
683 {
684 if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
685 goto done;
686 if (!callback( hinf, buffer, arg ))
687 {
688 WARN("callback failed for %s %s err %ld\n",
689 debugstr_w(section), debugstr_w(buffer), GetLastError() );
690 goto done;
691 }
692 }
693 ok = SetupFindNextMatchLineW( &context, key, &context );
694 }
695 ret = TRUE;
696 done:
697 if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
698 return ret;
699 }
700
701
702 /***********************************************************************
703 * SetupInstallFilesFromInfSectionA (SETUPAPI.@)
704 */
705 BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
706 PCSTR section, PCSTR src_root, UINT flags )
707 {
708 UNICODE_STRING sectionW;
709 BOOL ret = FALSE;
710
711 if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
712 {
713 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
714 return FALSE;
715 }
716 if (!src_root)
717 ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
718 NULL, flags );
719 else
720 {
721 UNICODE_STRING srcW;
722 if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
723 {
724 ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
725 srcW.Buffer, flags );
726 RtlFreeUnicodeString( &srcW );
727 }
728 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
729 }
730 RtlFreeUnicodeString( &sectionW );
731 return ret;
732 }
733
734
735 /***********************************************************************
736 * SetupInstallFilesFromInfSectionW (SETUPAPI.@)
737 */
738 BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
739 PCWSTR section, PCWSTR src_root, UINT flags )
740 {
741 struct files_callback_info info;
742
743 info.queue = queue;
744 info.src_root = src_root;
745 info.copy_flags = flags;
746 info.layout = hlayout;
747 return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
748 }
749
750
751 /***********************************************************************
752 * SetupInstallFromInfSectionA (SETUPAPI.@)
753 */
754 BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
755 HKEY key_root, PCSTR src_root, UINT copy_flags,
756 PSP_FILE_CALLBACK_A callback, PVOID context,
757 HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
758 {
759 UNICODE_STRING sectionW, src_rootW;
760 struct callback_WtoA_context ctx;
761 BOOL ret = FALSE;
762
763 src_rootW.Buffer = NULL;
764 if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
765 {
766 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
767 return FALSE;
768 }
769
770 if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
771 {
772 ctx.orig_context = context;
773 ctx.orig_handler = callback;
774 ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
775 src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
776 &ctx, devinfo, devinfo_data );
777 RtlFreeUnicodeString( &sectionW );
778 }
779 else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
780
781 RtlFreeUnicodeString( &src_rootW );
782 return ret;
783 }
784
785
786 /***********************************************************************
787 * SetupInstallFromInfSectionW (SETUPAPI.@)
788 */
789 BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
790 HKEY key_root, PCWSTR src_root, UINT copy_flags,
791 PSP_FILE_CALLBACK_W callback, PVOID context,
792 HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
793 {
794 INFCONTEXT include_context;
795
796 /* Parse 'Include' line */
797 if (SetupFindFirstLineW( hinf, section, Include, &include_context ))
798 {
799 DWORD index = 1;
800 while (TRUE)
801 {
802 static WCHAR szBuffer[MAX_PATH];
803 PWSTR pBuffer = NULL;
804 DWORD required;
805 BOOL ok;
806
807 ok = SetupGetStringFieldW( &include_context, index, szBuffer, MAX_PATH, &required );
808 if (!ok && GetLastError() == ERROR_INVALID_PARAMETER)
809 break;
810 else if (!ok && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
811 {
812 pBuffer = MyMalloc(required);
813 ok = SetupGetStringFieldW( &include_context, index, pBuffer, required, &required );
814 }
815 if (ok)
816 ok = SetupOpenAppendInfFileW( pBuffer ? pBuffer : szBuffer, hinf, NULL );
817
818 MyFree(pBuffer);
819 if (!ok) return FALSE;
820 index++;
821 }
822 }
823
824 /* Parse 'Needs' line */
825 if (SetupFindFirstLineW( hinf, section, Needs, &include_context ))
826 {
827 DWORD index = 1;
828 while (TRUE)
829 {
830 static WCHAR szBuffer[MAX_PATH];
831 PWSTR pBuffer = NULL;
832 DWORD required;
833 BOOL ok;
834
835 ok = SetupGetStringFieldW( &include_context, index, szBuffer, MAX_PATH, &required );
836 if (!ok && GetLastError() == ERROR_INVALID_PARAMETER)
837 break;
838 else if (!ok && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
839 {
840 pBuffer = MyMalloc(required);
841 ok = SetupGetStringFieldW( &include_context, index, pBuffer, required, &required );
842 }
843 if (ok)
844 {
845 ok = SetupInstallFromInfSectionW( owner, hinf, pBuffer ? pBuffer : szBuffer,
846 flags, key_root, src_root, copy_flags, callback, context, devinfo, devinfo_data );
847 }
848
849 MyFree(pBuffer);
850 if (!ok) return FALSE;
851 index++;
852 }
853 }
854
855 if (flags & SPINST_FILES)
856 {
857 struct files_callback_info info;
858 HSPFILEQ queue;
859 BOOL ret;
860
861 if (!(queue = SetupOpenFileQueue())) return FALSE;
862 info.queue = queue;
863 info.src_root = src_root;
864 info.copy_flags = copy_flags;
865 info.layout = hinf;
866 ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
867 iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
868 iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
869 SetupCommitFileQueueW( owner, queue, callback, context ));
870 SetupCloseFileQueue( queue );
871 if (!ret) return FALSE;
872 }
873 if (flags & SPINST_INIFILES)
874 {
875 if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
876 !iterate_section_fields( hinf, section, UpdateIniFields,
877 update_ini_fields_callback, NULL ))
878 return FALSE;
879 }
880 if (flags & SPINST_INI2REG)
881 {
882 if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
883 return FALSE;
884 }
885 if (flags & SPINST_LOGCONFIG)
886 {
887 if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
888 return FALSE;
889 }
890 if (flags & SPINST_REGSVR)
891 {
892 struct register_dll_info info;
893
894 info.unregister = FALSE;
895 if (flags & SPINST_REGISTERCALLBACKAWARE)
896 {
897 info.callback = callback;
898 info.callback_context = context;
899 }
900 else info.callback = NULL;
901
902 if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
903 return FALSE;
904 }
905 if (flags & SPINST_UNREGSVR)
906 {
907 struct register_dll_info info;
908
909 info.unregister = TRUE;
910 if (flags & SPINST_REGISTERCALLBACKAWARE)
911 {
912 info.callback = callback;
913 info.callback_context = context;
914 }
915 else info.callback = NULL;
916
917 if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
918 return FALSE;
919 }
920 if (flags & SPINST_REGISTRY)
921 {
922 struct registry_callback_info info;
923
924 info.default_root = key_root;
925 info.delete = TRUE;
926 if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
927 return FALSE;
928 info.delete = FALSE;
929 if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
930 return FALSE;
931 }
932 if (flags & SPINST_BITREG)
933 {
934 if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
935 return FALSE;
936 }
937 if (flags & SPINST_PROFILEITEMS)
938 {
939 if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
940 return FALSE;
941 }
942 if (flags & SPINST_COPYINF)
943 {
944 if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
945 return FALSE;
946 }
947
948 return TRUE;
949 }
950
951
952 /***********************************************************************
953 * InstallHinfSectionW (SETUPAPI.@)
954 *
955 * NOTE: 'cmdline' is <section> <mode> <path> from
956 * RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
957 */
958 void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
959 {
960 WCHAR *p, *path, section[MAX_PATH];
961 void *callback_context;
962 UINT mode;
963 HINF hinf;
964
965 TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
966
967 lstrcpynW( section, cmdline, sizeof(section)/sizeof(WCHAR) );
968
969 if (!(p = strchrW( section, ' ' ))) return;
970 *p++ = 0;
971 while (*p == ' ') p++;
972 mode = atoiW( p );
973
974 if (!(p = strchrW( p, ' ' ))) return;
975 path = p + 1;
976 while (*path == ' ') path++;
977
978 hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
979 if (hinf == INVALID_HANDLE_VALUE) return;
980
981 callback_context = SetupInitDefaultQueueCallback( hwnd );
982 SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
983 SetupDefaultQueueCallbackW, callback_context,
984 NULL, NULL );
985 SetupTermDefaultQueueCallback( callback_context );
986 SetupCloseInfFile( hinf );
987
988 /* FIXME: should check the mode and maybe reboot */
989 /* there isn't much point in doing that since we */
990 /* don't yet handle deferred file copies anyway. */
991 }
992
993
994 /***********************************************************************
995 * InstallHinfSectionA (SETUPAPI.@)
996 */
997 void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
998 {
999 UNICODE_STRING cmdlineW;
1000
1001 if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
1002 {
1003 InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
1004 RtlFreeUnicodeString( &cmdlineW );
1005 }
1006 }
1007
1008
1009 /***********************************************************************
1010 * SetupInstallServicesFromInfSectionA (SETUPAPI.@)
1011 */
1012 BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF hinf, PCSTR sectionname, DWORD flags )
1013 {
1014 return SetupInstallServicesFromInfSectionExA( hinf, sectionname, flags,
1015 NULL, NULL, NULL, NULL );
1016 }
1017
1018
1019 /***********************************************************************
1020 * SetupInstallServicesFromInfSectionW (SETUPAPI.@)
1021 */
1022 BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF hinf, PCWSTR sectionname, DWORD flags )
1023 {
1024 return SetupInstallServicesFromInfSectionExW( hinf, sectionname, flags,
1025 NULL, NULL, NULL, NULL );
1026 }
1027
1028
1029 /***********************************************************************
1030 * SetupInstallServicesFromInfSectionExA (SETUPAPI.@)
1031 */
1032 BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
1033 {
1034 UNICODE_STRING sectionnameW;
1035 BOOL ret = FALSE;
1036
1037 if (RtlCreateUnicodeStringFromAsciiz( &sectionnameW, sectionname ))
1038 {
1039 ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
1040 RtlFreeUnicodeString( &sectionnameW );
1041 }
1042 else
1043 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1044
1045 return ret;
1046 }
1047
1048
1049 static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
1050 {
1051 DWORD required;
1052 PWSTR buf = NULL;
1053
1054 *value = NULL;
1055
1056 if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
1057 && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
1058 return FALSE;
1059
1060 buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
1061 if ( ! buf )
1062 {
1063 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1064 return FALSE;
1065 }
1066
1067 if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
1068 {
1069 HeapFree( GetProcessHeap(), 0, buf );
1070 return FALSE;
1071 }
1072
1073 *value = buf;
1074 return TRUE;
1075 }
1076
1077
1078 static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
1079 {
1080 LPWSTR buffer, end;
1081 INT res;
1082
1083 if (! GetLineText( hinf, section_name, key_name, &buffer ) )
1084 return FALSE;
1085
1086 res = wcstol( buffer, &end, 0 );
1087 if (end != buffer && !*end)
1088 {
1089 HeapFree(GetProcessHeap(), 0, buffer);
1090 *value = res;
1091 return TRUE;
1092 }
1093 else
1094 {
1095 HeapFree(GetProcessHeap(), 0, buffer);
1096 SetLastError( ERROR_INVALID_DATA );
1097 return FALSE;
1098 }
1099 }
1100
1101
1102 /***********************************************************************
1103 * SetupInstallServicesFromInfSectionExW (SETUPAPI.@)
1104 */
1105 BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
1106 {
1107 SC_HANDLE hSCManager = NULL;
1108 SC_HANDLE hService = NULL;
1109 LPDWORD GroupOrder = NULL;
1110 LPQUERY_SERVICE_CONFIG ServiceConfig = NULL;
1111 struct DeviceInfoSet *list;
1112 BOOL ret = FALSE;
1113
1114 TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
1115 flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
1116
1117 if (!DeviceInfoSet)
1118 SetLastError(ERROR_INVALID_PARAMETER);
1119 else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
1120 SetLastError(ERROR_INVALID_HANDLE);
1121 else if ((list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
1122 SetLastError(ERROR_INVALID_HANDLE);
1123 else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
1124 SetLastError(ERROR_INVALID_USER_BUFFER);
1125 else if (!reserved1)
1126 {
1127 /* FIXME: I don't know how to get the service name. ATM, just fail the call */
1128 /* Maybe find it in DeviceInfoSet/DeviceInfoData? */
1129 FIXME("Service name not specified!\n");
1130 SetLastError(ERROR_GEN_FAILURE);
1131 goto cleanup;
1132 }
1133 else
1134 {
1135 HKEY hGroupOrderListKey = INVALID_HANDLE_VALUE;
1136 LPWSTR ServiceBinary = NULL;
1137 LPWSTR LoadOrderGroup = NULL;
1138 LPWSTR DisplayName = NULL;
1139 LPWSTR Description = NULL;
1140 LPWSTR Dependencies = NULL;
1141 INT ServiceType, StartType, ErrorControl;
1142 DWORD dwRegType;
1143 DWORD tagId = (DWORD)-1;
1144 BOOL useTag;
1145
1146 if (!GetIntField(hinf, sectionname, L"ServiceType", &ServiceType))
1147 goto cleanup;
1148 if (!GetIntField(hinf, sectionname, L"StartType", &StartType))
1149 goto cleanup;
1150 if (!GetIntField(hinf, sectionname, L"ErrorControl", &ErrorControl))
1151 goto cleanup;
1152 useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
1153
1154 hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
1155 if (hSCManager == NULL)
1156 goto cleanup;
1157
1158 if (!GetLineText(hinf, sectionname, L"ServiceBinary", &ServiceBinary))
1159 goto cleanup;
1160
1161 /* Don't check return value, as these fields are optional and
1162 * GetLineText initialize output parameter even on failure */
1163 GetLineText(hinf, sectionname, L"LoadOrderGroup", &LoadOrderGroup);
1164 GetLineText(hinf, sectionname, L"DisplayName", &DisplayName);
1165 GetLineText(hinf, sectionname, L"Description", &Description);
1166 GetLineText(hinf, sectionname, L"Dependencies", &Dependencies);
1167
1168 hService = OpenServiceW(
1169 hSCManager,
1170 reserved1,
1171 GENERIC_READ | GENERIC_WRITE);
1172 if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
1173 goto cleanup;
1174
1175 if (flags & (SPSVCINST_STOPSERVICE | SPSVCINST_DELETEEVENTLOGENTRY))
1176 {
1177 if (hService == NULL)
1178 {
1179 SetLastError(ERROR_SERVICE_DOES_NOT_EXIST);
1180 goto cleanup;
1181 }
1182 if (flags & SPSVCINST_STOPSERVICE)
1183 {
1184 SERVICE_STATUS ServiceStatus;
1185 ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
1186 if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
1187 goto cleanup;
1188 if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
1189 {
1190 SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
1191 goto cleanup;
1192 }
1193 }
1194 if (flags & SPSVCINST_DELETEEVENTLOGENTRY)
1195 {
1196 ret = DeleteService(hService);
1197 if (!ret)
1198 goto cleanup;
1199 }
1200 ret = FALSE;
1201 }
1202
1203 if (hService == NULL)
1204 {
1205 /* Create new service */
1206 hService = CreateServiceW(
1207 hSCManager,
1208 reserved1,
1209 DisplayName,
1210 0,
1211 ServiceType,
1212 StartType,
1213 ErrorControl,
1214 ServiceBinary,
1215 LoadOrderGroup,
1216 useTag ? &tagId : NULL,
1217 Dependencies,
1218 NULL, NULL);
1219 if (hService == NULL)
1220 goto cleanup;
1221 }
1222 else
1223 {
1224 DWORD bufferSize;
1225 /* Read current configuration */
1226 if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
1227 {
1228 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1229 goto cleanup;
1230 ServiceConfig = HeapAlloc(GetProcessHeap(), 0, bufferSize);
1231 if (!ServiceConfig)
1232 {
1233 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1234 goto cleanup;
1235 }
1236 if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
1237 goto cleanup;
1238 }
1239 tagId = ServiceConfig->dwTagId;
1240
1241 /* Update configuration */
1242 ret = ChangeServiceConfigW(
1243 hService,
1244 ServiceType,
1245 (flags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
1246 (flags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
1247 ServiceBinary,
1248 (flags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
1249 useTag ? &tagId : NULL,
1250 (flags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
1251 NULL, NULL,
1252 (flags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
1253 if (!ret)
1254 goto cleanup;
1255 }
1256
1257 /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
1258
1259 if (useTag)
1260 {
1261 /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
1262 LONG rc;
1263 LPCWSTR lpLoadOrderGroup;
1264 DWORD bufferSize;
1265
1266 lpLoadOrderGroup = LoadOrderGroup;
1267 if ((flags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
1268 lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
1269
1270 rc = RegOpenKey(list->HKLM, L"SYSTEM\\CurrentControlSet\\Control\\GroupOrderList", &hGroupOrderListKey);
1271 if (rc != ERROR_SUCCESS)
1272 {
1273 SetLastError(rc);
1274 goto cleanup;
1275 }
1276 rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
1277 if (rc == ERROR_FILE_NOT_FOUND)
1278 bufferSize = sizeof(DWORD);
1279 else if (rc != ERROR_SUCCESS)
1280 {
1281 SetLastError(rc);
1282 goto cleanup;
1283 }
1284 else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
1285 {
1286 SetLastError(ERROR_GEN_FAILURE);
1287 goto cleanup;
1288 }
1289 /* Allocate buffer to store existing data + the new tag */
1290 GroupOrder = HeapAlloc(GetProcessHeap(), 0, bufferSize + sizeof(DWORD));
1291 if (!GroupOrder)
1292 {
1293 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1294 goto cleanup;
1295 }
1296 if (rc == ERROR_SUCCESS)
1297 {
1298 /* Read existing data */
1299 rc = RegQueryValueExW(
1300 hGroupOrderListKey,
1301 lpLoadOrderGroup,
1302 NULL,
1303 NULL,
1304 (BYTE*)GroupOrder,
1305 &bufferSize);
1306 if (rc != ERROR_SUCCESS)
1307 {
1308 SetLastError(rc);
1309 goto cleanup;
1310 }
1311 if (flags & SPSVCINST_TAGTOFRONT)
1312 memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
1313 }
1314 else
1315 {
1316 GroupOrder[0] = 0;
1317 }
1318 GroupOrder[0]++;
1319 if (flags & SPSVCINST_TAGTOFRONT)
1320 GroupOrder[1] = tagId;
1321 else
1322 GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
1323
1324 rc = RegSetValueExW(
1325 hGroupOrderListKey,
1326 lpLoadOrderGroup,
1327 0,
1328 REG_BINARY,
1329 (BYTE*)GroupOrder,
1330 bufferSize + sizeof(DWORD));
1331 if (rc != ERROR_SUCCESS)
1332 {
1333 SetLastError(rc);
1334 goto cleanup;
1335 }
1336 }
1337
1338 ret = TRUE;
1339
1340 cleanup:
1341 if (hSCManager != NULL)
1342 CloseServiceHandle(hSCManager);
1343 if (hService != NULL)
1344 CloseServiceHandle(hService);
1345 if (hGroupOrderListKey != INVALID_HANDLE_VALUE)
1346 RegCloseKey(hGroupOrderListKey);
1347 HeapFree(GetProcessHeap(), 0, ServiceConfig);
1348 HeapFree(GetProcessHeap(), 0, ServiceBinary);
1349 HeapFree(GetProcessHeap(), 0, LoadOrderGroup);
1350 HeapFree(GetProcessHeap(), 0, DisplayName);
1351 HeapFree(GetProcessHeap(), 0, Description);
1352 HeapFree(GetProcessHeap(), 0, Dependencies);
1353 HeapFree(GetProcessHeap(), 0, GroupOrder);
1354 }
1355
1356 TRACE("Returning %d\n", ret);
1357 return ret;
1358 }