Release 980822
[wine] / loader / resource.c
1 /*
2  * Resources
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Alexandre Julliard
6  */
7
8 #include <assert.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include "windows.h"
16 #include "gdi.h"
17 #include "global.h"
18 #include "heap.h"
19 #include "neexe.h"
20 #include "task.h"
21 #include "process.h"
22 #include "module.h"
23 #include "resource.h"
24 #include "debug.h"
25 #include "libres.h"
26 #include "winerror.h"
27 #include "debugstr.h"
28
29 extern WORD WINE_LanguageId;
30
31
32 /**********************************************************************
33  *          FindResource32A    (KERNEL32.128)
34  */
35 HANDLE32 WINAPI FindResource32A( HMODULE32 hModule, LPCSTR name, LPCSTR type)
36 {
37     return FindResourceEx32A(hModule,name,type,WINE_LanguageId);
38 }
39
40 /**********************************************************************
41  *          FindResourceEx32A    (KERNEL32.129)
42  */
43 HANDLE32 WINAPI FindResourceEx32A( HMODULE32 hModule, LPCSTR name, LPCSTR type,
44                                    WORD lang
45 ) {
46     LPWSTR xname,xtype;
47     HANDLE32 ret;
48
49     if (HIWORD((DWORD)name))
50         xname = HEAP_strdupAtoW( GetProcessHeap(), 0, name );
51     else
52         xname = (LPWSTR)name;
53     if (HIWORD((DWORD)type))
54         xtype = HEAP_strdupAtoW( GetProcessHeap(), 0, type);
55     else
56         xtype = (LPWSTR)type;
57     ret = FindResourceEx32W( hModule, xname, xtype, lang );
58     if (HIWORD((DWORD)name)) HeapFree( GetProcessHeap(), 0, xname );
59     if (HIWORD((DWORD)type)) HeapFree( GetProcessHeap(), 0, xtype );
60     return ret;
61 }
62
63
64 /**********************************************************************
65  *          FindResourceEx32W    (KERNEL32.130)
66  */
67 HRSRC32 WINAPI FindResourceEx32W( HMODULE32 hModule, LPCWSTR name,
68                                   LPCWSTR type, WORD lang )
69 {       HRSRC32 ret;
70     WINE_MODREF *wm = MODULE32_LookupHMODULE(PROCESS_Current(),hModule);
71     HRSRC32     hrsrc;
72
73     TRACE(resource, "module=%08x type=%s name=%s\n",
74           hModule,
75           debugres_w (type),
76           debugres_w (name));
77
78     if (__winelib) {
79         hrsrc = LIBRES_FindResource( hModule, name, type );
80         if (hrsrc)
81             return hrsrc;
82     }
83     if (wm) {
84         switch (wm->type) {
85         case MODULE32_PE:
86             ret =  PE_FindResourceEx32W(wm,name,type,lang);
87         if ( ret==0 )
88           ERR(resource,"%s not found!\n",debugres_w (name));
89             return ret;
90         default:
91             ERR(module,"unknown module type %d\n",wm->type);
92             break;
93         }
94     }
95     return (HRSRC32)0;
96 }
97
98
99 /**********************************************************************
100  *          FindResource32W    (KERNEL32.131)
101  */
102 HRSRC32 WINAPI FindResource32W(HINSTANCE32 hModule, LPCWSTR name, LPCWSTR type)
103 {
104     return FindResourceEx32W(hModule,name,type,WINE_LanguageId);
105 }
106
107
108 /**********************************************************************
109  *          LoadResource32    (KERNEL32.370)
110  * 'loads' a resource. The current implementation just returns a pointer
111  * into the already mapped image.
112  * RETURNS
113  *      pointer into the mapped resource of the passed module
114  */
115 HGLOBAL32 WINAPI LoadResource32( 
116         HINSTANCE32 hModule,    /* [in] module handle */
117         HRSRC32 hRsrc )         /* [in] resource handle */
118 {
119     WINE_MODREF *wm = MODULE32_LookupHMODULE(PROCESS_Current(),hModule);
120
121     TRACE(resource, "module=%04x res=%04x\n",
122                      hModule, hRsrc );
123     if (!hRsrc) {
124         ERR(resource,"hRsrc is 0, return 0.\n");
125         return 0;
126     }
127     if (wm)
128         switch (wm->type) {
129         case MODULE32_PE:
130             return PE_LoadResource32(wm,hRsrc);
131         default:
132             ERR(resource,"unknown module type %d\n",wm->type);
133             break;
134         }
135     if (__winelib)
136         return LIBRES_LoadResource( hModule, hRsrc );
137     return 0;
138 }
139
140
141 /**********************************************************************
142  *          LockResource32    (KERNEL32.384)
143  */
144 LPVOID WINAPI LockResource32( HGLOBAL32 handle )
145 {
146     return (LPVOID)handle;
147 }
148
149
150 /**********************************************************************
151  *          FreeResource32    (KERNEL32.145)
152  */
153 BOOL32 WINAPI FreeResource32( HGLOBAL32 handle )
154 {
155     /* no longer used in Win32 */
156     return TRUE;
157 }
158
159
160 /**********************************************************************
161  *          AccessResource32    (KERNEL32.64)
162  */
163 INT32 WINAPI AccessResource32( HMODULE32 hModule, HRSRC32 hRsrc )
164 {
165     FIXME(resource,"(module=%08x res=%08x),not implemented\n", hModule, hRsrc);
166     return 0;
167 }
168
169
170 /**********************************************************************
171  *          SizeofResource32    (KERNEL32.522)
172  */
173 DWORD WINAPI SizeofResource32( HINSTANCE32 hModule, HRSRC32 hRsrc )
174 {
175     WINE_MODREF *wm = MODULE32_LookupHMODULE(PROCESS_Current(),hModule);
176
177     TRACE(resource, "module=%08x res=%08x\n", hModule, hRsrc );
178     if (wm)
179         switch (wm->type)
180         {
181         case MODULE32_PE:
182             {
183                 DWORD ret;
184                 ret = PE_SizeofResource32(hModule,hRsrc);
185                 if (ret) 
186                     return ret;
187                 break;
188             }
189         default:
190             ERR(module,"unknown module type %d\n",wm->type);
191             break;
192         }
193     if (__winelib)
194         FIXME(module,"Not implemented for WINELIB\n");
195     return 0;
196 }
197
198
199 /**********************************************************************
200  *                      LoadAccelerators16      [USER.177]
201  */
202 HACCEL16 WINAPI LoadAccelerators16(HINSTANCE16 instance, SEGPTR lpTableName)
203 {
204     HRSRC16     hRsrc;
205
206     if (HIWORD(lpTableName))
207         TRACE(accel, "%04x '%s'\n",
208                       instance, (char *)PTR_SEG_TO_LIN( lpTableName ) );
209     else
210         TRACE(accel, "%04x %04x\n",
211                        instance, LOWORD(lpTableName) );
212
213     if (!(hRsrc = FindResource16( instance, lpTableName, RT_ACCELERATOR16 ))) {
214       WARN(accel, "couldn't find accelerator table resource\n");
215       return 0;
216     }
217
218     TRACE(accel, "returning HACCEL 0x%x\n", hRsrc);
219     return LoadResource16(instance,hRsrc);
220 }
221
222 /**********************************************************************
223  *                      LoadAccelerators32W     [USER.177]
224  * The image layout seems to look like this (not 100% sure):
225  * 00:  BYTE    type            type of accelerator
226  * 01:  BYTE    pad             (to WORD boundary)
227  * 02:  WORD    event
228  * 04:  WORD    IDval           
229  * 06:  WORD    pad             (to DWORD boundary)
230  */
231 HACCEL32 WINAPI LoadAccelerators32W(HINSTANCE32 instance,LPCWSTR lpTableName)
232 {
233     HRSRC32 hRsrc;
234     HACCEL32 hRetval;
235     DWORD size;
236
237     if (HIWORD(lpTableName))
238         TRACE(accel, "%p '%s'\n",
239                       (LPVOID)instance, (char *)( lpTableName ) );
240     else
241         TRACE(accel, "%p 0x%04x\n",
242                        (LPVOID)instance, LOWORD(lpTableName) );
243
244     if (!(hRsrc = FindResource32W( instance, lpTableName, RT_ACCELERATOR32W )))
245     {
246       WARN(accel, "couldn't find accelerator table resource\n");
247       hRetval = 0;
248     }
249     else {
250       hRetval = LoadResource32( instance, hRsrc );
251       size = SizeofResource32( instance, hRsrc );
252       if(size>=sizeof(ACCEL32))
253       {
254         LPACCEL32 accel_table = (LPACCEL32) hRetval;
255         /* mark last element as such - sometimes it is not marked in image */
256         accel_table[size/sizeof(ACCEL32)-1].fVirt |= 0x80;
257       }
258     }
259
260     TRACE(accel, "returning HACCEL 0x%x\n", hRsrc);
261     return hRetval;
262 }
263
264 HACCEL32 WINAPI LoadAccelerators32A(HINSTANCE32 instance,LPCSTR lpTableName)
265 {
266         LPWSTR   uni;
267         HACCEL32 result;
268         if (HIWORD(lpTableName))
269                 uni = HEAP_strdupAtoW( GetProcessHeap(), 0, lpTableName );
270         else
271                 uni = (LPWSTR)lpTableName;
272         result = LoadAccelerators32W(instance,uni);
273         if (HIWORD(uni)) HeapFree( GetProcessHeap(), 0, uni);
274         return result;
275 }
276
277 /**********************************************************************
278  *             CopyAcceleratorTable32A   (USER32.58)
279  */
280 INT32 WINAPI CopyAcceleratorTable32A(HACCEL32 src, LPACCEL32 dst, INT32 entries)
281 {
282   return CopyAcceleratorTable32W(src, dst, entries);
283 }
284
285 /**********************************************************************
286  *             CopyAcceleratorTable32W   (USER32.59)
287  *
288  * By mortene@pvv.org 980321
289  */
290 INT32 WINAPI CopyAcceleratorTable32W(HACCEL32 src, LPACCEL32 dst,
291                                      INT32 entries)
292 {
293   int i;
294   LPACCEL32 accel = (LPACCEL32)src;
295   BOOL32 done = FALSE;
296
297   /* Do parameter checking to avoid the explosions and the screaming
298      as far as possible. */
299   if((dst && (entries < 1)) || (src == (HACCEL32)NULL)) {
300     WARN(accel, "Application sent invalid parameters (%p %p %d).\n",
301          (LPVOID)src, (LPVOID)dst, entries);
302     return 0;
303   }
304
305
306   i=0;
307   while(!done) {
308     /* Spit out some debugging information. */
309     TRACE(accel, "accel %d: type 0x%02x, event '%c', IDval 0x%04x.\n",
310           i, accel[i].fVirt, accel[i].key, accel[i].cmd);
311
312     /* Copy data to the destination structure array (if dst == NULL,
313        we're just supposed to count the number of entries). */
314     if(dst) {
315       memcpy(&dst[i], &accel[i], sizeof(ACCEL32));
316
317       /* Check if we've reached the end of the application supplied
318          accelerator table. */
319       if(i+1 == entries) {
320         /* Turn off the high order bit, just in case. */
321         dst[i].fVirt &= 0x7f;
322         done = TRUE;
323       }
324     }
325
326     /* The highest order bit seems to mark the end of the accelerator
327        resource table. (?) */
328     if((accel[i].fVirt & 0x80) != 0) done = TRUE;
329
330     i++;
331   }
332
333   return i;
334 }
335
336 /*********************************************************************
337  *                    CreateAcceleratorTable   (USER32.64)
338  *
339  * By mortene@pvv.org 980321
340  */
341 HACCEL32 WINAPI CreateAcceleratorTable32A(LPACCEL32 lpaccel, INT32 cEntries)
342 {
343   HACCEL32 hAccel;
344
345   /* Do parameter checking just in case someone's trying to be
346      funny. */
347   if(cEntries < 1) {
348     WARN(accel, "Application sent invalid parameters (%p %d).\n",
349          lpaccel, cEntries);
350     SetLastError(ERROR_INVALID_PARAMETER);
351     return (HACCEL32)NULL;
352   }
353   FIXME(accel, "should check that the accelerator descriptions are valid,"
354         " return NULL and SetLastError() if not.\n");
355
356
357   /* Allocate memory and copy the table. */
358   hAccel = (HACCEL32)HeapAlloc(GetProcessHeap(), 0,
359                                cEntries * sizeof(ACCEL32));
360   TRACE(accel, "handle %p\n", (LPVOID)hAccel);
361   if(!hAccel) {
362     ERR(accel, "Out of memory.\n");
363     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
364     return (HACCEL32)NULL;
365   }
366   memcpy((LPACCEL32)hAccel, lpaccel, cEntries * sizeof(ACCEL32));
367
368   /* Set the end-of-table terminator. */
369   ((LPACCEL32)hAccel)[cEntries-1].fVirt |= 0x80;
370
371   TRACE(accel, "Allocated accelerator handle %x\n", hAccel);
372   return hAccel;
373 }
374
375
376 /******************************************************************************
377  * DestroyAcceleratorTable [USER32.130]
378  * Destroys an accelerator table
379  *
380  * NOTES
381  *    By mortene@pvv.org 980321
382  *
383  * PARAMS
384  *    handle [I] Handle to accelerator table
385  *
386  * RETURNS STD
387  */
388 BOOL32 WINAPI DestroyAcceleratorTable( HACCEL32 handle )
389 {
390     FIXME(accel, "(0x%x): stub\n", handle);
391
392
393   /* Weird.. I thought this should work. According to the API
394      specification, DestroyAcceleratorTable() should only be called on
395      HACCEL32's made by CreateAcceleratorTable(), but Microsoft Visual
396      Studio 97 calls this function with a series of different handle
397      values without ever calling CreateAcceleratorTable(). Something
398      is very fishy in Denmark... */
399   /* Update: looks like the calls to this function matches the calls
400      to LoadAccelerators() in M$ Visual Studio, except that the handle
401      values are off by some variable size from the HACCEL's returned
402      from LoadAccelerators(). WTH? */
403   
404   /* Parameter checking to avoid any embarassing situations. */
405 #if 0
406      if(!handle) {
407        WARN(accel, "Application sent NULL ptr.\n");
408        SetLastError(ERROR_INVALID_PARAMETER);
409        return FALSE;
410      }
411   
412      HeapFree(GetProcessHeap(), 0, (LPACCEL32)handle);
413 #endif
414
415   return TRUE;
416 }
417   
418 /**********************************************************************
419  *                                      LoadString16
420  */
421 INT16 WINAPI LoadString16( HINSTANCE16 instance, UINT16 resource_id,
422                            LPSTR buffer, INT16 buflen )
423 {
424     HGLOBAL16 hmem;
425     HRSRC16 hrsrc;
426     unsigned char *p;
427     int string_num;
428     int i;
429
430     TRACE(resource,"inst=%04x id=%04x buff=%08x len=%d\n",
431                      instance, resource_id, (int) buffer, buflen);
432
433     hrsrc = FindResource16( instance, (SEGPTR)((resource_id>>4)+1), RT_STRING16 );
434     if (!hrsrc) return 0;
435     hmem = LoadResource16( instance, hrsrc );
436     if (!hmem) return 0;
437     
438     p = LockResource16(hmem);
439     string_num = resource_id & 0x000f;
440     for (i = 0; i < string_num; i++)
441         p += *p + 1;
442     
443     TRACE(resource, "strlen = %d\n", (int)*p );
444     
445     i = MIN(buflen - 1, *p);
446     if (buffer == NULL)
447         return i;
448     if (i > 0) {
449         memcpy(buffer, p + 1, i);
450         buffer[i] = '\0';
451     } else {
452         if (buflen > 1) {
453             buffer[0] = '\0';
454             return 0;
455         }
456         WARN(resource,"Dont know why caller give buflen=%d *p=%d trying to obtain string '%s'\n", buflen, *p, p + 1);
457     }
458     FreeResource16( hmem );
459
460     TRACE(resource,"'%s' copied !\n", buffer);
461     return i;
462 }
463
464 /**********************************************************************
465  *      LoadString32W           (USER32.376)
466  */
467 INT32 WINAPI LoadString32W( HINSTANCE32 instance, UINT32 resource_id,
468                             LPWSTR buffer, INT32 buflen )
469 {
470     HGLOBAL32 hmem;
471     HRSRC32 hrsrc;
472     WCHAR *p;
473     int string_num;
474     int i;
475
476     if (HIWORD(resource_id)==0xFFFF) /* netscape 3 passes this */
477         resource_id = (UINT32)(-((INT32)resource_id));
478     TRACE(resource, "instance = %04x, id = %04x, buffer = %08x, "
479            "length = %d\n", instance, (int)resource_id, (int) buffer, buflen);
480
481     hrsrc = FindResource32W( instance, (LPCWSTR)((resource_id>>4)+1),
482                              RT_STRING32W );
483     if (!hrsrc) return 0;
484     hmem = LoadResource32( instance, hrsrc );
485     if (!hmem) return 0;
486     
487     p = LockResource32(hmem);
488     string_num = resource_id & 0x000f;
489     for (i = 0; i < string_num; i++)
490         p += *p + 1;
491     
492     TRACE(resource, "strlen = %d\n", (int)*p );
493     
494     i = MIN(buflen - 1, *p);
495     if (buffer == NULL)
496         return i;
497     if (i > 0) {
498         memcpy(buffer, p + 1, i * sizeof (WCHAR));
499         buffer[i] = (WCHAR) 0;
500     } else {
501         if (buflen > 1) {
502             buffer[0] = (WCHAR) 0;
503             return 0;
504         }
505 #if 0
506         WARN(resource,"Dont know why caller give buflen=%d *p=%d trying to obtain string '%s'\n", buflen, *p, p + 1);
507 #endif
508     }
509
510     TRACE(resource,"'%s' copied !\n", (char *)buffer);
511     return i;
512 }
513
514 /**********************************************************************
515  *      LoadString32A   (USER32.375)
516  */
517 INT32 WINAPI LoadString32A( HINSTANCE32 instance, UINT32 resource_id,
518                             LPSTR buffer, INT32 buflen )
519 {
520     INT32 retval;
521     LPWSTR buffer2 = NULL;
522     if (buffer && buflen)
523         buffer2 = HeapAlloc( GetProcessHeap(), 0, buflen * 2 );
524     retval = LoadString32W(instance,resource_id,buffer2,buflen);
525
526     if (buffer2)
527     {
528         if (retval) {
529             lstrcpynWtoA( buffer, buffer2, buflen );
530             retval = lstrlen32A( buffer );
531         }
532         else
533             *buffer = 0;
534         HeapFree( GetProcessHeap(), 0, buffer2 );
535     }
536     return retval;
537 }
538
539 /* Messages...used by FormatMessage32* (KERNEL32.something)
540  * 
541  * They can be specified either directly or using a message ID and
542  * loading them from the resource.
543  * 
544  * The resourcedata has following format:
545  * start:
546  * 0: DWORD nrofentries
547  * nrofentries * subentry:
548  *      0: DWORD firstentry
549  *      4: DWORD lastentry
550  *      8: DWORD offset from start to the stringentries
551  *
552  * (lastentry-firstentry) * stringentry:
553  * 0: WORD len (0 marks end)
554  * 2: WORD unknown (flags?)
555  * 4: CHAR[len-4]
556  *      (stringentry i of a subentry refers to the ID 'firstentry+i')
557  *
558  * Yes, ANSI strings in win32 resources. Go figure.
559  */
560
561 /**********************************************************************
562  *      LoadMessage32A          (internal)
563  */
564 INT32 WINAPI LoadMessage32A( HMODULE32 instance, UINT32 id, WORD lang,
565                       LPSTR buffer, INT32 buflen )
566 {
567     HGLOBAL32   hmem;
568     HRSRC32     hrsrc;
569     BYTE        *p;
570     int         nrofentries,i,slen;
571     struct      _subentry {
572         DWORD   firstentry;
573         DWORD   lastentry;
574         DWORD   offset;
575     } *se;
576     struct      _stringentry {
577         WORD    len;
578         WORD    unknown;
579         CHAR    str[1];
580     } *stre;
581
582     TRACE(resource, "instance = %08lx, id = %08lx, buffer = %p, length = %ld\n", (DWORD)instance, (DWORD)id, buffer, (DWORD)buflen);
583
584     /*FIXME: I am not sure about the '1' ... But I've only seen those entries*/
585     hrsrc = FindResourceEx32W(instance,(LPWSTR)1,RT_MESSAGELIST32W,lang);
586     if (!hrsrc) return 0;
587     hmem = LoadResource32( instance, hrsrc );
588     if (!hmem) return 0;
589     
590     p = LockResource32(hmem);
591     nrofentries = *(DWORD*)p;
592     stre = NULL;
593     se = (struct _subentry*)(p+4);
594     for (i=nrofentries;i--;) {
595         if ((id>=se->firstentry) && (id<=se->lastentry)) {
596             stre = (struct _stringentry*)(p+se->offset);
597             id  -= se->firstentry;
598             break;
599         }
600         se++;
601     }
602     if (!stre)
603         return 0;
604     for (i=id;i--;) {
605         if (!(slen=stre->len))
606                 return 0;
607         stre = (struct _stringentry*)(((char*)stre)+slen);
608     }
609     slen=stre->len;
610     TRACE(resource,"    - strlen=%d\n",slen);
611     i = MIN(buflen - 1, slen);
612     if (buffer == NULL)
613         return slen; /* different to LoadString */
614     if (i>0) {
615         lstrcpyn32A(buffer,stre->str,i);
616         buffer[i]=0;
617     } else {
618         if (buflen>1) {
619             buffer[0]=0;
620             return 0;
621         }
622     }
623     if (buffer)
624             TRACE(resource,"'%s' copied !\n", buffer);
625     return i;
626 }
627
628 /**********************************************************************
629  *      LoadMessage32W  (internal)
630  */
631 INT32 WINAPI LoadMessage32W( HMODULE32 instance, UINT32 id, WORD lang,
632                       LPWSTR buffer, INT32 buflen )
633 {
634     INT32 retval;
635     LPSTR buffer2 = NULL;
636     if (buffer && buflen)
637         buffer2 = HeapAlloc( GetProcessHeap(), 0, buflen );
638     retval = LoadMessage32A(instance,id,lang,buffer2,buflen);
639     if (buffer)
640     {
641         if (retval) {
642             lstrcpynAtoW( buffer, buffer2, buflen );
643             retval = lstrlen32W( buffer );
644         }
645         HeapFree( GetProcessHeap(), 0, buffer2 );
646     }
647     return retval;
648 }
649
650
651 /**********************************************************************
652  *      EnumResourceTypesA      (KERNEL32.90)
653  */
654 BOOL32 WINAPI EnumResourceTypes32A( HMODULE32 hmodule,ENUMRESTYPEPROC32A lpfun,
655                                     LONG lParam)
656 {
657         /* FIXME: move WINE_MODREF stuff here */
658     return PE_EnumResourceTypes32A(hmodule,lpfun,lParam);
659 }
660
661 /**********************************************************************
662  *      EnumResourceTypesW      (KERNEL32.91)
663  */
664 BOOL32 WINAPI EnumResourceTypes32W( HMODULE32 hmodule,ENUMRESTYPEPROC32W lpfun,
665                                     LONG lParam)
666 {
667         /* FIXME: move WINE_MODREF stuff here */
668     return PE_EnumResourceTypes32W(hmodule,lpfun,lParam);
669 }
670
671 /**********************************************************************
672  *      EnumResourceNamesA      (KERNEL32.88)
673  */
674 BOOL32 WINAPI EnumResourceNames32A( HMODULE32 hmodule, LPCSTR type,
675                                     ENUMRESNAMEPROC32A lpfun, LONG lParam )
676 {
677         /* FIXME: move WINE_MODREF stuff here */
678     return PE_EnumResourceNames32A(hmodule,type,lpfun,lParam);
679 }
680 /**********************************************************************
681  *      EnumResourceNamesW      (KERNEL32.89)
682  */
683 BOOL32 WINAPI EnumResourceNames32W( HMODULE32 hmodule, LPCWSTR type,
684                                     ENUMRESNAMEPROC32W lpfun, LONG lParam )
685 {
686         /* FIXME: move WINE_MODREF stuff here */
687     return PE_EnumResourceNames32W(hmodule,type,lpfun,lParam);
688 }
689
690 /**********************************************************************
691  *      EnumResourceLanguagesA  (KERNEL32.86)
692  */
693 BOOL32 WINAPI EnumResourceLanguages32A( HMODULE32 hmodule, LPCSTR type,
694                                         LPCSTR name, ENUMRESLANGPROC32A lpfun,
695                                         LONG lParam)
696 {
697         /* FIXME: move WINE_MODREF stuff here */
698     return PE_EnumResourceLanguages32A(hmodule,type,name,lpfun,lParam);
699 }
700 /**********************************************************************
701  *      EnumResourceLanguagesW  (KERNEL32.87)
702  */
703 BOOL32 WINAPI EnumResourceLanguages32W( HMODULE32 hmodule, LPCWSTR type,
704                                         LPCWSTR name, ENUMRESLANGPROC32W lpfun,
705                                         LONG lParam)
706 {
707         /* FIXME: move WINE_MODREF stuff here */
708     return PE_EnumResourceLanguages32W(hmodule,type,name,lpfun,lParam);
709 }