2 * PE (Portable Execute) File Resources
4 * Copyright 1995 Thomas Sandford
5 * Copyright 1996 Martin von Loewis
7 * Based on the Win16 resource handling code in loader/resource.c
8 * Copyright 1993 Robert J. Amstadt
9 * Copyright 1995 Alexandre Julliard
10 * Copyright 1997 Marcus Meissner
14 #include <sys/types.h>
16 #include "wine/winestring.h"
17 #include "wine/unicode.h"
25 #include "stackframe.h"
27 #include "debugtools.h"
29 DEFAULT_DEBUG_CHANNEL(resource);
31 /**********************************************************************
34 * Get the resource directory of a PE module
36 static const IMAGE_RESOURCE_DIRECTORY* get_resdir( HMODULE hmod )
38 const IMAGE_DATA_DIRECTORY *dir;
39 const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
41 if (!hmod) hmod = GetModuleHandleA( NULL );
42 else if (!HIWORD(hmod))
44 FIXME("Enumeration of 16-bit resources is not supported\n");
45 SetLastError(ERROR_INVALID_HANDLE);
48 dir = &PE_HEADER(hmod)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
49 if (dir->Size && dir->VirtualAddress)
50 ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)hmod + dir->VirtualAddress);
55 /**********************************************************************
58 * Find an entry by name in a resource directory
60 static IMAGE_RESOURCE_DIRECTORY *find_entry_by_name( const IMAGE_RESOURCE_DIRECTORY *dir,
61 LPCWSTR name, const char *root )
64 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
65 const IMAGE_RESOURCE_DIR_STRING_U *str;
66 int min, max, res, pos, namelen = strlenW(name);
68 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
70 max = dir->NumberOfNamedEntries - 1;
73 pos = (min + max) / 2;
74 str = (IMAGE_RESOURCE_DIR_STRING_U *)(root + entry[pos].u1.s.NameOffset);
75 res = strncmpiW( name, str->NameString, str->Length );
76 if (!res && namelen == str->Length)
77 return (IMAGE_RESOURCE_DIRECTORY *)(root + entry[pos].u2.s.OffsetToDirectory);
78 if (res < 0) max = pos - 1;
82 /* now do a linear search just in case */
83 for (pos = 0; pos < dir->NumberOfNamedEntries; pos++)
85 str = (IMAGE_RESOURCE_DIR_STRING_U *)(root + entry[pos].u1.s.NameOffset);
86 if (namelen != str->Length) continue;
87 if (!strncmpiW( name, str->NameString, str->Length ))
89 ERR( "entry '%s' required linear search, please report\n", debugstr_w(name) );
90 return (IMAGE_RESOURCE_DIRECTORY *)(root + entry[pos].u2.s.OffsetToDirectory);
97 /**********************************************************************
100 * Find an entry by id in a resource directory
102 static IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
103 WORD id, const char *root )
105 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
108 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
109 min = dir->NumberOfNamedEntries;
110 max = min + dir->NumberOfIdEntries - 1;
113 pos = (min + max) / 2;
114 if (entry[pos].u1.Id == id)
115 return (IMAGE_RESOURCE_DIRECTORY *)(root + entry[pos].u2.s.OffsetToDirectory);
116 if (entry[pos].u1.Id > id) max = pos - 1;
120 /* now do a linear search just in case */
121 min = dir->NumberOfNamedEntries;
122 max = min + dir->NumberOfIdEntries - 1;
123 for (pos = min; pos <= max; pos++)
125 if (entry[pos].u1.Id == id)
127 ERR( "entry %04x required linear search, please report\n", id );
128 return (IMAGE_RESOURCE_DIRECTORY *)(root + entry[pos].u2.s.OffsetToDirectory);
135 /**********************************************************************
138 * Find a default entry in a resource directory
140 static IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
143 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
145 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
146 if (dir->NumberOfNamedEntries || dir->NumberOfIdEntries)
147 return (IMAGE_RESOURCE_DIRECTORY *)(root + entry->u2.s.OffsetToDirectory);
152 /**********************************************************************
155 * Helper function - goes down one level of PE resource tree
158 const IMAGE_RESOURCE_DIRECTORY *GetResDirEntryW(const IMAGE_RESOURCE_DIRECTORY *resdirptr,
159 LPCWSTR name, LPCVOID root,
167 if (!WideCharToMultiByte( CP_ACP, 0, name+1, -1, buf, sizeof(buf), NULL, NULL ))
169 return find_entry_by_id( resdirptr, atoi(buf), root );
171 return find_entry_by_name( resdirptr, name, root );
175 const IMAGE_RESOURCE_DIRECTORY *ret;
176 ret = find_entry_by_id( resdirptr, LOWORD(name), root );
177 if (!ret && !name && allowdefault) ret = find_entry_default( resdirptr, root );
182 /**********************************************************************
185 const IMAGE_RESOURCE_DIRECTORY *GetResDirEntryA( const IMAGE_RESOURCE_DIRECTORY *resdirptr,
186 LPCSTR name, LPCVOID root,
189 const IMAGE_RESOURCE_DIRECTORY *retv;
190 LPWSTR nameW = HIWORD(name)? HEAP_strdupAtoW( GetProcessHeap(), 0, name )
193 retv = GetResDirEntryW( resdirptr, nameW, root, allowdefault );
195 if ( HIWORD(name) ) HeapFree( GetProcessHeap(), 0, nameW );
200 /**********************************************************************
203 * FindResourceExA/W does search in the following order:
204 * 1. Exact specified language
205 * 2. Language with neutral sublanguage
206 * 3. Neutral language with neutral sublanguage
207 * 4. Neutral language with default sublanguage
209 HRSRC PE_FindResourceExW( HMODULE hmod, LPCWSTR name, LPCWSTR type, WORD lang )
211 const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
215 if (!resdirptr) return 0;
218 if ((resdirptr = GetResDirEntryW(resdirptr, type, root, FALSE)) == NULL)
220 if ((resdirptr = GetResDirEntryW(resdirptr, name, root, FALSE)) == NULL)
223 /* 1. Exact specified language */
224 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
226 /* 2. Language with neutral sublanguage */
227 lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
228 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
230 /* 3. Neutral language with neutral sublanguage */
231 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
232 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
234 /* 4. Neutral language with default sublanguage */
235 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
236 result = (HRSRC)find_entry_by_id( resdirptr, lang, root );
242 /**********************************************************************
245 * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
246 * FindResourceA/W does search in the following order:
247 * 1. Neutral language with neutral sublanguage
248 * 2. Neutral language with default sublanguage
249 * 3. Current locale lang id
250 * 4. Current locale lang id with neutral sublanguage
251 * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
252 * 6. Return first in the list
254 HRSRC PE_FindResourceW( HMODULE hmod, LPCWSTR name, LPCWSTR type )
256 const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
261 if (!resdirptr) return 0;
264 if ((resdirptr = GetResDirEntryW(resdirptr, type, root, FALSE)) == NULL)
266 if ((resdirptr = GetResDirEntryW(resdirptr, name, root, FALSE)) == NULL)
269 /* 1. Neutral language with neutral sublanguage */
270 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
271 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
273 /* 2. Neutral language with default sublanguage */
274 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
275 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
277 /* 3. Current locale lang id */
278 lang = LANGIDFROMLCID(GetUserDefaultLCID());
279 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
281 /* 4. Current locale lang id with neutral sublanguage */
282 lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
283 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
285 /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
286 lang = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
287 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
289 /* 6. Return first in the list */
290 result = (HRSRC)find_entry_default( resdirptr, root );
297 /**********************************************************************
300 HANDLE PE_LoadResource( HMODULE hmod, HANDLE hRsrc )
302 if (!hRsrc) return 0;
303 return (HANDLE)(hmod + ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->OffsetToData);
307 /**********************************************************************
310 DWORD PE_SizeofResource( HANDLE hRsrc )
312 if (!hRsrc) return 0;
313 return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
317 /**********************************************************************
318 * EnumResourceTypesA (KERNEL32.90)
320 BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG lparam)
323 const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
324 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
327 if (!resdir) return FALSE;
329 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
331 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
334 if (et[i].u1.s.NameIsString)
336 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s.NameOffset);
337 DWORD len = WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
338 NULL, 0, NULL, NULL);
339 if (!(type = HeapAlloc(GetProcessHeap(), 0, len + 1)))
341 WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
342 type, len, NULL, NULL);
344 ret = lpfun(hmod,type,lparam);
345 HeapFree(GetProcessHeap(), 0, type);
349 type = (LPSTR)(int)et[i].u1.Id;
350 ret = lpfun(hmod,type,lparam);
359 /**********************************************************************
360 * EnumResourceTypesW (KERNEL32.91)
362 BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG lparam)
365 const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
366 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
369 if (!resdir) return FALSE;
371 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
373 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
376 if (et[i].u1.s.NameIsString)
378 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s.NameOffset);
379 if (!(type = HeapAlloc(GetProcessHeap(), 0, (pResString->Length+1) * sizeof (WCHAR))))
381 memcpy(type, pResString->NameString, pResString->Length * sizeof (WCHAR));
382 type[pResString->Length] = '\0';
383 ret = lpfun(hmod,type,lparam);
384 HeapFree(GetProcessHeap(), 0, type);
388 type = (LPWSTR)(int)et[i].u1.Id;
389 ret = lpfun(hmod,type,lparam);
398 /**********************************************************************
399 * EnumResourceNamesA (KERNEL32.88)
401 BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG lparam )
404 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
405 const IMAGE_RESOURCE_DIRECTORY *resdir;
406 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
409 if (!basedir) return FALSE;
411 if (!(resdir = GetResDirEntryA(basedir,type,basedir,FALSE))) return FALSE;
413 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
415 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
418 if (et[i].u1.s.NameIsString)
420 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s.NameOffset);
421 DWORD len = WideCharToMultiByte(CP_ACP, 0, pResString->NameString, pResString->Length,
422 NULL, 0, NULL, NULL);
423 if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 )))
425 WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
426 name, len, NULL, NULL );
428 ret = lpfun(hmod,type,name,lparam);
429 HeapFree( GetProcessHeap(), 0, name );
433 name = (LPSTR)(int)et[i].u1.Id;
434 ret = lpfun(hmod,type,name,lparam);
443 /**********************************************************************
444 * EnumResourceNamesW (KERNEL32.89)
446 BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG lparam )
449 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
450 const IMAGE_RESOURCE_DIRECTORY *resdir;
451 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
454 if (!basedir) return FALSE;
456 resdir = GetResDirEntryW(basedir,type,basedir,FALSE);
459 et = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
461 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
464 if (et[i].u1.s.NameIsString)
466 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s.NameOffset);
467 if (!(name = HeapAlloc(GetProcessHeap(), 0, (pResString->Length + 1) * sizeof (WCHAR))))
469 memcpy(name, pResString->NameString, pResString->Length * sizeof (WCHAR));
470 name[pResString->Length] = '\0';
471 ret = lpfun(hmod,type,name,lparam);
472 HeapFree(GetProcessHeap(), 0, name);
476 name = (LPWSTR)(int)et[i].u1.Id;
477 ret = lpfun(hmod,type,name,lparam);
486 /**********************************************************************
487 * EnumResourceLanguagesA (KERNEL32.86)
489 BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name,
490 ENUMRESLANGPROCA lpfun, LONG lparam )
493 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
494 const IMAGE_RESOURCE_DIRECTORY *resdir;
495 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
498 if (!basedir) return FALSE;
499 if (!(resdir = GetResDirEntryA(basedir,type,basedir,FALSE))) return FALSE;
500 if (!(resdir = GetResDirEntryA(resdir,name,basedir,FALSE))) return FALSE;
502 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
504 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
505 /* languages are just ids... I hope */
506 ret = lpfun(hmod,type,name,et[i].u1.Id,lparam);
514 /**********************************************************************
515 * EnumResourceLanguagesW (KERNEL32.87)
517 BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name,
518 ENUMRESLANGPROCW lpfun, LONG lparam )
521 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
522 const IMAGE_RESOURCE_DIRECTORY *resdir;
523 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
526 if (!basedir) return FALSE;
528 resdir = GetResDirEntryW(basedir,type,basedir,FALSE);
531 resdir = GetResDirEntryW(resdir,name,basedir,FALSE);
534 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
536 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
537 ret = lpfun(hmod,type,name,et[i].u1.Id,lparam);