wine/dlls/setupapi/install.c
Rob Shearman 66d5cb2967 setupapi: The inf file containing the SourceDisksFiles and SourceDisksNames sections is the same as hinf if there is no layout file specified.
So pass the correct inf handle to SetupQueueDefaultCopyW and SetupQueueCopySectionW.
2007-05-04 12:40:00 +02:00

1105 lines
36 KiB
C

/*
* Setupapi install routines
*
* Copyright 2002 Alexandre Julliard for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#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);
/* info passed to callback functions dealing with files */
struct files_callback_info
{
HSPFILEQ queue;
PCWSTR src_root;
UINT copy_flags;
HINF layout;
};
/* info passed to callback functions dealing with the registry */
struct registry_callback_info
{
HKEY default_root;
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;
};
typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
/* Unicode constants */
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 Ini2Reg[] = {'I','n','i','2','R','e','g',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 WineFakeDlls[] = {'W','i','n','e','F','a','k','e','D','l','l','s',0};
/***********************************************************************
* get_field_string
*
* Retrieve the contents of a field, dynamically growing the buffer if necessary.
*/
static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
WCHAR *static_buffer, DWORD *size )
{
DWORD required;
if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
/* now grow the buffer */
if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
*size = required;
if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
}
if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
return NULL;
}
/***********************************************************************
* copy_files_callback
*
* Called once for each CopyFiles entry in a given section.
*/
static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
{
struct files_callback_info *info = arg;
if (field[0] == '@') /* special case: copy single file */
SetupQueueDefaultCopyW( info->queue, info->layout ? info->layout : hinf, info->src_root, NULL, field+1, info->copy_flags );
else
SetupQueueCopySectionW( info->queue, info->src_root, info->layout ? info->layout : hinf, hinf, field, info->copy_flags );
return TRUE;
}
/***********************************************************************
* delete_files_callback
*
* Called once for each DelFiles entry in a given section.
*/
static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
{
struct files_callback_info *info = arg;
SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
return TRUE;
}
/***********************************************************************
* rename_files_callback
*
* Called once for each RenFiles entry in a given section.
*/
static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
{
struct files_callback_info *info = arg;
SetupQueueRenameSectionW( info->queue, hinf, 0, field );
return TRUE;
}
/***********************************************************************
* get_root_key
*
* Retrieve the registry root key from its name.
*/
static HKEY get_root_key( const WCHAR *name, HKEY def_root )
{
static const WCHAR HKCR[] = {'H','K','C','R',0};
static const WCHAR HKCU[] = {'H','K','C','U',0};
static const WCHAR HKLM[] = {'H','K','L','M',0};
static const WCHAR HKU[] = {'H','K','U',0};
static const WCHAR HKR[] = {'H','K','R',0};
if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
if (!strcmpiW( name, HKU )) return HKEY_USERS;
if (!strcmpiW( name, HKR )) return def_root;
return 0;
}
/***********************************************************************
* append_multi_sz_value
*
* Append a multisz string to a multisz registry value.
*/
static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
DWORD str_size )
{
DWORD size, type, total;
WCHAR *buffer, *p;
if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
if (type != REG_MULTI_SZ) return;
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
/* compare each string against all the existing ones */
total = size;
while (*strings)
{
int len = strlenW(strings) + 1;
for (p = buffer; *p; p += strlenW(p) + 1)
if (!strcmpiW( p, strings )) break;
if (!*p) /* not found, need to append it */
{
memcpy( p, strings, len * sizeof(WCHAR) );
p[len] = 0;
total += len;
}
strings += len;
}
if (total != size)
{
TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
}
done:
HeapFree( GetProcessHeap(), 0, buffer );
}
/***********************************************************************
* delete_multi_sz_value
*
* Remove a string from a multisz registry value.
*/
static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
{
DWORD size, type;
WCHAR *buffer, *src, *dst;
if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
if (type != REG_MULTI_SZ) return;
/* allocate double the size, one for value before and one for after */
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
src = buffer;
dst = buffer + size;
while (*src)
{
int len = strlenW(src) + 1;
if (strcmpiW( src, string ))
{
memcpy( dst, src, len * sizeof(WCHAR) );
dst += len;
}
src += len;
}
*dst++ = 0;
if (dst != buffer + 2*size) /* did we remove something? */
{
TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
(BYTE *)(buffer + size), dst - (buffer + size) );
}
done:
HeapFree( GetProcessHeap(), 0, buffer );
}
/***********************************************************************
* do_reg_operation
*
* Perform an add/delete registry operation depending on the flags.
*/
static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
{
DWORD type, size;
if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */
{
if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
{
if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
{
WCHAR *str;
if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
SetupGetStringFieldW( context, 5, str, size, NULL );
delete_multi_sz_value( hkey, value, str );
HeapFree( GetProcessHeap(), 0, str );
}
else RegDeleteValueW( hkey, value );
}
else RegDeleteKeyW( hkey, NULL );
return TRUE;
}
if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
{
BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
if (!exists && (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
}
switch(flags & FLG_ADDREG_TYPE_MASK)
{
case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break;
case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break;
case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break;
case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break;
case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break;
default: type = flags >> 16; break;
}
if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
(type == REG_DWORD && SetupGetFieldCount(context) == 5))
{
static const WCHAR empty;
WCHAR *str = NULL;
if (type == REG_MULTI_SZ)
{
if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
if (size)
{
if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
SetupGetMultiSzFieldW( context, 5, str, size, NULL );
}
if (flags & FLG_ADDREG_APPEND)
{
if (!str) return TRUE;
append_multi_sz_value( hkey, value, str, size );
HeapFree( GetProcessHeap(), 0, str );
return TRUE;
}
/* else fall through to normal string handling */
}
else
{
if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
if (size)
{
if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
SetupGetStringFieldW( context, 5, str, size, NULL );
}
}
if (type == REG_DWORD)
{
DWORD dw = str ? strtoulW( str, NULL, 0 ) : 0;
TRACE( "setting dword %s to %x\n", debugstr_w(value), dw );
RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
}
else
{
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, (const BYTE *)&empty, sizeof(WCHAR) );
}
HeapFree( GetProcessHeap(), 0, str );
return TRUE;
}
else /* get the binary data */
{
BYTE *data = NULL;
if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
if (size)
{
if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
TRACE( "setting binary data %s len %d\n", debugstr_w(value), size );
SetupGetBinaryField( context, 5, data, size, NULL );
}
RegSetValueExW( hkey, value, 0, type, data, size );
HeapFree( GetProcessHeap(), 0, data );
return TRUE;
}
}
/***********************************************************************
* registry_callback
*
* Called once for each AddReg and DelReg entry in a given section.
*/
static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
{
struct registry_callback_info *info = arg;
INFCONTEXT context;
HKEY root_key, hkey;
BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
for (; ok; ok = SetupFindNextLine( &context, &context ))
{
WCHAR buffer[MAX_INF_STRING_LENGTH];
INT flags;
/* get root */
if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
continue;
if (!(root_key = get_root_key( buffer, info->default_root )))
continue;
/* get key */
if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
*buffer = 0;
/* get flags */
if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
if (!info->delete)
{
if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */
}
else
{
if (!flags) flags = FLG_ADDREG_DELREG_BIT;
else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */
}
if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
{
if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */
}
else if (RegCreateKeyW( root_key, buffer, &hkey ))
{
ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) );
continue;
}
TRACE( "key %p %s\n", root_key, debugstr_w(buffer) );
/* get value name */
if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
*buffer = 0;
/* and now do it */
if (!do_reg_operation( hkey, buffer, &context, flags ))
{
RegCloseKey( hkey );
return FALSE;
}
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;
IMAGE_NT_HEADERS *nt;
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 ((nt = RtlImageNtHeader( module )) && !(nt->FileHeader.Characteristics & IMAGE_FILE_DLL))
{
/* file is an executable, not a dll */
STARTUPINFOW startup;
PROCESS_INFORMATION info;
WCHAR *cmd_line;
BOOL res;
static const WCHAR format[] = {'"','%','s','"',' ','%','s',0};
static const WCHAR default_args[] = {'/','R','e','g','S','e','r','v','e','r',0};
FreeLibrary( module );
module = NULL;
if (!args) args = default_args;
cmd_line = HeapAlloc( GetProcessHeap(), 0, (strlenW(path) + strlenW(args) + 4) * sizeof(WCHAR) );
sprintfW( cmd_line, format, path, args );
memset( &startup, 0, sizeof(startup) );
startup.cb = sizeof(startup);
TRACE( "executing %s\n", debugstr_w(cmd_line) );
res = CreateProcessW( NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info );
HeapFree( GetProcessHeap(), 0, cmd_line );
if (!res)
{
status.FailureCode = SPREG_LOADLIBRARY;
status.Win32Error = GetLastError();
goto done;
}
CloseHandle( info.hThread );
if (WaitForSingleObject( info.hProcess, timeout*1000 ) == WAIT_TIMEOUT)
{
/* timed out, kill the process */
TerminateProcess( info.hProcess, 1 );
status.FailureCode = SPREG_TIMEOUT;
status.Win32Error = ERROR_TIMEOUT;
}
CloseHandle( info.hProcess );
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 %x\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 %x\n", debugstr_w(path), res );
status.FailureCode = SPREG_REGSVR;
status.Win32Error = res;
goto done;
}
}
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;
}
/***********************************************************************
* fake_dlls_callback
*
* Called once for each WineFakeDlls entry in a given section.
*/
static BOOL fake_dlls_callback( HINF hinf, PCWSTR field, void *arg )
{
INFCONTEXT context;
BOOL ret = TRUE;
BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
for (; ok; ok = SetupFindNextLine( &context, &context ))
{
WCHAR *path, *p;
WCHAR buffer[MAX_INF_STRING_LENGTH];
/* 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 source dll */
if (SetupGetStringFieldW( &context, 4, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
p = buffer; /* otherwise use target base name as default source */
create_fake_dll( path, p ); /* ignore errors */
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;
BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
for (; ok; ok = SetupFindNextLine( &context, &context ))
{
WCHAR buffer[MAX_INF_STRING_LENGTH];
WCHAR filename[MAX_INF_STRING_LENGTH];
WCHAR section[MAX_INF_STRING_LENGTH];
WCHAR entry[MAX_INF_STRING_LENGTH];
WCHAR string[MAX_INF_STRING_LENGTH];
LPWSTR divider;
if (!SetupGetStringFieldW( &context, 1, filename,
sizeof(filename)/sizeof(WCHAR), NULL ))
continue;
if (!SetupGetStringFieldW( &context, 2, section,
sizeof(section)/sizeof(WCHAR), NULL ))
continue;
if (!SetupGetStringFieldW( &context, 4, buffer,
sizeof(buffer)/sizeof(WCHAR), NULL ))
continue;
divider = strchrW(buffer,'=');
if (divider)
{
*divider = 0;
strcpyW(entry,buffer);
divider++;
strcpyW(string,divider);
}
else
{
strcpyW(entry,buffer);
string[0]=0;
}
TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
debugstr_w(string),debugstr_w(section),debugstr_w(filename));
WritePrivateProfileStringW(section,entry,string,filename);
}
return TRUE;
}
static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
{
FIXME( "should update ini fields %s\n", debugstr_w(field) );
return TRUE;
}
static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
{
FIXME( "should do ini2reg %s\n", debugstr_w(field) );
return TRUE;
}
static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
{
FIXME( "should do logconf %s\n", debugstr_w(field) );
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
*
* Iterate over all fields of a certain key of a certain section
*/
static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
iterate_fields_func callback, void *arg )
{
WCHAR static_buffer[200];
WCHAR *buffer = static_buffer;
DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
INFCONTEXT context;
BOOL ret = FALSE;
BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
while (ok)
{
UINT i, count = SetupGetFieldCount( &context );
for (i = 1; i <= count; i++)
{
if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
goto done;
if (!callback( hinf, buffer, arg ))
{
WARN("callback failed for %s %s err %d\n",
debugstr_w(section), debugstr_w(buffer), GetLastError() );
goto done;
}
}
ok = SetupFindNextMatchLineW( &context, key, &context );
}
ret = TRUE;
done:
if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
return ret;
}
/***********************************************************************
* SetupInstallFilesFromInfSectionA (SETUPAPI.@)
*/
BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
PCSTR section, PCSTR src_root, UINT flags )
{
UNICODE_STRING sectionW;
BOOL ret = FALSE;
if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
if (!src_root)
ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
NULL, flags );
else
{
UNICODE_STRING srcW;
if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
{
ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
srcW.Buffer, flags );
RtlFreeUnicodeString( &srcW );
}
else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
}
RtlFreeUnicodeString( &sectionW );
return ret;
}
/***********************************************************************
* SetupInstallFilesFromInfSectionW (SETUPAPI.@)
*/
BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
PCWSTR section, PCWSTR src_root, UINT flags )
{
struct files_callback_info info;
info.queue = queue;
info.src_root = src_root;
info.copy_flags = flags;
info.layout = hlayout;
return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
}
/***********************************************************************
* SetupInstallFromInfSectionA (SETUPAPI.@)
*/
BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
HKEY key_root, PCSTR src_root, UINT copy_flags,
PSP_FILE_CALLBACK_A callback, PVOID context,
HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
{
UNICODE_STRING sectionW, src_rootW;
struct callback_WtoA_context ctx;
BOOL ret = FALSE;
src_rootW.Buffer = NULL;
if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
{
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return FALSE;
}
if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
{
ctx.orig_context = context;
ctx.orig_handler = callback;
ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
&ctx, devinfo, devinfo_data );
RtlFreeUnicodeString( &sectionW );
}
else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
RtlFreeUnicodeString( &src_rootW );
return ret;
}
/***********************************************************************
* SetupInstallFromInfSectionW (SETUPAPI.@)
*/
BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
HKEY key_root, PCWSTR src_root, UINT copy_flags,
PSP_FILE_CALLBACK_W callback, PVOID context,
HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
{
if (flags & SPINST_FILES)
{
struct files_callback_info info;
HSPFILEQ queue;
BOOL ret;
if (!(queue = SetupOpenFileQueue())) return FALSE;
info.queue = queue;
info.src_root = src_root;
info.copy_flags = copy_flags;
info.layout = hinf;
ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
SetupCommitFileQueueW( owner, queue, callback, context ));
SetupCloseFileQueue( queue );
if (!ret) return FALSE;
}
if (flags & SPINST_INIFILES)
{
if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
!iterate_section_fields( hinf, section, UpdateIniFields,
update_ini_fields_callback, NULL ))
return FALSE;
}
if (flags & SPINST_INI2REG)
{
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 (!iterate_section_fields( hinf, section, WineFakeDlls, fake_dlls_callback, NULL ))
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;
info.default_root = key_root;
info.delete = TRUE;
if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
return FALSE;
info.delete = FALSE;
if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
return FALSE;
}
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 )
{
#ifdef __i386__
static const WCHAR nt_platformW[] = {'.','n','t','x','8','6',0};
#elif defined(__x86_64)
static const WCHAR nt_platformW[] = {'.','n','t','a','m','d','6','4',0};
#else /* FIXME: other platforms */
static const WCHAR nt_platformW[] = {'.','n','t',0};
#endif
static const WCHAR nt_genericW[] = {'.','n','t',0};
WCHAR *s, *d, *path, section[MAX_PATH + sizeof(nt_platformW)/sizeof(WCHAR)];
void *callback_context;
UINT mode, in_quotes, bcount;
HINF hinf;
TRACE("hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_w(cmdline));
lstrcpynW( section, cmdline, MAX_PATH );
if (!(s = strchrW( section, ' ' ))) return;
*s++ = 0;
while (*s == ' ') s++;
mode = atoiW( s );
if (!(s = strchrW( s, ' ' ))) return;
while (*s == ' ') s++;
/* The inf path may be quoted. Code adapted from CommandLineToArgvW() */
bcount=0;
in_quotes=0;
path=d=s;
while (*s)
{
if (*s==0 || ((*s=='\t' || *s==' ') && !in_quotes)) {
/* end of this command line argument */
break;
} else if (*s=='\\') {
/* '\\' */
*d++=*s++;
bcount++;
} else if (*s=='"') {
/* '"' */
if ((bcount & 1)==0) {
/* Preceded by an even number of '\', this is half that
* number of '\', plus a quote which we erase.
*/
d-=bcount/2;
in_quotes=!in_quotes;
s++;
} else {
/* Preceded by an odd number of '\', this is half that
* number of '\' followed by a '"'
*/
d=d-bcount/2-1;
*d++='"';
s++;
}
bcount=0;
} else {
/* a regular character */
*d++=*s++;
bcount=0;
}
}
*d=0;
hinf = SetupOpenInfFileW( path, NULL, INF_STYLE_WIN4, NULL );
if (hinf == INVALID_HANDLE_VALUE) return;
if (!(GetVersion() & 0x80000000))
{
INFCONTEXT context;
/* check for <section>.ntx86 (or corresponding name for the current platform)
* and then <section>.nt */
s = section + strlenW(section);
memcpy( s, nt_platformW, sizeof(nt_platformW) );
if (!(SetupFindFirstLineW( hinf, section, NULL, &context )))
{
memcpy( s, nt_genericW, sizeof(nt_genericW) );
if (!(SetupFindFirstLineW( hinf, section, NULL, &context ))) *s = 0;
}
if (*s) TRACE( "using section %s instead\n", debugstr_w(section) );
}
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 );
}
}