d3d8: Render state additions.
[wine/multimedia.git] / dlls / ntdll / loadorder.c
blob8f4f15890e1977225a4dbfc0559540126a67ee45
1 /*
2 * Dlls load order support
4 * Copyright 1999 Bertho Stultiens
5 * Copyright 2003 Alexandre Julliard
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
30 #include "windef.h"
31 #include "winternl.h"
32 #include "ntdll_misc.h"
33 #include "module.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(module);
40 #define LOADORDER_ALLOC_CLUSTER 32 /* Allocate with 32 entries at a time */
42 typedef struct module_loadorder
44 const WCHAR *modulename;
45 enum loadorder_type loadorder[LOADORDER_NTYPES];
46 } module_loadorder_t;
48 struct loadorder_list
50 int count;
51 int alloc;
52 module_loadorder_t *order;
55 /* dll to load as builtins if not explicitly specified otherwise */
56 /* the list must remain sorted by dll name */
57 static const WCHAR default_builtins[][10] =
59 { 'g','d','i','3','2',0 },
60 { 'i','c','m','p',0 },
61 { 'k','e','r','n','e','l','3','2',0 },
62 { 'n','t','d','l','l',0 },
63 { 'o','d','b','c','3','2',0 },
64 { 't','t','y','d','r','v',0 },
65 { 'u','s','e','r','3','2',0 },
66 { 'w','3','2','s','k','r','n','l',0 },
67 { 'w','i','n','e','d','o','s',0 },
68 { 'w','i','n','e','p','s',0 },
69 { 'w','i','n','m','m',0 },
70 { 'w','n','a','s','p','i','3','2',0 },
71 { 'w','o','w','3','2',0 },
72 { 'w','s','2','_','3','2',0 },
73 { 'w','s','o','c','k','3','2',0 },
74 { 'x','1','1','d','r','v',0 }
77 /* default if nothing else specified */
78 static const enum loadorder_type default_loadorder[LOADORDER_NTYPES] =
80 LOADORDER_BI, LOADORDER_DLL, 0
83 static const WCHAR separatorsW[] = {',',' ','\t',0};
85 static int init_done;
86 static struct loadorder_list env_list;
89 /***************************************************************************
90 * cmp_sort_func (internal, static)
92 * Sorting and comparing function used in sort and search of loadorder
93 * entries.
95 static int cmp_sort_func(const void *s1, const void *s2)
97 return strcmpiW(((const module_loadorder_t *)s1)->modulename, ((const module_loadorder_t *)s2)->modulename);
101 /***************************************************************************
102 * strcmp_func
104 static int strcmp_func(const void *s1, const void *s2)
106 return strcmpiW( (const WCHAR *)s1, (const WCHAR *)s2 );
110 /***************************************************************************
111 * get_basename
113 * Return the base name of a file name (i.e. remove the path components).
115 static const WCHAR *get_basename( const WCHAR *name )
117 const WCHAR *ptr;
119 if (name[0] && name[1] == ':') name += 2; /* strip drive specification */
120 if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1;
121 if ((ptr = strrchrW( name, '/' ))) name = ptr + 1;
122 return name;
125 /***************************************************************************
126 * remove_dll_ext
128 * Remove extension if it is ".dll".
130 static inline void remove_dll_ext( WCHAR *ext )
132 if (ext[0] == '.' &&
133 toupperW(ext[1]) == 'D' &&
134 toupperW(ext[2]) == 'L' &&
135 toupperW(ext[3]) == 'L' &&
136 !ext[4]) ext[0] = 0;
140 /***************************************************************************
141 * debugstr_loadorder
143 * Return a loadorder in printable form.
145 static const char *debugstr_loadorder( enum loadorder_type lo[] )
147 int i;
148 char buffer[LOADORDER_NTYPES*3+1];
150 buffer[0] = 0;
151 for(i = 0; i < LOADORDER_NTYPES; i++)
153 if (lo[i] == LOADORDER_INVALID) break;
154 switch(lo[i])
156 case LOADORDER_DLL: strcat( buffer, "n," ); break;
157 case LOADORDER_BI: strcat( buffer, "b," ); break;
158 default: strcat( buffer, "?," ); break;
161 if (buffer[0]) buffer[strlen(buffer)-1] = 0;
162 return debugstr_a(buffer);
166 /***************************************************************************
167 * append_load_order
169 * Append a load order to the list if necessary.
171 static void append_load_order(enum loadorder_type lo[], enum loadorder_type append)
173 int i;
175 for (i = 0; i < LOADORDER_NTYPES; i++)
177 if (lo[i] == LOADORDER_INVALID) /* append it here */
179 lo[i++] = append;
180 lo[i] = LOADORDER_INVALID;
181 return;
183 if (lo[i] == append) return; /* already in the list */
185 assert(0); /* cannot get here */
189 /***************************************************************************
190 * parse_load_order
192 * Parses the loadorder options from the configuration and puts it into
193 * a structure.
195 static void parse_load_order( const WCHAR *order, enum loadorder_type lo[] )
197 lo[0] = LOADORDER_INVALID;
198 while (*order)
200 order += strspnW( order, separatorsW );
201 switch(*order)
203 case 'N': /* Native */
204 case 'n':
205 append_load_order( lo, LOADORDER_DLL );
206 break;
207 case 'B': /* Builtin */
208 case 'b':
209 append_load_order( lo, LOADORDER_BI );
210 break;
212 order += strcspnW( order, separatorsW );
217 /***************************************************************************
218 * add_load_order
220 * Adds an entry in the list of environment overrides.
222 static void add_load_order( const module_loadorder_t *plo )
224 int i;
226 for(i = 0; i < env_list.count; i++)
228 if(!cmp_sort_func(plo, &env_list.order[i] ))
230 /* replace existing option */
231 memcpy( env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
232 return;
236 if (i >= env_list.alloc)
238 /* No space in current array, make it larger */
239 env_list.alloc += LOADORDER_ALLOC_CLUSTER;
240 if (env_list.order)
241 env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order,
242 env_list.alloc * sizeof(module_loadorder_t));
243 else
244 env_list.order = RtlAllocateHeap(GetProcessHeap(), 0,
245 env_list.alloc * sizeof(module_loadorder_t));
246 if(!env_list.order)
248 MESSAGE("Virtual memory exhausted\n");
249 exit(1);
252 memcpy(env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
253 env_list.order[i].modulename = plo->modulename;
254 env_list.count++;
258 /***************************************************************************
259 * add_load_order_set
261 * Adds a set of entries in the list of command-line overrides from the key parameter.
263 static void add_load_order_set( WCHAR *entry )
265 module_loadorder_t ldo;
266 WCHAR *end = strchrW( entry, '=' );
268 if (!end) return;
269 *end++ = 0;
270 parse_load_order( end, ldo.loadorder );
272 while (*entry)
274 entry += strspnW( entry, separatorsW );
275 end = entry + strcspnW( entry, separatorsW );
276 if (*end) *end++ = 0;
277 if (*entry)
279 WCHAR *ext = strrchrW(entry, '.');
280 if (ext) remove_dll_ext( ext );
281 ldo.modulename = entry;
282 add_load_order( &ldo );
283 entry = end;
289 /***************************************************************************
290 * init_load_order
292 static void init_load_order(void)
294 const char *order = getenv( "WINEDLLOVERRIDES" );
295 UNICODE_STRING strW;
296 WCHAR *entry, *next;
298 init_done = 1;
299 if (!order) return;
301 if (!strcmp( order, "help" ))
303 MESSAGE( "Syntax:\n"
304 " WINEDLLOVERRIDES=\"entry;entry;entry...\"\n"
305 " where each entry is of the form:\n"
306 " module[,module...]={native|builtin}[,{b|n}]\n"
307 "\n"
308 " Only the first letter of the override (native or builtin)\n"
309 " is significant.\n\n"
310 "Example:\n"
311 " WINEDLLOVERRIDES=\"comdlg32=n,b;shell32,shlwapi=b\"\n" );
312 exit(0);
315 RtlCreateUnicodeStringFromAsciiz( &strW, order );
316 entry = strW.Buffer;
317 while (*entry)
319 while (*entry && *entry == ';') entry++;
320 if (!*entry) break;
321 next = strchrW( entry, ';' );
322 if (next) *next++ = 0;
323 else next = entry + strlenW(entry);
324 add_load_order_set( entry );
325 entry = next;
328 /* sort the array for quick lookup */
329 if (env_list.count)
330 qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func);
332 /* Note: we don't free the Unicode string because the
333 * stored module names point inside it */
337 /***************************************************************************
338 * get_env_load_order
340 * Get the load order for a given module from the WINEDLLOVERRIDES environment variable.
342 static inline BOOL get_env_load_order( const WCHAR *module, enum loadorder_type lo[] )
344 module_loadorder_t tmp, *res = NULL;
346 tmp.modulename = module;
347 /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
348 if (env_list.count &&
349 (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func)))
350 memcpy( lo, res->loadorder, sizeof(res->loadorder) );
351 return (res != NULL);
355 /***************************************************************************
356 * get_default_load_order
358 * Get the load order for a given module from the default list.
360 static inline BOOL get_default_load_order( const WCHAR *module, enum loadorder_type lo[] )
362 const int count = sizeof(default_builtins) / sizeof(default_builtins[0]);
363 if (!bsearch( module, default_builtins, count, sizeof(default_builtins[0]), strcmp_func ))
364 return FALSE;
365 lo[0] = LOADORDER_BI;
366 lo[1] = LOADORDER_INVALID;
367 return TRUE;
371 /***************************************************************************
372 * open_app_key
374 * Open the registry key to the app-specific DllOverrides list.
376 static HANDLE open_app_key( const WCHAR *app_name, const WCHAR *module )
378 OBJECT_ATTRIBUTES attr;
379 UNICODE_STRING nameW;
380 HANDLE root, hkey;
381 WCHAR *str;
382 static const WCHAR AppDefaultsW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
383 'A','p','p','D','e','f','a','u','l','t','s','\\',0};
384 static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0};
386 str = RtlAllocateHeap( GetProcessHeap(), 0,
387 sizeof(AppDefaultsW) + sizeof(DllOverridesW) +
388 strlenW(app_name) * sizeof(WCHAR) );
389 if (!str) return 0;
390 strcpyW( str, AppDefaultsW );
391 strcatW( str, app_name );
392 strcatW( str, DllOverridesW );
394 TRACE( "searching %s in %s\n", debugstr_w(module), debugstr_w(str) );
396 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
397 attr.Length = sizeof(attr);
398 attr.RootDirectory = root;
399 attr.ObjectName = &nameW;
400 attr.Attributes = 0;
401 attr.SecurityDescriptor = NULL;
402 attr.SecurityQualityOfService = NULL;
403 RtlInitUnicodeString( &nameW, str );
405 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DllOverrides */
406 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
407 NtClose( root );
408 RtlFreeHeap( GetProcessHeap(), 0, str );
409 return hkey;
413 /***************************************************************************
414 * get_registry_value
416 * Load the registry loadorder value for a given module.
418 static BOOL get_registry_value( HANDLE hkey, const WCHAR *module, enum loadorder_type lo[] )
420 UNICODE_STRING valueW;
421 char buffer[80];
422 DWORD count;
423 BOOL ret;
425 RtlInitUnicodeString( &valueW, module );
427 if ((ret = !NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation,
428 buffer, sizeof(buffer), &count )))
430 int i, n = 0;
431 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
433 while (*str)
435 enum loadorder_type type = LOADORDER_INVALID;
437 while (*str == ',' || isspaceW(*str)) str++;
438 if (!*str) break;
440 switch(tolowerW(*str))
442 case 'n': type = LOADORDER_DLL; break;
443 case 'b': type = LOADORDER_BI; break;
444 case 's': break; /* no longer supported, ignore */
445 case 0: break; /* end of string */
446 default:
447 ERR("Invalid load order module-type %s, ignored\n", debugstr_w(str));
448 break;
450 if (type != LOADORDER_INVALID)
452 for (i = 0; i < n; i++) if (lo[i] == type) break; /* already specified */
453 if (i == n) lo[n++] = type;
455 while (*str && *str != ',' && !isspaceW(*str)) str++;
457 lo[n] = LOADORDER_INVALID;
459 return ret;
463 /***************************************************************************
464 * MODULE_GetLoadOrderW (internal)
466 * Return the loadorder of a module.
467 * The system directory and '.dll' extension is stripped from the path.
469 void MODULE_GetLoadOrderW( enum loadorder_type loadorder[], const WCHAR *app_name,
470 const WCHAR *path )
472 static const WCHAR DllOverridesW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
473 'D','l','l','O','v','e','r','r','i','d','e','s',0};
475 static HANDLE std_key = (HANDLE)-1; /* key to standard section, cached */
477 HANDLE app_key = 0;
478 WCHAR *module, *basename;
479 UNICODE_STRING path_str;
480 int len;
482 if (!init_done) init_load_order();
484 TRACE("looking for %s\n", debugstr_w(path));
486 loadorder[0] = LOADORDER_INVALID; /* in case something bad happens below */
488 /* Strip path information if the module resides in the system directory
490 RtlInitUnicodeString( &path_str, path );
491 if (RtlPrefixUnicodeString( &system_dir, &path_str, TRUE ))
493 const WCHAR *p = path + system_dir.Length / sizeof(WCHAR);
494 while (*p == '\\' || *p == '/') p++;
495 if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p;
498 if (!(len = strlenW(path))) return;
499 if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return;
500 strcpyW( module+1, path ); /* reserve module[0] for the wildcard char */
502 if (len >= 4) remove_dll_ext( module + 1 + len - 4 );
504 /* check environment variable first */
505 if (get_env_load_order( module+1, loadorder ))
507 TRACE( "got environment %s for %s\n",
508 debugstr_loadorder(loadorder), debugstr_w(path) );
509 goto done;
512 /* then explicit module name in AppDefaults */
513 if (app_name)
515 app_key = open_app_key( app_name, module+1 );
516 if (app_key && get_registry_value( app_key, module+1, loadorder ))
518 TRACE( "got app defaults %s for %s\n",
519 debugstr_loadorder(loadorder), debugstr_w(path) );
520 goto done;
524 /* then explicit module name in standard section */
525 if (std_key == (HANDLE)-1)
527 OBJECT_ATTRIBUTES attr;
528 UNICODE_STRING nameW;
529 HANDLE root;
531 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
532 attr.Length = sizeof(attr);
533 attr.RootDirectory = root;
534 attr.ObjectName = &nameW;
535 attr.Attributes = 0;
536 attr.SecurityDescriptor = NULL;
537 attr.SecurityQualityOfService = NULL;
538 RtlInitUnicodeString( &nameW, DllOverridesW );
540 /* @@ Wine registry key: HKCU\Software\Wine\DllOverrides */
541 if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0;
542 NtClose( root );
545 if (std_key && get_registry_value( std_key, module+1, loadorder ))
547 TRACE( "got standard entry %s for %s\n",
548 debugstr_loadorder(loadorder), debugstr_w(path) );
549 goto done;
552 /* then module basename preceded by '*' in environment */
553 basename = (WCHAR *)get_basename( module+1 );
554 basename[-1] = '*';
555 if (get_env_load_order( basename-1, loadorder ))
557 TRACE( "got environment basename %s for %s\n",
558 debugstr_loadorder(loadorder), debugstr_w(path) );
559 goto done;
562 /* then module basename preceded by '*' in AppDefaults */
563 if (app_key && get_registry_value( app_key, basename-1, loadorder ))
565 TRACE( "got app defaults basename %s for %s\n",
566 debugstr_loadorder(loadorder), debugstr_w(path) );
567 goto done;
570 /* then module name preceded by '*' in standard section */
571 if (std_key && get_registry_value( std_key, basename-1, loadorder ))
573 TRACE( "got standard base name %s for %s\n",
574 debugstr_loadorder(loadorder), debugstr_w(path) );
575 goto done;
578 if (basename == module+1) /* module doesn't contain a path */
580 static const WCHAR wildcardW[] = {'*',0};
582 /* then base name matching compiled-in defaults */
583 if (get_default_load_order( basename, loadorder ))
585 TRACE( "got compiled-in default %s for %s\n",
586 debugstr_loadorder(loadorder), debugstr_w(path) );
587 goto done;
590 /* then wildcard entry in AppDefaults (only if no explicit path) */
591 if (app_key && get_registry_value( app_key, wildcardW, loadorder ))
593 TRACE( "got app defaults wildcard %s for %s\n",
594 debugstr_loadorder(loadorder), debugstr_w(path) );
595 goto done;
598 /* then wildcard entry in standard section (only if no explicit path) */
599 if (std_key && get_registry_value( std_key, wildcardW, loadorder ))
601 TRACE( "got standard wildcard %s for %s\n",
602 debugstr_loadorder(loadorder), debugstr_w(path) );
603 goto done;
606 else /* module contains an explicit path */
608 /* then base name without '*' in environment */
609 if (get_env_load_order( basename, loadorder ))
611 TRACE( "got environment basename %s for %s\n",
612 debugstr_loadorder(loadorder), debugstr_w(path) );
613 goto done;
616 /* then base name without '*' in AppDefaults */
617 if (app_key && get_registry_value( app_key, basename, loadorder ))
619 TRACE( "got basename app defaults %s for %s\n",
620 debugstr_loadorder(loadorder), debugstr_w(path) );
621 goto done;
624 /* then base name without '*' in standard section */
625 if (std_key && get_registry_value( std_key, basename, loadorder ))
627 TRACE( "got basename standard entry %s for %s\n",
628 debugstr_loadorder(loadorder), debugstr_w(path) );
629 goto done;
632 /* then base name matching compiled-in defaults */
633 if (get_default_load_order( basename, loadorder ))
635 TRACE( "got compiled-in default %s for %s\n",
636 debugstr_loadorder(loadorder), debugstr_w(path) );
637 goto done;
641 /* and last the hard-coded default */
642 memcpy( loadorder, default_loadorder, sizeof(default_loadorder) );
643 TRACE( "got hardcoded default %s for %s\n",
644 debugstr_loadorder(loadorder), debugstr_w(path) );
646 done:
647 if (app_key) NtClose( app_key );
648 RtlFreeHeap( GetProcessHeap(), 0, module );