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