* Setupapi install routines
*
* Copyright 2002 Alexandre Julliard for CodeWeavers
+ * 2005 Hervé Poussineau (hpoussin@reactos.org)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include "config.h"
-#include "wine/port.h"
-
-#include <stdarg.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "winreg.h"
-#include "winternl.h"
-#include "winerror.h"
-#include "wingdi.h"
-#include "winuser.h"
-#include "winnls.h"
-#include "setupapi.h"
#include "setupapi_private.h"
-#include "wine/unicode.h"
-#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
BOOL delete;
};
+/* info passed to callback functions dealing with registering dlls */
+struct register_dll_info
+{
+ PSP_FILE_CALLBACK_W callback;
+ PVOID callback_context;
+ BOOL unregister;
+};
+
+/* info passed to callback functions dealing with Needs directives */
+struct needs_callback_info
+{
+ UINT type;
+
+ HWND owner;
+ UINT flags;
+ HKEY key_root;
+ LPCWSTR src_root;
+ UINT copy_flags;
+ PVOID callback;
+ PVOID context;
+ HDEVINFO devinfo;
+ PSP_DEVINFO_DATA devinfo_data;
+ PVOID reserved1;
+ PVOID reserved2;
+};
+
typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
/* Unicode constants */
+static const WCHAR AddService[] = {'A','d','d','S','e','r','v','i','c','e',0};
static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0};
static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0};
static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0};
static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0};
static const WCHAR AddReg[] = {'A','d','d','R','e','g',0};
static const WCHAR DelReg[] = {'D','e','l','R','e','g',0};
+static const WCHAR BitReg[] = {'B','i','t','R','e','g',0};
static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
+static const WCHAR CopyINF[] = {'C','o','p','y','I','N','F',0};
static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
+static const WCHAR RegisterDlls[] = {'R','e','g','i','s','t','e','r','D','l','l','s',0};
+static const WCHAR UnregisterDlls[] = {'U','n','r','e','g','i','s','t','e','r','D','l','l','s',0};
+static const WCHAR ProfileItems[] = {'P','r','o','f','i','l','e','I','t','e','m','s',0};
+static const WCHAR Include[] = {'I','n','c','l','u','d','e',0};
+static const WCHAR Needs[] = {'N','e','e','d','s',0};
/***********************************************************************
if (type == REG_DWORD)
{
- DWORD dw = str ? strtoulW( str, NULL, 16 ) : 0;
+ DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw );
RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
}
{
TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
- else RegSetValueExW( hkey, value, 0, type, (BYTE *)&empty, sizeof(WCHAR) );
+ else RegSetValueExW( hkey, value, 0, type, (const BYTE *)&empty, sizeof(WCHAR) );
}
HeapFree( GetProcessHeap(), 0, str );
return TRUE;
/* and now do it */
if (!do_reg_operation( hkey, buffer, &context, flags ))
{
- RegCloseKey( hkey );
+ if (hkey != root_key) RegCloseKey( hkey );
+ return FALSE;
+ }
+ if (hkey != root_key) RegCloseKey( hkey );
+ }
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * do_register_dll
+ *
+ * Register or unregister a dll.
+ */
+static BOOL do_register_dll( const struct register_dll_info *info, const WCHAR *path,
+ INT flags, INT timeout, const WCHAR *args )
+{
+ HMODULE module;
+ HRESULT res;
+ SP_REGISTER_CONTROL_STATUSW status;
+
+ status.cbSize = sizeof(status);
+ status.FileName = path;
+ status.FailureCode = SPREG_SUCCESS;
+ status.Win32Error = ERROR_SUCCESS;
+
+ if (info->callback)
+ {
+ switch(info->callback( info->callback_context, SPFILENOTIFY_STARTREGISTRATION,
+ (UINT_PTR)&status, !info->unregister ))
+ {
+ case FILEOP_ABORT:
+ SetLastError( ERROR_OPERATION_ABORTED );
return FALSE;
+ case FILEOP_SKIP:
+ return TRUE;
+ case FILEOP_DOIT:
+ break;
+ }
+ }
+
+ if (!(module = LoadLibraryExW( path, 0, LOAD_WITH_ALTERED_SEARCH_PATH )))
+ {
+ WARN( "could not load %s\n", debugstr_w(path) );
+ status.FailureCode = SPREG_LOADLIBRARY;
+ status.Win32Error = GetLastError();
+ goto done;
+ }
+
+ if (flags & FLG_REGSVR_DLLREGISTER)
+ {
+ const char *entry_point = info->unregister ? "DllUnregisterServer" : "DllRegisterServer";
+ HRESULT (WINAPI *func)(void) = (void *)GetProcAddress( module, entry_point );
+
+ if (!func)
+ {
+ status.FailureCode = SPREG_GETPROCADDR;
+ status.Win32Error = GetLastError();
+ goto done;
+ }
+
+ TRACE( "calling %s in %s\n", entry_point, debugstr_w(path) );
+ res = func();
+
+ if (FAILED(res))
+ {
+ WARN( "calling %s in %s returned error %lx\n", entry_point, debugstr_w(path), res );
+ status.FailureCode = SPREG_REGSVR;
+ status.Win32Error = res;
+ goto done;
+ }
+ }
+
+ if (flags & FLG_REGSVR_DLLINSTALL)
+ {
+ HRESULT (WINAPI *func)(BOOL,LPCWSTR) = (void *)GetProcAddress( module, "DllInstall" );
+
+ if (!func)
+ {
+ status.FailureCode = SPREG_GETPROCADDR;
+ status.Win32Error = GetLastError();
+ goto done;
+ }
+
+ TRACE( "calling DllInstall(%d,%s) in %s\n",
+ !info->unregister, debugstr_w(args), debugstr_w(path) );
+ res = func( !info->unregister, args );
+
+ if (FAILED(res))
+ {
+ WARN( "calling DllInstall in %s returned error %lx\n", debugstr_w(path), res );
+ status.FailureCode = SPREG_REGSVR;
+ status.Win32Error = res;
+ goto done;
}
- RegCloseKey( hkey );
}
+
+done:
+ if (module) FreeLibrary( module );
+ if (info->callback) info->callback( info->callback_context, SPFILENOTIFY_ENDREGISTRATION,
+ (UINT_PTR)&status, !info->unregister );
return TRUE;
}
+/***********************************************************************
+ * register_dlls_callback
+ *
+ * Called once for each RegisterDlls entry in a given section.
+ */
+static BOOL register_dlls_callback( HINF hinf, PCWSTR field, void *arg )
+{
+ struct register_dll_info *info = arg;
+ INFCONTEXT context;
+ BOOL ret = TRUE;
+ BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
+
+ for (; ok; ok = SetupFindNextLine( &context, &context ))
+ {
+ WCHAR *path, *args, *p;
+ WCHAR buffer[MAX_INF_STRING_LENGTH];
+ INT flags, timeout;
+
+ /* get directory */
+ if (!(path = PARSER_get_dest_dir( &context ))) continue;
+
+ /* get dll name */
+ if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
+ goto done;
+ if (!(p = HeapReAlloc( GetProcessHeap(), 0, path,
+ (strlenW(path) + strlenW(buffer) + 2) * sizeof(WCHAR) ))) goto done;
+ path = p;
+ p += strlenW(p);
+ if (p == path || p[-1] != '\\') *p++ = '\\';
+ strcpyW( p, buffer );
+
+ /* get flags */
+ if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
+
+ /* get timeout */
+ if (!SetupGetIntField( &context, 5, &timeout )) timeout = 60;
+
+ /* get command line */
+ args = NULL;
+ if (SetupGetStringFieldW( &context, 6, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
+ args = buffer;
+
+ ret = do_register_dll( info, path, flags, timeout, args );
+
+ done:
+ HeapFree( GetProcessHeap(), 0, path );
+ if (!ret) break;
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * update_ini_callback
+ *
+ * Called once for each UpdateInis entry in a given section.
+ */
static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
{
INFCONTEXT context;
return TRUE;
}
+static BOOL bitreg_callback( HINF hinf, PCWSTR field, void *arg )
+{
+ FIXME( "should do bitreg %s\n", debugstr_w(field) );
+ return TRUE;
+}
+
+static BOOL profile_items_callback( HINF hinf, PCWSTR field, void *arg )
+{
+ FIXME( "should do profile items %s\n", debugstr_w(field) );
+ return TRUE;
+}
+
+static BOOL copy_inf_callback( HINF hinf, PCWSTR field, void *arg )
+{
+ FIXME( "should do copy inf %s\n", debugstr_w(field) );
+ return TRUE;
+}
+
/***********************************************************************
* iterate_section_fields
goto done;
if (!callback( hinf, buffer, arg ))
{
- ERR("callback failed for %s %s\n", debugstr_w(section), debugstr_w(buffer) );
+ WARN("callback failed for %s %s err %ld\n",
+ debugstr_w(section), debugstr_w(buffer), GetLastError() );
goto done;
}
}
}
+/***********************************************************************
+ * include_callback
+ *
+ * Called once for each Include entry in a given section.
+ */
+static BOOL include_callback( HINF hinf, PCWSTR field, void *arg )
+{
+ return SetupOpenAppendInfFileW( field, hinf, NULL );
+}
+
+
+/***********************************************************************
+ * needs_callback
+ *
+ * Called once for each Needs entry in a given section.
+ */
+static BOOL needs_callback( HINF hinf, PCWSTR field, void *arg )
+{
+ struct needs_callback_info *info = arg;
+
+ switch (info->type)
+ {
+ case 0:
+ return SetupInstallFromInfSectionW(info->owner, hinf, field, info->flags,
+ info->key_root, info->src_root, info->copy_flags, info->callback,
+ info->context, info->devinfo, info->devinfo_data);
+ case 1:
+ return SetupInstallServicesFromInfSectionExW(hinf, field, info->flags,
+ info->devinfo, info->devinfo_data, info->reserved1, info->reserved2);
+ default:
+ ERR("Unknown info type %ld\n", info->type);
+ return FALSE;
+ }
+}
+
+
/***********************************************************************
* SetupInstallFromInfSectionW (SETUPAPI.@)
*/
PSP_FILE_CALLBACK_W callback, PVOID context,
HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
{
+ struct needs_callback_info needs_info;
+
+ /* Parse 'Include' and 'Needs' directives */
+ iterate_section_fields( hinf, section, Include, include_callback, NULL);
+ needs_info.type = 0;
+ needs_info.owner = owner;
+ needs_info.flags = flags;
+ needs_info.key_root = key_root;
+ needs_info.src_root = src_root;
+ needs_info.copy_flags = copy_flags;
+ needs_info.callback = callback;
+ needs_info.context = context;
+ needs_info.devinfo = devinfo;
+ needs_info.devinfo_data = devinfo_data;
+ iterate_section_fields( hinf, section, Needs, needs_callback, &needs_info);
+
if (flags & SPINST_FILES)
{
struct files_callback_info info;
if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
return FALSE;
}
-
if (flags & SPINST_LOGCONFIG)
{
if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
return FALSE;
}
+ if (flags & SPINST_REGSVR)
+ {
+ struct register_dll_info info;
+
+ info.unregister = FALSE;
+ if (flags & SPINST_REGISTERCALLBACKAWARE)
+ {
+ info.callback = callback;
+ info.callback_context = context;
+ }
+ else info.callback = NULL;
+
+ if (!iterate_section_fields( hinf, section, RegisterDlls, register_dlls_callback, &info ))
+ return FALSE;
+ }
+ if (flags & SPINST_UNREGSVR)
+ {
+ struct register_dll_info info;
+ info.unregister = TRUE;
+ if (flags & SPINST_REGISTERCALLBACKAWARE)
+ {
+ info.callback = callback;
+ info.callback_context = context;
+ }
+ else info.callback = NULL;
+
+ if (!iterate_section_fields( hinf, section, UnregisterDlls, register_dlls_callback, &info ))
+ return FALSE;
+ }
if (flags & SPINST_REGISTRY)
{
struct registry_callback_info info;
if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
return FALSE;
}
- if (flags & (SPINST_BITREG|SPINST_REGSVR|SPINST_UNREGSVR|SPINST_PROFILEITEMS|SPINST_COPYINF))
- FIXME( "unsupported flags %x\n", flags );
+ if (flags & SPINST_BITREG)
+ {
+ if (!iterate_section_fields( hinf, section, BitReg, bitreg_callback, NULL ))
+ return FALSE;
+ }
+ if (flags & SPINST_PROFILEITEMS)
+ {
+ if (!iterate_section_fields( hinf, section, ProfileItems, profile_items_callback, NULL ))
+ return FALSE;
+ }
+ if (flags & SPINST_COPYINF)
+ {
+ if (!iterate_section_fields( hinf, section, CopyINF, copy_inf_callback, NULL ))
+ return FALSE;
+ }
+
return TRUE;
}
+
+
+/***********************************************************************
+ * InstallHinfSectionW (SETUPAPI.@)
+ *
+ * NOTE: 'cmdline' is <section> <mode> <path> from
+ * RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection <section> <mode> <path>
+ */
+void WINAPI InstallHinfSectionW( HWND hwnd, HINSTANCE handle, LPCWSTR cmdline, INT show )
+{
+ WCHAR *p, *path, section[MAX_PATH];
+ void *callback_context;
+ UINT mode;
+ HINF hinf;
+
+ TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
+
+ lstrcpynW( section, cmdline, sizeof(section)/sizeof(WCHAR) );
+
+ if (!(p = strchrW( section, ' ' ))) return;
+ *p++ = 0;
+ while (*p == ' ') p++;
+ mode = atoiW( p );
+
+ if (!(p = strchrW( p, ' ' ))) return;
+ path = p + 1;
+ while (*path == ' ') path++;
+
+ hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
+ if (hinf == INVALID_HANDLE_VALUE) return;
+
+ callback_context = SetupInitDefaultQueueCallback( hwnd );
+ SetupInstallFromInfSectionW( hwnd, hinf, section, SPINST_ALL, NULL, NULL, SP_COPY_NEWER,
+ SetupDefaultQueueCallbackW, callback_context,
+ NULL, NULL );
+ SetupTermDefaultQueueCallback( callback_context );
+ SetupCloseInfFile( hinf );
+
+ /* FIXME: should check the mode and maybe reboot */
+ /* there isn't much point in doing that since we */
+ /* don't yet handle deferred file copies anyway. */
+}
+
+
+/***********************************************************************
+ * InstallHinfSectionA (SETUPAPI.@)
+ */
+void WINAPI InstallHinfSectionA( HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show )
+{
+ UNICODE_STRING cmdlineW;
+
+ if (RtlCreateUnicodeStringFromAsciiz( &cmdlineW, cmdline ))
+ {
+ InstallHinfSectionW( hwnd, handle, cmdlineW.Buffer, show );
+ RtlFreeUnicodeString( &cmdlineW );
+ }
+}
+
+
+/***********************************************************************
+ * SetupInstallServicesFromInfSectionA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallServicesFromInfSectionA( HINF hinf, PCSTR sectionname, DWORD flags )
+{
+ return SetupInstallServicesFromInfSectionExA( hinf, sectionname, flags,
+ NULL, NULL, NULL, NULL );
+}
+
+
+/***********************************************************************
+ * SetupInstallServicesFromInfSectionW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallServicesFromInfSectionW( HINF hinf, PCWSTR sectionname, DWORD flags )
+{
+ return SetupInstallServicesFromInfSectionExW( hinf, sectionname, flags,
+ NULL, NULL, NULL, NULL );
+}
+
+
+/***********************************************************************
+ * SetupInstallServicesFromInfSectionExA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallServicesFromInfSectionExA( HINF hinf, PCSTR sectionname, DWORD flags, HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data, PVOID reserved1, PVOID reserved2 )
+{
+ UNICODE_STRING sectionnameW;
+ BOOL ret = FALSE;
+
+ if (RtlCreateUnicodeStringFromAsciiz( §ionnameW, sectionname ))
+ {
+ ret = SetupInstallServicesFromInfSectionExW( hinf, sectionnameW.Buffer, flags, devinfo, devinfo_data, reserved1, reserved2 );
+ RtlFreeUnicodeString( §ionnameW );
+ }
+ else
+ SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+
+ return ret;
+}
+
+
+static BOOL GetLineText( HINF hinf, PCWSTR section_name, PCWSTR key_name, PWSTR *value)
+{
+ DWORD required;
+ PWSTR buf = NULL;
+
+ *value = NULL;
+
+ if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, NULL, 0, &required )
+ && GetLastError() != ERROR_INSUFFICIENT_BUFFER )
+ return FALSE;
+
+ buf = HeapAlloc( GetProcessHeap(), 0, required * sizeof(WCHAR) );
+ if ( ! buf )
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ if (! SetupGetLineTextW( NULL, hinf, section_name, key_name, buf, required, &required ) )
+ {
+ HeapFree( GetProcessHeap(), 0, buf );
+ return FALSE;
+ }
+
+ *value = buf;
+ return TRUE;
+}
+
+
+static BOOL GetIntField( HINF hinf, PCWSTR section_name, PCWSTR key_name, INT *value)
+{
+ LPWSTR buffer, end;
+ INT res;
+
+ if (! GetLineText( hinf, section_name, key_name, &buffer ) )
+ return FALSE;
+
+ res = wcstol( buffer, &end, 0 );
+ if (end != buffer && !*end)
+ {
+ HeapFree(GetProcessHeap(), 0, buffer);
+ *value = res;
+ return TRUE;
+ }
+ else
+ {
+ HeapFree(GetProcessHeap(), 0, buffer);
+ SetLastError( ERROR_INVALID_DATA );
+ return FALSE;
+ }
+}
+
+
+BOOL GetStringField( PINFCONTEXT context, DWORD index, PWSTR *value)
+{
+ DWORD RequiredSize;
+ BOOL ret;
+
+ ret = SetupGetStringFieldW(
+ context,
+ index,
+ NULL, 0,
+ &RequiredSize);
+ if (!ret)
+ return FALSE;
+ else if (RequiredSize == 0)
+ {
+ *value = NULL;
+ return TRUE;
+ }
+
+ /* We got the needed size for the buffer */
+ *value = MyMalloc(RequiredSize * sizeof(WCHAR));
+ if (!*value)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ ret = SetupGetStringFieldW(
+ context,
+ index,
+ *value, RequiredSize, NULL);
+ if (!ret)
+ MyFree(*value);
+
+ return ret;
+}
+
+
+static BOOL InstallOneService(
+ struct DeviceInfoSet *list,
+ IN HINF hInf,
+ IN LPCWSTR ServiceSection,
+ IN LPCWSTR ServiceName,
+ IN UINT ServiceFlags)
+{
+ SC_HANDLE hSCManager = NULL;
+ SC_HANDLE hService = NULL;
+ LPDWORD GroupOrder = NULL;
+ LPQUERY_SERVICE_CONFIG ServiceConfig = NULL;
+ BOOL ret = FALSE;
+
+ HKEY hGroupOrderListKey = INVALID_HANDLE_VALUE;
+ LPWSTR ServiceBinary = NULL;
+ LPWSTR LoadOrderGroup = NULL;
+ LPWSTR DisplayName = NULL;
+ LPWSTR Description = NULL;
+ LPWSTR Dependencies = NULL;
+ INT ServiceType, StartType, ErrorControl;
+ DWORD dwRegType;
+ DWORD tagId = (DWORD)-1;
+ BOOL useTag;
+
+ if (!GetIntField(hInf, ServiceSection, L"ServiceType", &ServiceType))
+ goto cleanup;
+ if (!GetIntField(hInf, ServiceSection, L"StartType", &StartType))
+ goto cleanup;
+ if (!GetIntField(hInf, ServiceSection, L"ErrorControl", &ErrorControl))
+ goto cleanup;
+ useTag = (ServiceType == SERVICE_BOOT_START || ServiceType == SERVICE_SYSTEM_START);
+
+ hSCManager = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CREATE_SERVICE);
+ if (hSCManager == NULL)
+ goto cleanup;
+
+ if (!GetLineText(hInf, ServiceSection, L"ServiceBinary", &ServiceBinary))
+ goto cleanup;
+
+ /* Don't check return value, as these fields are optional and
+ * GetLineText initialize output parameter even on failure */
+ GetLineText(hInf, ServiceSection, L"LoadOrderGroup", &LoadOrderGroup);
+ GetLineText(hInf, ServiceSection, L"DisplayName", &DisplayName);
+ GetLineText(hInf, ServiceSection, L"Description", &Description);
+ GetLineText(hInf, ServiceSection, L"Dependencies", &Dependencies);
+
+ hService = OpenServiceW(
+ hSCManager,
+ ServiceName,
+ GENERIC_READ | GENERIC_WRITE);
+ if (hService == NULL && GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
+ goto cleanup;
+
+ if (hService && (ServiceFlags & SPSVCINST_DELETEEVENTLOGENTRY))
+ {
+ ret = DeleteService(hService);
+ if (!ret && GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
+ goto cleanup;
+ }
+
+ if (hService == NULL)
+ {
+ /* Create new service */
+ hService = CreateServiceW(
+ hSCManager,
+ ServiceName,
+ DisplayName,
+ 0,
+ ServiceType,
+ StartType,
+ ErrorControl,
+ ServiceBinary,
+ LoadOrderGroup,
+ useTag ? &tagId : NULL,
+ Dependencies,
+ NULL, NULL);
+ if (hService == NULL)
+ goto cleanup;
+ }
+ else
+ {
+ DWORD bufferSize;
+ /* Read current configuration */
+ if (!QueryServiceConfigW(hService, NULL, 0, &bufferSize))
+ {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ goto cleanup;
+ ServiceConfig = MyMalloc(bufferSize);
+ if (!ServiceConfig)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto cleanup;
+ }
+ if (!QueryServiceConfigW(hService, ServiceConfig, bufferSize, &bufferSize))
+ goto cleanup;
+ }
+ tagId = ServiceConfig->dwTagId;
+
+ /* Update configuration */
+ ret = ChangeServiceConfigW(
+ hService,
+ ServiceType,
+ (ServiceFlags & SPSVCINST_NOCLOBBER_STARTTYPE) ? SERVICE_NO_CHANGE : StartType,
+ (ServiceFlags & SPSVCINST_NOCLOBBER_ERRORCONTROL) ? SERVICE_NO_CHANGE : ErrorControl,
+ ServiceBinary,
+ (ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP && ServiceConfig->lpLoadOrderGroup) ? NULL : LoadOrderGroup,
+ useTag ? &tagId : NULL,
+ (ServiceFlags & SPSVCINST_NOCLOBBER_DEPENDENCIES && ServiceConfig->lpDependencies) ? NULL : Dependencies,
+ NULL, NULL,
+ (ServiceFlags & SPSVCINST_NOCLOBBER_DISPLAYNAME && ServiceConfig->lpDisplayName) ? NULL : DisplayName);
+ if (!ret)
+ goto cleanup;
+ }
+
+ /* FIXME: use Description and SPSVCINST_NOCLOBBER_DESCRIPTION */
+
+ if (useTag)
+ {
+ /* Add the tag to SYSTEM\CurrentControlSet\Control\GroupOrderList key */
+ LONG rc;
+ LPCWSTR lpLoadOrderGroup;
+ DWORD bufferSize;
+
+ lpLoadOrderGroup = LoadOrderGroup;
+ if ((ServiceFlags & SPSVCINST_NOCLOBBER_LOADORDERGROUP) && ServiceConfig && ServiceConfig->lpLoadOrderGroup)
+ lpLoadOrderGroup = ServiceConfig->lpLoadOrderGroup;
+
+ rc = RegOpenKey(
+ list ? list->HKLM : HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\GroupOrderList",
+ &hGroupOrderListKey);
+ if (rc != ERROR_SUCCESS)
+ {
+ SetLastError(rc);
+ goto cleanup;
+ }
+ rc = RegQueryValueExW(hGroupOrderListKey, lpLoadOrderGroup, NULL, &dwRegType, NULL, &bufferSize);
+ if (rc == ERROR_FILE_NOT_FOUND)
+ bufferSize = sizeof(DWORD);
+ else if (rc != ERROR_SUCCESS)
+ {
+ SetLastError(rc);
+ goto cleanup;
+ }
+ else if (dwRegType != REG_BINARY || bufferSize == 0 || bufferSize % sizeof(DWORD) != 0)
+ {
+ SetLastError(ERROR_GEN_FAILURE);
+ goto cleanup;
+ }
+ /* Allocate buffer to store existing data + the new tag */
+ GroupOrder = MyMalloc(bufferSize + sizeof(DWORD));
+ if (!GroupOrder)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ goto cleanup;
+ }
+ if (rc == ERROR_SUCCESS)
+ {
+ /* Read existing data */
+ rc = RegQueryValueExW(
+ hGroupOrderListKey,
+ lpLoadOrderGroup,
+ NULL,
+ NULL,
+ (BYTE*)GroupOrder,
+ &bufferSize);
+ if (rc != ERROR_SUCCESS)
+ {
+ SetLastError(rc);
+ goto cleanup;
+ }
+ if (ServiceFlags & SPSVCINST_TAGTOFRONT)
+ memmove(&GroupOrder[2], &GroupOrder[1], bufferSize - sizeof(DWORD));
+ }
+ else
+ {
+ GroupOrder[0] = 0;
+ }
+ GroupOrder[0]++;
+ if (ServiceFlags & SPSVCINST_TAGTOFRONT)
+ GroupOrder[1] = tagId;
+ else
+ GroupOrder[bufferSize / sizeof(DWORD)] = tagId;
+
+ rc = RegSetValueExW(
+ hGroupOrderListKey,
+ lpLoadOrderGroup,
+ 0,
+ REG_BINARY,
+ (BYTE*)GroupOrder,
+ bufferSize + sizeof(DWORD));
+ if (rc != ERROR_SUCCESS)
+ {
+ SetLastError(rc);
+ goto cleanup;
+ }
+ }
+
+ ret = TRUE;
+
+cleanup:
+ if (hSCManager != NULL)
+ CloseServiceHandle(hSCManager);
+ if (hService != NULL)
+ CloseServiceHandle(hService);
+ if (hGroupOrderListKey != INVALID_HANDLE_VALUE)
+ RegCloseKey(hGroupOrderListKey);
+ MyFree(ServiceConfig);
+ MyFree(ServiceBinary);
+ MyFree(LoadOrderGroup);
+ MyFree(DisplayName);
+ MyFree(Description);
+ MyFree(Dependencies);
+ MyFree(GroupOrder);
+
+ TRACE("Returning %d\n", ret);
+ return ret;
+}
+
+
+/***********************************************************************
+ * SetupInstallServicesFromInfSectionExW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallServicesFromInfSectionExW( HINF hinf, PCWSTR sectionname, DWORD flags, HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PVOID reserved1, PVOID reserved2 )
+{
+ struct DeviceInfoSet *list = NULL;
+ BOOL ret = FALSE;
+
+ TRACE("%p, %s, 0x%lx, %p, %p, %p, %p\n", hinf, debugstr_w(sectionname),
+ flags, DeviceInfoSet, DeviceInfoData, reserved1, reserved2);
+
+ if (!sectionname)
+ SetLastError(ERROR_INVALID_PARAMETER);
+ else if (flags & ~(SPSVCINST_TAGTOFRONT | SPSVCINST_DELETEEVENTLOGENTRY | SPSVCINST_NOCLOBBER_DISPLAYNAME | SPSVCINST_NOCLOBBER_STARTTYPE | SPSVCINST_NOCLOBBER_ERRORCONTROL | SPSVCINST_NOCLOBBER_LOADORDERGROUP | SPSVCINST_NOCLOBBER_DEPENDENCIES | SPSVCINST_STOPSERVICE))
+ {
+ 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));
+ SetLastError(ERROR_INVALID_FLAGS);
+ }
+ else if (DeviceInfoSet == (HDEVINFO)INVALID_HANDLE_VALUE)
+ SetLastError(ERROR_INVALID_HANDLE);
+ else if (DeviceInfoSet && (list = (struct DeviceInfoSet *)DeviceInfoSet)->magic != SETUP_DEV_INFO_SET_MAGIC)
+ SetLastError(ERROR_INVALID_HANDLE);
+ else if (DeviceInfoData && DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA))
+ SetLastError(ERROR_INVALID_USER_BUFFER);
+ else if (reserved1 != NULL || reserved2 != NULL)
+ SetLastError(ERROR_INVALID_PARAMETER);
+ else
+ {
+ struct needs_callback_info needs_info;
+ LPWSTR ServiceName = NULL;
+ LPWSTR ServiceSection = NULL;
+ INT ServiceFlags;
+ INFCONTEXT ContextService;
+ BOOL bNeedReboot = FALSE;
+
+ /* Parse 'Include' and 'Needs' directives */
+ iterate_section_fields( hinf, sectionname, Include, include_callback, NULL);
+ needs_info.type = 1;
+ needs_info.flags = flags;
+ needs_info.devinfo = DeviceInfoSet;
+ needs_info.devinfo_data = DeviceInfoData;
+ needs_info.reserved1 = reserved1;
+ needs_info.reserved2 = reserved2;
+ iterate_section_fields( hinf, sectionname, Needs, needs_callback, &needs_info);
+
+ if (flags & SPSVCINST_STOPSERVICE)
+ {
+ FIXME("Stopping the device not implemented\n");
+ /* This may lead to require a reboot */
+ /* bNeedReboot = TRUE; */
+#if 0
+ SERVICE_STATUS ServiceStatus;
+ ret = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
+ if (!ret && GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
+ goto cleanup;
+ if (ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING && ServiceStatus.dwCurrentState != SERVICE_STOPPED)
+ {
+ SetLastError(ERROR_INSTALL_SERVICE_FAILURE);
+ goto cleanup;
+ }
+#endif
+ flags &= ~SPSVCINST_STOPSERVICE;
+ }
+
+ ret = SetupFindFirstLineW(hinf, sectionname, AddService, &ContextService);
+ while (ret)
+ {
+ if (!GetStringField(&ContextService, 1, &ServiceName))
+ goto nextservice;
+
+ ret = SetupGetIntField(
+ &ContextService,
+ 2, /* Field index */
+ &ServiceFlags);
+ if (!ret)
+ {
+ /* The field may be empty. Ignore the error */
+ ServiceFlags = 0;
+ }
+
+ if (!GetStringField(&ContextService, 3, &ServiceSection))
+ goto nextservice;
+
+ ret = InstallOneService(list, hinf, ServiceSection, ServiceName, (ServiceFlags & ~SPSVCINST_ASSOCSERVICE) | flags);
+ if (!ret)
+ goto nextservice;
+
+ if (ServiceFlags & SPSVCINST_ASSOCSERVICE)
+ {
+ ret = SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet, DeviceInfoData, SPDRP_SERVICE, (LPBYTE)ServiceName, (strlenW(ServiceName) + 1) * sizeof(WCHAR));
+ if (!ret)
+ goto nextservice;
+ }
+
+nextservice:
+ HeapFree(GetProcessHeap(), 0, ServiceName);
+ HeapFree(GetProcessHeap(), 0, ServiceSection);
+ ServiceName = ServiceSection = NULL;
+ ret = SetupFindNextMatchLineW(&ContextService, AddService, &ContextService);
+ }
+
+ if (bNeedReboot)
+ SetLastError(ERROR_SUCCESS_REBOOT_REQUIRED);
+ else
+ SetLastError(ERROR_SUCCESS);
+ ret = TRUE;
+ }
+
+ TRACE("Returning %d\n", ret);
+ return ret;
+}