4 * Copyright 1995 Alexandre Julliard
11 #include <sys/types.h>
20 #include "stackframe.h"
29 static HMODULE hFirstModule
= 0;
30 static HMODULE hCachedModule
= 0; /* Module cached by MODULE_OpenFile */
33 static HANDLE hInitialStack32
= 0;
35 /***********************************************************************
38 * Load a built-in module. If the 'force' parameter is FALSE, we only
39 * load the module if it has not been disabled via the -dll option.
42 static HMODULE
MODULE_LoadBuiltin( LPCSTR name
, BOOL force
)
46 SEGTABLEENTRY
*pSegTable
;
47 struct dll_table_s
*table
;
51 /* Fix the name in case we have a full path and extension */
53 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
54 strncpy( dllname
, name
, 15 );
56 if ((p
= strrchr( dllname
, '.' ))) *p
= '\0';
58 for (i
= 0, table
= dll_builtin_table
; i
< N_BUILTINS
; i
++, table
++)
59 if (!strcasecmp( table
->name
, dllname
)) break;
60 if (i
>= N_BUILTINS
) return 0;
61 if (!table
->used
&& !force
) return 0;
63 hModule
= GLOBAL_CreateBlock( GMEM_MOVEABLE
, table
->module_start
,
64 table
->module_end
- table
->module_start
,
65 0, FALSE
, FALSE
, FALSE
, NULL
);
66 if (!hModule
) return 0;
67 FarSetOwner( hModule
, hModule
);
69 table
->hModule
= hModule
;
71 dprintf_module( stddeb
, "Built-in %s: hmodule=%04x\n",
72 table
->name
, hModule
);
74 /* Allocate the code segment */
76 pModule
= (NE_MODULE
*)GlobalLock( hModule
);
77 pSegTable
= NE_SEG_TABLE( pModule
);
79 pSegTable
->selector
= GLOBAL_CreateBlock( GMEM_FIXED
, table
->code_start
,
80 pSegTable
->minsize
, hModule
,
81 TRUE
, TRUE
, FALSE
, NULL
);
82 if (!pSegTable
->selector
) return 0;
85 /* Allocate the data segment */
87 pSegTable
->selector
= GLOBAL_Alloc( GMEM_FIXED
, pSegTable
->minsize
,
88 hModule
, FALSE
, FALSE
, FALSE
);
89 if (!pSegTable
->selector
) return 0;
90 memcpy( GlobalLock( pSegTable
->selector
),
91 table
->data_start
, pSegTable
->minsize
);
93 pModule
->next
= hFirstModule
;
94 hFirstModule
= hModule
;
99 /***********************************************************************
102 * Create the built-in modules.
104 BOOL
MODULE_Init(void)
106 /* For these, built-in modules are always used */
109 if (!MODULE_LoadBuiltin( "KERNEL", TRUE
) ||
110 !MODULE_LoadBuiltin( "GDI", TRUE
) ||
111 !MODULE_LoadBuiltin( "USER", TRUE
) ||
112 !MODULE_LoadBuiltin( "WINPROCS", TRUE
)) return FALSE
;
115 fprintf(stderr
, "JBP: MODULE_Init() ignored.\n");
117 /* Initialize KERNEL.178 (__WINFLAGS) with the correct flags value */
119 MODULE_SetEntryPoint( GetModuleHandle( "KERNEL" ), 178, GetWinFlags() );
124 /***********************************************************************
127 void MODULE_PrintModule( HMODULE hmodule
)
133 NE_MODULE
*pModule
= (NE_MODULE
*)GlobalLock( hmodule
);
135 /* Dump the module info */
137 printf( "Module "NPFMT
":\n", hmodule
);
138 printf( "count=%d flags=%04x heap=%d stack=%d\n",
139 pModule
->count
, pModule
->flags
,
140 pModule
->heap_size
, pModule
->stack_size
);
141 printf( "cs:ip=%04x:%04x ss:sp=%04x:%04x ds=%04x nb seg=%d modrefs=%d\n",
142 pModule
->cs
, pModule
->ip
, pModule
->ss
, pModule
->sp
, pModule
->dgroup
,
143 pModule
->seg_count
, pModule
->modref_count
);
144 printf( "os_flags=%d swap_area=%d version=%04x\n",
145 pModule
->os_flags
, pModule
->min_swap_area
,
146 pModule
->expected_version
);
148 /* Dump the file info */
150 printf( "Filename: '%s'\n",
151 ((LOADEDFILEINFO
*)((BYTE
*)pModule
+ pModule
->fileinfo
))->filename
);
153 /* Dump the segment table */
155 printf( "\nSegment table:\n" );
156 pSeg
= NE_SEG_TABLE( pModule
);
157 for (i
= 0; i
< pModule
->seg_count
; i
++, pSeg
++)
158 printf( "%02x: pos=%d size=%d flags=%04x minsize=%d sel="NPFMT
"\n",
159 i
+ 1, pSeg
->filepos
, pSeg
->size
, pSeg
->flags
,
160 pSeg
->minsize
, pSeg
->selector
);
162 /* Dump the resource table */
164 printf( "\nResource table:\n" );
165 if (pModule
->res_table
)
167 pword
= (WORD
*)((BYTE
*)pModule
+ pModule
->res_table
);
168 printf( "Alignment: %d\n", *pword
++ );
171 struct resource_typeinfo_s
*ptr
= (struct resource_typeinfo_s
*)pword
;
172 struct resource_nameinfo_s
*pname
= (struct resource_nameinfo_s
*)(ptr
+ 1);
173 printf( "id=%04x count=%d\n", ptr
->type_id
, ptr
->count
);
174 for (i
= 0; i
< ptr
->count
; i
++, pname
++)
175 printf( "offset=%d len=%d id=%04x\n",
176 pname
->offset
, pname
->length
, pname
->id
);
177 pword
= (WORD
*)pname
;
180 else printf( "None\n" );
182 /* Dump the resident name table */
184 printf( "\nResident-name table:\n" );
185 pstr
= (char *)pModule
+ pModule
->name_table
;
188 printf( "%*.*s: %d\n", *pstr
, *pstr
, pstr
+ 1,
189 *(WORD
*)(pstr
+ *pstr
+ 1) );
190 pstr
+= *pstr
+ 1 + sizeof(WORD
);
193 /* Dump the module reference table */
195 printf( "\nModule ref table:\n" );
196 if (pModule
->modref_table
)
198 pword
= (WORD
*)((BYTE
*)pModule
+ pModule
->modref_table
);
199 for (i
= 0; i
< pModule
->modref_count
; i
++, pword
++)
201 char *name
= (char *)pModule
+ pModule
->import_table
+ *pword
;
202 printf( "%d: %04x -> '%*.*s'\n",
203 i
, *pword
, *name
, *name
, name
+ 1 );
206 else printf( "None\n" );
208 /* Dump the entry table */
210 printf( "\nEntry table:\n" );
211 pstr
= (char *)pModule
+ pModule
->entry_table
;
215 printf( "Bundle %d-%d: %02x\n", ordinal
, ordinal
+ *pstr
- 1, pstr
[1]);
221 else if ((BYTE
)pstr
[1] == 0xff) /* moveable */
223 struct entry_tab_movable_s
*pe
= (struct entry_tab_movable_s
*)(pstr
+2);
224 for (i
= 0; i
< *pstr
; i
++, pe
++)
225 printf( "%d: %02x:%04x (moveable)\n",
226 ordinal
++, pe
->seg_number
, pe
->offset
);
231 struct entry_tab_fixed_s
*pe
= (struct entry_tab_fixed_s
*)(pstr
+2);
232 for (i
= 0; i
< *pstr
; i
++, pe
++)
233 printf( "%d: %04x (fixed)\n",
234 ordinal
++, pe
->offset
[0] + (pe
->offset
[1] << 8) );
239 /* Dump the non-resident names table */
241 printf( "\nNon-resident names table:\n" );
242 if (pModule
->nrname_handle
)
244 pstr
= (char *)GlobalLock( pModule
->nrname_handle
);
247 printf( "%*.*s: %d\n", *pstr
, *pstr
, pstr
+ 1,
248 *(WORD
*)(pstr
+ *pstr
+ 1) );
249 pstr
+= *pstr
+ 1 + sizeof(WORD
);
256 /***********************************************************************
259 int MODULE_OpenFile( HMODULE hModule
)
264 static int cachedfd
= -1;
266 hModule
= GetExePtr( hModule
); /* In case we were passed an hInstance */
267 dprintf_module( stddeb
, "MODULE_OpenFile("NPFMT
") cache: mod="NPFMT
" fd=%d\n",
268 hModule
, hCachedModule
, cachedfd
);
269 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return -1;
270 if (hCachedModule
== hModule
) return cachedfd
;
272 hCachedModule
= hModule
;
273 name
= ((LOADEDFILEINFO
*)((char*)pModule
+ pModule
->fileinfo
))->filename
;
274 if ((cachedfd
= open( DOS_GetUnixFileName( name
), O_RDONLY
)) == -1)
275 fprintf( stderr
, "MODULE_OpenFile: can't open file '%s' for module "NPFMT
"\n",
277 dprintf_module( stddeb
, "MODULE_OpenFile: opened '%s' -> %d\n",
281 /***********************************************************************
285 /* This function translates NE segment flags to GlobalAlloc flags */
287 static WORD
MODULE_Ne2MemFlags(WORD flags
)
291 if (flags
& NE_SEGFLAGS_DISCARDABLE
)
292 memflags
|= GMEM_DISCARDABLE
;
293 if (flags
& NE_SEGFLAGS_MOVEABLE
||
294 ( ! (flags
& NE_SEGFLAGS_DATA
) &&
295 ! (flags
& NE_SEGFLAGS_LOADED
) &&
296 ! (flags
& NE_SEGFLAGS_ALLOCATED
)
299 memflags
|= GMEM_MOVEABLE
;
300 memflags
|= GMEM_ZEROINIT
;
302 memflags
= GMEM_ZEROINIT
| GMEM_FIXED
;
307 /***********************************************************************
308 * MODULE_AllocateSegment (WINPROCS.26)
311 DWORD
MODULE_AllocateSegment(WORD wFlags
, WORD wSize
, WORD wElem
)
313 WORD size
= wSize
<< wElem
;
314 HANDLE hMem
= GlobalAlloc( MODULE_Ne2MemFlags(wFlags
), size
);
316 return (DWORD
)GlobalLock(hMem
);
318 WORD selector
= HIWORD(GlobalLock(hMem
));
319 return MAKELONG(hMem
, selector
);
323 /***********************************************************************
324 * MODULE_CreateSegments
327 static BOOL
MODULE_CreateSegments( HMODULE hModule
)
329 SEGTABLEENTRY
*pSegment
;
333 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return FALSE
;
334 pSegment
= NE_SEG_TABLE( pModule
);
335 for (i
= 1; i
<= pModule
->seg_count
; i
++, pSegment
++)
337 minsize
= pSegment
->minsize
? pSegment
->minsize
: 0x10000;
338 if (i
== pModule
->ss
) minsize
+= pModule
->stack_size
;
339 /* The DGROUP is allocated by MODULE_CreateInstance */
340 if (i
== pModule
->dgroup
) continue;
341 pSegment
->selector
= GLOBAL_Alloc( MODULE_Ne2MemFlags(pSegment
->flags
),
343 !(pSegment
->flags
& NE_SEGFLAGS_DATA
),
345 FALSE
/*pSegment->flags & NE_SEGFLAGS_READONLY*/ );
346 if (!pSegment
->selector
) return FALSE
;
349 pModule
->dgroup_entry
= pModule
->dgroup
? pModule
->seg_table
+
350 (pModule
->dgroup
- 1) * sizeof(SEGTABLEENTRY
) : 0;
356 /***********************************************************************
360 static HINSTANCE
MODULE_GetInstance( HMODULE hModule
)
362 SEGTABLEENTRY
*pSegment
;
365 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
366 if (pModule
->dgroup
== 0) return hModule
;
368 pSegment
= NE_SEG_TABLE( pModule
) + pModule
->dgroup
- 1;
370 return pSegment
->selector
;
375 /***********************************************************************
376 * MODULE_CreateInstance
378 HINSTANCE
MODULE_CreateInstance( HMODULE hModule
, LOADPARAMS
*params
)
380 SEGTABLEENTRY
*pSegment
;
383 HINSTANCE hNewInstance
, hPrevInstance
;
385 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
386 if (pModule
->dgroup
== 0) return hModule
;
388 pSegment
= NE_SEG_TABLE( pModule
) + pModule
->dgroup
- 1;
389 hPrevInstance
= pSegment
->selector
;
391 /* if it's a library, create a new instance only the first time */
394 if (pModule
->flags
& NE_FFLAGS_LIBMODULE
) return hPrevInstance
;
395 if (params
== (LOADPARAMS
*)-1) return hPrevInstance
;
398 minsize
= pSegment
->minsize
? pSegment
->minsize
: 0x10000;
399 if (pModule
->ss
== pModule
->dgroup
) minsize
+= pModule
->stack_size
;
400 minsize
+= pModule
->heap_size
;
401 hNewInstance
= GLOBAL_Alloc( GMEM_ZEROINIT
| GMEM_FIXED
,
402 minsize
, hModule
, FALSE
, FALSE
, FALSE
);
403 if (!hNewInstance
) return 0;
404 pSegment
->selector
= hNewInstance
;
409 /***********************************************************************
410 * MODULE_LoadExeHeader
412 HMODULE
MODULE_LoadExeHeader( int fd
, OFSTRUCT
*ofs
)
414 struct mz_header_s mz_header
;
415 struct ne_header_s ne_header
;
420 char *buffer
, *fastload
= NULL
;
421 int fastload_offset
= 0, fastload_length
= 0;
423 /* Read a block from either the file or the fast-load area. */
424 #define READ(offset,size,buffer) \
425 ((fastload && ((offset) >= fastload_offset) && \
426 ((offset)+(size) <= fastload_offset+fastload_length)) ? \
427 (memcpy( buffer, fastload+(offset)-fastload_offset, (size) ), TRUE) : \
428 (lseek( fd, mz_header.ne_offset+(offset), SEEK_SET), \
429 read( fd, (buffer), (size) ) == (size)))
431 lseek( fd
, 0, SEEK_SET
);
432 if ((read( fd
, &mz_header
, sizeof(mz_header
) ) != sizeof(mz_header
)) ||
433 (mz_header
.mz_magic
!= MZ_SIGNATURE
)) return (HMODULE
)11; /* invalid exe */
435 lseek( fd
, mz_header
.ne_offset
, SEEK_SET
);
436 if (read( fd
, &ne_header
, sizeof(ne_header
) ) != sizeof(ne_header
))
437 return (HMODULE
)11; /* invalid exe */
439 if (ne_header
.ne_magic
== PE_SIGNATURE
) return (HMODULE
)21; /* win32 exe */
440 if (ne_header
.ne_magic
!= NE_SIGNATURE
) return (HMODULE
)11; /* invalid exe */
442 /* We now have a valid NE header */
444 size
= sizeof(NE_MODULE
) +
445 /* loaded file info */
446 sizeof(LOADEDFILEINFO
) + strlen(ofs
->szPathName
) +
448 ne_header
.n_segment_tab
* sizeof(SEGTABLEENTRY
) +
450 ne_header
.rname_tab_offset
- ne_header
.resource_tab_offset
+
451 /* resident names table */
452 ne_header
.moduleref_tab_offset
- ne_header
.rname_tab_offset
+
453 /* module ref table */
454 ne_header
.n_mod_ref_tab
* sizeof(WORD
) +
455 /* imported names table */
456 ne_header
.entry_tab_offset
- ne_header
.iname_tab_offset
+
457 /* entry table length */
458 ne_header
.entry_tab_length
;
460 hModule
= GlobalAlloc( GMEM_MOVEABLE
| GMEM_ZEROINIT
, size
);
461 if (!hModule
) return (HMODULE
)11; /* invalid exe */
462 FarSetOwner( hModule
, (WORD
)(DWORD
)hModule
);
463 pModule
= (NE_MODULE
*)GlobalLock( hModule
);
464 memcpy( pModule
, &ne_header
, sizeof(NE_MODULE
) );
466 pData
= (BYTE
*)(pModule
+ 1);
468 /* Read the fast-load area */
470 if (ne_header
.additional_flags
& NE_AFLAGS_FASTLOAD
)
472 fastload_offset
=ne_header
.fastload_offset
<<ne_header
.align_shift_count
;
473 fastload_length
=ne_header
.fastload_length
<<ne_header
.align_shift_count
;
474 dprintf_module( stddeb
, "Using fast-load area offset=%x len=%d\n",
475 fastload_offset
, fastload_length
);
476 if ((fastload
= (char *)malloc( fastload_length
)) != NULL
)
478 lseek( fd
, mz_header
.ne_offset
+ fastload_offset
, SEEK_SET
);
479 if (read( fd
, fastload
, fastload_length
) != fastload_length
)
487 /* Store the filename information */
489 pModule
->fileinfo
= (int)pData
- (int)pModule
;
490 ((LOADEDFILEINFO
*)pData
)->length
= sizeof(LOADEDFILEINFO
)+strlen(ofs
->szPathName
);
491 ((LOADEDFILEINFO
*)pData
)->fixed_media
= TRUE
;
492 ((LOADEDFILEINFO
*)pData
)->error
= 0;
493 ((LOADEDFILEINFO
*)pData
)->date
= 0;
494 ((LOADEDFILEINFO
*)pData
)->time
= 0;
495 strcpy( ((LOADEDFILEINFO
*)pData
)->filename
, ofs
->szPathName
);
496 pData
+= ((LOADEDFILEINFO
*)pData
)->length
--;
498 /* Get the segment table */
500 pModule
->seg_table
= (int)pData
- (int)pModule
;
501 buffer
= malloc( ne_header
.n_segment_tab
* sizeof(struct ne_segment_table_entry_s
) );
505 struct ne_segment_table_entry_s
*pSeg
;
507 if (!READ( ne_header
.segment_tab_offset
,
508 ne_header
.n_segment_tab
* sizeof(struct ne_segment_table_entry_s
),
509 buffer
)) return (HMODULE
)11; /* invalid exe */
510 pSeg
= (struct ne_segment_table_entry_s
*)buffer
;
511 for (i
= ne_header
.n_segment_tab
; i
> 0; i
--, pSeg
++)
513 memcpy( pData
, pSeg
, sizeof(*pSeg
) );
514 pData
+= sizeof(SEGTABLEENTRY
);
518 else return (HMODULE
)11; /* invalid exe */
520 /* Get the resource table */
522 if (ne_header
.resource_tab_offset
< ne_header
.rname_tab_offset
)
524 pModule
->res_table
= (int)pData
- (int)pModule
;
525 if (!READ(ne_header
.resource_tab_offset
,
526 ne_header
.rname_tab_offset
- ne_header
.resource_tab_offset
,
527 pData
)) return (HMODULE
)11; /* invalid exe */
528 pData
+= ne_header
.rname_tab_offset
- ne_header
.resource_tab_offset
;
530 else pModule
->res_table
= 0; /* No resource table */
532 /* Get the resident names table */
534 pModule
->name_table
= (int)pData
- (int)pModule
;
535 if (!READ( ne_header
.rname_tab_offset
,
536 ne_header
.moduleref_tab_offset
- ne_header
.rname_tab_offset
,
537 pData
)) return (HMODULE
)11; /* invalid exe */
538 pData
+= ne_header
.moduleref_tab_offset
- ne_header
.rname_tab_offset
;
540 /* Get the module references table */
542 if (ne_header
.n_mod_ref_tab
> 0)
544 pModule
->modref_table
= (int)pData
- (int)pModule
;
545 if (!READ( ne_header
.moduleref_tab_offset
,
546 ne_header
.n_mod_ref_tab
* sizeof(WORD
),
547 pData
)) return (HMODULE
)11; /* invalid exe */
548 pData
+= ne_header
.n_mod_ref_tab
* sizeof(WORD
);
550 else pModule
->modref_table
= 0; /* No module references */
552 /* Get the imported names table */
554 pModule
->import_table
= (int)pData
- (int)pModule
;
555 if (!READ( ne_header
.iname_tab_offset
,
556 ne_header
.entry_tab_offset
- ne_header
.iname_tab_offset
,
557 pData
)) return (HMODULE
)11; /* invalid exe */
558 pData
+= ne_header
.entry_tab_offset
- ne_header
.iname_tab_offset
;
560 /* Get the entry table */
562 pModule
->entry_table
= (int)pData
- (int)pModule
;
563 if (!READ( ne_header
.entry_tab_offset
,
564 ne_header
.entry_tab_length
,
565 pData
)) return (HMODULE
)11; /* invalid exe */
566 pData
+= ne_header
.entry_tab_length
;
568 /* Get the non-resident names table */
570 if (ne_header
.nrname_tab_length
)
572 pModule
->nrname_handle
= GLOBAL_Alloc( 0, ne_header
.nrname_tab_length
,
573 hModule
, FALSE
, FALSE
, FALSE
);
574 if (!pModule
->nrname_handle
) return (HMODULE
)11; /* invalid exe */
575 buffer
= GlobalLock( pModule
->nrname_handle
);
576 lseek( fd
, ne_header
.nrname_tab_offset
, SEEK_SET
);
577 if (read( fd
, buffer
, ne_header
.nrname_tab_length
)
578 != ne_header
.nrname_tab_length
) return (HMODULE
)11; /* invalid exe */
580 else pModule
->nrname_handle
= 0;
582 /* Allocate a segment for the implicitly-loaded DLLs */
584 if (pModule
->modref_count
)
586 pModule
->dlls_to_init
= GLOBAL_Alloc(GMEM_ZEROINIT
,
587 (pModule
->modref_count
+1)*sizeof(HMODULE
),
588 hModule
, FALSE
, FALSE
, FALSE
);
589 if (!pModule
->dlls_to_init
) return (HMODULE
)11; /* invalid exe */
591 else pModule
->dlls_to_init
= 0;
593 if (debugging_module
) MODULE_PrintModule( hModule
);
594 pModule
->next
= hFirstModule
;
595 hFirstModule
= hModule
;
600 /***********************************************************************
603 * Lookup the ordinal for a given name.
605 WORD
MODULE_GetOrdinal( HMODULE hModule
, char *name
)
607 char buffer
[256], *cpnt
;
611 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
613 dprintf_module( stddeb
, "MODULE_GetOrdinal("NPFMT
",'%s')\n",
616 /* First handle names of the form '#xxxx' */
618 if (name
[0] == '#') return atoi( name
+ 1 );
620 /* Now copy and uppercase the string */
622 strcpy( buffer
, name
);
624 len
= strlen( buffer
);
626 /* First search the resident names */
628 cpnt
= (char *)pModule
+ pModule
->name_table
;
630 /* Skip the first entry (module name) */
631 cpnt
+= *cpnt
+ 1 + sizeof(WORD
);
634 dprintf_module( stddeb
, " Checking '%*.*s'\n", *cpnt
, *cpnt
, cpnt
+1 );
635 if (((BYTE
)*cpnt
== len
) && !memcmp( cpnt
+1, buffer
, len
))
637 dprintf_module( stddeb
, " Found: ordinal=%d\n",
638 *(WORD
*)(cpnt
+ *cpnt
+ 1) );
639 return *(WORD
*)(cpnt
+ *cpnt
+ 1);
641 cpnt
+= *cpnt
+ 1 + sizeof(WORD
);
644 /* Now search the non-resident names table */
646 if (!pModule
->nrname_handle
) return 0; /* No non-resident table */
647 cpnt
= (char *)GlobalLock( pModule
->nrname_handle
);
649 /* Skip the first entry (module description string) */
650 cpnt
+= *cpnt
+ 1 + sizeof(WORD
);
653 dprintf_module( stddeb
, " Checking '%*.*s'\n", *cpnt
, *cpnt
, cpnt
+1 );
654 if (((BYTE
)*cpnt
== len
) && !memcmp( cpnt
+1, buffer
, len
))
656 dprintf_module( stddeb
, " Found: ordinal=%d\n",
657 *(WORD
*)(cpnt
+ *cpnt
+ 1) );
658 return *(WORD
*)(cpnt
+ *cpnt
+ 1);
660 cpnt
+= *cpnt
+ 1 + sizeof(WORD
);
666 /***********************************************************************
667 * MODULE_GetEntryPoint
669 * Return the entry point for a given ordinal.
671 SEGPTR
MODULE_GetEntryPoint( HMODULE hModule
, WORD ordinal
)
678 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
680 p
= (BYTE
*)pModule
+ pModule
->entry_table
;
681 while (*p
&& (curOrdinal
+ *p
<= ordinal
))
683 /* Skipping this bundle */
687 case 0: p
+= 2; break; /* unused */
688 case 0xff: p
+= 2 + *p
* 6; break; /* moveable */
689 default: p
+= 2 + *p
* 3; break; /* fixed */
698 case 0xff: /* moveable */
699 p
+= 2 + 6 * (ordinal
- curOrdinal
);
701 offset
= *(WORD
*)(p
+ 4);
705 p
+= 2 + 3 * (ordinal
- curOrdinal
);
706 offset
= *(WORD
*)(p
+ 1);
710 if (sel
== 0xfe) sel
= 0xffff; /* constant entry */
711 else sel
= (WORD
)(DWORD
)NE_SEG_TABLE(pModule
)[sel
-1].selector
;
712 return (SEGPTR
)MAKELONG( offset
, sel
);
716 /***********************************************************************
717 * MODULE_SetEntryPoint
719 * Change the value of an entry point. Use with caution!
720 * It can only change the offset value, not the selector.
722 BOOL
MODULE_SetEntryPoint( HMODULE hModule
, WORD ordinal
, WORD offset
)
728 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return FALSE
;
730 p
= (BYTE
*)pModule
+ pModule
->entry_table
;
731 while (*p
&& (curOrdinal
+ *p
<= ordinal
))
733 /* Skipping this bundle */
737 case 0: p
+= 2; break; /* unused */
738 case 0xff: p
+= 2 + *p
* 6; break; /* moveable */
739 default: p
+= 2 + *p
* 3; break; /* fixed */
742 if (!*p
) return FALSE
;
748 case 0xff: /* moveable */
749 p
+= 2 + 6 * (ordinal
- curOrdinal
);
750 *(WORD
*)(p
+ 4) = offset
;
753 p
+= 2 + 3 * (ordinal
- curOrdinal
);
754 *(WORD
*)(p
+ 1) = offset
;
761 /***********************************************************************
762 * MODULE_GetEntryPointName
764 * Return the entry point name for a given ordinal.
765 * Used only by relay debugging.
766 * Warning: returned pointer is to a Pascal-type string.
768 LPSTR
MODULE_GetEntryPointName( HMODULE hModule
, WORD ordinal
)
773 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
775 /* First search the resident names */
777 cpnt
= (char *)pModule
+ pModule
->name_table
;
780 cpnt
+= *cpnt
+ 1 + sizeof(WORD
);
781 if (*(WORD
*)(cpnt
+ *cpnt
+ 1) == ordinal
) return cpnt
;
784 /* Now search the non-resident names table */
786 if (!pModule
->nrname_handle
) return 0; /* No non-resident table */
787 cpnt
= (char *)GlobalLock( pModule
->nrname_handle
);
790 cpnt
+= *cpnt
+ 1 + sizeof(WORD
);
791 if (*(WORD
*)(cpnt
+ *cpnt
+ 1) == ordinal
) return cpnt
;
797 /***********************************************************************
798 * MODULE_GetModuleName
800 LPSTR
MODULE_GetModuleName( HMODULE hModule
)
804 static char buffer
[10];
806 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return NULL
;
807 p
= (BYTE
*)pModule
+ pModule
->name_table
;
809 memcpy( buffer
, p
+ 1, len
);
815 /**********************************************************************
818 * Find a module from a path name.
820 HMODULE
MODULE_FindModule( LPCSTR path
)
822 HMODULE hModule
= hFirstModule
;
823 LPCSTR filename
, dotptr
, modulepath
, modulename
;
824 BYTE len
, *name_table
;
826 if (!(filename
= strrchr( path
, '\\' ))) filename
= path
;
828 if ((dotptr
= strrchr( filename
, '.' )) != NULL
)
829 len
= (BYTE
)(dotptr
- filename
);
830 else len
= strlen( filename
);
834 NE_MODULE
*pModule
= (NE_MODULE
*)GlobalLock( hModule
);
836 modulepath
= ((LOADEDFILEINFO
*)((char*)pModule
+ pModule
->fileinfo
))->filename
;
837 if (!(modulename
= strrchr( modulepath
, '\\' )))
838 modulename
= modulepath
;
840 if (!strcasecmp( modulename
, filename
)) return hModule
;
842 name_table
= (BYTE
*)pModule
+ pModule
->name_table
;
843 if ((*name_table
== len
) && !strncasecmp(filename
, name_table
+1, len
))
845 hModule
= pModule
->next
;
851 /**********************************************************************
854 * Remove a module from memory.
856 static void MODULE_FreeModule( HMODULE hModule
)
858 HMODULE
*hPrevModule
;
860 SEGTABLEENTRY
*pSegment
;
864 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return;
865 if (pModule
->flags
& NE_FFLAGS_BUILTIN
)
866 return; /* Can't free built-in module */
868 /* FIXME: should call the exit code for the library here */
870 /* Remove it from the linked list */
872 hPrevModule
= &hFirstModule
;
873 while (*hPrevModule
&& (*hPrevModule
!= hModule
))
875 hPrevModule
= &((NE_MODULE
*)GlobalLock( *hPrevModule
))->next
;
877 if (*hPrevModule
) *hPrevModule
= pModule
->next
;
879 /* Free all the segments */
881 pSegment
= NE_SEG_TABLE( pModule
);
882 for (i
= 1; i
<= pModule
->seg_count
; i
++, pSegment
++)
884 GlobalFree( pSegment
->selector
);
887 /* Free the referenced modules */
889 pModRef
= (HMODULE
*)NE_MODULE_TABLE( pModule
);
890 for (i
= 0; i
< pModule
->modref_count
; i
++, pModRef
++)
892 FreeModule( *pModRef
);
895 /* Free the module storage */
897 if (pModule
->nrname_handle
) GlobalFree( pModule
->nrname_handle
);
898 if (pModule
->dlls_to_init
) GlobalFree( pModule
->dlls_to_init
);
899 GlobalFree( hModule
);
901 /* Remove module from cache */
903 if (hCachedModule
== hModule
) hCachedModule
= 0;
907 HINSTANCE
PE_LoadModule(int fd
, OFSTRUCT
*ofs
, LOADPARAMS
* params
);
909 /**********************************************************************
910 * LoadModule (KERNEL.45)
912 HINSTANCE
LoadModule( LPCSTR name
, LPVOID paramBlock
)
915 HANDLE hInstance
, hPrevInstance
;
917 LOADPARAMS
*params
= (LOADPARAMS
*)paramBlock
;
919 WORD
*pModRef
, *pDLLs
;
922 hModule
= MODULE_FindModule( name
);
924 if (!hModule
) /* We have to load the module */
928 /* Try to load the built-in first if not disabled */
929 if ((hModule
= MODULE_LoadBuiltin( name
, FALSE
))) return hModule
;
931 if (strchr( name
, '/' )) name
= DOS_GetDosFileName( name
);
932 if ((fd
= OpenFile( name
, &ofs
, OF_READ
)) == -1)
934 /* Now try the built-in even if disabled */
935 if ((hModule
= MODULE_LoadBuiltin( name
, TRUE
)))
937 fprintf( stderr
, "Warning: could not load Windows DLL '%s', using built-in module.\n", name
);
940 return 2; /* File not found */
943 /* Create the module structure */
945 if ((hModule
= MODULE_LoadExeHeader( fd
, &ofs
)) < 32)
948 return PE_LoadModule(fd
,&ofs
,paramBlock
);
950 fprintf( stderr
, "LoadModule: can't load '%s', error=%d\n",
954 pModule
= (NE_MODULE
*)GlobalLock( hModule
);
956 /* Allocate the segments for this module */
958 MODULE_CreateSegments( hModule
);
961 hInstance
= MODULE_CreateInstance( hModule
, (LOADPARAMS
*)paramBlock
);
963 /* Load the referenced DLLs */
965 pModRef
= (WORD
*)((char *)pModule
+ pModule
->modref_table
);
966 pDLLs
= (WORD
*)GlobalLock( pModule
->dlls_to_init
);
967 for (i
= 0; i
< pModule
->modref_count
; i
++, pModRef
++)
970 BYTE
*pstr
= (BYTE
*)pModule
+ pModule
->import_table
+ *pModRef
;
971 memcpy( buffer
, pstr
+ 1, *pstr
);
972 strcpy( buffer
+ *pstr
, ".dll" );
973 dprintf_module( stddeb
, "Loading '%s'\n", buffer
);
974 if (!(*pModRef
= MODULE_FindModule( buffer
)))
976 /* If the DLL is not loaded yet, load it and store */
977 /* its handle in the list of DLLs to initialize. */
980 if ((hDLL
= LoadModule( buffer
, (LPVOID
)-1 )) == 2) /* file not found */
984 /* Try with prepending the path of the current module */
985 GetModuleFileName( hModule
, buffer
, 256 );
986 if (!(p
= strrchr( buffer
, '\\' ))) p
= buffer
;
987 memcpy( p
+ 1, pstr
+ 1, *pstr
);
988 strcpy( p
+ 1 + *pstr
, ".dll" );
989 hDLL
= LoadModule( buffer
, (LPVOID
)-1 );
993 fprintf( stderr
, "Could not load '%s' required by '%s', error = %d\n",
994 buffer
, name
, hDLL
);
995 return 2; /* file not found */
997 *pModRef
= GetExePtr( hDLL
);
1000 else /* Increment the reference count of the DLL */
1002 NE_MODULE
*pOldDLL
= (NE_MODULE
*)GlobalLock( *pModRef
);
1003 if (pOldDLL
) pOldDLL
->count
++;
1007 /* Load the segments */
1009 if (pModule
->flags
& NE_FFLAGS_SELFLOAD
)
1011 /* Handle self loading modules */
1012 SEGTABLEENTRY
* pSegTable
= (SEGTABLEENTRY
*) NE_SEG_TABLE(pModule
);
1013 SELFLOADHEADER
*selfloadheader
;
1014 HMODULE hselfload
= GetModuleHandle("WINPROCS");
1015 WORD oldss
, oldsp
, saved_dgroup
= pSegTable
[pModule
->dgroup
- 1].selector
;
1016 fprintf (stderr
, "Warning: %*.*s is a self-loading module\n"
1017 "Support for self-loading modules is very experimental\n",
1018 *((BYTE
*)pModule
+ pModule
->name_table
),
1019 *((BYTE
*)pModule
+ pModule
->name_table
),
1020 (char *)pModule
+ pModule
->name_table
+ 1);
1021 NE_LoadSegment( hModule
, 1 );
1022 selfloadheader
= (SELFLOADHEADER
*)
1023 PTR_SEG_OFF_TO_LIN(pSegTable
->selector
, 0);
1024 selfloadheader
->EntryAddrProc
=
1025 MODULE_GetEntryPoint(hselfload
,27);
1026 selfloadheader
->MyAlloc
= MODULE_GetEntryPoint(hselfload
,28);
1027 selfloadheader
->SetOwner
= MODULE_GetEntryPoint(GetModuleHandle("KERNEL"),403);
1028 pModule
->self_loading_sel
= GlobalHandleToSel(
1029 GLOBAL_Alloc (GMEM_ZEROINIT
,
1030 0xFF00, hModule
, FALSE
, FALSE
, FALSE
)
1032 oldss
= IF1632_Saved16_ss
;
1033 oldsp
= IF1632_Saved16_sp
;
1034 IF1632_Saved16_ss
= pModule
->self_loading_sel
;
1035 IF1632_Saved16_sp
= 0xFF00;
1036 if (!IF1632_Stack32_base
) {
1037 STACK32FRAME
* frame32
;
1039 /* Setup an initial 32 bit stack frame */
1040 hInitialStack32
= GLOBAL_Alloc( GMEM_FIXED
, 0x10000,
1041 hModule
, FALSE
, FALSE
,
1044 /* Create the 32-bit stack frame */
1046 *(DWORD
*)GlobalLock(hInitialStack32
) = 0xDEADBEEF;
1047 stack32Top
= (char*)GlobalLock(hInitialStack32
) +
1049 frame32
= (STACK32FRAME
*)stack32Top
- 1;
1050 frame32
->saved_esp
= (DWORD
)stack32Top
;
1057 frame32
->retaddr
= 0;
1058 frame32
->codeselector
= WINE_CODE_SELECTOR
;
1059 /* pTask->esp = (DWORD)frame32; */
1060 IF1632_Stack32_base
= WIN16_GlobalLock(hInitialStack32
);
1063 CallTo16_word_ww (selfloadheader
->BootApp
,
1064 pModule
->self_loading_sel
, hModule
, fd
);
1065 /* some BootApp procs overwrite the selector of dgroup */
1066 pSegTable
[pModule
->dgroup
- 1].selector
= saved_dgroup
;
1067 IF1632_Saved16_ss
= oldss
;
1068 IF1632_Saved16_sp
= oldsp
;
1069 for (i
= 2; i
<= pModule
->seg_count
; i
++) NE_LoadSegment( hModule
, i
);
1070 if (hInitialStack32
){
1071 GlobalUnlock (hInitialStack32
);
1072 GlobalFree (hInitialStack32
);
1073 IF1632_Stack32_base
= hInitialStack32
= 0;
1078 for (i
= 1; i
<= pModule
->seg_count
; i
++)
1079 NE_LoadSegment( hModule
, i
);
1082 /* Fixup the functions prologs */
1084 NE_FixupPrologs( hModule
);
1086 /* Make sure the usage count is 1 on the first loading of */
1087 /* the module, even if it contains circular DLL references */
1091 /* Clear built-in flag in case it was set in the EXE file */
1093 pModule
->flags
&= ~NE_FFLAGS_BUILTIN
;
1097 pModule
= (NE_MODULE
*)GlobalLock( hModule
);
1098 hPrevInstance
= MODULE_GetInstance( hModule
);
1099 hInstance
= MODULE_CreateInstance( hModule
, params
);
1100 if (hInstance
!= hPrevInstance
) /* not a library */
1101 NE_LoadSegment( hModule
, pModule
->dgroup
);
1105 hModule
= GlobalAlloc( GMEM_MOVEABLE
| GMEM_ZEROINIT
, sizeof(NE_MODULE
) );
1106 pModule
= (NE_MODULE
*)GlobalLock( hModule
);
1108 pModule
->magic
= 0x454e;
1110 hInstance
= MODULE_CreateInstance( hModule
, (LOADPARAMS
*)paramBlock
);
1111 #endif /* WINELIB */
1113 /* Create a task for this instance */
1115 if (!(pModule
->flags
& NE_FFLAGS_LIBMODULE
) && (paramBlock
!= (LPVOID
)-1))
1117 TASK_CreateTask( hModule
, hInstance
, hPrevInstance
,
1118 params
->hEnvironment
,
1119 (LPSTR
)PTR_SEG_TO_LIN( params
->cmdLine
),
1120 *((WORD
*)PTR_SEG_TO_LIN(params
->showCmd
)+1) );
1127 /**********************************************************************
1128 * FreeModule (KERNEL.46)
1130 BOOL
FreeModule( HANDLE hModule
)
1134 hModule
= GetExePtr( hModule
); /* In case we were passed an hInstance */
1135 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return FALSE
;
1137 dprintf_module( stddeb
, "FreeModule: %s count %d\n",
1138 MODULE_GetModuleName(hModule
), pModule
->count
);
1139 if (--pModule
->count
== 0) MODULE_FreeModule( hModule
);
1144 /**********************************************************************
1145 * GetModuleHandle (KERNEL.47)
1147 HMODULE
WIN16_GetModuleHandle( SEGPTR name
)
1150 if (HIWORD(name
) == 0) return GetExePtr( name
);
1152 if (HIWORD(name
) == 0) return GetExePtr( LOWORD(name
) );
1154 return MODULE_FindModule( PTR_SEG_TO_LIN(name
) );
1157 HMODULE
GetModuleHandle( LPCSTR name
)
1159 return MODULE_FindModule( name
);
1163 /**********************************************************************
1164 * GetModuleUsage (KERNEL.48)
1166 int GetModuleUsage( HANDLE hModule
)
1170 hModule
= GetExePtr( hModule
); /* In case we were passed an hInstance */
1171 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
1172 dprintf_module( stddeb
, "GetModuleUsage("NPFMT
"): returning %d\n",
1173 hModule
, pModule
->count
);
1174 return pModule
->count
;
1178 /**********************************************************************
1179 * GetModuleFileName (KERNEL.49)
1181 int GetModuleFileName( HANDLE hModule
, LPSTR lpFileName
, short nSize
)
1186 hModule
= GetExePtr( hModule
); /* In case we were passed an hInstance */
1187 if (!(pModule
= (NE_MODULE
*)GlobalLock( hModule
))) return 0;
1188 name
= ((LOADEDFILEINFO
*)((char*)pModule
+ pModule
->fileinfo
))->filename
;
1189 lstrcpyn( lpFileName
, name
, nSize
);
1190 dprintf_module( stddeb
, "GetModuleFilename: %s\n", lpFileName
);
1191 return strlen(lpFileName
);
1195 /***********************************************************************
1196 * LoadLibrary (KERNEL.95)
1198 HANDLE
LoadLibrary( LPCSTR libname
)
1201 dprintf_module( stddeb
, "LoadLibrary: (%08x) %s\n", (int)libname
, libname
);
1202 WINELIB_UNIMP("LoadLibrary()");
1207 dprintf_module( stddeb
, "LoadLibrary: (%08x) %s\n", (int)libname
, libname
);
1209 /* This does not increment the module reference count, and will
1210 * therefore cause crashes on FreeLibrary calls.
1211 if ((handle = MODULE_FindModule( libname )) != 0) return handle;
1213 handle
= LoadModule( libname
, (LPVOID
)-1 );
1214 if (handle
== (HANDLE
)2) /* file not found */
1217 strcpy( buffer
, libname
);
1218 strcat( buffer
, ".dll" );
1219 handle
= LoadModule( buffer
, (LPVOID
)-1 );
1221 if (handle
>= (HANDLE
)32) NE_InitializeDLLs( GetExePtr(handle
) );
1227 /***********************************************************************
1228 * FreeLibrary (KERNEL.96)
1230 void FreeLibrary( HANDLE handle
)
1232 dprintf_module( stddeb
,"FreeLibrary: "NPFMT
"\n", handle
);
1233 FreeModule( handle
);
1237 /***********************************************************************
1238 * WinExec (KERNEL.166)
1240 HANDLE
WinExec( LPSTR lpCmdLine
, WORD nCmdShow
)
1243 HLOCAL cmdShowHandle
, cmdLineHandle
;
1246 char *p
, *cmdline
, filename
[256];
1248 if (!(cmdShowHandle
= GlobalAlloc( 0, 2 * sizeof(WORD
) ))) return 0;
1249 if (!(cmdLineHandle
= GlobalAlloc( 0, 256 ))) return 0;
1251 /* Store nCmdShow */
1253 cmdShowPtr
= (WORD
*)GlobalLock( cmdShowHandle
);
1255 cmdShowPtr
[1] = nCmdShow
;
1257 /* Build the filename and command-line */
1259 cmdline
= (char *)GlobalLock( cmdLineHandle
);
1260 strncpy( filename
, lpCmdLine
, 256 );
1261 filename
[255] = '\0';
1262 for (p
= filename
; *p
&& (*p
!= ' ') && (*p
!= '\t'); p
++);
1265 strncpy( cmdline
, p
+ 1, 128 );
1266 cmdline
[127] = '\0';
1268 else cmdline
[0] = '\0';
1271 /* Now load the executable file */
1274 params
.hEnvironment
= (HANDLE
)GetDOSEnvironment();
1276 params
.hEnvironment
= (HANDLE
)SELECTOROF( GetDOSEnvironment() );
1278 params
.cmdLine
= (SEGPTR
)WIN16_GlobalLock( cmdLineHandle
);
1279 params
.showCmd
= (SEGPTR
)WIN16_GlobalLock( cmdShowHandle
);
1280 params
.reserved
= 0;
1281 handle
= LoadModule( filename
, ¶ms
);
1282 if (handle
== (HANDLE
)2) /* file not found */
1284 /* Check that the original file name did not have a suffix */
1285 p
= strrchr(filename
, '.');
1286 if (p
&& !(strchr(p
, '/') || strchr(p
, '\\')))
1287 return handle
; /* filename already includes a suffix! */
1288 strcat( filename
, ".exe" );
1289 handle
= LoadModule( filename
, ¶ms
);
1292 GlobalFree( cmdShowHandle
);
1293 GlobalFree( cmdLineHandle
);
1296 if (handle
< (HANDLE
)32) /* Error? */
1299 /* FIXME: Yield never returns!
1300 We may want to run more applications or start the debugger
1301 before calling Yield. If we don't Yield will be called immdiately
1302 after returning. Why is it needed for Word anyway? */
1303 Yield(); /* program is executed immediately ....needed for word */
1310 /***********************************************************************
1311 * GetProcAddress (KERNEL.50)
1313 FARPROC
GetProcAddress( HANDLE hModule
, SEGPTR name
)
1318 if (!hModule
) hModule
= GetCurrentTask();
1319 hModule
= GetExePtr( hModule
);
1321 if (HIWORD(name
) != 0)
1323 ordinal
= MODULE_GetOrdinal( hModule
, (LPSTR
)PTR_SEG_TO_LIN(name
) );
1324 dprintf_module( stddeb
, "GetProcAddress: "NPFMT
" '%s'\n",
1325 hModule
, (LPSTR
)PTR_SEG_TO_LIN(name
) );
1329 ordinal
= LOWORD(name
);
1330 dprintf_module( stddeb
, "GetProcAddress: "NPFMT
" %04x\n",
1333 if (!ordinal
) return (FARPROC
)0;
1335 ret
= MODULE_GetEntryPoint( hModule
, ordinal
);
1337 dprintf_module( stddeb
, "GetProcAddress: returning "SPFMT
"\n", ret
);
1338 return (FARPROC
)ret
;
1342 /**********************************************************************
1343 * GetExpWinVer (KERNEL.167)
1345 WORD
GetExpWinVer( HMODULE hModule
)
1347 NE_MODULE
*pModule
= (NE_MODULE
*)GlobalLock( hModule
);
1349 return pModule
->expected_version
;
1353 /***********************************************************************
1354 * GetWndProcEntry16 (not a Windows API function)
1356 * Return an entry point from the WINPROCS dll.
1359 WNDPROC
GetWndProcEntry16( char *name
)
1362 static HMODULE hModule
= 0;
1364 if (!hModule
) hModule
= GetModuleHandle( "WINPROCS" );
1365 ordinal
= MODULE_GetOrdinal( hModule
, name
);
1366 return MODULE_GetEntryPoint( hModule
, ordinal
);
1371 /**********************************************************************
1372 * ModuleFirst (TOOLHELP.59)
1374 BOOL
ModuleFirst( MODULEENTRY
*lpme
)
1376 lpme
->wNext
= hFirstModule
;
1377 return ModuleNext( lpme
);
1381 /**********************************************************************
1382 * ModuleNext (TOOLHELP.60)
1384 BOOL
ModuleNext( MODULEENTRY
*lpme
)
1388 if (!lpme
->wNext
) return FALSE
;
1389 if (!(pModule
= (NE_MODULE
*)GlobalLock( lpme
->wNext
))) return FALSE
;
1390 strncpy( lpme
->szModule
, (char *)pModule
+ pModule
->name_table
,
1392 lpme
->szModule
[MAX_MODULE_NAME
] = '\0';
1393 lpme
->hModule
= lpme
->wNext
;
1394 lpme
->wcUsage
= pModule
->count
;
1395 strncpy( lpme
->szExePath
,
1396 ((LOADEDFILEINFO
*)((char*)pModule
+ pModule
->fileinfo
))->filename
,
1398 lpme
->szExePath
[MAX_PATH
] = '\0';
1399 lpme
->wNext
= pModule
->next
;
1404 /**********************************************************************
1405 * ModuleFindName (TOOLHELP.61)
1407 BOOL
ModuleFindName( MODULEENTRY
*lpme
, LPCSTR name
)
1409 lpme
->wNext
= GetModuleHandle( name
);
1410 return ModuleNext( lpme
);
1414 /**********************************************************************
1415 * ModuleFindHandle (TOOLHELP.62)
1417 BOOL
ModuleFindHandle( MODULEENTRY
*lpme
, HMODULE hModule
)
1419 hModule
= GetExePtr( hModule
); /* In case we were passed an hInstance */
1420 lpme
->wNext
= hModule
;
1421 return ModuleNext( lpme
);