Welcome to MSDN Blogs Sign in | Join | Help

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