;----------------------------------------------------------------------------- ; ; SAMPLE code for WINMEM32 DLL ; ;----------------------------------------------------------------------------- .386p memS equ 1 .xlist include cmacros.inc ; ; NOTE that we CANNOT use the normal CMACROS segment macros: ; ; CreateSeg ; sBegin ; sEnd ; ; because since we are .386p the default segment type is USE32. Our segments ; need to be USE16 so we have to declare our segements manually so that the ; USE16 segment attribute is included. ; include windows.inc .list ; ; These equates would normally be in an app specific include file ; error_bad_file EQU 08001h error_wrong_mode EQU 08002h ;----------------------------------------------------------------------------- ; ; External WINMEM32 Procedures ; externFP GetWinMem32Version externFP Global32Alloc externFP Global32Realloc externFP Global32Free externFP Global16PointerAlloc externFP Global16PointerFree externFP Global32CodeAlias externFP Global32CodeAliasFree ;----------------------------------------------------------------------------- ; ; External Windows Procedures ; externFP OpenFile externFP GetWinFlags externFP _llseek externFP _lread externFP _lclose externFP OemToAnsi ; ; MANUAL VERSION OF: createSeg _HELPERCODE,hcode,word,public,CODE ; ; NOTE that this segment MUST NOT BE DISCARDABLE, it should be fixed. ; This is because the segment is called by USE32 code. ; _HELPERCODE segment word public 'CODE' use16 _HELPERCODE ends ; ; MANUAL VERSION of the automatic data segment declaration ; _DATA segment word public 'DATA' use16 _DATA ends _DATA segment use16 globalD AddrOEMToANSI,0 ; Address of OEMToANSI helper function globalD AddrDOSGetFreeSpace,0 ; Address of DOS Get disk Free space ; helper function globalD U32RetVal,0 ; Return code from USE32 call globalD U16StackAlias,0 ; Alias for the stack globalD EntryStackSave,0 ; stack ptr save location ; ; This FWORD forms the entry point for the USE32 code ; U32EntryPt LABEL FWORD globalD U32EntOff,00010000h ; Entry assumed at offset 64K globalW U32CodeSel,0 ; CODE alias for the BIG object globalW U32DataSel,0 ; DATA selector for the BIG object _DATA ends _HELPERCODE segment use16 assume cs:_HELPERCODE ;***************************************************************************** ; ; SetupCallUSE32 ; ; SetupCallUSE32(fName) ; ; Setup and call into USE32 code ; ; ASSUMPTIONS: ; The USE32 Image is a 0 ORGed 32 bit code image with NO HEADER. ; The first 64k of the image (offsets 00000000-0000FFFFh) is reserved ; for the stack. We put the stack here so that the required stack ; switching (USE32<->USE16) is simply a matter of changing SS. ; ; The entry point of the USE32 code is assumed to be right after the ; stack at offset 00010000h in the image. We enter with DS, FS, GS ; and SS set to the FLAT data segment, and CS set to the flat code ; segment. It is the responsibility of the USE32 entry point to set ; ES AND PRESERVE IT FOR US. ; ; When this code wishes to call the two provided USE16 helper routines, ; it looks up the call addresses in the AddrOEMToANSI and ; AddrDOSGetFreeSpace variables in the _DATA segment. ; This "loader" code actually needs to pass the selector for the ; _DATA segment to the USE32 code so that it can access the data ; segment, or it needs to copy the call addresses into the USE32 ; code/data segment. This detail of the implementation is NOT ; included in this code. ; ; ENTRY: ; FName - DWORD pointer to file name of USE32 image to load ; ; EXIT: ; AX != 0 If an error occurs ; AX = error code ; Else ; AX = 0 and U32RetVal contains the return code from the ; USE32 code. ; USES: ; C Standard ; ;***************************************************************************** cProc StartupCallUSE32,, ParmD fName LocalD fSize ; Size of file LocalD U16RdAlias ; Alias for reading image LocalD FileOff ; Current file offset for read LocalW fHand ; File handle LocalV OpnBuf, ; Open file struct for openfile call cBegin assume ds:_DATA assume es:nothing assume ss:_DATA ; ; First check if we are running in enhanced mode ; ; NOTE THAT SINCE WE DO NOT KNOW AS YET WHAT MODE WE ARE IN WE NEED ; TO AVOID USING 386 SPECIFIC INSTRUCTIONS ; cCall GetWinFlags and ax,WF_PMODE + WF_ENHANCED cmp ax,WF_PMODE + WF_ENHANCED je short OKtoLoad ; MUST BE SHORT mov ax,error_wrong_mode jmp Done1 ; ; We now know we are in the proper mode and that 386 instructions ; are now OK. ; OKtoLoad: ; ; Set up the addresses for the USE32 code to call the helper routines ; mov ax,cs mov word ptr [AddrOEMToANSI+2],ax mov word ptr [AddrOEMToANSI],offset _HELPERCODE:U32OEMtoANSI mov word ptr [AddrDOSGetFreeSpace+2],ax mov word ptr [AddrDOSGetFreeSpace],offset _HELPERCODE:U32GetDskFree ; ; Open the file ; lea bx,OpnBuf regptr ssbx,ss,bx cCall OpenFile, cmp ax,-1 ; Did we find it? je Done1FlErr ; No, file error mov fHand,ax ; Save file handle ; ; Get file size ; cCall _llseek, shl edx,16 mov dx,ax inc edx jz Done1FlErr ; seek failed, file error dec edx mov fSize,edx cmp edx,10000h ; Image is at least 64k? jbe Done1FlErr ; No, size is too small, file error ; ; Move file pointer back to start of file for read ; cCall _llseek, ; ; Allocate big USE32 object ; mov si,dataOffset U32DataSel regptr Selpt,ds,si cCall Global32Alloc, or ax,ax ; Worked? jnz FcloseEr ; No, return WINMEM32 error code ; ; Allocate USE16 stack alias for first 64K of object ; mov si,dataOffset U16StackAlias regptr Alipt,ds,si mov ecx,00010000h ; 64K cCall Global16PointerAlloc,<[U32DataSel],0,0,Alipt,ecx,0> or ax,ax ; Worked? jnz AliasErrF3 ; No, return WINMEM32 error code ; ; Allocate USE32 code alias ; mov si,dataOffset U32CodeSel regptr Alipt,ds,si cCall Global32CodeAlias,<[U32DataSel],Alipt,0> or ax,ax ; Worked? jnz AliasErrF2 ; No, return WINMEM32 error code ; ; Now read in the image. We will do this in 32K hunks. ; mov FileOff,0 ; Starting at file offset 0 ReadLp: mov ecx,00008000h ; 32k cmp ecx,fSize jbe short Read32k mov ecx,fSize Read32k: ; ; Make a USE16 alias for this region of the object ; push ecx lea si,U16RdAlias regptr Alipt,ss,si cCall Global16PointerAlloc,<[U32DataSel],FileOff,Alipt,ecx,0> pop ecx or ax,ax jnz short AliasErrF1 push ecx cCall _lread, push ax cCall Global16PointerFree,<[U32DataSel],U16RdAlias,0> pop ax pop ecx inc ax jz short FlRdErr dec ax cmp ax,cx jne short FlRdErr add FileOff,ecx sub fSize,ecx ja short ReadLp ; ; We are now ready to set up and call into the USE32 code ; ; Save the current stack so we can switch to the USE32 stack ; ; NOTE CAREFULLY THAT THIS MAKES THIS ROUTINE NON-REENTRANT ; SINCE IT SAVES THE CURRENT SS:SP IN A STATIC MEMORY LOCATION. ; mov word ptr [EntryStackSave],sp mov word ptr [EntryStackSave+2],ss mov ax,[U32DataSel] push ds pop es assume es:_DATA ; ; Set up all the segs, and call into USE32 ; ; NOTE that we just leave the file open across the call. ; mov ds,ax assume ds:nothing mov fs,ax mov gs,ax mov ss,ax assume ss:nothing mov esp,0000FFFCh call [U32EntryPt] ; ; Recover DS and stack. ; mov bx,es mov ds,bx assume ds:_DATA mov ss,word ptr [EntryStackSave+2] assume ss:_DATA mov sp,word ptr [EntryStackSave] ; ; Set success return and clean up. ; mov [U32RetVal],eax xor ax,ax ; Return success jmp short AliasErrF1 FlRdErr: mov ax,error_bad_file AliasErrF1: ; ; Free USE32 code alias ; push ax ; Save error code cCall Global32CodeAliasFree,<[U32DataSel],[U32CodeSel],0> pop ax AliasErrF2: ; ; Free USE16 stack alias ; push ax ; Save error code cCall Global16PointerFree,<[U32DataSel],[U16StackAlias],0> pop ax AliasErrF3: ; ; Free the object ; push ax ; Save error code cCall Global32Free,<[U32DataSel],0> pop ax FcloseEr: ; ; Close the file ; push ax ; Save error code cCall _lclose, pop ax jmp short Done1 Done1FlErr: mov ax,error_bad_file Done1: cEnd ;***************************************************************************** ; ; U32OEMtoANSI - Call OemToANSI from USE32 segment ; ; Assumes PASCAL calling convention ; ; ENTRY: ; U32OEMtoANSI(lpOemStr,lpAnsiStr) ; ; NOTE that these pointer arguments are NOT really LPSTRs. They ; are near pointers into the USE32 data object (implied segment ; is U32DataSel) ; ; EXIT: ; EAX is return code ; ; USES: ; 32 bit C Standard ; ; ;***************************************************************************** PUBLIC U32OEMtoANSI U32OEMtoANSI proc far assume ds:nothing assume es:nothing assume ss:nothing ; ; First switch to the USE16 stack ; mov cx,ds ; Save entry DS in cx till we get the stack switched mov ax,SEG _DATA mov ds,ax assume ds:_DATA mov ss,word ptr [U16StackAlias+2] push ecx ; Entry DS, as DWORD to keep stack aligned push ebp mov bp,sp ; ; Frame now looks like this: ; ; dword ptr [bp + 20] --> First arg to OEMToANSI lpOemStr (actually a 32 bit near pointer) ; dword ptr [bp + 16] --> Second arg to OEMToANSI lpAnsiStr (actually a 32 bit near pointer) ; dword ptr [bp + 12] --> Return CS ; dword ptr [bp + 8] --> Return EIP ; dword ptr [bp + 4] --> Entry DS pushed as DWORD ; dword ptr [bp + 0] --> Entry EBP ; lpOemStr equ dword ptr [bp+20] lpAnsiStr equ dword ptr [bp+16] sub sp,8 ; Need two LPSTRs for the aliases AlsOemStr equ dword ptr [bp-4] ; Alias for lpOemStr AlsAnsiStr equ dword ptr [bp-8] ; Alias for lpAnsiStr push esi push edi push ebx push es ; Preserve "flat" ES, FS, GS push fs push gs ; ; There is a ?, how BIG is lpOemStr? Need to know this to set the ; size of the alias(s). What we will do is "cheat". We will set ; the size to 64k (or size to end of USE32 object, whichever is ; smaller). NOTE that this assumes that the string is <= 64K which ; is a reasonable assumption since we can't alias something larger ; than that anyway. ; lsl eax,dword ptr [U32DataSel] ; Get limit of USE32 object inc eax ; Limit -> size mov edx,eax sub eax,lpOemStr ; Number of bytes to end of USE32 object jc SkipCall ; Bad string ptr sub edx,lpAnsiStr ; Number of bytes to end of USE32 object jc short SkipCall ; Bad string ptr cmp eax,edx jbe short UseSrcLim mov eax,edx ; lpAnsiStr is closer to end of object UseSrcLim: mov ecx,00010000h ; 64k cmp ecx,eax jbe short Use64k mov ecx,eax ; Limited by size to end of object Use64k: ; ; Create Alias for lpOemStr ; push ecx lea bx,AlsOemStr regptr AlsPt,ss,bx cCall Global16PointerAlloc,<[U32DataSel],lpOemStr,AlsPt,ecx,0> pop ecx or ax,ax jnz short SkipCall ; ; Create Alias for lpAnsiStr ; lea bx,AlsAnsiStr cCall Global16PointerAlloc,<[U32DataSel],lpAnsiStr,AlsPt,ecx,0> or ax,ax jnz short FreeOemAls ; ; Call OemToAnsi ; cCall OemToAnsi, ; ; Free the aliases ; push ax ; Save RET code cCall Global16PointerFree,<[U32DataSel],AlsAnsiStr,0> pop ax ; Restore RET code FreeOemAls: push ax ; Save RET code cCall Global16PointerFree,<[U32DataSel],AlsOemStr,0> pop ax ; Restore RET code SkipCall: pop gs pop fs pop es pop ebx pop edi pop esi add sp,8 pop ebp pop ecx ; Entry DS in CX ; ; Sign extend the return to make it 32 bit ; movsx eax,ax ; ; Switch back to the USE32 stack MAKING SURE TO SET HIGH 16 BITS OF ESP. ; mov ss,[U32DataSel] movzx esp,sp mov ds,cx assume ds:nothing db 66h ; USE32 override on far ret so it returns to EIP ret (2 * 4) U32OEMtoANSI endp ;***************************************************************************** ; ; U32GetDskFree - Issue DOS call to get disk free space ; ; Assumes PASCAL calling convention ; ; ENTRY: ; U32GetDskFree(drvnum) ; ; EXIT: ; EAX = Disk free space in bytes ; EAX == 0FFFFFFFFh if error ; ; USES: ; 32 bit C Standard ; ; ;***************************************************************************** PUBLIC U32GetDskFree U32GetDskFree proc far assume ds:nothing assume es:nothing assume ss:nothing ; ; First switch to the USE16 stack ; mov cx,ds ; Save entry DS in cx till we get the stack switched mov ax,SEG _DATA mov ds,ax assume ds:_DATA mov ss,word ptr [U16StackAlias+2] push ecx ; Entry DS, as DWORD to keep stack aligned push ebp mov bp,sp ; ; Frame now looks like this: ; ; dword ptr [bp + 16] --> Drive # argument (0 = default, A = 1 ...) ; dword ptr [bp + 12] --> Return CS ; dword ptr [bp + 8] --> Return EIP ; dword ptr [bp + 4] --> Entry DS pushed as DWORD ; dword ptr [bp + 0] --> Entry EBP ; ArgDrv equ dword ptr [bp+16] push esi push edi push ebx push es ; Preserve "flat" ES, FS, GS push fs push gs mov edx,ArgDrv ; Drive # to DL mov ah,36h int 21h ; Make DOS call movsx eax,ax ; Sign extend AX for error cmp ax,0FFFFh ; Error? je short BadDrv ; Yes, return 0FFFFFFFFh movzx eax,ax ; Convert sectors/cluster to 32 bit movzx ebx,bx ; Convert Available clusters to 32 bit movzx ecx,cx ; Convert bytes/sector to 32 bit mul ecx ; EAX = sectors/cluster * bytes/sector = ; bytes/cluster mul ebx ; EAX = bytes/cluster * Available clusters = ; free bytes BadDrv: pop gs pop fs pop es pop ebx pop edi pop esi pop ebp pop ecx ; Entry DS in CX ; ; Switch back to the USE32 stack MAKING SURE TO SET HIGH 16 BITS OF ESP. ; mov ss,[U32DataSel] movzx esp,sp mov ds,cx assume ds:nothing db 66h ; USE32 override on far ret so it returns to EIP ret (1 * 4) U32GetDskFree endp _HELPERCODE ends end