Windows脚本编程核心技术精解Chapter12.pdf
Chapter 12Gaining Full Control Over Any WindowIn This Chapter?Access any window directly using its handle?Retrieve any windows class name for safe identification?Bring a window safely into the foreground?Make windows invisible and change window style dynamically?Flash a windows title bar?Capture window content and save it to disk?Explore window internals and walking window hierarchiesWindow handles are the key to manipulating any window.In this chapter,you learn many new methods for solving common problems.Amongthe many new solutions,your scripts will be able to switch windows to theforeground and capture entire window contents.Full Control Over Any WindowWindows are all over the placethey resemble universe containers for yourprograms and play an important role in reserving space.Surprisingly enough,the Scripting Host doesnt offer any window support at all.Microsoft didntinclude any methods to control the size of windows,move them around,or bring them into the foreground.Fortunately,the Windows API contains numerous functions to remote controlany window.To access these features,all you need to do is open the COMproject I have designed for you:componentswinmanagerwinmanager.vbp.Even easier,you can install the prepared software package:installwinmanagersetup.exe.4684-8 ch12.f.qc 3/3/00 9:38 AM Page 367Accessing any windowThe first step into the window world is accessing individual windows.Windowsadministers individual windows similar to processes.Each window has itsindividual ID,the window handle.Its just a number.You have numerous ways to find out the window handle of any window.Accessing a window by nameIf you know the name of the window you want to access,use GetWindow-Handle.The following example asks you for a window name.Type in anywindow name,but take care to type in the window name exactly as it appears in the windows title bar.The script returns the window class(see Figure 12-1).12-1.VBSset tool=CreateObject(“window.manager”)winname=InputBox(“Please enter the window name as it appears in”_&“its title bar!”)handle=tool.GetWindowHandle(winname)if handle=0 thenMsgBox“Couldnt find a window named“”&winname&“”elseclassname=tool.GetWindowClassName(handle)msg=“Window“”&winname&“”ID“&handle&vbCrmsg=msg&“is of class“”&classname&“”MsgBox msgend ifFigure 12-1:Script returns window handle and class name.It worked.Your script will retrieve the window handle and the window class,as shown in Figure 12-1.In addition to the window handle,Windows groups each window by kind.Each application can assign a class name to its windows.On Windows 2000,for example,each Explorer window carries the“CabinetWClass”class tag.Your script can find out the class of any window.Youll see a little later in the chapter how the class name can help identify the right window.368Part III:Starting and Controlling Other SoftwareII4684-8 ch12.f.qc 3/3/00 9:38 AM Page 368Accessing window by class nameAccessing a window by name is not very safe.For example,the applicationmight display the document name in its title bar,so the window name dependson the kind of document opened by the application.Class names can help.Lets assume you want to get access to a Notepadeditor window.Each Notepad window uses“notepad”as class name.Theclass name never changes,no matter which document the editor has loaded.The next script finds out if an editor window is open and displays its name:12-2.VBSset tool=CreateObject(“window.manager”)handle=tool.GetWindowHandleFromClass(“notepad”)if handle=0 thenMsgBox“Currently,no editor window open!”elseMsgBox“Notepad title:“&tool.GetWindowName(handle)end ifClass is a reserved VBScript key word.Dont use it as a variable name.I did,and I regretted it.Accessing a window by class name and titleTo safely identify a window,you can even provide both pieces of information.Retrieve the handle by window name and class name.This way,you wont geta window of unexpected type just because it may share the same name.Use GetWindowHandleand supply the class name as second argument.Accessing the foreground windowThe foreground window is especially important.Its the window that receivesthe keyboard and mouse events,and your SendKeysmethod sends keystrokesto this window.Finding out the foreground window is especially useful.For example,you canautomatically“hook”to a program window your script launched:12-3.VBSset tool=CreateObject(“window.manager”)set wshshell=CreateObject(“WScript.Shell”)launch editorwshshell.Run“notepad.exe”wait until window really is in foregrounddohandle=tool.GetForegroundWindowHandle find out window classclassname=lcase(tool.GetWindowClassName(handle)is right window in foreground?Chapter 12:Gaining Full Control Over Any Window369II4684-8 ch12.f.qc 3/3/00 9:38 AM Page 369if not classname=“notepad”then wait 1 secWScript.Sleep 1000counter=counter+1 waited 5 secs,ask whats going on!if counter5 thenMsgBox classnameresponse=MsgBox(“Please click notepad to”_&“foreground!”,vbOkCancel)if response=vbCancel then WScript.Quitcounter=0end ifelseok=trueend ifloop until okMsgBox“Notepad in foreground:Handle“&handleWhen comparing the class name,always use lcaseto convert it to lowercaseletters and compare it to lowercase names.This way,the class name becomescase-insensitive.Wondering which class name a specific window uses?Checkout the scripts above to retrieve the class name.Class names never change,and once you know the class name of your application,you are set forever.The script demonstrates a solution to a common problem.Sometimes,the window you launch from your script doesnt jump into the foreground.Instead,it just nervously flashes its button in the task bar.The reason for thisodd behavior was already explained in Chapter 10.The script works aroundit by checking the foreground window class and asking the user to manuallybring the window to the foreground.Its not very elegant,but since you dontknow the window handle of your launched window,you cant get around it.There are better ways,thoughread on!Accessing a launched windowTheres a better solution!Just use the custom Runcommand you developedin the previous chapter!It returns the ProcessID,and with the help of theProcessID,you can retrieve the associated window handle.As a side effect,you now have a safe way to determine when the window has initialized itself.12-4.VBSset proc=CreateObject(“process.id”)set win=CreateObject(“window.manager”)procID=proc.Run(“notepad.exe”)if procID=0 thenMsgBox“Error launching the program.”WScript.Quitend if wait for the program to initializedohandle=win.GetWindowFromProcess(procID)370Part III:Starting and Controlling Other SoftwareII4684-8 ch12.f.qc 3/3/00 9:38 AM Page 370 wait if window isnt on screen yetif handle=0 thencounter=counter+1WScript.Sleep 200 wait 10 secs maxif counter50 thenMsgBox“Your software didnt launch!”WScript.Quitend ifend ifloop while handle=0MsgBox“Editor launched:ID“&handle,vbSystemModalMsgBox“Bury it under some window so I can bring it up-front!”,_vbSystemModal bring window to frontwin.ActivateWindow handleThis script does a number of surprising things.First,it uses your custom Runmethod to retrieve the ProcessID of the program you launched.Next,it loops and waits for GetWindowFromProcessto return the window handle.The moment GetWindowFromProcessreturns a valid handle(non-zero),yourscript knows the program is visible on-screen.It outputs the window handle,as shown by Figure 12-2.Figure 12-2:Check whether or not a program launched,retrieve its window handle,andbring it to the front.GetWindowFromProcessis a complicated method.It must enumerate allrunning windows to find the one with the specified ProcessID.This is why theloop uses a delay of 200 milliseconds between the checks.Without the delay,the loop would eat up way too much CPU power.Bringing a window to the foregroundThe script can do even more.It also demonstrates how to safely bring awindow up-front.Once the script asks you to bury the editor window,clicksome other window so that the editor gets covered.The dialog box will stillbe visible because of the vbSystemModaloption.Click OK.The script usesActivateWindowin conjunction with the window handle to bring your editorwindow in front.From now on,you can make sure a program window really is visible.Chapter 12:Gaining Full Control Over Any Window371II4684-8 ch12.f.qc 3/3/00 9:38 AM Page 371ActivateWindowworks on all Windows versions because internally ittemporarily turns off focus lock.Without this step,your script wouldnt beable to send windows in front.Read more about focus lock in Chapter 10.Closing a windowKnowing the window handle,you can also close the window.QuitWindowacts as if you sent Alt+F4 to the window.Its up to the window to decidewhether it first wants to pop up some dialog box.QuitWindowexpects thewindow name as argument.If you must close down a window,dont tamper with it.Instead,use Get-ProcessIDFromWindowto retrieve the ProcessID,then use EndProcessor KillProcessas outlined in the previous chapter.12-5.VBSset tool=CreateObject(“window.manager”)MsgBox“I am going to close the foreground window!”,vbSystemModalhandle=tool.GetForegroundWindowHandlename=tool.GetWindowName(handle)if MsgBox(“Do you want to quit“”&name&“”?”,vbYesNo)=vbYes thentool.QuitWindow(name)end ifYou can also specify the window handle directly:Use QuitWindowHandle.Making Windows InvisibleWindow handling isnt dynamic with the Scripting Host.You can choose thewindow style when launching the window through the Runmethod.You cantchange the window style later on,though.This is too bad.It would be very helpful to run a program as hidden and pop up its windowonce a certain timeout is reachedjust to make sure the program is stillrunning smoothly.But how?Your script loses control over the window the moment it launches theexternal program,but you still have its handle.This handle is the key todynamic changes.Finding out the current window styleThe window style is just a number.It represents the window size and mode.Table 12-1 lists all available window styles:372Part III:Starting and Controlling Other SoftwareII4684-8 ch12.f.qc 3/3/00 9:38 AM Page 372Table 12-1Window StylesWindow StyleDescription0Hidden1Maximized2Minimized,next window in order activated3Normal size4Activates window5Activates as maximized6Activates as minimized7Minimized,not activated8Shows window in current mode,not activated9Shows window in most recent mode,not activated10Shows window as normal windowMany of the options sound similar.They arent.For example,if you minimizea window using window style 2,the window will float around with its title barstill visible.To minimize a window to the task bar,use window style 7.Its easy to find out the current window style of any window.The script asksyou to click a window,then it reports the current window style,as shown in Figure 12-3.12-6.VBSset tool=CreateObject(“window.manager”)MsgBox“Click the window of choice,then click OK!”,vbSystemModalhandle=tool.GetForegroundWindowHandlemsg=“”&tool.GetWindowName(handle)&“”uses window mode“msg=msg&tool.GetShowCommand(handle)MsgBox msg,vbSystemModalFigure 12-3:Find out the current window style of any window.Chapter 12:Gaining Full Control Over Any Window373II4684-8 ch12.f.qc 3/3/00 9:38 AM Page 373Changing window style dynamicallyYou can change window style any time.To check out the various windowstyles available,use the next script to enter a new window style.It thenapplies the new style to the foreground window.Click Cancel to stop the experiment.12-7.VBSset tool=CreateObject(“window.manager”)MsgBox“Click the window of choice,then click OK!”,vbSystemModalhandle=tool.GetForegroundWindowHandleoriginalstyle=tool.GetShowCommand(handle)dooldstyle=tool.GetShowCommand(handle)style=InputBox(“Current window style:“&oldstyle _&vbCr&“Please enter new style(0-10):”)if style=vbEmpty thenexit doelseif not isNumeric(style)thenMsgBox“Please enter a number!”elseif Fix(style)10 thenMsgBox“Please enter a number between 0 and 10!”elsetool.SetShowCommand handle,styleend iflooptool.SetShowCommand handle,originalstyleMsgBox“Done.”Now its easy to hide a window.Heres how:12-8.VBSset win=CreateObject(“window.manager”)set proc=CreateObject(“process.id”)launch editorprocID=proc.Run(“NOTEPAD.EXE”)make sure program launcheddohandle=win.GetWindowFromProcess(procID)if handle=0 thenWScript.Sleep 200counter=counter+1if counter10 thenMsgBox“Program didnt start”WScript.Quitend ifend ifloop while handle=0win.SetShowCommand handle,0374Part III:Starting and Controlling Other SoftwareII4684-8 ch12.f.qc 3/3/00 9:38 AM Page 374MsgBox“Notepad now is invisible.”,vbSystemModalwin.SetShowCommand handle,10win.ActivateWindow handleMsgBox“Now its visible again!”Use ActivateWindowto make sure the window really gets the focus.If youdont,the window may be buried behind other windows.Changing Window Size and Moving WindowsTheres no way for your script to determine the window size.Not yet.Through your window handle,you can not only resize a window but alsomove the window around on-screen.Finding out current window dimensionsWondering how big a window is?Find out!12-9.VBSset tool=CreateObject(“window.manager”)MsgBox“Click the window of choice,then click OK!”,vbSystemModalhandle=tool.GetForegroundWindowHandleMsgBox“Current Window Size:”&vbCr _&tool.GetWindowCoordinates(handle)The script returns the window coordinates as left,top,right,and bottom(see Figure 12-4).Figure 12-4:The script reports the windows size.Wait a minute!The coordinates may be off.This is perfectly normal,because thewindow coordinates apply to the restored window mode only.If your window is minimized or maximized,youll still get the window coordinates for restored(normal)mode.This makes sensea minimized or maximized window has noreal“size.”With the previous methods,you can find out whether the window is in normal mode,and if not,you can easily switch to normal mode and useindividual window sizes.Chapter 12:Gaining Full Control Over Any Window375II4684-8 ch12.f.qc 3/3/00 9:38 AM Page 375Changing window sizeThe opposite way works well,too:You can re-size any window any time.Heres the solution:12-10.VBSset win=CreateObject(“window.manager”)set proc=CreateObject(“process.id”)launch editorprocID=proc.Run(“NOTEPAD.EXE”)make sure program launcheddohandle=win.GetWindowFromProcess(procID)if handle=0 thenWScript.Sleep 200counter=counter+1if counter10 thenMsgBox“Program didnt start”WScript.Quitend ifend ifloop while handle=0 reset window to(100,150)-(500,400)win.SetWindowCoordinates handle,100,150,500,400MsgBox“New coordinates:“&vbCr&win.GetWindowCoordinates(handle)reset window to(-40,30),width 300,height 400win.SetWindowCoordinates2 handle,-40,30,300,400MsgBox“New coordinates:“&vbCr&win.GetWindowCoordinates(handle)Use negative numbers to move the window off-screen to the left.In turn,youcan use this script to move any“lost”window into the visible desktop area.SetWindowCoordinates2automatically calculates the width and height into the appropriate screen coordinates.Moving windows aroundTo make transitions smoothl