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