Commit | Line | Data |
---|---|---|
cebb2f11 | 1 | /* |
2052538a | 2 | * Dlls load order support |
cebb2f11 BS |
3 | * |
4 | * Copyright 1999 Bertho Stultiens | |
2052538a | 5 | * Copyright 2003 Alexandre Julliard |
0799c1a7 AJ |
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 | |
cebb2f11 BS |
20 | */ |
21 | ||
e5ddd26d | 22 | #include "config.h" |
9aab47ed | 23 | #include "wine/port.h" |
e5ddd26d | 24 | |
e37c6e18 | 25 | #include <stdarg.h> |
cebb2f11 BS |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
28 | #include <assert.h> | |
29 | ||
30 | #include "windef.h" | |
e37c6e18 | 31 | #include "winbase.h" |
b9c9cdc5 | 32 | #include "winerror.h" |
e37c6e18 | 33 | #include "winreg.h" |
81bdcf12 | 34 | #include "winternl.h" |
2052538a | 35 | #include "ntdll_misc.h" |
f9be2f3d | 36 | #include "module.h" |
cf07e100 | 37 | |
0799c1a7 | 38 | #include "wine/debug.h" |
cf07e100 | 39 | #include "wine/unicode.h" |
cebb2f11 | 40 | |
0799c1a7 | 41 | WINE_DEFAULT_DEBUG_CHANNEL(module); |
b4b9fae6 | 42 | |
b9c9cdc5 | 43 | #define LOADORDER_ALLOC_CLUSTER 32 /* Allocate with 32 entries at a time */ |
cebb2f11 | 44 | |
b9c9cdc5 AJ |
45 | typedef struct module_loadorder |
46 | { | |
1cb92bbe | 47 | const WCHAR *modulename; |
b9c9cdc5 AJ |
48 | enum loadorder_type loadorder[LOADORDER_NTYPES]; |
49 | } module_loadorder_t; | |
cebb2f11 | 50 | |
b9c9cdc5 AJ |
51 | struct loadorder_list |
52 | { | |
53 | int count; | |
54 | int alloc; | |
55 | module_loadorder_t *order; | |
56 | }; | |
cebb2f11 | 57 | |
1cb92bbe | 58 | /* dll to load as builtins if not explicitly specified otherwise */ |
b9c9cdc5 | 59 | /* the list must remain sorted by dll name */ |
7412125e | 60 | static const WCHAR default_builtins[][10] = |
b9c9cdc5 | 61 | { |
1cb92bbe AJ |
62 | { 'g','d','i','3','2',0 }, |
63 | { 'i','c','m','p',0 }, | |
64 | { 'k','e','r','n','e','l','3','2',0 }, | |
1cb92bbe AJ |
65 | { 'n','t','d','l','l',0 }, |
66 | { 'o','d','b','c','3','2',0 }, | |
1cb92bbe AJ |
67 | { 't','t','y','d','r','v',0 }, |
68 | { 'u','s','e','r','3','2',0 }, | |
69 | { 'w','3','2','s','k','r','n','l',0 }, | |
1cb92bbe | 70 | { 'w','i','n','e','d','o','s',0 }, |
1cb92bbe AJ |
71 | { 'w','i','n','e','p','s',0 }, |
72 | { 'w','i','n','m','m',0 }, | |
1cb92bbe AJ |
73 | { 'w','n','a','s','p','i','3','2',0 }, |
74 | { 'w','o','w','3','2',0 }, | |
75 | { 'w','s','2','_','3','2',0 }, | |
76 | { 'w','s','o','c','k','3','2',0 }, | |
77 | { 'x','1','1','d','r','v',0 } | |
431cf324 AJ |
78 | }; |
79 | ||
69622dbd AJ |
80 | /* default if nothing else specified */ |
81 | static const enum loadorder_type default_loadorder[LOADORDER_NTYPES] = | |
82 | { | |
d4fcc4bd | 83 | LOADORDER_BI, LOADORDER_DLL, 0 |
69622dbd AJ |
84 | }; |
85 | ||
851d25d9 AJ |
86 | /* default for modules with an explicit path */ |
87 | static const enum loadorder_type default_path_loadorder[LOADORDER_NTYPES] = | |
88 | { | |
d4fcc4bd | 89 | LOADORDER_DLL, LOADORDER_BI, 0 |
851d25d9 AJ |
90 | }; |
91 | ||
1cb92bbe AJ |
92 | static const WCHAR separatorsW[] = {',',' ','\t',0}; |
93 | ||
2052538a AJ |
94 | static int init_done; |
95 | static struct loadorder_list env_list; | |
b9c9cdc5 AJ |
96 | |
97 | ||
cebb2f11 BS |
98 | /*************************************************************************** |
99 | * cmp_sort_func (internal, static) | |
100 | * | |
101 | * Sorting and comparing function used in sort and search of loadorder | |
102 | * entries. | |
103 | */ | |
104 | static int cmp_sort_func(const void *s1, const void *s2) | |
105 | { | |
1cb92bbe AJ |
106 | return strcmpiW(((module_loadorder_t *)s1)->modulename, ((module_loadorder_t *)s2)->modulename); |
107 | } | |
108 | ||
109 | ||
110 | /*************************************************************************** | |
111 | * strcmp_func | |
112 | */ | |
113 | static int strcmp_func(const void *s1, const void *s2) | |
114 | { | |
115 | return strcmpiW( (WCHAR *)s1, (WCHAR *)s2 ); | |
cebb2f11 BS |
116 | } |
117 | ||
118 | ||
69622dbd AJ |
119 | /*************************************************************************** |
120 | * get_basename | |
121 | * | |
122 | * Return the base name of a file name (i.e. remove the path components). | |
123 | */ | |
1cb92bbe | 124 | static const WCHAR *get_basename( const WCHAR *name ) |
69622dbd | 125 | { |
1cb92bbe | 126 | const WCHAR *ptr; |
69622dbd AJ |
127 | |
128 | if (name[0] && name[1] == ':') name += 2; /* strip drive specification */ | |
1cb92bbe AJ |
129 | if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1; |
130 | if ((ptr = strrchrW( name, '/' ))) name = ptr + 1; | |
69622dbd AJ |
131 | return name; |
132 | } | |
133 | ||
1cb92bbe AJ |
134 | /*************************************************************************** |
135 | * remove_dll_ext | |
136 | * | |
137 | * Remove extension if it is ".dll". | |
138 | */ | |
139 | static inline void remove_dll_ext( WCHAR *ext ) | |
140 | { | |
141 | if (ext[0] == '.' && | |
142 | toupperW(ext[1]) == 'D' && | |
143 | toupperW(ext[2]) == 'L' && | |
144 | toupperW(ext[3]) == 'L' && | |
145 | !ext[4]) ext[0] = 0; | |
146 | } | |
147 | ||
69622dbd AJ |
148 | |
149 | /*************************************************************************** | |
150 | * debugstr_loadorder | |
151 | * | |
152 | * Return a loadorder in printable form. | |
153 | */ | |
154 | static const char *debugstr_loadorder( enum loadorder_type lo[] ) | |
155 | { | |
156 | int i; | |
157 | char buffer[LOADORDER_NTYPES*3+1]; | |
158 | ||
159 | buffer[0] = 0; | |
160 | for(i = 0; i < LOADORDER_NTYPES; i++) | |
161 | { | |
162 | if (lo[i] == LOADORDER_INVALID) break; | |
163 | switch(lo[i]) | |
164 | { | |
165 | case LOADORDER_DLL: strcat( buffer, "n," ); break; | |
69622dbd AJ |
166 | case LOADORDER_BI: strcat( buffer, "b," ); break; |
167 | default: strcat( buffer, "?," ); break; | |
168 | } | |
169 | } | |
170 | if (buffer[0]) buffer[strlen(buffer)-1] = 0; | |
171 | return debugstr_a(buffer); | |
172 | } | |
173 | ||
174 | ||
cebb2f11 | 175 | /*************************************************************************** |
2052538a | 176 | * append_load_order |
cebb2f11 | 177 | * |
2052538a | 178 | * Append a load order to the list if necessary. |
cebb2f11 | 179 | */ |
2052538a | 180 | static void append_load_order(enum loadorder_type lo[], enum loadorder_type append) |
cebb2f11 | 181 | { |
2052538a | 182 | int i; |
cebb2f11 | 183 | |
2052538a AJ |
184 | for (i = 0; i < LOADORDER_NTYPES; i++) |
185 | { | |
186 | if (lo[i] == LOADORDER_INVALID) /* append it here */ | |
187 | { | |
188 | lo[i++] = append; | |
189 | lo[i] = LOADORDER_INVALID; | |
190 | return; | |
191 | } | |
192 | if (lo[i] == append) return; /* already in the list */ | |
193 | } | |
194 | assert(0); /* cannot get here */ | |
195 | } | |
cebb2f11 | 196 | |
cebb2f11 | 197 | |
2052538a AJ |
198 | /*************************************************************************** |
199 | * parse_load_order | |
200 | * | |
201 | * Parses the loadorder options from the configuration and puts it into | |
202 | * a structure. | |
203 | */ | |
1cb92bbe | 204 | static void parse_load_order( const WCHAR *order, enum loadorder_type lo[] ) |
2052538a AJ |
205 | { |
206 | lo[0] = LOADORDER_INVALID; | |
207 | while (*order) | |
208 | { | |
1cb92bbe | 209 | order += strspnW( order, separatorsW ); |
2052538a AJ |
210 | switch(*order) |
211 | { | |
212 | case 'N': /* Native */ | |
213 | case 'n': | |
214 | append_load_order( lo, LOADORDER_DLL ); | |
215 | break; | |
216 | case 'B': /* Builtin */ | |
217 | case 'b': | |
218 | append_load_order( lo, LOADORDER_BI ); | |
219 | break; | |
220 | } | |
1cb92bbe | 221 | order += strcspnW( order, separatorsW ); |
2052538a | 222 | } |
cebb2f11 BS |
223 | } |
224 | ||
225 | ||
226 | /*************************************************************************** | |
2052538a | 227 | * add_load_order |
cebb2f11 | 228 | * |
2052538a | 229 | * Adds an entry in the list of environment overrides. |
cebb2f11 | 230 | */ |
2052538a | 231 | static void add_load_order( const module_loadorder_t *plo ) |
cebb2f11 | 232 | { |
2052538a | 233 | int i; |
cebb2f11 | 234 | |
2052538a AJ |
235 | for(i = 0; i < env_list.count; i++) |
236 | { | |
237 | if(!cmp_sort_func(plo, &env_list.order[i] )) | |
238 | { | |
239 | /* replace existing option */ | |
240 | memcpy( env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder)); | |
241 | return; | |
242 | } | |
243 | } | |
cebb2f11 | 244 | |
2052538a AJ |
245 | if (i >= env_list.alloc) |
246 | { | |
247 | /* No space in current array, make it larger */ | |
248 | env_list.alloc += LOADORDER_ALLOC_CLUSTER; | |
249 | if (env_list.order) | |
250 | env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order, | |
251 | env_list.alloc * sizeof(module_loadorder_t)); | |
252 | else | |
253 | env_list.order = RtlAllocateHeap(GetProcessHeap(), 0, | |
254 | env_list.alloc * sizeof(module_loadorder_t)); | |
255 | if(!env_list.order) | |
256 | { | |
257 | MESSAGE("Virtual memory exhausted\n"); | |
258 | exit(1); | |
259 | } | |
260 | } | |
261 | memcpy(env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder)); | |
1cb92bbe | 262 | env_list.order[i].modulename = plo->modulename; |
2052538a | 263 | env_list.count++; |
cebb2f11 BS |
264 | } |
265 | ||
266 | ||
267 | /*************************************************************************** | |
2052538a | 268 | * add_load_order_set |
cebb2f11 | 269 | * |
b9c9cdc5 | 270 | * Adds a set of entries in the list of command-line overrides from the key parameter. |
cebb2f11 | 271 | */ |
1cb92bbe | 272 | static void add_load_order_set( WCHAR *entry ) |
cebb2f11 | 273 | { |
2052538a | 274 | module_loadorder_t ldo; |
1cb92bbe | 275 | WCHAR *end = strchrW( entry, '=' ); |
cebb2f11 | 276 | |
2052538a AJ |
277 | if (!end) return; |
278 | *end++ = 0; | |
279 | parse_load_order( end, ldo.loadorder ); | |
cebb2f11 | 280 | |
2052538a AJ |
281 | while (*entry) |
282 | { | |
1cb92bbe AJ |
283 | entry += strspnW( entry, separatorsW ); |
284 | end = entry + strcspnW( entry, separatorsW ); | |
2052538a AJ |
285 | if (*end) *end++ = 0; |
286 | if (*entry) | |
1cb92bbe AJ |
287 | { |
288 | WCHAR *ext = strrchrW(entry, '.'); | |
289 | if (ext) remove_dll_ext( ext ); | |
2052538a AJ |
290 | ldo.modulename = entry; |
291 | add_load_order( &ldo ); | |
292 | entry = end; | |
1cb92bbe | 293 | } |
2052538a | 294 | } |
cebb2f11 BS |
295 | } |
296 | ||
297 | ||
298 | /*************************************************************************** | |
2052538a | 299 | * init_load_order |
cebb2f11 | 300 | */ |
2052538a | 301 | static void init_load_order(void) |
cebb2f11 | 302 | { |
2052538a | 303 | const char *order = getenv( "WINEDLLOVERRIDES" ); |
1cb92bbe AJ |
304 | UNICODE_STRING strW; |
305 | WCHAR *entry, *next; | |
b9c9cdc5 | 306 | |
2052538a AJ |
307 | init_done = 1; |
308 | if (!order) return; | |
b9c9cdc5 | 309 | |
2052538a AJ |
310 | if (!strcmp( order, "help" )) |
311 | { | |
312 | MESSAGE( "Syntax:\n" | |
313 | " WINEDLLOVERRIDES=\"entry;entry;entry...\"\n" | |
314 | " where each entry is of the form:\n" | |
315 | " module[,module...]={native|builtin}[,{b|n}]\n" | |
316 | "\n" | |
317 | " Only the first letter of the override (native or builtin)\n" | |
318 | " is significant.\n\n" | |
319 | "Example:\n" | |
7412125e | 320 | " WINEDLLOVERRIDES=\"comdlg32=n,b;shell32,shlwapi=b\"\n" ); |
2052538a AJ |
321 | exit(0); |
322 | } | |
b9c9cdc5 | 323 | |
1cb92bbe AJ |
324 | RtlCreateUnicodeStringFromAsciiz( &strW, order ); |
325 | entry = strW.Buffer; | |
2052538a AJ |
326 | while (*entry) |
327 | { | |
328 | while (*entry && *entry == ';') entry++; | |
329 | if (!*entry) break; | |
1cb92bbe | 330 | next = strchrW( entry, ';' ); |
2052538a | 331 | if (next) *next++ = 0; |
1cb92bbe | 332 | else next = entry + strlenW(entry); |
2052538a AJ |
333 | add_load_order_set( entry ); |
334 | entry = next; | |
335 | } | |
b9c9cdc5 AJ |
336 | |
337 | /* sort the array for quick lookup */ | |
2052538a AJ |
338 | if (env_list.count) |
339 | qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func); | |
1cb92bbe AJ |
340 | |
341 | /* Note: we don't free the Unicode string because the | |
342 | * stored module names point inside it */ | |
cebb2f11 BS |
343 | } |
344 | ||
345 | ||
b9c9cdc5 | 346 | /*************************************************************************** |
1cb92bbe | 347 | * get_env_load_order |
b9c9cdc5 | 348 | * |
1cb92bbe | 349 | * Get the load order for a given module from the WINEDLLOVERRIDES environment variable. |
b9c9cdc5 | 350 | */ |
1cb92bbe | 351 | static inline BOOL get_env_load_order( const WCHAR *module, enum loadorder_type lo[] ) |
cebb2f11 | 352 | { |
624cbd7c | 353 | module_loadorder_t tmp, *res = NULL; |
cebb2f11 | 354 | |
b9c9cdc5 | 355 | tmp.modulename = module; |
624cbd7c | 356 | /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */ |
1cb92bbe AJ |
357 | if (env_list.count && |
358 | (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func))) | |
b9c9cdc5 AJ |
359 | memcpy( lo, res->loadorder, sizeof(res->loadorder) ); |
360 | return (res != NULL); | |
361 | } | |
cebb2f11 | 362 | |
cebb2f11 | 363 | |
1cb92bbe AJ |
364 | /*************************************************************************** |
365 | * get_default_load_order | |
366 | * | |
367 | * Get the load order for a given module from the default list. | |
368 | */ | |
369 | static inline BOOL get_default_load_order( const WCHAR *module, enum loadorder_type lo[] ) | |
370 | { | |
371 | const int count = sizeof(default_builtins) / sizeof(default_builtins[0]); | |
372 | if (!bsearch( module, default_builtins, count, sizeof(default_builtins[0]), strcmp_func )) | |
373 | return FALSE; | |
374 | lo[0] = LOADORDER_BI; | |
375 | lo[1] = LOADORDER_INVALID; | |
376 | return TRUE; | |
377 | } | |
378 | ||
379 | ||
b9c9cdc5 | 380 | /*************************************************************************** |
69622dbd | 381 | * open_app_key |
b9c9cdc5 | 382 | * |
69622dbd | 383 | * Open the registry key to the app-specific DllOverrides list. |
b9c9cdc5 | 384 | */ |
1cb92bbe | 385 | static HKEY open_app_key( const WCHAR *app_name, const WCHAR *module ) |
b9c9cdc5 | 386 | { |
81bdcf12 AJ |
387 | OBJECT_ATTRIBUTES attr; |
388 | UNICODE_STRING nameW; | |
2052538a AJ |
389 | HKEY hkey; |
390 | WCHAR *str; | |
81bdcf12 AJ |
391 | static const WCHAR AppDefaultsW[] = {'M','a','c','h','i','n','e','\\', |
392 | 'S','o','f','t','w','a','r','e','\\', | |
393 | 'W','i','n','e','\\', | |
394 | 'W','i','n','e','\\', | |
395 | 'C','o','n','f','i','g','\\', | |
2052538a AJ |
396 | 'A','p','p','D','e','f','a','u','l','t','s','\\',0}; |
397 | static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0}; | |
b9c9cdc5 | 398 | |
2052538a AJ |
399 | str = RtlAllocateHeap( GetProcessHeap(), 0, |
400 | sizeof(AppDefaultsW) + sizeof(DllOverridesW) + | |
401 | strlenW(app_name) * sizeof(WCHAR) ); | |
402 | if (!str) return 0; | |
403 | strcpyW( str, AppDefaultsW ); | |
404 | strcatW( str, app_name ); | |
405 | strcatW( str, DllOverridesW ); | |
b9c9cdc5 | 406 | |
1cb92bbe | 407 | TRACE( "searching %s in %s\n", debugstr_w(module), debugstr_w(str) ); |
b9c9cdc5 | 408 | |
81bdcf12 AJ |
409 | attr.Length = sizeof(attr); |
410 | attr.RootDirectory = 0; | |
411 | attr.ObjectName = &nameW; | |
412 | attr.Attributes = 0; | |
413 | attr.SecurityDescriptor = NULL; | |
414 | attr.SecurityQualityOfService = NULL; | |
2052538a AJ |
415 | RtlInitUnicodeString( &nameW, str ); |
416 | ||
417 | if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0; | |
418 | RtlFreeHeap( GetProcessHeap(), 0, str ); | |
419 | return hkey; | |
b9c9cdc5 | 420 | } |
a739a69c | 421 | |
cebb2f11 | 422 | |
b9c9cdc5 | 423 | /*************************************************************************** |
69622dbd | 424 | * get_registry_value |
b9c9cdc5 | 425 | * |
69622dbd | 426 | * Load the registry loadorder value for a given module. |
b9c9cdc5 | 427 | */ |
1cb92bbe | 428 | static BOOL get_registry_value( HKEY hkey, const WCHAR *module, enum loadorder_type lo[] ) |
b9c9cdc5 | 429 | { |
81bdcf12 | 430 | UNICODE_STRING valueW; |
b9c9cdc5 | 431 | char buffer[80]; |
81bdcf12 AJ |
432 | DWORD count; |
433 | BOOL ret; | |
434 | ||
1cb92bbe | 435 | RtlInitUnicodeString( &valueW, module ); |
81bdcf12 AJ |
436 | |
437 | if ((ret = !NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation, | |
438 | buffer, sizeof(buffer), &count ))) | |
439 | { | |
440 | int i, n = 0; | |
441 | WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data; | |
b9c9cdc5 | 442 | |
81bdcf12 AJ |
443 | while (*str) |
444 | { | |
445 | enum loadorder_type type = LOADORDER_INVALID; | |
446 | ||
447 | while (*str == ',' || isspaceW(*str)) str++; | |
448 | if (!*str) break; | |
449 | ||
450 | switch(tolowerW(*str)) | |
451 | { | |
452 | case 'n': type = LOADORDER_DLL; break; | |
81bdcf12 | 453 | case 'b': type = LOADORDER_BI; break; |
7dd3f7c7 | 454 | case 's': break; /* no longer supported, ignore */ |
81bdcf12 AJ |
455 | case 0: break; /* end of string */ |
456 | default: | |
457 | ERR("Invalid load order module-type %s, ignored\n", debugstr_w(str)); | |
458 | break; | |
459 | } | |
460 | if (type != LOADORDER_INVALID) | |
461 | { | |
462 | for (i = 0; i < n; i++) if (lo[i] == type) break; /* already specified */ | |
463 | if (i == n) lo[n++] = type; | |
464 | } | |
465 | while (*str && *str != ',' && !isspaceW(*str)) str++; | |
466 | } | |
467 | lo[n] = LOADORDER_INVALID; | |
468 | } | |
81bdcf12 | 469 | return ret; |
b9c9cdc5 | 470 | } |
cebb2f11 | 471 | |
cebb2f11 | 472 | |
146afcc9 AJ |
473 | /*************************************************************************** |
474 | * MODULE_GetSystemDirectory | |
475 | * | |
476 | * Retrieve the system directory. The string must be freed by the caller. | |
477 | */ | |
478 | BOOL MODULE_GetSystemDirectory( UNICODE_STRING *sysdir ) | |
479 | { | |
480 | static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0}; | |
481 | UNICODE_STRING name; | |
482 | ||
483 | RtlInitUnicodeString( &name, winsysdirW ); | |
484 | sysdir->MaximumLength = 0; | |
485 | if (RtlQueryEnvironmentVariable_U( NULL, &name, sysdir ) != STATUS_BUFFER_TOO_SMALL) | |
486 | return FALSE; | |
487 | sysdir->MaximumLength = sysdir->Length + sizeof(WCHAR); | |
488 | if (!(sysdir->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, sysdir->MaximumLength ))) | |
489 | return FALSE; | |
490 | if (RtlQueryEnvironmentVariable_U( NULL, &name, sysdir ) == STATUS_SUCCESS) | |
491 | return TRUE; | |
492 | RtlFreeUnicodeString( sysdir ); | |
493 | return FALSE; | |
494 | } | |
495 | ||
496 | ||
cebb2f11 | 497 | /*************************************************************************** |
1cb92bbe | 498 | * MODULE_GetLoadOrderW (internal) |
cebb2f11 BS |
499 | * |
500 | * Locate the loadorder of a module. | |
501 | * Any path is stripped from the path-argument and so are the extension | |
c672c00f | 502 | * '.dll' and '.exe'. A lookup in the table can yield an override for |
5fce6fb7 | 503 | * the specific dll. Otherwise the default load order is returned. |
cebb2f11 | 504 | */ |
1cb92bbe | 505 | void MODULE_GetLoadOrderW( enum loadorder_type loadorder[], const WCHAR *app_name, |
16b44116 | 506 | const WCHAR *path ) |
cebb2f11 | 507 | { |
81bdcf12 AJ |
508 | static const WCHAR DllOverridesW[] = {'M','a','c','h','i','n','e','\\', |
509 | 'S','o','f','t','w','a','r','e','\\', | |
510 | 'W','i','n','e','\\', | |
511 | 'W','i','n','e','\\', | |
512 | 'C','o','n','f','i','g','\\', | |
513 | 'D','l','l','O','v','e','r','r','i','d','e','s',0}; | |
514 | ||
69622dbd | 515 | static HKEY std_key = (HKEY)-1; /* key to standard section, cached */ |
16b44116 AJ |
516 | static UNICODE_STRING sysdir; |
517 | ||
69622dbd | 518 | HKEY app_key = 0; |
1cb92bbe | 519 | WCHAR *module, *basename; |
16b44116 | 520 | UNICODE_STRING path_str; |
69622dbd | 521 | int len; |
cebb2f11 | 522 | |
2052538a AJ |
523 | if (!init_done) init_load_order(); |
524 | ||
1cb92bbe | 525 | TRACE("looking for %s\n", debugstr_w(path)); |
cebb2f11 | 526 | |
69622dbd | 527 | loadorder[0] = LOADORDER_INVALID; /* in case something bad happens below */ |
cebb2f11 | 528 | |
9ca159b6 | 529 | /* Strip path information if the module resides in the system directory |
9ca159b6 | 530 | */ |
16b44116 AJ |
531 | if (!sysdir.Buffer && !MODULE_GetSystemDirectory( &sysdir )) return; |
532 | RtlInitUnicodeString( &path_str, path ); | |
533 | if (RtlPrefixUnicodeString( &sysdir, &path_str, TRUE )) | |
69622dbd | 534 | { |
16b44116 AJ |
535 | const WCHAR *p = path + sysdir.Length / sizeof(WCHAR); |
536 | while (*p == '\\' || *p == '/') p++; | |
537 | if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p; | |
69622dbd AJ |
538 | } |
539 | ||
1cb92bbe AJ |
540 | if (!(len = strlenW(path))) return; |
541 | if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return; | |
542 | strcpyW( module+1, path ); /* reserve module[0] for the wildcard char */ | |
9a624916 | 543 | |
1cb92bbe | 544 | if (len >= 4) remove_dll_ext( module + 1 + len - 4 ); |
9a624916 | 545 | |
2052538a | 546 | /* check environment variable first */ |
1cb92bbe | 547 | if (get_env_load_order( module+1, loadorder )) |
69622dbd | 548 | { |
2052538a | 549 | TRACE( "got environment %s for %s\n", |
1cb92bbe | 550 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd AJ |
551 | goto done; |
552 | } | |
9a624916 | 553 | |
69622dbd | 554 | /* then explicit module name in AppDefaults */ |
2052538a | 555 | if (app_name) |
69622dbd | 556 | { |
2052538a AJ |
557 | app_key = open_app_key( app_name, module+1 ); |
558 | if (app_key && get_registry_value( app_key, module+1, loadorder )) | |
559 | { | |
560 | TRACE( "got app defaults %s for %s\n", | |
1cb92bbe | 561 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
2052538a AJ |
562 | goto done; |
563 | } | |
69622dbd | 564 | } |
9a624916 | 565 | |
69622dbd AJ |
566 | /* then explicit module name in standard section */ |
567 | if (std_key == (HKEY)-1) | |
81bdcf12 AJ |
568 | { |
569 | OBJECT_ATTRIBUTES attr; | |
570 | UNICODE_STRING nameW; | |
571 | ||
572 | attr.Length = sizeof(attr); | |
573 | attr.RootDirectory = 0; | |
574 | attr.ObjectName = &nameW; | |
575 | attr.Attributes = 0; | |
576 | attr.SecurityDescriptor = NULL; | |
577 | attr.SecurityQualityOfService = NULL; | |
578 | RtlInitUnicodeString( &nameW, DllOverridesW ); | |
579 | ||
580 | if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0; | |
581 | } | |
69622dbd AJ |
582 | |
583 | if (std_key && get_registry_value( std_key, module+1, loadorder )) | |
584 | { | |
585 | TRACE( "got standard entry %s for %s\n", | |
1cb92bbe | 586 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd AJ |
587 | goto done; |
588 | } | |
cebb2f11 | 589 | |
69622dbd | 590 | /* then module basename preceded by '*' in AppDefaults */ |
1cb92bbe | 591 | basename = (WCHAR *)get_basename( module+1 ); |
69622dbd AJ |
592 | basename[-1] = '*'; |
593 | if (app_key && get_registry_value( app_key, basename-1, loadorder )) | |
594 | { | |
595 | TRACE( "got app defaults basename %s for %s\n", | |
1cb92bbe | 596 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd AJ |
597 | goto done; |
598 | } | |
cebb2f11 | 599 | |
69622dbd AJ |
600 | /* then module name preceded by '*' in standard section */ |
601 | if (std_key && get_registry_value( std_key, basename-1, loadorder )) | |
602 | { | |
603 | TRACE( "got standard base name %s for %s\n", | |
1cb92bbe | 604 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd AJ |
605 | goto done; |
606 | } | |
cebb2f11 | 607 | |
69622dbd | 608 | /* then base name matching compiled-in defaults */ |
1cb92bbe | 609 | if (get_default_load_order( basename, loadorder )) |
69622dbd AJ |
610 | { |
611 | TRACE( "got compiled-in default %s for %s\n", | |
1cb92bbe | 612 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd AJ |
613 | goto done; |
614 | } | |
615 | ||
616 | if (basename == module+1) | |
617 | { | |
1cb92bbe AJ |
618 | static const WCHAR wildcardW[] = {'*',0}; |
619 | ||
69622dbd | 620 | /* then wildcard entry in AppDefaults (only if no explicit path) */ |
1cb92bbe | 621 | if (app_key && get_registry_value( app_key, wildcardW, loadorder )) |
fde1b0cb | 622 | { |
69622dbd | 623 | TRACE( "got app defaults wildcard %s for %s\n", |
1cb92bbe | 624 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd | 625 | goto done; |
fde1b0cb | 626 | } |
cebb2f11 | 627 | |
69622dbd | 628 | /* then wildcard entry in standard section (only if no explicit path) */ |
1cb92bbe | 629 | if (std_key && get_registry_value( std_key, wildcardW, loadorder )) |
fde1b0cb | 630 | { |
69622dbd | 631 | TRACE( "got standard wildcard %s for %s\n", |
1cb92bbe | 632 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
69622dbd | 633 | goto done; |
fde1b0cb | 634 | } |
cebb2f11 | 635 | |
851d25d9 AJ |
636 | /* and last the hard-coded default */ |
637 | memcpy( loadorder, default_loadorder, sizeof(default_loadorder) ); | |
638 | TRACE( "got hardcoded default %s for %s\n", | |
1cb92bbe | 639 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
851d25d9 AJ |
640 | } |
641 | else /* module contains an explicit path */ | |
642 | { | |
643 | memcpy( loadorder, default_path_loadorder, sizeof(default_path_loadorder) ); | |
644 | TRACE( "got hardcoded path default %s for %s\n", | |
1cb92bbe | 645 | debugstr_loadorder(loadorder), debugstr_w(path) ); |
851d25d9 | 646 | } |
b9c9cdc5 AJ |
647 | |
648 | done: | |
81bdcf12 | 649 | if (app_key) NtClose( app_key ); |
2052538a | 650 | RtlFreeHeap( GetProcessHeap(), 0, module ); |
b9c9cdc5 | 651 | } |