Find which DLLs in your system are rebased
You can use CreateToolhelp32Snapshot and its family of functions to enumerate the running processes on your machine, including the modules loaded by each process
My prior post (DLL Image base addresses are the same in XP, different on Vista) described how Dlls are loaded and how some can be rebased, causing more memory use and a decrease in performance.
The code in Create your own Flip Task Bar with live thumbnails using Vista Desktop Window Manager DWM includes a reusable class called CEnumWIndows which inherits from another reusable class CAsmLib.
CAsmLib contains some basic utility routines to generate assembly code that can be executed. Specifically, it includes a routine to call an export from a DLL given the function and DLL names, and some routines to handle string manipulation and memory allocation.
CEnumWindows generates Asm code that can be called.
On Vista, the CThumb class (inherits from CEnumWindows) creates live thumbnails on a form. The Vista code is commented out below.
On Win XP, the CModules class (inherits from CEnumWindows) calls some of the CreateToolhelp32Snapshot functions to identify running processes and their modules.
It’s used to run a few SQL queries to create a few cursors that can be examined.
- hWnds: a list of all windows currently created along with their titles, if any.
- ModuleCount: A list of all modules currently loaded into all processes along with how many times they’re loaded: kernel32.dll is loaded into each process.
- MultiReloc: list those modules that are loaded at different base addresses: this means that they the loader has to fixup addresses in them and that they take more memory due to dirty write pages.
On my Win XP machine, I had 58 processes with 1510 modules and 44 total rebases. Normaliz.Dll (Unicode Normalization DLL) is rebased to 8 different addresses in 8 processes.
On my tablet with Win XP, 90 processes with 2199 modules and 98 total rebases. nbmatip.dll is rebased 17 times in 17 processes, and xpsp2res.dll 13 times in 25 processes!
On one of my Vista machines, (running as Admin so as to get Admin processes) 60 processes and 1264 modules with only 4 total modules rebased.
Of course, a better comparison between machines would be to have the same processes running on them.
Now the challenge: write a program that finds all the available virtual address space from each process and see if there’s enough room to rebase the multireloc DLLs to improve performance! Each process has 4 gigs of address space (of which the lower 2 gigs are available (unless you’re using Physical Address Extension (PAE) or the /3GB switch in boot.ini). You have the information required: tables of processes, modules, their image size and default base address.
See also:
Write your own Task Manager
Find all statically linked libraries required before your process can start
How to log application API calls using import module addresses
What external code does your EXE depend on?
Under the Hood: Optimizing DLL Load Time Performance -- MSDN Magazine ...
SET SAFETY OFF
CLEAR ALL
CLEAR
MODIFY COMMAND PROGRAM() NOWAIT
PUBLIC oForm
#iF .f.
oForm=CREATEOBJECT("CThumbForm") && the DWM thumbs for Vista: see
&& http://blogs.msdn.com/calvin_hsia/archive/2007/05/05/create-your-own-flip-task-bar-with-live-thumbnails-using-vista-desktop-window-manager-dwm.aspx
#ELSE
oForm=CREATEOBJECT("cModules")
SELECT hWnd,title,hwnds.pid,file from hwnds INNER JOIN procs ON hwnds.pid = procs.pid ORDER BY hwnds.pid INTO CURSOR hWnds
BROWSE LAST NOWAIT
SELECT FILE,baseaddress,count(*) from modules GROUP BY file,baseaddress INTO cursor ModuleCount
BROWSE LAST NOWAIT
SELECT file,count(*) as nRebases from ModuleCount GROUP BY file HAVING nRebases > 1 INTO CURSOR multireloc
BROWSE LAST NOWAIT
CALCULATE SUM(nRebases) TO nTotalRebases
?"Total # of processes = ",RECCOUNT("procs")
?"Total # of modules = " , RECCOUNT("modules")
?"Total # of Rebases = ",nTotalRebases
#define TH32CS_SNAPHEAPLIST 0x00000001
#define TH32CS_SNAPPROCESS 0x00000002
#define TH32CS_SNAPTHREAD 0x00000004
#define TH32CS_SNAPMODULE 0x00000008
#define TH32CS_SNAPMODULE32 0x00000010
#define TH32CS_SNAPALL (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
#define TH32CS_INHERIT 0x80000000
#define INVALID_HANDLE_VALUE -1
DEFINE CLASS CModules as CEnumWindows
PROCEDURE Init
DODEFAULT()
DECLARE integer CreateToolhelp32Snapshot IN WIN32API integer dwFlags, integer pid
DECLARE integer CloseHandle IN WIN32API integer handle
DECLARE integer Process32First IN WIN32API integer hSnap, string @ pe32
DECLARE integer Process32Next IN WIN32API integer hSnap, string @ pe32
DECLARE integer Module32First IN WIN32API integer hSnap, string @ me32
DECLARE integer Module32Next IN WIN32API integer hSnap, string @ me32
CREATE CURSOR procs (pid n, file c(100), ParentPid n, ThreadCount n)
CREATE CURSOR modules (pid n, file c(100), baseaddress c(12),size n)
this.GetProcesses()
PROTECTED PROCEDURE GetProcesses()
LOCAL fContinue,hSnapProc,dwSize,pe32,nPid,cFile
hSnapProc=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS ,0)
IF hSnapProc != INVALID_HANDLE_VALUE
dwSize= 10*4+260
pe32 = SPACE(dwSize)
fContinue =Process32First(hSnapProc,@pe32)
DO WHILE fContinue>0
nPid=CTOBIN(SUBSTR(pe32,2*4+1,4),"4rs")
nThreads=CTOBIN(SUBSTR(pe32,5*4+1,4),"4rs")
nParentPid=CTOBIN(SUBSTR(pe32,6*4+1,4),"4rs")
cFile=SUBSTR(pe32,10*4-4+1)
cFile=JUSTSTEM(LEFT(cFile,AT(CHR(0),cFile)-1))
INSERT INTO procs (pid,file,ParentPid, ThreadCount) VALUES (nPid,cFile,nParentPid,nThreads)
* ?nPid,nThreads,nParentPid,LEFT(cFile,20)
IF nPid >0 && indicates do current process, and we don't want to do it twice
this.GetModules(nPid)
ENDIF
pe32 = SPACE(dwSize)
fContinue=Process32Next(hSnapProc,@pe32)
ENDDO
CloseHandle(hSnapProc)
ENDIF
* INDEX ON pid TAG pid && if you want it a little faster
PROTECTED PROCEDURE GetModules(npid as Integer)
LOCAL fContinue,hSnapMod,dwSize,me32,cFile
hSnapMod=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE ,npid)
IF hSnapMod != INVALID_HANDLE_VALUE
dwSize= 8*4 + 255 + 1 + 260 && see TlHelp32.h
me32 = SPACE(dwSize)
fContinue =Module32First(hSnapMod,@me32)
DO WHILE fContinue>0
* nPid=CTOBIN(SUBSTR(me32,2*4+1,4),"4rs")
cFile=SUBSTR(me32,9*4-4+1)
cFile=LEFT(cFile,AT(CHR(0),cFile)-1)
nBase=CTOBIN(SUBSTR(me32,5*4+1,4),"4rs")
nSize=CTOBIN(SUBSTR(me32,6*4+1,4),"4rs")
* ?nPid,TRANSFORM(nBase,"@0x"),TRANSFORM(nSize,"@0x"),cFile
INSERT INTO modules (pid,file,baseaddress,size) VALUES (nPid,cFile,TRANSFORM(nBase,"@0x"),nSize)
me32 = SPACE(dwSize)
fContinue=Module32Next(hSnapMod,@me32)
ENDDO
CloseHandle(hSnapMod)
ENDIF
ENDDEFINE
#ENDIF
#define WS_VISIBLE 0x10000000
#define WS_BORDER 0x00800000
DEFINE CLASS CThumbForm as Form
ShowWindow=2 && Top Level
width=SYSMETRIC(1) && entire width of display
height=100
MinButton=.f. && don't allow minimize for us
nThumbs=0 && number of thumbs currently on form
fDWM = .f. && are we running under Vista Desktop Window Management?
nThumbWidth=400 && size of thumb to draw
nThumbHeight=this.nThumbWidth * SYSMETRIC(2)/SYSMETRIC(1) && same aspect ratio as desktop
ADD OBJECT cmdQuit as CommandButton WITH Caption="\<Quit",cancel=.t.
ADD OBJECT cmdRefresh as CommandButton WITH Caption="\<Refresh",left=120
ADD OBJECT oSlider as cSlider WITH left=250
ADD OBJECT oTimer as Timer WITH interval=2000 && millisecs
PROCEDURE oTimer.Timer
thisform.GetHWnds("NewTopLevel")
SELECT hWnd,Title FROM NewTopLevel WHERE hWnd NOT in (SELECT hWnd FROM TopLevel) UNION ;
SELECT hWnd,Title FROM TopLevel WHERE hWnd NOT in (SELECT hWnd FROM NewTopLevel) ;
INTO CURSOR temp
IF _Tally>0 && a new window was created or destroyed
thisform.GetThumbNails() && ToDo: optimize for only the change
ENDIF
PROCEDURE cmdRefresh.Click
thisform.GetThumbNails()
PROCEDURE cmdQuit.Click
thisform.release
PROCEDURE Init
SET TALK OFF
IF VAL(OS(3))>=6 && runnning under Vista. Check for Desktop Composition enabled
DECLARE integer DwmIsCompositionEnabled IN dwmapi integer @ dwEnabled
dwEnabled=0
IF DwmIsCompositionEnabled(@dwEnabled) = 0 AND dwEnabled>0
this.fDWM = .t.
ENDIF
ENDIF
IF this.fDWM
DECLARE integer DwmRegisterThumbnail IN dwmapi integer hwndDest, integer hwndSrc, integer @ nThumbnailId
DECLARE integer DwmUnregisterThumbnail IN dwmapi integer nThumbnailId
DECLARE integer DwmQueryThumbnailSourceSize IN dwmapi integer nThumbnailId, string @pSize
DECLARE integer DwmUpdateThumbnailProperties IN dwmapi integer hThumbnailId, string @ ptnProperties
DECLARE integer SetForegroundWindow IN WIN32API integer
DECLARE integer GetWindowPlacement IN WIN32API integer hWnd, string @ pPlacement
DECLARE integer SetWindowPlacement IN WIN32API integer hWnd, string @ pPlacement
this.Visible=1
* &nb