[UPDATED 11-Sep-2000 - see change history in code]
[UPDATED 08-Jul-2000 - see change history in code]
[UPDATED 07-Jul-2000 - see change history in code]
Hi
I know that this issue has been thrashed out a few times but I've
got an application suite which consists of an executable and
any number of plugins (dlls) which are dynamically loaded
at the start.
I was not so concerned about load times but I was concerned that
with all my dlls based at the same address as the executable
(0x00400000) that this might increase page faults at run-time.
So I put together a rebase utility which also serves as a basic
example of using the VERY useful imagehlp.dll.
The utility accepts either a "batch file" consisting of names
of dlls to rebase or the name of an excutable or dll to rebase.
Additionally you can specify the start address on the command line
(see usage for details).
The way I use it is to rebase all dlls (not the executable) in my
application suite using a simple algorithm based on the first letter
of the dll. If there is a conflict with a couple of rebased dlls
I rebase the dll individually, specifying a new start address
offset.
Disclaimer: this code requires the imagehlp.dll which comes with
the SDK or WinNt. It's probably included with Win98 and is available
as a redistributable download for Win95 from Microsoft (don't know
the address). THIS CODE HAS ONLY BEEN TESTED ON NT - USE AT YOUR
OWN RISK. YMMV
[This message has been edited by Florent Heyworth (edited September 10, 2000).]
[UPDATED 08-Jul-2000 - see change history in code]
[UPDATED 07-Jul-2000 - see change history in code]
Hi
I know that this issue has been thrashed out a few times but I've
got an application suite which consists of an executable and
any number of plugins (dlls) which are dynamically loaded
at the start.
I was not so concerned about load times but I was concerned that
with all my dlls based at the same address as the executable
(0x00400000) that this might increase page faults at run-time.
So I put together a rebase utility which also serves as a basic
example of using the VERY useful imagehlp.dll.
The utility accepts either a "batch file" consisting of names
of dlls to rebase or the name of an excutable or dll to rebase.
Additionally you can specify the start address on the command line
(see usage for details).
The way I use it is to rebase all dlls (not the executable) in my
application suite using a simple algorithm based on the first letter
of the dll. If there is a conflict with a couple of rebased dlls
I rebase the dll individually, specifying a new start address
offset.
Disclaimer: this code requires the imagehlp.dll which comes with
the SDK or WinNt. It's probably included with Win98 and is available
as a redistributable download for Win95 from Microsoft (don't know
the address). THIS CODE HAS ONLY BEEN TESTED ON NT - USE AT YOUR
OWN RISK. YMMV
Code:
#IF 0 ************************************************************************************ * ' REBASE.BAS: A PB rebasing tool - reads a file comprised of a list of files * ' to rebase and attempts to rebase them according to a simple * ' algorithm based on filenames. It logs all successful rebases * ' to rebase.log - to view usage enter run rebase.exe without * ' parameters. * ' * ' Rebase.exe logs output to rebase.log * ' * ' The structure of the file passed should look like (1 line per file): * ' * ' c:\pbdll60\talkbox.dll * ' c:\pbdll60\helper.dll * ' c:\pbdll60\mem.dll * ' * ' Note : This file uses the imagehlp.dll to retrieve information and * ' rebase files. The imagehlp.dll comes with NT and probably * ' Win98 - it is available as a redistributable file for Win95 * ' * ' Written : Florent Heyworth * ' Date : 07-Jul-2000 * ' Last edit : 11-Sep-2000 * ' Version : 0.0.2 * ' * ' Change history (most recent change last): * ' + 07-Jul-2000: added test for location of input "batch" file * ' + 07-Jul-2000: added TRIM$() to command line option tests * ' + 08-Jul-2000: changed return for RebaseImage, MapAndLoad * ' to LONGs and GetFileTitle to INTEGER. The return types for * ' RebaseImage and MapAndLoad were defined as BOOL (LONG) and * ' not BOOLEAN (BYTE). This is for correctness - the functions * ' were working fine before * ' + 11-Sep-2000: update declaration for T_LOADED_IMAGE since the * ' tLinks entry was incorrectly defined. While the rebasing worked * ' fine the code didn't report the file size correctly due to the * ' invalid size of the T_LOADED_IMAGE.tLinks entry (redefined as a pointer) * ' to a tLinks structure ************************************************************************************ #ENDIF #IF NOT %DEF(%WINAPI) #INCLUDE "win32api.inc" #ENDIF %FROM_ADDR = &H60000000& 'Start of user application address space %MAX_ADDR = &H68000000& 'End of user application address space %MIN_ADDR = &H10000000& %INCR_BY = &H1000000& 'increment by 'START of imagehlp.dll declares (incomplete) %IMAGE_SIZEOF_SHORT_NAME = 8 %IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 %IMAGE_DOS_SIGNATURE = &H5A4D ' MZ %IMAGE_OS2_SIGNATURE = &H454E ' NE %IMAGE_OS2_SIGNATURE_LE = &H454C ' LE %IMAGE_VXD_SIGNATURE = &H454C ' LE %IMAGE_NT_SIGNATURE = &H00004550 ' PE00 %SYMNONE = 0 %SYMCOFF = 1 %SYSCV = 2 %SYMPDB = 3 %SYMEXPORT = 4 %SYMDEFERRED= 5 %SYSSYM = 6 %IMAGE_FILE_RELOCS_STRIPPED = &H0001 ' Relocation info stripped FROM file. %IMAGE_FILE_EXECUTABLE_IMAGE = &H0002 ' File is executable (i.e. no unresolved externel references). %IMAGE_FILE_LINE_NUMS_STRIPPED = &H0004 ' LINE nunbers stripped FROM file. %IMAGE_FILE_LOCAL_SYMS_STRIPPED = &H0008 ' LOCAL symbols stripped FROM file. %IMAGE_FILE_AGGRESIVE_WS_TRIM = &H0010 ' Agressively trim working SET %IMAGE_FILE_LARGE_ADDRESS_AWARE = &H0020 ' App can HANDLE >2gb addresses %IMAGE_FILE_BYTES_REVERSED_LO = &H0080 ' Bytes OF machine WORD are reversed. %IMAGE_FILE_32BIT_MACHINE = &H0100 ' 32 BIT WORD machine. %IMAGE_FILE_DEBUG_STRIPPED = &H0200 ' Debugging info stripped FROM file IN .DBG file %IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = &H0400 ' IF Image is ON removable media, copy AND run FROM the SWAP file. %IMAGE_FILE_NET_RUN_FROM_SWAP = &H0800 ' IF Image is ON Net, copy AND run FROM the SWAP file. %IMAGE_FILE_SYSTEM = &H1000 ' System File. %IMAGE_FILE_DLL = &H2000 ' File is a DLL. %IMAGE_FILE_UP_SYSTEM_ONLY = &H4000 ' File should only be run ON a UP machine %IMAGE_FILE_BYTES_REVERSED_HI = &H8000 ' Bytes OF machine WORD are reversed. %IMAGE_FILE_MACHINE_UNKNOWN = 0 %IMAGE_FILE_MACHINE_I386 = &H014c ' Intel 386. %IMAGE_FILE_MACHINE_R3000 = &H0162 ' MIPS little-endian, = &H160 big-endian %IMAGE_FILE_MACHINE_R4000 = &H0166 ' MIPS little-endian %IMAGE_FILE_MACHINE_R10000 = &H0168 ' MIPS little-endian %IMAGE_FILE_MACHINE_WCEMIPSV2 = &H0169 ' MIPS little-endian WCE v2 %IMAGE_FILE_MACHINE_ALPHA = &H0184 ' Alpha_AXP %IMAGE_FILE_MACHINE_POWERPC = &H01F0 ' IBM PowerPC Little-Endian %IMAGE_FILE_MACHINE_SH3 = &H01a2 ' SH3 little-endian %IMAGE_FILE_MACHINE_SH3E = &H01a4 ' SH3E little-endian %IMAGE_FILE_MACHINE_SH4 = &H01a6 ' SH4 little-endian %IMAGE_FILE_MACHINE_ARM = &H01c0 ' ARM Little-Endian %IMAGE_FILE_MACHINE_THUMB = &H01c2 %IMAGE_FILE_MACHINE_IA64 = &H0200 ' Intel 64 %IMAGE_FILE_MACHINE_MIPS16 = &H0266 ' MIPS %IMAGE_FILE_MACHINE_MIPSFPU = &H0366 ' MIPS %IMAGE_FILE_MACHINE_MIPSFPU16 = &H0466 ' MIPS %IMAGE_FILE_MACHINE_ALPHA64 = &H0284 ' ALPHA64 %IMAGE_FILE_MACHINE_AXP64 = %IMAGE_FILE_MACHINE_ALPHA64 TYPE T_IMAGEHLP_MODULE dwSizeOfStruct AS DWORD 'Set to SIZEOF(T_IMAGEHLP_MODULE) dwBaseOfImage AS DWORD dwImageSize AS DWORD dwCheckSum AS DWORD lSymType AS LONG szModuleName AS ASCIIZ * 32 szImageName AS ASCIIZ * %MAX_PATH szLoadedImageName AS ASCIIZ * %MAX_PATH END TYPE TYPE T_IMAGE_DATA_DIRECTORY dwVirtualAddress AS DWORD dwSize AS DWORD END TYPE TYPE T_IMAGE_OPTIONAL_HEADER32 'Standard fields wMagic AS WORD bMajorLinkerVersion AS BYTE bMinorLinkerVersion AS BYTE dwSizeOfCode AS DWORD dwSizeOfInitializedData AS DWORD dwSizeOfUninitializedData AS DWORD dwAddressOfEntryPoint AS DWORD dwBaseOfCode AS DWORD dwBaseOfData AS DWORD 'Nt optional fields dwImageBase AS DWORD dwSectionAlignement AS DWORD dwFileAlignment AS DWORD wMajorOperatingSystemVersion AS WORD wMinorOperatingSystemVersion AS WORD wMajorImageVersion AS WORD wMinorImageVersion AS WORD wMajorSubsystemVersion AS WORD wMinorSubsystemVersion AS WORD dwWin32VersionValue AS DWORD dwSizeOfImage AS DWORD dwSizeOfHeader AS DWORD dwCheckSum AS DWORD wSubsystem AS WORD wDllCharacteristics AS WORD dwSizeOfStackReserve AS DWORD dwSizeOfStackCommit AS DWORD dwSizeOfHeapReserve AS DWORD dwSizeOfHeapCommit AS DWORD dwLoaderFlags AS DWORD dwNumberOfRvaAndSizes AS DWORD tDataDirectory(%IMAGE_NUMBEROF_DIRECTORY_ENTRIES) AS T_IMAGE_DATA_DIRECTORY END TYPE TYPE T_IMAGE_FILE_HEADER wMachine AS WORD wNumberOfSections AS WORD dwTimeDateStamp AS DWORD dwPointerToSymbolsTable AS DWORD dwNumberOfSymbols AS DWORD wSizeOfOptionalHeader AS WORD wCharacteristics AS WORD END TYPE TYPE T_IMAGE_NT_HEADERS dwSignature AS DWORD tImageFileHeader AS T_IMAGE_FILE_HEADER tOptionalHeader AS T_IMAGE_OPTIONAL_HEADER32 END TYPE UNION U_VT_ADDRESS dwPhysicalAddress AS DWORD dwVirtualSize AS DWORD END UNION TYPE T_IMAGE_SECTION_HEADER pszName AS ASCIIZ PTR uMisc AS U_VT_ADDRESS dwVirtualAddress AS DWORD dwPointerToRawData AS DWORD dwPointerToRelocations AS DWORD dwNumberOfRelocations AS DWORD dwNumberOfLineNumbers AS DWORD dwCharacteristics AS DWORD END TYPE TYPE T_LIST_ENTRY tFlink AS T_LIST_ENTRY PTR tBlink AS T_LIST_ENTRY PTR END TYPE TYPE T_LOADED_IMAGE pszModuleName AS ASCIIZ PTR lFileHandle AS LONG dwMappedAddress AS DWORD tFileHeader AS T_IMAGE_NT_HEADERS PTR tLastRvaSection AS T_IMAGE_SECTION_HEADER PTR dwNumberOfSections AS DWORD tSections AS T_IMAGE_SECTION_HEADER PTR dwCharacteristics AS DWORD lfSystemImage AS LONG lfDOSImage AS LONG tLinks AS T_LIST_ENTRY PTR dwSizeOfImage AS DWORD END TYPE DECLARE FUNCTION ReBaseImage LIB "imagehlp.dll" ALIAS "ReBaseImage" ( szCurrentImageName AS ASCIIZ, _ szSymbolPath AS ASCIIZ, _ BYVAL bfRebase AS BYTE, _ BYVAL bfRebaseSysfileOk AS BYTE, _ BYVAL bfGoingDown AS BYTE, _ BYVAL dwCheckImageSize AS DWORD, _ BYVAL pdwOldImageSize AS DWORD, _ BYVAL pdwOldImageBase AS DWORD, _ BYVAL pdwNewImageSize AS DWORD, _ BYVAL pdwNewImageBase AS DWORD, _ BYVAL dwTimeStamp AS DWORD ) AS LONG DECLARE FUNCTION MapAndLoad LIB "imagehlp.dll" ALIAS "MapAndLoad" ( szImageName AS ASCIIZ, _ szDllPath AS ASCIIZ, _ tLoadedImage AS T_LOADED_IMAGE, _ BYVAL bfDotDll AS BYTE, _ BYVAL bfReadOnly AS BYTE ) AS LONG DECLARE FUNCTION UnMapAndLoad LIB "imagehlp.dll" ALIAS "UnMapAndLoad" ( tLoadedImage AS T_LOADED_IMAGE ) AS LONG 'END of imagehlp.dll declares (incomplete) 'not part of imagehlp.dll DECLARE FUNCTION GetFileTitle LIB "comdlg32.dll" ALIAS "GetFileTitleA" ( szFilePath AS ASCIIZ, _ szFileName AS ASCIIZ, _ BYVAL dwSize AS WORD ) AS INTEGER SUB tell ( szMessage AS ASCIIZ ) #IF %DEF(%pb_cc32) STDOUT szMessage #ELSEIF %DEF(%pb_dll32) IF LEN(szMessage) > 0 THEN MSGBOX szMessage END IF #ENDIF END SUB SUB logfile( BYVAL lFileHandle AS LONG, szMessage AS ASCIIZ ) PRINT #lFileHandle, szMessage END SUB FUNCTION rebase( BYVAL lFileHandle AS LONG, BYVAL dwFromAddr AS DWORD, szFilePath AS ASCIIZ ) AS LONG 'Returns %FALSE on failure, %TRUE if successful LOCAL lResult AS LONG LOCAL lHandle AS LONG LOCAL dwOldImageSize AS DWORD LOCAL dwOldImageBase AS DWORD LOCAL dwNewImageSize AS DWORD LOCAL dwNewImageBase AS DWORD LOCAL dwCompare AS DWORD LOCAL iResult AS INTEGER LOCAL sDebugInfo AS STRING LOCAL tLoadedImage AS T_LOADED_IMAGE LOCAL tFindData AS WIN32_FIND_DATA LOCAL szBuffer AS ASCIIZ * %MAX_PATH lHandle = FindFirstFile( szFilePath, tFindData ) IF lHandle = %INVALID_HANDLE_VALUE THEN CALL tell( "Could not locate " + szFilePath ) CALL logfile( lFileHandle, "Could not locate " + szFilePath ) FUNCTION = %FALSE EXIT FUNCTION END IF CALL FindClose( lHandle ) lResult = MapAndLoad( szFilePath, _ BYVAL %NULL, _ tLoadedImage, _ %FALSE, _ %TRUE ) IF ISFALSE(lResult) OR [email protected] <> %IMAGE_NT_SIGNATURE THEN CALL tell( szFilePath + " is not a valid PE file" ) FUNCTION = %FALSE GOTO Unload END IF CALL logfile( lFileHandle, STRING$( LEN([email protected]), "-" ) ) CALL logfile( lFileHandle, [email protected] + $CRLF + STRING$( LEN([email protected]), "-" ) ) CALL logfile( lFileHandle, "Load Address : 0x" + HEX$([email protected] , 8) ) CALL logfile( lFileHandle, "File Size : " + FORMAT$(tLoadedImage.dwSizeOfImage) ) CALL logfile( lFileHandle, "Timestamp : 0x" + HEX$([email protected], 8) ) CALL logfile( lFileHandle, "Checksum : 0x" + HEX$([email protected], 8) ) CALL logfile( lFileHandle, "Symbol count : " + FORMAT$([email protected]) ) 'Get image characteristics IF %IMAGE_FILE_DEBUG_STRIPPED = (%IMAGE_FILE_DEBUG_STRIPPED AND tLoadedImage.dwCharacteristics ) THEN sDebugInfo = "Yes" ELSE sDebugInfo = "No" END IF IF %IMAGE_FILE_32BIT_MACHINE = (%IMAGE_FILE_32BIT_MACHINE AND tLoadedImage.dwCharacteristics) THEN CALL logfile( lFileHandle, "32 Bit machine : Yes" ) END IF CALL logfile( lFileHandle, "File debug info stripped : " + sDebugInfo ) IF %IMAGE_FILE_DLL = (%IMAGE_FILE_DLL AND tLoadedImage.dwCharacteristics) THEN CALL logfile( lFileHandle, "File is a : Dll" ) END IF IF %IMAGE_FILE_EXECUTABLE_IMAGE = (%IMAGE_FILE_EXECUTABLE_IMAGE AND tLoadedImage.dwCharacteristics) THEN CALL logfile( lFileHandle, "File is executable : Yes") END IF 'Get file name from path iResult = GetFileTitle( szFilePath, szBuffer, SIZEOF(szBuffer) ) IF iResult <> 0 THEN CALL tell( "Could not retrieve file name for : " + szFilePath ) END IF IF dwFromAddr = 0 THEN dwFromaddr = %FROM_ADDR END IF SELECT CASE ASC(MID$(LCASE$(szBuffer), 1, 1)) CASE 97 TO 99 dwNewImageBase = dwFromaddr CASE 100 TO 102 dwNewImageBase = dwFromaddr + %INCR_BY * 1 CASE 103 TO 105 dwNewImageBase = dwFromaddr + %INCR_BY * 2 CASE 106 TO 108 dwNewImageBase = dwFromaddr + %INCR_BY * 3 CASE 109 TO 111 dwNewImageBase = dwFromaddr + %INCR_BY * 4 CASE 112 TO 114 dwNewImageBase = dwFromaddr + %INCR_BY * 5 CASE 115 TO 117 dwNewImageBase = dwFromaddr + %INCR_BY * 6 CASE 118 TO 120 dwNewImageBase = dwFromaddr + %INCR_BY * 7 CASE 121 TO 122 dwNewImageBase = dwFromaddr + %INCR_BY * 8 END SELECT dwCompare = dwNewImageBase 'Rebase the file lResult = ReBaseImage( szFilePath, _ BYVAL %NULL, _ %TRUE, _ %FALSE, _ %FALSE, _ 0???, _ VARPTR(dwOldImageSize), _ VARPTR(dwOldImageBase), _ VARPTR(dwNewImageSize), _ VARPTR(dwNewImageBase), _ [email protected] ) IF ISTRUE(lResult) AND dwOldImageBase <> dwCompare THEN 'unload and re-map to see effective changes as dwNewAddress returns an offset from asked for address CALL UnMapAndLoad( tLoadedImage ) lResult = MapAndLoad( szFilePath, _ BYVAL %NULL, _ tLoadedImage, _ %TRUE, _ %TRUE ) IF ISFALSE(lResult) OR [email protected] <> %IMAGE_NT_SIGNATURE THEN CALL tell( szFilePath + " is not a valid PE file" ) CALL logfile( lFileHandle, szFilePath + " is not a valid PE file" ) GOTO Unload END IF CALL logfile( lFileHandle, "Successfully rebased file: " + szBuffer ) CALL logfile( lFileHandle, "Old base address : 0x" + HEX$(dwOldImageBase, 8) ) CALL logfile( lFileHandle, "New base address : 0x" + HEX$([email protected], 8) ) FUNCTION = %TRUE ELSE CALL tell( "Could not rebase file: " + szBuffer ) CALL logfile( lFileHandle, "Could not rebase file: " + szBuffer ) IF dwOldImageBase = dwCompare THEN CALL tell( "Reason: " + szBuffer + " is already based at 0x" + HEX$(dwCompare, 8) ) CALL logfile( lFileHandle, "Reason: " + szBuffer + " is already based at 0x" + HEX$(dwCompare, 8) ) END IF FUNCTION = %FALSE END IF FLUSH #lFileHandle 'MUST UnMapAndLoad Unload: CALL UnMapAndLoad( tLoadedImage ) END FUNCTION SUB usage() CALL tell( "Valid parameters are: " + $CRLF _ + "-b<input_file_to_read_from> [-s<hex_start_address>]" + $CRLF _ + "-f<name_of_dll_or_exe> [-s<hex_start_address>]" + $CRLF _ + "as in : rebase.exe -brebase.txt -s12000000" + $CRLF _ + "or : rebase.exe -fmydll.dll" ) END SUB FUNCTION PBMAIN() AS LONG 'command line switches - batch input file -binput_file_path.txt or command line -fname_of_file_to_rebase 'as in rebase -btorebase.txt to read from a batch input file or -ftalkbox.dll 'accepts an optional switch -sbase_address_in_hex as in -s600000000 ' 'example calls: 'rebase -binput_file.txt -> attempts to rebase all files in input_file.txt - defaults to pre-defined range 'rebase -binput_file.txt -s600000000 -> ditto but starts at range specified in -s switch 'rebase -fname_of_dll_or_exe -> attempts to rebase file specified on command line 'rebase -fname_of_dll_or_exe -s600000000 -> ditto but starts at range specified in -s switch LOCAL lResult AS LONG LOCAL lHandle AS LONG LOCAL lReadHandle AS LONG LOCAL lFindHandle AS LONG LOCAL lParamCount AS LONG LOCAL lRebaseCount AS LONG LOCAL lBatch AS LONG LOCAL lValid AS LONG LOCAL i AS LONG LOCAL dwStartAddress AS DWORD LOCAL szFile AS ASCIIZ * %MAX_PATH LOCAL szBatchFile AS ASCIIZ * %MAX_PATH LOCAL sValue AS STRING LOCAL sStartAddress AS STRING LOCAL tFindData AS WIN32_FIND_DATA IF LEN(COMMAND$) = 0 THEN CALL tell( "No command line parameters were specified" + $CRLF ) CALL usage() EXIT FUNCTION END IF 'check command line parameters lParamCount = PARSECOUNT(COMMAND$, "-") IF lParamCount = 0 THEN CALL tell( "Invalid command line parameters" ) CALL usage() EXIT FUNCTION END IF FOR i = 1 TO lParamCount sValue = TRIM$(PARSE$(COMMAND$, "-", i)) SELECT CASE MID$(LCASE$(sValue), 1, 1) CASE "b" szFile = TRIM$(MID$( sValue, 2 )) lBatch = %TRUE IF lValid = %TRUE THEN lValid = %FALSE ELSE lValid = %TRUE END IF CASE "f" szFile = TRIM$(MID$( sValue, 2)) IF lValid = %TRUE THEN lValid = %FALSE ELSE lValid = %TRUE END IF CASE "s" sStartAddress = TRIM$(UCASE$(MID$(sValue, 2))) IF LEFT$(sStartAddress, 2) = "0X" OR LEFT$(sStartAddress, 2) = "&H" THEN sStartAddress = MID$( sStartAddress, 3 ) END IF dwStartAddress = VAL( "&H" + STRING$(8 -LEN(sStartAddress), "0") + sStartAddress ) IF dwStartAddress < %MIN_ADDR OR dwStartAddress > %MAX_ADDR THEN CALL tell( "Invalid start address - valid range is 0x" + HEX$(%MIN_ADDR) + " to 0x" + HEX$(%MAX_ADDR) ) EXIT FUNCTION END IF END SELECT NEXT IF ISFALSE( lValid ) THEN CALL tell( "Invalid or missing command line parameters" ) CALL usage() EXIT FUNCTION END IF 'open the log file lHandle = FREEFILE OPEN CURDIR$ + "\rebase.log" FOR OUTPUT AS lHandle IF ISTRUE( lBatch ) THEN lFindHandle = FindFirstFile( szFile, tFindData ) IF lFindHandle = %INVALID_HANDLE_VALUE THEN CALL tell( "Unable to locate input file " + szFile + " - exiting") EXIT FUNCTION END IF CALL FindClose( lFindHandle ) lReadHandle = FREEFILE OPEN szFile FOR INPUT AS lReadHandle DO WHILE ISFALSE( EOF(lReadHandle) ) LINE INPUT #lReadHandle, szBatchFile IF ISTRUE( rebase( lHandle, dwStartAddress, szBatchFile) ) THEN INCR lRebaseCount END IF szBatchFile = "" LOOP CLOSE #lReadHandle ELSE IF ISTRUE( rebase( lHandle, dwStartAddress, szFile ) ) THEN INCR lRebaseCount END IF END IF CLOSE #lHandle CALL tell( "" ) CALL tell( "Successfully rebased: " + FORMAT$(lRebaseCount) + " files. See rebase.log for details" ) END FUNCTION
[This message has been edited by Florent Heyworth (edited September 10, 2000).]
Comment