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
16 #include <sys/types.h>
18 #include "wine/unicode.h"
23 #include "stackframe.h"
24 #include "debugtools.h"
26 DEFAULT_DEBUG_CHANNEL(resource
);
29 /**********************************************************************
32 * Get the base address of a module
34 static const void *get_module_base( HMODULE hmod
)
36 if (!hmod
) hmod
= GetModuleHandleA( NULL
);
37 else if (!HIWORD(hmod
))
39 FIXME("Enumeration of 16-bit resources is not supported\n");
40 SetLastError(ERROR_INVALID_HANDLE
);
44 /* clear low order bit in case of LOAD_LIBRARY_AS_DATAFILE module */
45 return (void *)((ULONG_PTR
)hmod
& ~1);
49 /**********************************************************************
52 * Get the resource directory of a PE module
54 static const IMAGE_RESOURCE_DIRECTORY
* get_resdir( HMODULE hmod
)
56 const IMAGE_DATA_DIRECTORY
*dir
;
57 const IMAGE_RESOURCE_DIRECTORY
*ret
= NULL
;
58 const void *base
= get_module_base( hmod
);
62 dir
= &PE_HEADER(base
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
];
63 if (dir
->Size
&& dir
->VirtualAddress
)
64 ret
= (IMAGE_RESOURCE_DIRECTORY
*)((char *)base
+ dir
->VirtualAddress
);
70 /**********************************************************************
73 * Find an entry by id in a resource directory
75 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY
*dir
,
76 WORD id
, const void *root
)
78 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
81 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
82 min
= dir
->NumberOfNamedEntries
;
83 max
= min
+ dir
->NumberOfIdEntries
- 1;
86 pos
= (min
+ max
) / 2;
87 if (entry
[pos
].u1
.s2
.Id
== id
)
88 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
[pos
].u2
.s3
.OffsetToDirectory
);
89 if (entry
[pos
].u1
.s2
.Id
> id
) max
= pos
- 1;
96 /**********************************************************************
99 * Find an entry by name in a resource directory
101 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY
*dir
,
102 LPCWSTR name
, const void *root
)
104 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
105 const IMAGE_RESOURCE_DIR_STRING_U
*str
;
106 int min
, max
, res
, pos
, namelen
;
108 if (!HIWORD(name
)) return find_entry_by_id( dir
, LOWORD(name
), root
);
112 if (!WideCharToMultiByte( CP_ACP
, 0, name
+1, -1, buf
, sizeof(buf
), NULL
, NULL
))
114 return find_entry_by_id( dir
, atoi(buf
), root
);
117 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
118 namelen
= strlenW(name
);
120 max
= dir
->NumberOfNamedEntries
- 1;
123 pos
= (min
+ max
) / 2;
124 str
= (IMAGE_RESOURCE_DIR_STRING_U
*)((char *)root
+ entry
[pos
].u1
.s1
.NameOffset
);
125 res
= strncmpiW( name
, str
->NameString
, str
->Length
);
126 if (!res
&& namelen
== str
->Length
)
127 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
[pos
].u2
.s3
.OffsetToDirectory
);
128 if (res
< 0) max
= pos
- 1;
135 /**********************************************************************
136 * find_entry_by_nameA
138 * Find an entry by name in a resource directory
140 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY
*dir
,
141 LPCSTR name
, const void *root
)
143 const IMAGE_RESOURCE_DIRECTORY
*ret
= NULL
;
147 if (!HIWORD(name
)) return find_entry_by_id( dir
, LOWORD(name
), root
);
150 return find_entry_by_id( dir
, atoi(name
+1), root
);
153 len
= MultiByteToWideChar( CP_ACP
, 0, name
, -1, NULL
, 0 );
154 if ((nameW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
156 MultiByteToWideChar( CP_ACP
, 0, name
, -1, nameW
, len
);
157 ret
= find_entry_by_nameW( dir
, nameW
, root
);
158 HeapFree( GetProcessHeap(), 0, nameW
);
164 /**********************************************************************
167 * Find a default entry in a resource directory
169 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_default( const IMAGE_RESOURCE_DIRECTORY
*dir
,
172 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
174 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
175 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
->u2
.s3
.OffsetToDirectory
);
179 /**********************************************************************
182 * FindResourceExA/W does search in the following order:
183 * 1. Exact specified language
184 * 2. Language with neutral sublanguage
185 * 3. Neutral language with neutral sublanguage
186 * 4. Neutral language with default sublanguage
188 HRSRC
PE_FindResourceExW( HMODULE hmod
, LPCWSTR name
, LPCWSTR type
, WORD lang
)
190 const IMAGE_RESOURCE_DIRECTORY
*resdirptr
= get_resdir(hmod
);
194 if (!resdirptr
) return 0;
197 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, type
, root
))) return 0;
198 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, name
, root
))) return 0;
200 /* 1. Exact specified language */
201 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
203 /* 2. Language with neutral sublanguage */
204 lang
= MAKELANGID(PRIMARYLANGID(lang
), SUBLANG_NEUTRAL
);
205 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
207 /* 3. Neutral language with neutral sublanguage */
208 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_NEUTRAL
);
209 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
211 /* 4. Neutral language with default sublanguage */
212 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
);
213 result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
);
219 /**********************************************************************
222 * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
223 * FindResourceA/W does search in the following order:
224 * 1. Neutral language with neutral sublanguage
225 * 2. Neutral language with default sublanguage
226 * 3. Current locale lang id
227 * 4. Current locale lang id with neutral sublanguage
228 * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
229 * 6. Return first in the list
231 HRSRC
PE_FindResourceW( HMODULE hmod
, LPCWSTR name
, LPCWSTR type
)
233 const IMAGE_RESOURCE_DIRECTORY
*resdirptr
= get_resdir(hmod
);
238 if (!resdirptr
) return 0;
241 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, type
, root
))) return 0;
242 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, name
, root
))) return 0;
244 /* 1. Neutral language with neutral sublanguage */
245 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_NEUTRAL
);
246 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
248 /* 2. Neutral language with default sublanguage */
249 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
);
250 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
252 /* 3. Current locale lang id */
253 lang
= LANGIDFROMLCID(GetUserDefaultLCID());
254 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
256 /* 4. Current locale lang id with neutral sublanguage */
257 lang
= MAKELANGID(PRIMARYLANGID(lang
), SUBLANG_NEUTRAL
);
258 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
260 /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
261 lang
= MAKELANGID(LANG_ENGLISH
, SUBLANG_DEFAULT
);
262 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
264 /* 6. Return first in the list */
265 result
= (HRSRC
)find_entry_default( resdirptr
, root
);
272 /**********************************************************************
275 HGLOBAL
PE_LoadResource( HMODULE hmod
, HRSRC hRsrc
)
277 const void *base
= get_module_base( hmod
);
278 if (!hRsrc
) return 0;
279 return (HANDLE
)((char *)base
+ ((PIMAGE_RESOURCE_DATA_ENTRY
)hRsrc
)->OffsetToData
);
283 /**********************************************************************
286 DWORD
PE_SizeofResource( HRSRC hRsrc
)
288 if (!hRsrc
) return 0;
289 return ((PIMAGE_RESOURCE_DATA_ENTRY
)hRsrc
)->Size
;
293 /**********************************************************************
294 * EnumResourceTypesA (KERNEL32.@)
296 BOOL WINAPI
EnumResourceTypesA( HMODULE hmod
, ENUMRESTYPEPROCA lpfun
, LONG lparam
)
299 const IMAGE_RESOURCE_DIRECTORY
*resdir
= get_resdir(hmod
);
300 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
303 if (!resdir
) return FALSE
;
305 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
307 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
310 if (et
[i
].u1
.s1
.NameIsString
)
312 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) resdir
+ et
[i
].u1
.s1
.NameOffset
);
313 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
314 NULL
, 0, NULL
, NULL
);
315 if (!(type
= HeapAlloc(GetProcessHeap(), 0, len
+ 1)))
317 WideCharToMultiByte( CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
318 type
, len
, NULL
, NULL
);
320 ret
= lpfun(hmod
,type
,lparam
);
321 HeapFree(GetProcessHeap(), 0, type
);
325 type
= (LPSTR
)(int)et
[i
].u1
.s2
.Id
;
326 ret
= lpfun(hmod
,type
,lparam
);
335 /**********************************************************************
336 * EnumResourceTypesW (KERNEL32.@)
338 BOOL WINAPI
EnumResourceTypesW( HMODULE hmod
, ENUMRESTYPEPROCW lpfun
, LONG lparam
)
341 const IMAGE_RESOURCE_DIRECTORY
*resdir
= get_resdir(hmod
);
342 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
345 if (!resdir
) return FALSE
;
347 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
349 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
352 if (et
[i
].u1
.s1
.NameIsString
)
354 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) resdir
+ et
[i
].u1
.s1
.NameOffset
);
355 if (!(type
= HeapAlloc(GetProcessHeap(), 0, (pResString
->Length
+1) * sizeof (WCHAR
))))
357 memcpy(type
, pResString
->NameString
, pResString
->Length
* sizeof (WCHAR
));
358 type
[pResString
->Length
] = '\0';
359 ret
= lpfun(hmod
,type
,lparam
);
360 HeapFree(GetProcessHeap(), 0, type
);
364 type
= (LPWSTR
)(int)et
[i
].u1
.s2
.Id
;
365 ret
= lpfun(hmod
,type
,lparam
);
374 /**********************************************************************
375 * EnumResourceNamesA (KERNEL32.@)
377 BOOL WINAPI
EnumResourceNamesA( HMODULE hmod
, LPCSTR type
, ENUMRESNAMEPROCA lpfun
, LONG lparam
)
380 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
381 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
382 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
385 if (!basedir
) return FALSE
;
387 if (!(resdir
= find_entry_by_nameA( basedir
, type
, basedir
))) return FALSE
;
389 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
391 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
394 if (et
[i
].u1
.s1
.NameIsString
)
396 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) basedir
+ et
[i
].u1
.s1
.NameOffset
);
397 DWORD len
= WideCharToMultiByte(CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
398 NULL
, 0, NULL
, NULL
);
399 if (!(name
= HeapAlloc(GetProcessHeap(), 0, len
+ 1 )))
401 WideCharToMultiByte( CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
402 name
, len
, NULL
, NULL
);
404 ret
= lpfun(hmod
,type
,name
,lparam
);
405 HeapFree( GetProcessHeap(), 0, name
);
409 name
= (LPSTR
)(int)et
[i
].u1
.s2
.Id
;
410 ret
= lpfun(hmod
,type
,name
,lparam
);
419 /**********************************************************************
420 * EnumResourceNamesW (KERNEL32.@)
422 BOOL WINAPI
EnumResourceNamesW( HMODULE hmod
, LPCWSTR type
, ENUMRESNAMEPROCW lpfun
, LONG lparam
)
425 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
426 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
427 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
430 if (!basedir
) return FALSE
;
432 if (!(resdir
= find_entry_by_nameW( basedir
, type
, basedir
))) return FALSE
;
434 et
= (PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
436 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
439 if (et
[i
].u1
.s1
.NameIsString
)
441 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) basedir
+ et
[i
].u1
.s1
.NameOffset
);
442 if (!(name
= HeapAlloc(GetProcessHeap(), 0, (pResString
->Length
+ 1) * sizeof (WCHAR
))))
444 memcpy(name
, pResString
->NameString
, pResString
->Length
* sizeof (WCHAR
));
445 name
[pResString
->Length
] = '\0';
446 ret
= lpfun(hmod
,type
,name
,lparam
);
447 HeapFree(GetProcessHeap(), 0, name
);
451 name
= (LPWSTR
)(int)et
[i
].u1
.s2
.Id
;
452 ret
= lpfun(hmod
,type
,name
,lparam
);
461 /**********************************************************************
462 * EnumResourceLanguagesA (KERNEL32.@)
464 BOOL WINAPI
EnumResourceLanguagesA( HMODULE hmod
, LPCSTR type
, LPCSTR name
,
465 ENUMRESLANGPROCA lpfun
, LONG lparam
)
468 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
469 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
470 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
473 if (!basedir
) return FALSE
;
474 if (!(resdir
= find_entry_by_nameA( basedir
, type
, basedir
))) return FALSE
;
475 if (!(resdir
= find_entry_by_nameA( resdir
, name
, basedir
))) return FALSE
;
477 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
479 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
480 /* languages are just ids... I hope */
481 ret
= lpfun(hmod
,type
,name
,et
[i
].u1
.s2
.Id
,lparam
);
489 /**********************************************************************
490 * EnumResourceLanguagesW (KERNEL32.@)
492 BOOL WINAPI
EnumResourceLanguagesW( HMODULE hmod
, LPCWSTR type
, LPCWSTR name
,
493 ENUMRESLANGPROCW lpfun
, LONG lparam
)
496 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
497 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
498 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
501 if (!basedir
) return FALSE
;
503 if (!(resdir
= find_entry_by_nameW( basedir
, type
, basedir
))) return FALSE
;
504 if (!(resdir
= find_entry_by_nameW( resdir
, name
, basedir
))) return FALSE
;
506 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
508 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
509 ret
= lpfun(hmod
,type
,name
,et
[i
].u1
.s2
.Id
,lparam
);