在调试.NET应用程序中的转储文件时,有时我们可能会遇到这样的情况:我们希望得到引用RCW对象的System.__ComObject包装器引用的COM对象。
你可能会认为抛弃这个系统。也许你能给出答案,但事实并非如此。
如下例子
Name: System.__ComObject MethodTable: 79307098 EEClass: 790dfa34 Size: 16(0x10) bytes GC Generation: 2 (C:WINDOWSassemblyGAC_32mscorlib2.0.0.0__b77a5c561934e089mscorlib.dll) Fields: MT Field Offset Type VT Attr Value Name 79330740 400018a 4 System.Object 0 instance 00000000 __identity 79333178 400027e 8 ...ections.Hashtable 0 instance 00000000 m_ObjectToDataMap
Where is the COM object???
因此,在使用私有符号时,我从《高级Windows调试和高级.NET调试》那里学到了一系列步骤。我首先想到的是,当需要再次使用该技术时,我会忘记这些步骤,所以我创建了一个脚本来完成这项工作。
根据SOS 4.5的公开测试版,有一个命令可以做到:!DumpRCW <RCW address>
此命令列出有关运行时可调用包装的信息。你可以用!DumpObj获取与托管对象对应的RCW地址。输出包含RCW保留的所有COM接口指针,这对于研究互操作性强的应用程序的生存期问题非常有用。
如果COM对象被释放,而RCW引用计数器没有减少(例如,Marshal.FinalReleaseComObject未调用),您将知道,因为脚本将显示无效地址,例如:
另一种情况:
脚本源代码如下:
$$ $$ ============================================================================= $$ COM_FROM_RCW_PUBLIC.TXT $$ $$ Version: 1.2 $$ $$ Note: Create a folder called MyScripts where your WinDbg.exe is located and $$ save the script there. $$ $$ This script gives you the COM object used by System.__ComObject $$ $$ Note: This is the Public version. $$ Compatibility: Win32/Win64. $$ PSSCORx or SOS required. $$ $$ Usage: $$>a<myscriptsCOM_FROM_RCW.txt <address of System.__ComObject> $$ $$ Mario Hewardt $$ Roberto Alexis Farah - http://blogs.msdn.com/debuggingtoolbox/ $$ $$ 3/5/2012 - Fixed problem with x64. $$ $$ All my scripts are provided "AS IS" with no warranties, and confer no rights. $$ ============================================================================= $$ $$ Checks if user is providing the argument. .if(0 == ${/d:$arg1}) { .printf /D " <b> Please, provide the address of System.__ComObject as argument for the script.</b> " .printf "Usage: $$>a<myscripts\COM_FROM_RCW_PUBLIC.txt <address of System.__ComObject>" } .else { .catch { .printf /D " <b> Make sure you are using Symbols and PSSCOR or SOS is loaded...</b> " $$ Let's get the address of the object - 0x4 and the low order WORD from that address. r @$t0 = wo(${$arg1}-0x4) $$ Gets Sync Block because we need the first syncblk field. $$ To do that we need to redirect the output to a file and parse the file. .logopen TEMP.LOG !syncblk @$t0 .logclose $$ Now let's parse the output... We need token # 15 $$ This is what we need: $$ $$ Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner $$ 3 100e502c <<< 0 0 00000000 none 12a32b3c System.__ComObject $$ Counter to count tokens. /pS didn't work as I expected... r @$t1 = 0 r @$t2 = 0 .foreach /f (obj "TEMP.LOG") { r @$t1 = @$t1 + 1 $$ Is this field number 15? If yes we can ignore the other fields. .if(0n15 == @$t1) { .echo SyncBlock address = ${obj} $$ Let's save our address. Keep in mind that this line can store $$ garbage, like a field name from the output, if something goes wrong. r @$t2 = ${obj} .break } } $$ Protection against invalid pointers. .catch { .if(0n4 == @$ptrsize) { r @$t3 = poi(poi(poi(@$t2+0x1c)+@$ptrsize*0n3)+0x88) } .else { r @$t3 = poi(poi(poi(@$t2+0x28)+@$ptrsize*0n3)+0x100) } } .printf /D " <b>This is the COM object referenced by the RCW object from address %p:</b> ", @$t2 dps @$t3 L10 } }