pe_resource.c 15.8 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 * PE (Portable Execute) File Resources
Alexandre Julliard's avatar
Alexandre Julliard committed
3
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4 5
 * Copyright 1995 Thomas Sandford
 * Copyright 1996 Martin von Loewis
Alexandre Julliard's avatar
Alexandre Julliard committed
6
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
7 8 9
 * Based on the Win16 resource handling code in loader/resource.c
 * Copyright 1993 Robert J. Amstadt
 * Copyright 1995 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
10
 * Copyright 1997 Marcus Meissner
Alexandre Julliard's avatar
Alexandre Julliard committed
11 12
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
13
#include <stdlib.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
14
#include <sys/types.h>
15

16
#include "wine/unicode.h"
17
#include "windef.h"
18
#include "winnls.h"
19
#include "winerror.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
20
#include "module.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
21
#include "heap.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
22 23
#include "task.h"
#include "process.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
24 25
#include "stackframe.h"
#include "neexe.h"
26
#include "debugtools.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
27

28 29
DEFAULT_DEBUG_CHANNEL(resource);

Alexandre Julliard's avatar
Alexandre Julliard committed
30
/**********************************************************************
31
 *  get_resdir
Alexandre Julliard's avatar
Alexandre Julliard committed
32
 *
33
 * Get the resource directory of a PE module
Alexandre Julliard's avatar
Alexandre Julliard committed
34
 */
35
static const IMAGE_RESOURCE_DIRECTORY* get_resdir( HMODULE hmod )
36
{
37 38
    const IMAGE_DATA_DIRECTORY *dir;
    const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
39 40

    if (!hmod) hmod = GetModuleHandleA( NULL );
41 42 43 44 45 46
    else if (!HIWORD(hmod))
    {
        FIXME("Enumeration of 16-bit resources is not supported\n");
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }
47 48 49 50
    dir = &PE_HEADER(hmod)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
    if (dir->Size && dir->VirtualAddress)
        ret = (IMAGE_RESOURCE_DIRECTORY *)((char *)hmod + dir->VirtualAddress);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
51
}
Alexandre Julliard's avatar
Alexandre Julliard committed
52

53

54 55 56 57 58
/**********************************************************************
 *  find_entry_by_id
 *
 * Find an entry by id in a resource directory
 */
59 60
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                         WORD id, const void *root )
61 62 63 64 65 66 67 68 69 70 71
{
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
    int min, max, pos;

    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
    min = dir->NumberOfNamedEntries;
    max = min + dir->NumberOfIdEntries - 1;
    while (min <= max)
    {
        pos = (min + max) / 2;
        if (entry[pos].u1.Id == id)
72
            return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s2.OffsetToDirectory);
73 74 75 76 77 78 79 80
        if (entry[pos].u1.Id > id) max = pos - 1;
        else min = pos + 1;
    }
    return NULL;
}


/**********************************************************************
81
 *  find_entry_by_nameW
82
 *
83
 * Find an entry by name in a resource directory
84
 */
85 86
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameW( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                            LPCWSTR name, const void *root )
87 88
{
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
89 90 91 92 93 94 95 96 97 98 99
    const IMAGE_RESOURCE_DIR_STRING_U *str;
    int min, max, res, pos, namelen;

    if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
    if (name[0] == '#')
    {
        char buf[16];
        if (!WideCharToMultiByte( CP_ACP, 0, name+1, -1, buf, sizeof(buf), NULL, NULL ))
            return NULL;
        return find_entry_by_id( dir, atoi(buf), root );
    }
100 101

    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
102 103 104 105 106 107
    namelen = strlenW(name);
    min = 0;
    max = dir->NumberOfNamedEntries - 1;
    while (min <= max)
    {
        pos = (min + max) / 2;
108
        str = (IMAGE_RESOURCE_DIR_STRING_U *)((char *)root + entry[pos].u1.s1.NameOffset);
109 110
        res = strncmpiW( name, str->NameString, str->Length );
        if (!res && namelen == str->Length)
111
            return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s2.OffsetToDirectory);
112 113 114
        if (res < 0) max = pos - 1;
        else min = pos + 1;
    }
115 116 117 118
    return NULL;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
119
/**********************************************************************
120
 *  find_entry_by_nameA
Alexandre Julliard's avatar
Alexandre Julliard committed
121
 *
122
 * Find an entry by name in a resource directory
Alexandre Julliard's avatar
Alexandre Julliard committed
123
 */
124 125
static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_nameA( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                            LPCSTR name, const void *root )
Alexandre Julliard's avatar
Alexandre Julliard committed
126
{
127 128 129 130 131
    const IMAGE_RESOURCE_DIRECTORY *ret = NULL;
    LPWSTR nameW;

    if (!HIWORD(name)) return find_entry_by_id( dir, LOWORD(name), root );
    if (name[0] == '#')
132
    {
133
        return find_entry_by_id( dir, atoi(name+1), root );
134
    }
135 136

    if ((nameW = HEAP_strdupAtoW( GetProcessHeap(), 0, name )))
137
    {
138 139
        ret = find_entry_by_nameW( dir, nameW, root );
        HeapFree( GetProcessHeap(), 0, nameW );
Alexandre Julliard's avatar
Alexandre Julliard committed
140
    }
141
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
142 143
}

144

145
/**********************************************************************
146 147 148
 *  find_entry_default
 *
 * Find a default entry in a resource directory
149
 */
150 151
static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
                                                           const void *root )
152
{
153
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
154

155
    entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
156
    return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s2.OffsetToDirectory);
157 158
}

159

Alexandre Julliard's avatar
Alexandre Julliard committed
160
/**********************************************************************
161
 *	    PE_FindResourceExW
162 163 164 165 166 167
 *
 * FindResourceExA/W does search in the following order:
 * 1. Exact specified language
 * 2. Language with neutral sublanguage
 * 3. Neutral language with neutral sublanguage
 * 4. Neutral language with default sublanguage
Alexandre Julliard's avatar
Alexandre Julliard committed
168
 */
169
HRSRC PE_FindResourceExW( HMODULE hmod, LPCWSTR name, LPCWSTR type, WORD lang )
170
{
171 172
    const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
    const void *root;
173
    HRSRC result;
Alexandre Julliard's avatar
Alexandre Julliard committed
174

175
    if (!resdirptr) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
176

177
    root = resdirptr;
178 179
    if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
    if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
180 181

    /* 1. Exact specified language */
182
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
183

184 185 186
    /* 2. Language with neutral sublanguage */
    lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
187

188 189 190
    /* 3. Neutral language with neutral sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
191

192 193 194
    /* 4. Neutral language with default sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
    result = (HRSRC)find_entry_by_id( resdirptr, lang, root );
195

196
 found:
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
    return result;
}

/**********************************************************************
 *	    PE_FindResourceW
 *
 * Load[String]/[Icon]/[Menu]/[etc.] does use FindResourceA/W.
 * FindResourceA/W does search in the following order:
 * 1. Neutral language with neutral sublanguage
 * 2. Neutral language with default sublanguage
 * 3. Current locale lang id
 * 4. Current locale lang id with neutral sublanguage
 * 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT
 * 6. Return first in the list
 */
HRSRC PE_FindResourceW( HMODULE hmod, LPCWSTR name, LPCWSTR type )
{
214 215
    const IMAGE_RESOURCE_DIRECTORY *resdirptr = get_resdir(hmod);
    const void *root;
216 217 218 219 220
    HRSRC result;
    WORD lang;

    if (!resdirptr) return 0;

221
    root = resdirptr;
222 223
    if (!(resdirptr = find_entry_by_nameW(resdirptr, type, root))) return 0;
    if (!(resdirptr = find_entry_by_nameW(resdirptr, name, root))) return 0;
224 225 226

    /* 1. Neutral language with neutral sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
227
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
228

229 230 231
    /* 2. Neutral language with default sublanguage */
    lang = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
232

233 234 235
    /* 3. Current locale lang id */
    lang = LANGIDFROMLCID(GetUserDefaultLCID());
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
236

237 238 239
    /* 4. Current locale lang id with neutral sublanguage */
    lang = MAKELANGID(PRIMARYLANGID(lang), SUBLANG_NEUTRAL);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
240

241 242 243
    /* 5. (!) LANG_ENGLISH, SUBLANG_DEFAULT */
    lang = MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT);
    if ((result = (HRSRC)find_entry_by_id( resdirptr, lang, root ))) goto found;
244

245 246
    /* 6. Return first in the list */
    result = (HRSRC)find_entry_default( resdirptr, root );
247

248
 found:
Alexandre Julliard's avatar
Alexandre Julliard committed
249
    return result;
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251
}

Alexandre Julliard's avatar
Alexandre Julliard committed
252

Alexandre Julliard's avatar
Alexandre Julliard committed
253
/**********************************************************************
254
 *	    PE_LoadResource
Alexandre Julliard's avatar
Alexandre Julliard committed
255
 */
256
HANDLE PE_LoadResource( HMODULE hmod, HANDLE hRsrc )
Alexandre Julliard's avatar
Alexandre Julliard committed
257
{
258 259
    if (!hRsrc) return 0;
    return (HANDLE)(hmod + ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->OffsetToData);
Alexandre Julliard's avatar
Alexandre Julliard committed
260 261
}

Alexandre Julliard's avatar
Alexandre Julliard committed
262

Alexandre Julliard's avatar
Alexandre Julliard committed
263
/**********************************************************************
264
 *	    PE_SizeofResource
Alexandre Julliard's avatar
Alexandre Julliard committed
265
 */
266
DWORD PE_SizeofResource( HANDLE hRsrc )
Alexandre Julliard's avatar
Alexandre Julliard committed
267
{
268
    if (!hRsrc) return 0;
269
    return ((PIMAGE_RESOURCE_DATA_ENTRY)hRsrc)->Size;
Alexandre Julliard's avatar
Alexandre Julliard committed
270 271
}

272

Alexandre Julliard's avatar
Alexandre Julliard committed
273
/**********************************************************************
274
 *	EnumResourceTypesA	(KERNEL32.90)
Alexandre Julliard's avatar
Alexandre Julliard committed
275
 */
276 277
BOOL WINAPI EnumResourceTypesA( HMODULE hmod, ENUMRESTYPEPROCA lpfun, LONG lparam)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
278
    int		i;
279 280
    const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
281
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
282

283
    if (!resdir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
284

285
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
286 287
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
288 289
        LPSTR type;

290
        if (et[i].u1.s1.NameIsString)
291
        {
292
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
            DWORD len = WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
                                             NULL, 0, NULL, NULL);
            if (!(type = HeapAlloc(GetProcessHeap(), 0, len + 1)))
                return FALSE;
            WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
                                 type, len, NULL, NULL);
            type[len] = '\0';
            ret = lpfun(hmod,type,lparam);
            HeapFree(GetProcessHeap(), 0, type);
        }
        else
        {
            type = (LPSTR)(int)et[i].u1.Id;
            ret = lpfun(hmod,type,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
308 309 310 311 312 313
	if (!ret)
		break;
    }
    return ret;
}

314

Alexandre Julliard's avatar
Alexandre Julliard committed
315
/**********************************************************************
316
 *	EnumResourceTypesW	(KERNEL32.91)
Alexandre Julliard's avatar
Alexandre Julliard committed
317
 */
318 319
BOOL WINAPI EnumResourceTypesW( HMODULE hmod, ENUMRESTYPEPROCW lpfun, LONG lparam)
{
Alexandre Julliard's avatar
Alexandre Julliard committed
320
    int		i;
321 322
    const IMAGE_RESOURCE_DIRECTORY *resdir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
323
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
324

325
    if (!resdir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
326

327
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
328 329 330 331
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
	LPWSTR	type;

332
        if (et[i].u1.s1.NameIsString)
333
        {
334
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) resdir + et[i].u1.s1.NameOffset);
335 336 337 338 339 340 341 342 343 344 345 346
            if (!(type = HeapAlloc(GetProcessHeap(), 0, (pResString->Length+1) * sizeof (WCHAR))))
                return FALSE;
            memcpy(type, pResString->NameString, pResString->Length * sizeof (WCHAR));
            type[pResString->Length] = '\0';
            ret = lpfun(hmod,type,lparam);
            HeapFree(GetProcessHeap(), 0, type);
        }
        else
        {
            type = (LPWSTR)(int)et[i].u1.Id;
            ret = lpfun(hmod,type,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
347 348 349 350 351 352
	if (!ret)
		break;
    }
    return ret;
}

353

Alexandre Julliard's avatar
Alexandre Julliard committed
354
/**********************************************************************
355
 *	EnumResourceNamesA	(KERNEL32.88)
Alexandre Julliard's avatar
Alexandre Julliard committed
356
 */
357 358
BOOL WINAPI EnumResourceNamesA( HMODULE hmod, LPCSTR type, ENUMRESNAMEPROCA lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
359
    int		i;
360 361 362
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
363
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
364

365 366
    if (!basedir) return FALSE;

367
    if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
368

369
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
370 371
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
372 373
        LPSTR name;

374
        if (et[i].u1.s1.NameIsString)
375
        {
376
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
            DWORD len = WideCharToMultiByte(CP_ACP, 0, pResString->NameString, pResString->Length,
                                            NULL, 0, NULL, NULL);
            if (!(name = HeapAlloc(GetProcessHeap(), 0, len + 1 )))
                return FALSE;
            WideCharToMultiByte( CP_ACP, 0, pResString->NameString, pResString->Length,
                                 name, len, NULL, NULL );
            name[len] = '\0';
            ret = lpfun(hmod,type,name,lparam);
            HeapFree( GetProcessHeap(), 0, name );
        }
        else
        {
            name = (LPSTR)(int)et[i].u1.Id;
            ret = lpfun(hmod,type,name,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
392 393 394 395 396 397
	if (!ret)
		break;
    }
    return ret;
}

398

Alexandre Julliard's avatar
Alexandre Julliard committed
399
/**********************************************************************
400
 *	EnumResourceNamesW	(KERNEL32.89)
Alexandre Julliard's avatar
Alexandre Julliard committed
401
 */
402 403
BOOL WINAPI EnumResourceNamesW( HMODULE hmod, LPCWSTR type, ENUMRESNAMEPROCW lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
404
    int		i;
405 406 407
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
408
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
409

410
    if (!basedir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
411

412 413
    if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;

414
    et = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
415 416
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
417 418
        LPWSTR name;

419
        if (et[i].u1.s1.NameIsString)
420
        {
421
            PIMAGE_RESOURCE_DIR_STRING_U pResString = (PIMAGE_RESOURCE_DIR_STRING_U) ((LPBYTE) basedir + et[i].u1.s1.NameOffset);
422 423 424 425 426 427 428 429 430 431 432 433
            if (!(name = HeapAlloc(GetProcessHeap(), 0, (pResString->Length + 1) * sizeof (WCHAR))))
                return FALSE;
            memcpy(name, pResString->NameString, pResString->Length * sizeof (WCHAR));
            name[pResString->Length] = '\0';
            ret = lpfun(hmod,type,name,lparam);
            HeapFree(GetProcessHeap(), 0, name);
        }
        else
        {
            name = (LPWSTR)(int)et[i].u1.Id;
            ret = lpfun(hmod,type,name,lparam);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
434 435 436 437 438 439
	if (!ret)
		break;
    }
    return ret;
}

440

Alexandre Julliard's avatar
Alexandre Julliard committed
441
/**********************************************************************
442
 *	EnumResourceLanguagesA	(KERNEL32.86)
Alexandre Julliard's avatar
Alexandre Julliard committed
443
 */
444 445 446
BOOL WINAPI EnumResourceLanguagesA( HMODULE hmod, LPCSTR type, LPCSTR name,
                                    ENUMRESLANGPROCA lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
447
    int		i;
448 449 450
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
451
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
452

453
    if (!basedir) return FALSE;
454 455
    if (!(resdir = find_entry_by_nameA( basedir, type, basedir ))) return FALSE;
    if (!(resdir = find_entry_by_nameA( resdir, name, basedir ))) return FALSE;
456 457

    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
458 459
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
460
        /* languages are just ids... I hope */
461
	ret = lpfun(hmod,type,name,et[i].u1.Id,lparam);
Alexandre Julliard's avatar
Alexandre Julliard committed
462 463 464 465 466 467
	if (!ret)
		break;
    }
    return ret;
}

468

Alexandre Julliard's avatar
Alexandre Julliard committed
469
/**********************************************************************
470
 *	EnumResourceLanguagesW	(KERNEL32.87)
Alexandre Julliard's avatar
Alexandre Julliard committed
471
 */
472 473 474
BOOL WINAPI EnumResourceLanguagesW( HMODULE hmod, LPCWSTR type, LPCWSTR name,
                                    ENUMRESLANGPROCW lpfun, LONG lparam )
{
Alexandre Julliard's avatar
Alexandre Julliard committed
475
    int		i;
476 477 478
    const IMAGE_RESOURCE_DIRECTORY *basedir = get_resdir(hmod);
    const IMAGE_RESOURCE_DIRECTORY *resdir;
    const IMAGE_RESOURCE_DIRECTORY_ENTRY *et;
479
    BOOL	ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
480

481
    if (!basedir) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
482

483 484 485
    if (!(resdir = find_entry_by_nameW( basedir, type, basedir ))) return FALSE;
    if (!(resdir = find_entry_by_nameW( resdir, name, basedir ))) return FALSE;

486
    et =(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resdir + 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
487 488
    ret = FALSE;
    for (i=0;i<resdir->NumberOfNamedEntries+resdir->NumberOfIdEntries;i++) {
489
	ret = lpfun(hmod,type,name,et[i].u1.Id,lparam);
Alexandre Julliard's avatar
Alexandre Julliard committed
490 491 492 493
	if (!ret)
		break;
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
494
}