2 * Setupapi install routines
4 * Copyright 2002 Alexandre Julliard for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "wine/unicode.h"
27 #include "setupapi_private.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(setupapi
);
32 /* info passed to callback functions dealing with files */
33 struct files_callback_info
41 /* info passed to callback functions dealing with the registry */
42 struct registry_callback_info
48 typedef BOOL (*iterate_fields_func
)( HINF hinf
, PCWSTR field
, void *arg
);
50 /* Unicode constants */
51 static const WCHAR CopyFiles
[] = {'C','o','p','y','F','i','l','e','s',0};
52 static const WCHAR DelFiles
[] = {'D','e','l','F','i','l','e','s',0};
53 static const WCHAR RenFiles
[] = {'R','e','n','F','i','l','e','s',0};
54 static const WCHAR Ini2Reg
[] = {'I','n','i','2','R','e','g',0};
55 static const WCHAR LogConf
[] = {'L','o','g','C','o','n','f',0};
56 static const WCHAR AddReg
[] = {'A','d','d','R','e','g',0};
57 static const WCHAR DelReg
[] = {'D','e','l','R','e','g',0};
58 static const WCHAR UpdateInis
[] = {'U','p','d','a','t','e','I','n','i','s',0};
59 static const WCHAR UpdateIniFields
[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
62 /***********************************************************************
65 * Retrieve the contents of a field, dynamically growing the buffer if necessary.
67 static WCHAR
*get_field_string( INFCONTEXT
*context
, DWORD index
, WCHAR
*buffer
,
68 WCHAR
*static_buffer
, DWORD
*size
)
72 if (SetupGetStringFieldW( context
, index
, buffer
, *size
, &required
)) return buffer
;
73 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
75 /* now grow the buffer */
76 if (buffer
!= static_buffer
) HeapFree( GetProcessHeap(), 0, buffer
);
77 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, required
*sizeof(WCHAR
) ))) return NULL
;
79 if (SetupGetStringFieldW( context
, index
, buffer
, *size
, &required
)) return buffer
;
81 if (buffer
!= static_buffer
) HeapFree( GetProcessHeap(), 0, buffer
);
86 /***********************************************************************
89 * Called once for each CopyFiles entry in a given section.
91 static BOOL
copy_files_callback( HINF hinf
, PCWSTR field
, void *arg
)
93 struct files_callback_info
*info
= arg
;
95 if (field
[0] == '@') /* special case: copy single file */
96 SetupQueueDefaultCopyW( info
->queue
, info
->layout
, info
->src_root
, NULL
, field
, info
->copy_flags
);
98 SetupQueueCopySectionW( info
->queue
, info
->src_root
, info
->layout
, hinf
, field
, info
->copy_flags
);
103 /***********************************************************************
104 * delete_files_callback
106 * Called once for each DelFiles entry in a given section.
108 static BOOL
delete_files_callback( HINF hinf
, PCWSTR field
, void *arg
)
110 struct files_callback_info
*info
= arg
;
111 SetupQueueDeleteSectionW( info
->queue
, hinf
, 0, field
);
116 /***********************************************************************
117 * rename_files_callback
119 * Called once for each RenFiles entry in a given section.
121 static BOOL
rename_files_callback( HINF hinf
, PCWSTR field
, void *arg
)
123 struct files_callback_info
*info
= arg
;
124 SetupQueueRenameSectionW( info
->queue
, hinf
, 0, field
);
129 /***********************************************************************
132 * Retrieve the registry root key from its name.
134 static HKEY
get_root_key( const WCHAR
*name
, HKEY def_root
)
136 static const WCHAR HKCR
[] = {'H','K','C','R',0};
137 static const WCHAR HKCU
[] = {'H','K','C','U',0};
138 static const WCHAR HKLM
[] = {'H','K','L','M',0};
139 static const WCHAR HKU
[] = {'H','K','U',0};
140 static const WCHAR HKR
[] = {'H','K','R',0};
142 if (!strcmpiW( name
, HKCR
)) return HKEY_CLASSES_ROOT
;
143 if (!strcmpiW( name
, HKCU
)) return HKEY_CURRENT_USER
;
144 if (!strcmpiW( name
, HKLM
)) return HKEY_LOCAL_MACHINE
;
145 if (!strcmpiW( name
, HKU
)) return HKEY_USERS
;
146 if (!strcmpiW( name
, HKR
)) return def_root
;
151 /***********************************************************************
152 * append_multi_sz_value
154 * Append a multisz string to a multisz registry value.
156 static void append_multi_sz_value( HKEY hkey
, const WCHAR
*value
, const WCHAR
*strings
,
159 DWORD size
, type
, total
;
162 if (RegQueryValueExW( hkey
, value
, NULL
, &type
, NULL
, &size
)) return;
163 if (type
!= REG_MULTI_SZ
) return;
165 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, (size
+ str_size
) * sizeof(WCHAR
) ))) return;
166 if (RegQueryValueExW( hkey
, value
, NULL
, NULL
, (BYTE
*)buffer
, &size
)) goto done
;
168 /* compare each string against all the existing ones */
172 int len
= strlenW(strings
) + 1;
174 for (p
= buffer
; *p
; p
+= strlenW(p
) + 1)
175 if (!strcmpiW( p
, strings
)) break;
177 if (!*p
) /* not found, need to append it */
179 memcpy( p
, strings
, len
* sizeof(WCHAR
) );
187 TRACE( "setting value %s to %s\n", debugstr_w(value
), debugstr_w(buffer
) );
188 RegSetValueExW( hkey
, value
, 0, REG_MULTI_SZ
, (BYTE
*)buffer
, total
);
191 HeapFree( GetProcessHeap(), 0, buffer
);
195 /***********************************************************************
196 * delete_multi_sz_value
198 * Remove a string from a multisz registry value.
200 static void delete_multi_sz_value( HKEY hkey
, const WCHAR
*value
, const WCHAR
*string
)
203 WCHAR
*buffer
, *src
, *dst
;
205 if (RegQueryValueExW( hkey
, value
, NULL
, &type
, NULL
, &size
)) return;
206 if (type
!= REG_MULTI_SZ
) return;
207 /* allocate double the size, one for value before and one for after */
208 if (!(buffer
= HeapAlloc( GetProcessHeap(), 0, size
* 2 * sizeof(WCHAR
) ))) return;
209 if (RegQueryValueExW( hkey
, value
, NULL
, NULL
, (BYTE
*)buffer
, &size
)) goto done
;
214 int len
= strlenW(src
) + 1;
215 if (strcmpiW( src
, string
))
217 memcpy( dst
, src
, len
* sizeof(WCHAR
) );
223 if (dst
!= buffer
+ 2*size
) /* did we remove something? */
225 TRACE( "setting value %s to %s\n", debugstr_w(value
), debugstr_w(buffer
+ size
) );
226 RegSetValueExW( hkey
, value
, 0, REG_MULTI_SZ
,
227 (BYTE
*)(buffer
+ size
), dst
- (buffer
+ size
) );
230 HeapFree( GetProcessHeap(), 0, buffer
);
234 /***********************************************************************
237 * Perform an add/delete registry operation depending on the flags.
239 static BOOL
do_reg_operation( HKEY hkey
, const WCHAR
*value
, INFCONTEXT
*context
, INT flags
)
243 if (flags
& (FLG_ADDREG_DELREG_BIT
| FLG_ADDREG_DELVAL
)) /* deletion */
245 if (*value
&& !(flags
& FLG_DELREG_KEYONLY_COMMON
))
247 if ((flags
& FLG_DELREG_MULTI_SZ_DELSTRING
) == FLG_DELREG_MULTI_SZ_DELSTRING
)
251 if (!SetupGetStringFieldW( context
, 5, NULL
, 0, &size
) || !size
) return TRUE
;
252 if (!(str
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return FALSE
;
253 SetupGetStringFieldW( context
, 5, str
, size
, NULL
);
254 delete_multi_sz_value( hkey
, value
, str
);
255 HeapFree( GetProcessHeap(), 0, str
);
257 else RegDeleteValueW( hkey
, value
);
259 else RegDeleteKeyW( hkey
, NULL
);
263 if (flags
& (FLG_ADDREG_KEYONLY
|FLG_ADDREG_KEYONLY_COMMON
)) return TRUE
;
265 if (flags
& (FLG_ADDREG_NOCLOBBER
|FLG_ADDREG_OVERWRITEONLY
))
267 BOOL exists
= !RegQueryValueExW( hkey
, value
, NULL
, NULL
, NULL
, NULL
);
268 if (exists
&& (flags
& FLG_ADDREG_NOCLOBBER
)) return TRUE
;
269 if (!exists
& (flags
& FLG_ADDREG_OVERWRITEONLY
)) return TRUE
;
272 switch(flags
& FLG_ADDREG_TYPE_MASK
)
274 case FLG_ADDREG_TYPE_SZ
: type
= REG_SZ
; break;
275 case FLG_ADDREG_TYPE_MULTI_SZ
: type
= REG_MULTI_SZ
; break;
276 case FLG_ADDREG_TYPE_EXPAND_SZ
: type
= REG_EXPAND_SZ
; break;
277 case FLG_ADDREG_TYPE_BINARY
: type
= REG_BINARY
; break;
278 case FLG_ADDREG_TYPE_DWORD
: type
= REG_DWORD
; break;
279 case FLG_ADDREG_TYPE_NONE
: type
= REG_NONE
; break;
280 default: type
= flags
>> 16; break;
283 if (!(flags
& FLG_ADDREG_BINVALUETYPE
) ||
284 (type
== REG_DWORD
&& SetupGetFieldCount(context
) == 5))
286 static const WCHAR empty
;
289 if (type
== REG_MULTI_SZ
)
291 if (!SetupGetMultiSzFieldW( context
, 5, NULL
, 0, &size
)) size
= 0;
294 if (!(str
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return FALSE
;
295 SetupGetMultiSzFieldW( context
, 5, str
, size
, NULL
);
297 if (flags
& FLG_ADDREG_APPEND
)
299 if (!str
) return TRUE
;
300 append_multi_sz_value( hkey
, value
, str
, size
);
301 HeapFree( GetProcessHeap(), 0, str
);
304 /* else fall through to normal string handling */
308 if (!SetupGetStringFieldW( context
, 5, NULL
, 0, &size
)) size
= 0;
311 if (!(str
= HeapAlloc( GetProcessHeap(), 0, size
* sizeof(WCHAR
) ))) return FALSE
;
312 SetupGetStringFieldW( context
, 5, str
, size
, NULL
);
316 if (type
== REG_DWORD
)
318 DWORD dw
= str
? strtolW( str
, NULL
, 16 ) : 0;
319 TRACE( "setting dword %s to %lx\n", debugstr_w(value
), dw
);
320 RegSetValueExW( hkey
, value
, 0, type
, (BYTE
*)&dw
, sizeof(dw
) );
324 TRACE( "setting value %s to %s\n", debugstr_w(value
), debugstr_w(str
) );
325 if (str
) RegSetValueExW( hkey
, value
, 0, type
, (BYTE
*)str
, size
* sizeof(WCHAR
) );
326 else RegSetValueExW( hkey
, value
, 0, type
, (BYTE
*)&empty
, sizeof(WCHAR
) );
328 HeapFree( GetProcessHeap(), 0, str
);
331 else /* get the binary data */
335 if (!SetupGetBinaryField( context
, 5, NULL
, 0, &size
)) size
= 0;
338 if (!(data
= HeapAlloc( GetProcessHeap(), 0, size
))) return FALSE
;
339 TRACE( "setting binary data %s len %ld\n", debugstr_w(value
), size
);
340 SetupGetBinaryField( context
, 5, data
, size
, NULL
);
342 RegSetValueExW( hkey
, value
, 0, type
, data
, size
);
343 HeapFree( GetProcessHeap(), 0, data
);
349 /***********************************************************************
352 * Called once for each AddReg and DelReg entry in a given section.
354 static BOOL
registry_callback( HINF hinf
, PCWSTR field
, void *arg
)
356 struct registry_callback_info
*info
= arg
;
360 BOOL ok
= SetupFindFirstLineW( hinf
, field
, NULL
, &context
);
362 for (; ok
; ok
= SetupFindNextLine( &context
, &context
))
364 WCHAR buffer
[MAX_INF_STRING_LENGTH
];
368 if (!SetupGetStringFieldW( &context
, 1, buffer
, sizeof(buffer
)/sizeof(WCHAR
), NULL
))
370 if (!(root_key
= get_root_key( buffer
, info
->default_root
)))
374 if (!SetupGetStringFieldW( &context
, 2, buffer
, sizeof(buffer
)/sizeof(WCHAR
), NULL
))
378 if (!SetupGetIntField( &context
, 4, &flags
)) flags
= 0;
382 if (flags
& FLG_ADDREG_DELREG_BIT
) continue; /* ignore this entry */
386 if (!flags
) flags
= FLG_ADDREG_DELREG_BIT
;
387 else if (!(flags
& FLG_ADDREG_DELREG_BIT
)) continue; /* ignore this entry */
390 if (info
->delete || (flags
& FLG_ADDREG_OVERWRITEONLY
))
392 if (RegOpenKeyW( root_key
, buffer
, &hkey
)) continue; /* ignore if it doesn't exist */
394 else if (RegCreateKeyW( root_key
, buffer
, &hkey
))
396 ERR( "could not create key %p %s\n", root_key
, debugstr_w(buffer
) );
399 TRACE( "key %p %s\n", root_key
, debugstr_w(buffer
) );
402 if (!SetupGetStringFieldW( &context
, 3, buffer
, sizeof(buffer
)/sizeof(WCHAR
), NULL
))
406 if (!do_reg_operation( hkey
, buffer
, &context
, flags
))
417 static BOOL
update_ini_callback( HINF hinf
, PCWSTR field
, void *arg
)
421 BOOL ok
= SetupFindFirstLineW( hinf
, field
, NULL
, &context
);
423 for (; ok
; ok
= SetupFindNextLine( &context
, &context
))
425 WCHAR buffer
[MAX_INF_STRING_LENGTH
];
426 WCHAR filename
[MAX_INF_STRING_LENGTH
];
427 WCHAR section
[MAX_INF_STRING_LENGTH
];
428 WCHAR entry
[MAX_INF_STRING_LENGTH
];
429 WCHAR string
[MAX_INF_STRING_LENGTH
];
432 if (!SetupGetStringFieldW( &context
, 1, filename
,
433 sizeof(filename
)/sizeof(WCHAR
), NULL
))
436 if (!SetupGetStringFieldW( &context
, 2, section
,
437 sizeof(section
)/sizeof(WCHAR
), NULL
))
440 if (!SetupGetStringFieldW( &context
, 4, buffer
,
441 sizeof(buffer
)/sizeof(WCHAR
), NULL
))
444 divider
= strchrW(buffer
,'=');
448 strcpyW(entry
,buffer
);
450 strcpyW(string
,divider
);
454 strcpyW(entry
,buffer
);
458 TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry
),
459 debugstr_w(string
),debugstr_w(section
),debugstr_w(filename
));
460 WritePrivateProfileStringW(section
,entry
,string
,filename
);
466 static BOOL
update_ini_fields_callback( HINF hinf
, PCWSTR field
, void *arg
)
468 FIXME( "should update ini fields %s\n", debugstr_w(field
) );
472 static BOOL
ini2reg_callback( HINF hinf
, PCWSTR field
, void *arg
)
474 FIXME( "should do ini2reg %s\n", debugstr_w(field
) );
478 static BOOL
logconf_callback( HINF hinf
, PCWSTR field
, void *arg
)
480 FIXME( "should do logconf %s\n", debugstr_w(field
) );
485 /***********************************************************************
486 * iterate_section_fields
488 * Iterate over all fields of a certain key of a certain section
490 static BOOL
iterate_section_fields( HINF hinf
, PCWSTR section
, PCWSTR key
,
491 iterate_fields_func callback
, void *arg
)
493 WCHAR static_buffer
[200];
494 WCHAR
*buffer
= static_buffer
;
495 DWORD size
= sizeof(static_buffer
)/sizeof(WCHAR
);
499 BOOL ok
= SetupFindFirstLineW( hinf
, section
, key
, &context
);
502 UINT i
, count
= SetupGetFieldCount( &context
);
503 for (i
= 1; i
<= count
; i
++)
505 if (!(buffer
= get_field_string( &context
, i
, buffer
, static_buffer
, &size
)))
507 if (!callback( hinf
, buffer
, arg
))
509 ERR("callback failed for %s %s\n", debugstr_w(section
), debugstr_w(buffer
) );
513 ok
= SetupFindNextMatchLineW( &context
, key
, &context
);
517 if (buffer
&& buffer
!= static_buffer
) HeapFree( GetProcessHeap(), 0, buffer
);
522 /***********************************************************************
523 * SetupInstallFilesFromInfSectionA (SETUPAPI.@)
525 BOOL WINAPI
SetupInstallFilesFromInfSectionA( HINF hinf
, HINF hlayout
, HSPFILEQ queue
,
526 PCSTR section
, PCSTR src_root
, UINT flags
)
528 UNICODE_STRING sectionW
;
531 if (!RtlCreateUnicodeStringFromAsciiz( §ionW
, section
))
533 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
537 ret
= SetupInstallFilesFromInfSectionW( hinf
, hlayout
, queue
, sectionW
.Buffer
,
542 if (RtlCreateUnicodeStringFromAsciiz( &srcW
, src_root
))
544 ret
= SetupInstallFilesFromInfSectionW( hinf
, hlayout
, queue
, sectionW
.Buffer
,
545 srcW
.Buffer
, flags
);
546 RtlFreeUnicodeString( &srcW
);
548 else SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
550 RtlFreeUnicodeString( §ionW
);
555 /***********************************************************************
556 * SetupInstallFilesFromInfSectionW (SETUPAPI.@)
558 BOOL WINAPI
SetupInstallFilesFromInfSectionW( HINF hinf
, HINF hlayout
, HSPFILEQ queue
,
559 PCWSTR section
, PCWSTR src_root
, UINT flags
)
561 struct files_callback_info info
;
564 info
.src_root
= src_root
;
565 info
.copy_flags
= flags
;
566 info
.layout
= hlayout
;
567 return iterate_section_fields( hinf
, section
, CopyFiles
, copy_files_callback
, &info
);
571 /***********************************************************************
572 * SetupInstallFromInfSectionA (SETUPAPI.@)
574 BOOL WINAPI
SetupInstallFromInfSectionA( HWND owner
, HINF hinf
, PCSTR section
, UINT flags
,
575 HKEY key_root
, PCSTR src_root
, UINT copy_flags
,
576 PSP_FILE_CALLBACK_A callback
, PVOID context
,
577 HDEVINFO devinfo
, PSP_DEVINFO_DATA devinfo_data
)
579 UNICODE_STRING sectionW
, src_rootW
;
580 struct callback_WtoA_context ctx
;
583 src_rootW
.Buffer
= NULL
;
584 if (src_root
&& !RtlCreateUnicodeStringFromAsciiz( &src_rootW
, src_root
))
586 SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
590 if (RtlCreateUnicodeStringFromAsciiz( §ionW
, section
))
592 ctx
.orig_context
= context
;
593 ctx
.orig_handler
= callback
;
594 ret
= SetupInstallFromInfSectionW( owner
, hinf
, sectionW
.Buffer
, flags
, key_root
,
595 src_rootW
.Buffer
, copy_flags
, QUEUE_callback_WtoA
,
596 &ctx
, devinfo
, devinfo_data
);
597 RtlFreeUnicodeString( §ionW
);
599 else SetLastError( ERROR_NOT_ENOUGH_MEMORY
);
601 RtlFreeUnicodeString( &src_rootW
);
606 /***********************************************************************
607 * SetupInstallFromInfSectionW (SETUPAPI.@)
609 BOOL WINAPI
SetupInstallFromInfSectionW( HWND owner
, HINF hinf
, PCWSTR section
, UINT flags
,
610 HKEY key_root
, PCWSTR src_root
, UINT copy_flags
,
611 PSP_FILE_CALLBACK_W callback
, PVOID context
,
612 HDEVINFO devinfo
, PSP_DEVINFO_DATA devinfo_data
)
614 if (flags
& SPINST_FILES
)
616 struct files_callback_info info
;
620 if (!(queue
= SetupOpenFileQueue())) return FALSE
;
622 info
.src_root
= src_root
;
623 info
.copy_flags
= copy_flags
;
625 ret
= (iterate_section_fields( hinf
, section
, CopyFiles
, copy_files_callback
, &info
) &&
626 iterate_section_fields( hinf
, section
, DelFiles
, delete_files_callback
, &info
) &&
627 iterate_section_fields( hinf
, section
, RenFiles
, rename_files_callback
, &info
) &&
628 SetupCommitFileQueueW( owner
, queue
, callback
, context
));
629 SetupCloseFileQueue( queue
);
630 if (!ret
) return FALSE
;
632 if (flags
& SPINST_INIFILES
)
634 if (!iterate_section_fields( hinf
, section
, UpdateInis
, update_ini_callback
, NULL
) ||
635 !iterate_section_fields( hinf
, section
, UpdateIniFields
,
636 update_ini_fields_callback
, NULL
))
639 if (flags
& SPINST_INI2REG
)
641 if (!iterate_section_fields( hinf
, section
, Ini2Reg
, ini2reg_callback
, NULL
))
645 if (flags
& SPINST_LOGCONFIG
)
647 if (!iterate_section_fields( hinf
, section
, LogConf
, logconf_callback
, NULL
))
651 if (flags
& SPINST_REGISTRY
)
653 struct registry_callback_info info
;
655 info
.default_root
= key_root
;
657 if (!iterate_section_fields( hinf
, section
, DelReg
, registry_callback
, &info
))
660 if (!iterate_section_fields( hinf
, section
, AddReg
, registry_callback
, &info
))
663 if (flags
& (SPINST_BITREG
|SPINST_REGSVR
|SPINST_UNREGSVR
|SPINST_PROFILEITEMS
|SPINST_COPYINF
))
664 FIXME( "unsupported flags %x\n", flags
);