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
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include <sys/types.h>
32 #include "wine/unicode.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(resource);
42 /**********************************************************************
45 * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
47 inline static int is_data_file_module( HMODULE hmod )
49 return (ULONG_PTR)hmod & 1;
53 /**********************************************************************
56 * Get the resource directory of a PE module
58 static const IMAGE_RESOURCE_DIRECTORY* get_resdir( HMODULE hmod )
62 if (!hmod) hmod = GetModuleHandleA( NULL );
63 else if (!HIWORD(hmod))
65 FIXME("Enumeration of 16-bit resources is not supported\n");
66 SetLastError(ERROR_INVALID_HANDLE);
69 return RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_RESOURCE, &size );
73 /**********************************************************************
76 * Find an entry by id in a resource directory
78 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
79 WORD id, const void *root )
81 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
84 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
85 min = dir->NumberOfNamedEntries;
86 max = min + dir->NumberOfIdEntries - 1;
89 pos = (min + max) / 2;
90 if (entry[pos].u1.s2.Id == id)
91 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
92 if (entry[pos].u1.s2.Id > id) max = pos - 1;
99 /**********************************************************************
100 * find_entry_by_nameW
102 * Find an entry by name in a resource directory
104 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY *dir,
105 LPCWSTR name, const void *root )
107 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
108 const IMAGE_RESOURCE_DIR_STRING_U *str;
109 int min, max, res, pos, namelen;
111 if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
115 if (!WideCharToMultiByte( CP_ACP, 0, name+1, -1, buf, sizeof(buf), NULL, NULL ))
117 return find_entry_by_id( dir, atoi(buf), root );
120 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
121 namelen = strlenW(name);
123 max = dir->NumberOfNamedEntries - 1;
126 pos = (min + max) / 2;
127 str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
128 res = strncmpiW( name, str->NameString, str->Length );
129 if (!res && namelen == str->Length)
130 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
131 if (res < 0) max = pos - 1;
138 /**********************************************************************
139 * find_entry_by_nameA
141 * Find an entry by name in a resource directory
143 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY *dir,
144 LPCSTR name, const void *root )
146 const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
150 if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
153 return find_entry_by_id( dir, atoi(name+1), root );
156 len = MultiByteToWideChar( CP_ACP, 0, name, -1, NULL, 0 );
157 if ((nameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
159 MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, len );
160 ret = find_entry_by_nameW( dir, nameW, root );
161 HeapFree( GetProcessHeap(), 0, nameW );
167 /**********************************************************************
170 * Find a default entry in a resource directory
172 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
175 const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
177 entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
178 return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
182 /**********************************************************************
185 * FindResourceExA/W does search in the following order:
186 * 1. Exact specified language
187 * 2. Language with neutral sublanguage
188 * 3. Neutral language with neutral sublanguage
189 * 4. Neutral language with default sublanguage
191 HRSRC PE_FindResourceExW( HMODULE hmod, LPCWSTR name, LPCWSTR type, WORD lang )
193 const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
197 if (!resdirptr) return 0;
200 if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
201 if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
203 /* 1. Exact specified language */
204 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
206 /* 2. Language with neutral sublanguage */
207 lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
208 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
210 /* 3. Neutral language with neutral sublanguage */
211 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
212 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
214 /* 4. Neutral language with default sublanguage */
215 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
216 result = (HRSRC)find_entry_by_id( resdirptr, lang, root );
222 /**********************************************************************
225 * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
226 * FindResourceA/W does search in the following order:
227 * 1. Neutral language with neutral sublanguage
228 * 2. Neutral language with default sublanguage
229 * 3. Current locale lang id
230 * 4. Current locale lang id with neutral sublanguage
231 * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
232 * 6. Return first in the list
234 HRSRC PE_FindResourceW( HMODULE hmod, LPCWSTR name, LPCWSTR type )
236 const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
241 if (!resdirptr) return 0;
244 if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
245 if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
247 /* 1. Neutral language with neutral sublanguage */
248 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
249 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
251 /* 2. Neutral language with default sublanguage */
252 lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
253 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
255 /* 3. Current locale lang id */
256 lang = LANGIDFROMLCID(GetUserDefaultLCID());
257 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
259 /* 4. Current locale lang id with neutral sublanguage */
260 lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
261 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
263 /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
264 lang = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
265 if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
267 /* 6. Return first in the list */
268 result = (HRSRC)find_entry_default( resdirptr, root );
275 /**********************************************************************
278 HGLOBAL PE_LoadResource( HMODULE hmod, HRSRC hRsrc )
282 if (!hRsrc) return 0;
283 if (!hmod) hmod = GetModuleHandleA( NULL );
285 offset = ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->OffsetToData;
287 if (is_data_file_module(hmod))
289 hmod = (HMODULE)((ULONG_PTR)hmod & ~1);
290 return (HGLOBAL)RtlImageRvaToVa( RtlImageNtHeader(hmod), hmod, offset, NULL );
293 return (HGLOBAL)((char *)hmod + offset);
297 /**********************************************************************
300 DWORD PE_SizeofResource( HRSRC hRsrc )
302 if (!hRsrc) return 0;
303 return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
307 /**********************************************************************
308 * EnumResourceTypesA (KERNEL32.@)
310 BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG lparam)
313 const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
314 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
317 if (!resdir) return FALSE;
319 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
321 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
324 if (et[i].u1.s1.NameIsString)
326 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
327 DWORD len = WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
328 NULL, 0, NULL, NULL);
329 if (!(type = HeapAlloc(GetProcessHeap(), 0, len + 1)))
331 WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
332 type, len, NULL, NULL);
334 ret = lpfun(hmod,type,lparam);
335 HeapFree(GetProcessHeap(), 0, type);
339 type = (LPSTR)(int)et[i].u1.s2.Id;
340 ret = lpfun(hmod,type,lparam);
349 /**********************************************************************
350 * EnumResourceTypesW (KERNEL32.@)
352 BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG lparam)
355 const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
356 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
359 if (!resdir) return FALSE;
361 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
363 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
366 if (et[i].u1.s1.NameIsString)
368 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
369 if (!(type = HeapAlloc(GetProcessHeap(), 0, (pResString->Length+1) * sizeof (WCHAR))))
371 memcpy(type, pResString->NameString, pResString->Length * sizeof (WCHAR));
372 type[pResString->Length] = '\0';
373 ret = lpfun(hmod,type,lparam);
374 HeapFree(GetProcessHeap(), 0, type);
378 type = (LPWSTR)(int)et[i].u1.s2.Id;
379 ret = lpfun(hmod,type,lparam);
388 /**********************************************************************
389 * EnumResourceNamesA (KERNEL32.@)
391 BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG lparam )
394 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
395 const IMAGE_RESOURCE_DIRECTORY *resdir;
396 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
399 if (!basedir) return FALSE;
401 if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
403 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
405 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
408 if (et[i].u1.s1.NameIsString)
410 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
411 DWORD len = WideCharToMultiByte(CP_ACP, 0, pResString->NameString, pResString->Length,
412 NULL, 0, NULL, NULL);
413 if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 )))
415 WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
416 name, len, NULL, NULL );
418 ret = lpfun(hmod,type,name,lparam);
419 HeapFree( GetProcessHeap(), 0, name );
423 name = (LPSTR)(int)et[i].u1.s2.Id;
424 ret = lpfun(hmod,type,name,lparam);
433 /**********************************************************************
434 * EnumResourceNamesW (KERNEL32.@)
436 BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG lparam )
439 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
440 const IMAGE_RESOURCE_DIRECTORY *resdir;
441 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
444 if (!basedir) return FALSE;
446 if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
448 et = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
450 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
453 if (et[i].u1.s1.NameIsString)
455 PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
456 if (!(name = HeapAlloc(GetProcessHeap(), 0, (pResString->Length + 1) * sizeof (WCHAR))))
458 memcpy(name, pResString->NameString, pResString->Length * sizeof (WCHAR));
459 name[pResString->Length] = '\0';
460 ret = lpfun(hmod,type,name,lparam);
461 HeapFree(GetProcessHeap(), 0, name);
465 name = (LPWSTR)(int)et[i].u1.s2.Id;
466 ret = lpfun(hmod,type,name,lparam);
475 /**********************************************************************
476 * EnumResourceLanguagesA (KERNEL32.@)
478 BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name,
479 ENUMRESLANGPROCA lpfun, LONG lparam )
482 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
483 const IMAGE_RESOURCE_DIRECTORY *resdir;
484 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
487 if (!basedir) return FALSE;
488 if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
489 if (!(resdir = find_entry_by_nameA( resdir, name, basedir ))) return FALSE;
491 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
493 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
494 /* languages are just ids... I hope */
495 ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);
503 /**********************************************************************
504 * EnumResourceLanguagesW (KERNEL32.@)
506 BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name,
507 ENUMRESLANGPROCW lpfun, LONG lparam )
510 const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
511 const IMAGE_RESOURCE_DIRECTORY *resdir;
512 const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
515 if (!basedir) return FALSE;
517 if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
518 if (!(resdir = find_entry_by_nameW( resdir, name, basedir ))) return FALSE;
520 et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
522 for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
523 ret = lpfun(hmod,type,name,et[i].u1.s2.Id,lparam);