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 "stackframe.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(resource
);
43 /**********************************************************************
46 * Get the base address of a module
48 static const void *get_module_base( HMODULE hmod
)
50 if (!hmod
) hmod
= GetModuleHandleA( NULL
);
51 else if (!HIWORD(hmod
))
53 FIXME("Enumeration of 16-bit resources is not supported\n");
54 SetLastError(ERROR_INVALID_HANDLE
);
58 /* clear low order bit in case of LOAD_LIBRARY_AS_DATAFILE module */
59 return (void *)((ULONG_PTR
)hmod
& ~1);
63 /**********************************************************************
66 * Check if a module handle is for a LOAD_LIBRARY_AS_DATAFILE module.
68 inline static int is_data_file_module( HMODULE hmod
)
70 return (ULONG_PTR
)hmod
& 1;
74 /**********************************************************************
77 * Get a pointer to a given offset in a file mapped as data file.
79 static const void *get_data_file_ptr( const void *base
, DWORD offset
)
81 const IMAGE_NT_HEADERS
*nt
= PE_HEADER(base
);
82 const IMAGE_SECTION_HEADER
*sec
= (IMAGE_SECTION_HEADER
*)((char *)&nt
->OptionalHeader
+
83 nt
->FileHeader
.SizeOfOptionalHeader
);
86 /* find the section containing the virtual address */
87 for (i
= 0; i
< nt
->FileHeader
.NumberOfSections
; i
++, sec
++)
89 if ((sec
->VirtualAddress
<= offset
) && (sec
->VirtualAddress
+ sec
->SizeOfRawData
> offset
))
90 return (char *)base
+ sec
->PointerToRawData
+ (offset
- sec
->VirtualAddress
);
96 /**********************************************************************
99 * Get the resource directory of a PE module
101 static const IMAGE_RESOURCE_DIRECTORY
* get_resdir( HMODULE hmod
)
103 const IMAGE_DATA_DIRECTORY
*dir
;
104 const IMAGE_RESOURCE_DIRECTORY
*ret
= NULL
;
105 const void *base
= get_module_base( hmod
);
109 dir
= &PE_HEADER(base
)->OptionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_RESOURCE
];
110 if (dir
->Size
&& dir
->VirtualAddress
)
112 if (is_data_file_module(hmod
)) ret
= get_data_file_ptr( base
, dir
->VirtualAddress
);
113 else ret
= (IMAGE_RESOURCE_DIRECTORY
*)((char *)base
+ dir
->VirtualAddress
);
120 /**********************************************************************
123 * Find an entry by id in a resource directory
125 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY
*dir
,
126 WORD id
, const void *root
)
128 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
131 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
132 min
= dir
->NumberOfNamedEntries
;
133 max
= min
+ dir
->NumberOfIdEntries
- 1;
136 pos
= (min
+ max
) / 2;
137 if (entry
[pos
].u1
.s2
.Id
== id
)
138 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
[pos
].u2
.s3
.OffsetToDirectory
);
139 if (entry
[pos
].u1
.s2
.Id
> id
) max
= pos
- 1;
146 /**********************************************************************
147 * find_entry_by_nameW
149 * Find an entry by name in a resource directory
151 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY
*dir
,
152 LPCWSTR name
, const void *root
)
154 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
155 const IMAGE_RESOURCE_DIR_STRING_U
*str
;
156 int min
, max
, res
, pos
, namelen
;
158 if (!HIWORD(name
)) return find_entry_by_id( dir
, LOWORD(name
), root
);
162 if (!WideCharToMultiByte( CP_ACP
, 0, name
+1, -1, buf
, sizeof(buf
), NULL
, NULL
))
164 return find_entry_by_id( dir
, atoi(buf
), root
);
167 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
168 namelen
= strlenW(name
);
170 max
= dir
->NumberOfNamedEntries
- 1;
173 pos
= (min
+ max
) / 2;
174 str
= (IMAGE_RESOURCE_DIR_STRING_U
*)((char *)root
+ entry
[pos
].u1
.s1
.NameOffset
);
175 res
= strncmpiW( name
, str
->NameString
, str
->Length
);
176 if (!res
&& namelen
== str
->Length
)
177 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
[pos
].u2
.s3
.OffsetToDirectory
);
178 if (res
< 0) max
= pos
- 1;
185 /**********************************************************************
186 * find_entry_by_nameA
188 * Find an entry by name in a resource directory
190 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY
*dir
,
191 LPCSTR name
, const void *root
)
193 const IMAGE_RESOURCE_DIRECTORY
*ret
= NULL
;
197 if (!HIWORD(name
)) return find_entry_by_id( dir
, LOWORD(name
), root
);
200 return find_entry_by_id( dir
, atoi(name
+1), root
);
203 len
= MultiByteToWideChar( CP_ACP
, 0, name
, -1, NULL
, 0 );
204 if ((nameW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
206 MultiByteToWideChar( CP_ACP
, 0, name
, -1, nameW
, len
);
207 ret
= find_entry_by_nameW( dir
, nameW
, root
);
208 HeapFree( GetProcessHeap(), 0, nameW
);
214 /**********************************************************************
217 * Find a default entry in a resource directory
219 static const IMAGE_RESOURCE_DIRECTORY
*find_entry_default( const IMAGE_RESOURCE_DIRECTORY
*dir
,
222 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*entry
;
224 entry
= (const IMAGE_RESOURCE_DIRECTORY_ENTRY
*)(dir
+ 1);
225 return (IMAGE_RESOURCE_DIRECTORY
*)((char *)root
+ entry
->u2
.s3
.OffsetToDirectory
);
229 /**********************************************************************
232 * FindResourceExA/W does search in the following order:
233 * 1. Exact specified language
234 * 2. Language with neutral sublanguage
235 * 3. Neutral language with neutral sublanguage
236 * 4. Neutral language with default sublanguage
238 HRSRC
PE_FindResourceExW( HMODULE hmod
, LPCWSTR name
, LPCWSTR type
, WORD lang
)
240 const IMAGE_RESOURCE_DIRECTORY
*resdirptr
= get_resdir(hmod
);
244 if (!resdirptr
) return 0;
247 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, type
, root
))) return 0;
248 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, name
, root
))) return 0;
250 /* 1. Exact specified language */
251 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
253 /* 2. Language with neutral sublanguage */
254 lang
= MAKELANGID(PRIMARYLANGID(lang
), SUBLANG_NEUTRAL
);
255 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
257 /* 3. Neutral language with neutral sublanguage */
258 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_NEUTRAL
);
259 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
261 /* 4. Neutral language with default sublanguage */
262 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
);
263 result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
);
269 /**********************************************************************
272 * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
273 * FindResourceA/W does search in the following order:
274 * 1. Neutral language with neutral sublanguage
275 * 2. Neutral language with default sublanguage
276 * 3. Current locale lang id
277 * 4. Current locale lang id with neutral sublanguage
278 * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
279 * 6. Return first in the list
281 HRSRC
PE_FindResourceW( HMODULE hmod
, LPCWSTR name
, LPCWSTR type
)
283 const IMAGE_RESOURCE_DIRECTORY
*resdirptr
= get_resdir(hmod
);
288 if (!resdirptr
) return 0;
291 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, type
, root
))) return 0;
292 if (!(resdirptr
= find_entry_by_nameW(resdirptr
, name
, root
))) return 0;
294 /* 1. Neutral language with neutral sublanguage */
295 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_NEUTRAL
);
296 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
298 /* 2. Neutral language with default sublanguage */
299 lang
= MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
);
300 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
302 /* 3. Current locale lang id */
303 lang
= LANGIDFROMLCID(GetUserDefaultLCID());
304 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
306 /* 4. Current locale lang id with neutral sublanguage */
307 lang
= MAKELANGID(PRIMARYLANGID(lang
), SUBLANG_NEUTRAL
);
308 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
310 /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
311 lang
= MAKELANGID(LANG_ENGLISH
, SUBLANG_DEFAULT
);
312 if ((result
= (HRSRC
)find_entry_by_id( resdirptr
, lang
, root
))) goto found
;
314 /* 6. Return first in the list */
315 result
= (HRSRC
)find_entry_default( resdirptr
, root
);
322 /**********************************************************************
325 HGLOBAL
PE_LoadResource( HMODULE hmod
, HRSRC hRsrc
)
328 const void *base
= get_module_base( hmod
);
330 if (!hRsrc
|| !base
) return 0;
332 offset
= ((PIMAGE_RESOURCE_DATA_ENTRY
)hRsrc
)->OffsetToData
;
334 if (is_data_file_module(hmod
))
335 return (HANDLE
)get_data_file_ptr( base
, offset
);
337 return (HANDLE
)((char *)base
+ offset
);
341 /**********************************************************************
344 DWORD
PE_SizeofResource( HRSRC hRsrc
)
346 if (!hRsrc
) return 0;
347 return ((PIMAGE_RESOURCE_DATA_ENTRY
)hRsrc
)->Size
;
351 /**********************************************************************
352 * EnumResourceTypesA (KERNEL32.@)
354 BOOL WINAPI
EnumResourceTypesA( HMODULE hmod
, ENUMRESTYPEPROCA lpfun
, LONG lparam
)
357 const IMAGE_RESOURCE_DIRECTORY
*resdir
= get_resdir(hmod
);
358 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
361 if (!resdir
) return FALSE
;
363 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
365 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
368 if (et
[i
].u1
.s1
.NameIsString
)
370 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) resdir
+ et
[i
].u1
.s1
.NameOffset
);
371 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
372 NULL
, 0, NULL
, NULL
);
373 if (!(type
= HeapAlloc(GetProcessHeap(), 0, len
+ 1)))
375 WideCharToMultiByte( CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
376 type
, len
, NULL
, NULL
);
378 ret
= lpfun(hmod
,type
,lparam
);
379 HeapFree(GetProcessHeap(), 0, type
);
383 type
= (LPSTR
)(int)et
[i
].u1
.s2
.Id
;
384 ret
= lpfun(hmod
,type
,lparam
);
393 /**********************************************************************
394 * EnumResourceTypesW (KERNEL32.@)
396 BOOL WINAPI
EnumResourceTypesW( HMODULE hmod
, ENUMRESTYPEPROCW lpfun
, LONG lparam
)
399 const IMAGE_RESOURCE_DIRECTORY
*resdir
= get_resdir(hmod
);
400 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
403 if (!resdir
) return FALSE
;
405 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
407 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
410 if (et
[i
].u1
.s1
.NameIsString
)
412 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) resdir
+ et
[i
].u1
.s1
.NameOffset
);
413 if (!(type
= HeapAlloc(GetProcessHeap(), 0, (pResString
->Length
+1) * sizeof (WCHAR
))))
415 memcpy(type
, pResString
->NameString
, pResString
->Length
* sizeof (WCHAR
));
416 type
[pResString
->Length
] = '\0';
417 ret
= lpfun(hmod
,type
,lparam
);
418 HeapFree(GetProcessHeap(), 0, type
);
422 type
= (LPWSTR
)(int)et
[i
].u1
.s2
.Id
;
423 ret
= lpfun(hmod
,type
,lparam
);
432 /**********************************************************************
433 * EnumResourceNamesA (KERNEL32.@)
435 BOOL WINAPI
EnumResourceNamesA( HMODULE hmod
, LPCSTR type
, ENUMRESNAMEPROCA lpfun
, LONG lparam
)
438 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
439 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
440 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
443 if (!basedir
) return FALSE
;
445 if (!(resdir
= find_entry_by_nameA( basedir
, type
, basedir
))) return FALSE
;
447 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
449 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
452 if (et
[i
].u1
.s1
.NameIsString
)
454 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) basedir
+ et
[i
].u1
.s1
.NameOffset
);
455 DWORD len
= WideCharToMultiByte(CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
456 NULL
, 0, NULL
, NULL
);
457 if (!(name
= HeapAlloc(GetProcessHeap(), 0, len
+ 1 )))
459 WideCharToMultiByte( CP_ACP
, 0, pResString
->NameString
, pResString
->Length
,
460 name
, len
, NULL
, NULL
);
462 ret
= lpfun(hmod
,type
,name
,lparam
);
463 HeapFree( GetProcessHeap(), 0, name
);
467 name
= (LPSTR
)(int)et
[i
].u1
.s2
.Id
;
468 ret
= lpfun(hmod
,type
,name
,lparam
);
477 /**********************************************************************
478 * EnumResourceNamesW (KERNEL32.@)
480 BOOL WINAPI
EnumResourceNamesW( HMODULE hmod
, LPCWSTR type
, ENUMRESNAMEPROCW lpfun
, LONG lparam
)
483 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
484 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
485 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
488 if (!basedir
) return FALSE
;
490 if (!(resdir
= find_entry_by_nameW( basedir
, type
, basedir
))) return FALSE
;
492 et
= (PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
494 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
497 if (et
[i
].u1
.s1
.NameIsString
)
499 PIMAGE_RESOURCE_DIR_STRING_U pResString
= (PIMAGE_RESOURCE_DIR_STRING_U
) ((LPBYTE
) basedir
+ et
[i
].u1
.s1
.NameOffset
);
500 if (!(name
= HeapAlloc(GetProcessHeap(), 0, (pResString
->Length
+ 1) * sizeof (WCHAR
))))
502 memcpy(name
, pResString
->NameString
, pResString
->Length
* sizeof (WCHAR
));
503 name
[pResString
->Length
] = '\0';
504 ret
= lpfun(hmod
,type
,name
,lparam
);
505 HeapFree(GetProcessHeap(), 0, name
);
509 name
= (LPWSTR
)(int)et
[i
].u1
.s2
.Id
;
510 ret
= lpfun(hmod
,type
,name
,lparam
);
519 /**********************************************************************
520 * EnumResourceLanguagesA (KERNEL32.@)
522 BOOL WINAPI
EnumResourceLanguagesA( HMODULE hmod
, LPCSTR type
, LPCSTR name
,
523 ENUMRESLANGPROCA lpfun
, LONG lparam
)
526 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
527 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
528 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
531 if (!basedir
) return FALSE
;
532 if (!(resdir
= find_entry_by_nameA( basedir
, type
, basedir
))) return FALSE
;
533 if (!(resdir
= find_entry_by_nameA( resdir
, name
, basedir
))) return FALSE
;
535 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
537 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
538 /* languages are just ids... I hope */
539 ret
= lpfun(hmod
,type
,name
,et
[i
].u1
.s2
.Id
,lparam
);
547 /**********************************************************************
548 * EnumResourceLanguagesW (KERNEL32.@)
550 BOOL WINAPI
EnumResourceLanguagesW( HMODULE hmod
, LPCWSTR type
, LPCWSTR name
,
551 ENUMRESLANGPROCW lpfun
, LONG lparam
)
554 const IMAGE_RESOURCE_DIRECTORY
*basedir
= get_resdir(hmod
);
555 const IMAGE_RESOURCE_DIRECTORY
*resdir
;
556 const IMAGE_RESOURCE_DIRECTORY_ENTRY
*et
;
559 if (!basedir
) return FALSE
;
561 if (!(resdir
= find_entry_by_nameW( basedir
, type
, basedir
))) return FALSE
;
562 if (!(resdir
= find_entry_by_nameW( resdir
, name
, basedir
))) return FALSE
;
564 et
=(PIMAGE_RESOURCE_DIRECTORY_ENTRY
)(resdir
+ 1);
566 for (i
=0;i
<resdir
->NumberOfNamedEntries
+resdir
->NumberOfIdEntries
;i
++) {
567 ret
= lpfun(hmod
,type
,name
,et
[i
].u1
.s2
.Id
,lparam
);