Special Command—Parsing Strings, Files, and Commands Output Using .foreach
This is by far one of the most powerful WinDbg commands. Even if you don’t create scripts, you’ll benefit from this command. It’s powerful because it’s flexible. You can use it for a huge variety of operations.
The .foreach token parses the output of one or more debugger commands and uses each value in this output as the input to one or more additional commands.
Syntax
.foreach [Options] ( Variable { InCommands } ) { OutCommands }
.foreach[Options] /s ( Variable " InString" ) { OutCommands }
.foreach [Options] /f ( Variable " InFile" ) { OutCommands }
Options:
Can be any combination of the following options:
/pS InitialSkipNumber
Causes some initial tokens to be skipped. InitialSkipNumber specifies the number of output tokens that will not be passed to the specified OutCommands.
/ps SkipNumber
InString
Used with /s. Specifies a string that will be parsed; the resulting tokens will be passed to OutCommands.
InFile
Used with /f. Specifies a text file that will be parsed; the resulting tokens will be passed to OutCommands. The file name InFile must be enclosed in quotation marks.
I know it looks complicated. To help you understand the variations, I present several different examples ranging from the simplest to the most complex.
Dumping virtual memory addresses that have the “nt” string:
.foreach(obj {s -[1]a 0 0FFFFFFF "nt"}){da /c 0n100 ${obj}}
Using !DumpObj for each object address from !DumpStackObjects command. Both commands are from the extension SOS.DLL:
.foreach /pS 1 /ps 2 (obj {.shell -i - -ci "!dso" FIND "System."}){!do ${obj}}
In bold below is the part skipped using /pS 1 (skips 1 token) and in red are the parts skipped using /ps 2 (causes 2 tokens to be skipped repeatedly). The output is from !DumpStackObjects:
OS Thread Id: 0x1d3c (0)
ESP/REG Object Name
ebx 01d9eb80 System.Windows.Forms.Application+ThreadContext
esi 01e56a84 System.Collections.Hashtable+HashtableEnumerator
002df0a4 01e434e4 System.Windows.Forms.NativeMethods+MSG[]
002df0a8 01d9eb80 System.Windows.Forms.Application+ThreadContext
002df0b0 01d9ee10 System.Windows.Forms.Application+ComponentManager
002df0fc 01f63884 System.Windows.Forms.ApplicationContext
002df104 01f63884 System.Windows.Forms.ApplicationContext
002df128 01d9eb80 System.Windows.Forms.Application+ThreadContext
002df178 01f63884 System.Windows.Forms.ApplicationContext
002df17c 01e62b28 System.Windows.Forms.Application+ThreadContext
002df18c 01df1130 System.Windows.Forms.ApplicationContext
002df194 01f505a0 System.ComponentModel.EventHandlerList
002df198 01f63898 System.EventHandler
002df1a0 01f63898 System.EventHandler
002df1a4 01f63898 System.EventHandler
002df1a8 01e9e33c NetWiz.frmNetWiz
002df1ac 01f63884 System.Windows.Forms.ApplicationContext
002df1b0 01e62b28 System.Windows.Forms.Application+ThreadContext
002df1bc 01f63884 System.Windows.Forms.ApplicationContext
002df1c4 01e9e33c NetWiz.frmNetWiz
002df1d0 01d9ef48 System.IO.FileStream
002df1d4 01d9e960 System.Object[] (System.Diagnostics.Process[])
002df1d8 01d85a10 System.Diagnostics.Process
Not impressed yet? Ok, take a look at the code below. I’m going to run commands from a string:
$$ Creates an alias with several different commands.
as ${/v:CommandString} kb r !clrstack
$$ Extracts the commands from the string and executes them.
.foreach /s (obj "CommandString") {${obj}}
Same thing, but gets the commands from a text file:
.foreach /f (obj "C:\downloads\test.txt") {${obj}}
Content from text file:
kpn !clrstack !dso r
As I said, this is a very powerful command! I bet you’re going to find other ways to leverage it!
Comments
- Anonymous
March 14, 2009
PNG8 screenshots are much better