Windbg.exe的基础命令介绍


1 背景

工作多年,关于Windbg一直没有写过总结。为了以后查阅和方便分享,特写此文。

windbg.exe的优点就是绿色,体积小,速度快,功能强大。对于测试环境可以很快的部署。

一般情况,用windbg查死机非常方便。本文先介绍一下windbg常用的一些指令。


为了不涉密,选中一款开源软件做为调试目标。

2 调试目标

Notepad++.exe


3 源码&编译:

Notepad++ release 8.4.2.tar.gz

https://udomain.dl.sourceforge.net/project/notepadplusplus.mirror/v8.4.2/Notepad%2B%2B%20release%208.4.2.tar.gz


最新版本的Notepad++要求使用VS2019编译。这里不再介绍VS2019的安装.

VS2019编译Notepad++64位Release版本。编译结果如下:

clip_image002[4]


4 抓取DUMP

双击运行Notepad++.exe. 使用procdump.exe抓取一个DUMP

F:\Softwares\SysinternalsSuite_20211125>procdump.exe -ma -64 notepad++.exe

ProcDump v10.11 - Sysinternals process dump utility

Copyright (C) 2009-2021 Mark Russinovich and Andrew Richards

Sysinternals - www.sysinternals.com

[17:01:34] Dump 1 initiated: F:\Softwares\SysinternalsSuite_20211125\notepad++.exe_220709_170134.dmp

[17:01:34] Dump 1 writing: Estimated dump file size is 118 MB.

[17:01:34] Dump 1 complete: 118 MB written in 0.3 seconds

[17:01:35] Dump count reached.


5 安装Windbg

下载Windbg.exe

https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools

clip_image004[4]

clip_image006[4]

不再演示安装的具体过程。


6 启动调试

由于编译的是Notepad++的64位版本,此处一定要使用64位的Windbg.exe。

调试器的位数要与DUMP的位数保持一致。32位的dump使用32位windbg调试,64位dump使用64位windbg调试。

clip_image008[4]

D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\bin64


7 加载符号

自行编译notepad++就是为了产生符号文件。下面的分析会用到符号的解析:

加载方法:File->Symbol File Path…

clip_image010[4]


8 基本指令

8.1 vertarget命令:

查看环境信息

0:000> vertarget

Windows 10 Version 19044 MP (6 procs) Free x64

Product: WinNt, suite: SingleUserTS

Edition build lab: 19041.1.amd64fre.vb_release.191206-1406

Machine Name:

Debug session time: Sat Jul 9 17:01:34.000 2022 (UTC + 8:00)

System Uptime: 11 days 2:38:10.023

Process Uptime: 0 days 0:02:57.000

Kernel time: 0 days 0:00:00.000

User time: 0 days 0:00:00.000


8.2 ~命令:

查看线程列表

0:000> ~

. 0 Id: 2ca0.3858 Suspend: 0 Teb: 0000001a`514b7000 Unfrozen

1 Id: 2ca0.5430 Suspend: 0 Teb: 0000001a`514c5000 Unfrozen

2 Id: 2ca0.709c Suspend: 0 Teb: 0000001a`514c7000 Unfrozen

3 Id: 2ca0.1368 Suspend: 0 Teb: 0000001a`514c9000 Unfrozen

4 Id: 2ca0.81a8 Suspend: 0 Teb: 0000001a`514cb000 Unfrozen

5 Id: 2ca0.6edc Suspend: 0 Teb: 0000001a`514cd000 Unfrozen


8.3 lm命令:

查看模块列表

0:000> lmf start end module name 00007ff7`03c70000 00007ff7`04275000 notepad__ D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\bin64\notepad++.exe 00007ffc`89cd0000 00007ffc`89d0e000 dataexchange C:\Windows\System32\dataexchange.dll 00007ffc`95cc0000 00007ffc`95cc7000 msimg32 C:\Windows\System32\msimg32.dll 00007ffc`97360000 00007ffc`97836000 wininet C:\Windows\System32\wininet.dll

……


8.4 k命令:

打印堆栈。

0:000> k
 # Child-SP          RetAddr               Call Site
00 0000001a`513feaa8 00007ffc`b8261b3e     win32u!NtUserGetMessage+0x14
01 0000001a`513feab0 00007ff7`03f7f5f2     user32!GetMessageW+0x2e
02 0000001a`513feb10 00007ff7`03fb87b2     notepad__!wWinMain+0x2632 [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\winmain.cpp @ 733]
03 (Inline Function) --------`--------     notepad__!invoke_main+0x21 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 118]
04 0000001a`513ffbb0 00007ffc`b7e17034     notepad__!__scrt_common_main_seh+0x106 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288]
05 0000001a`513ffbf0 00007ffc`b8ac2651     kernel32!BaseThreadInitThunk+0x14
06 0000001a`513ffc20 00000000`00000000     ntdll!RtlUserThreadStart+0x21


使用~*打印所有线程,再加上p打印函数入参的值。即~* kp

0:000> ~* kp . 0 Id: 2ca0.3858 Suspend: 0 Teb: 0000001a`514b7000 Unfrozen # Child-SP RetAddr Call Site 00 0000001a`513feaa8 00007ffc`b8261b3e win32u!NtUserGetMessage+0x14 01 0000001a`513feab0 00007ff7`03f7f5f2 user32!GetMessageW+0x2e 02 0000001a`513feb10 00007ff7`03fb87b2 notepad__!wWinMain(struct HINSTANCE__ * hInstance = 0x00007ff7`03c70000, struct HINSTANCE__ * __formal = 0x00000000`00000001, wchar_t * pCmdLine = 0x00000000`00000000 "", int __formal = 0n0)+0x2632 [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\winmain.cpp @ 733] 03 (Inline Function) --------`-------- notepad__!invoke_main(void)+0x21 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 118] 04 0000001a`513ffbb0 00007ffc`b7e17034 notepad__!__scrt_common_main_seh(void)+0x106 [D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 05 0000001a`513ffbf0 00007ffc`b8ac2651 kernel32!BaseThreadInitThunk+0x14 06 0000001a`513ffc20 00000000`00000000 ntdll!RtlUserThreadStart+0x21 1 Id: 2ca0.5430 Suspend: 0 Teb: 0000001a`514c5000 Unfrozen # Child-SP RetAddr Call Site 00 0000001a`51cff608 00007ffc`b644cbc0 ntdll!ZwWaitForMultipleObjects+0x14 01 0000001a`51cff610 00007ffc`b644cabe KERNELBASE!WaitForMultipleObjectsEx+0xf0 02 0000001a`51cff900 00007ff7`03f8e487 KERNELBASE!WaitForMultipleObjects+0xe 03 0000001a`51cff940 00007ffc`b7e17034 notepad__!FolderUpdater::watching(void * params = 0x00000180`e427f790)+0x157 [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\FileBrowser\fileBrowser.cpp @ 1643] 04 0000001a`51cffb80 00007ffc`b8ac2651 kernel32!BaseThreadInitThunk+0x14 05 0000001a`51cffbb0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 2 Id: 2ca0.709c Suspend: 0 Teb: 0000001a`514c7000 Unfrozen # Child-SP RetAddr Call Site 00 0000001a`51dffbc8 00007ffc`b64496de ntdll!NtDelayExecution+0x14 01 0000001a`51dffbd0 00007ff7`03f8f59c KERNELBASE!SleepEx+0x9e 02 (Inline Function) --------`-------- notepad__!ReadDirectoryChangesPrivate::CReadChangesServer::Run(void)+0x1c [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\ReadDirectoryChanges\ReadDirectoryChangesPrivate.h @ 143] 03 0000001a`51dffc70 00007ff7`03fd50f8 notepad__!ReadDirectoryChangesPrivate::CReadChangesServer::ThreadStartProc(void * arg = 0x00000180`e4275cb0)+0x2c [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\ReadDirectoryChanges\ReadDirectoryChangesPrivate.h @ 115]

……



8.5 x命令:

查看模块notepad__的符号,x notepad__!*,直接输入此命令,符合太多。

本处做一些简单过滤,abc开头的没有,就看ab开头的。

0:000> x notepad__!abc* 0:000> x notepad__!ab* 00007ff7`04067230 notepad__!ablWordLists = char *[5] 00007ff7`040ef370 notepad__!abort_action = class __crt_state_management::dual_state_global<void (__cdecl*)(int)> 00007ff7`04067ac8 notepad__!abaqusWordListDesc = char *[7] 00007ff7`04033890 notepad__!AboutDlg::`vftable' = <function> *[16] 00007ff7`03e93f80 notepad__!AboutDlg::destroy (void) 00007ff7`03e0aff0 notepad__!AboutDlg::doDialog (void)

……


8.6 d命令:

查看内存数据。比如查看下AboutDlg的虚表内空。上图中,我们找们找到虚函数表的地址

00007ff7`04033890 notepad__!AboutDlg::`vftable' = <function> *[16]

根据C++的内存模型,我们查看下这个地址中存了什么内容?

使用d命令打印内存,但需要指定参数,a是ASCII字符串,b是字节,d是DWORD型,p是指针类型。

由于64位的内存是64位的,需要使用dp指令。

0:000> dp 00007ff7`04033890

00007ff7`04033890 00007ff7`03e8ed30 00007ff7`03e19e30

00007ff7`040338a0 00007ff7`03e93f80 00007ff7`03e227b0

00007ff7`040338b0 00007ff7`03e229f0 00007ff7`03e22a30
…


内存地址是打印出来了,那具体是什么函数呢?

在dp后面加上s,表示符号解析。效果如下:

0:000> dps 00007ff7`04033890 00007ff7`04033890 00007ff7`03e8ed30 notepad__!RecentFilesHistorySubDlg::`scalar deleting destructor' 00007ff7`04033898 00007ff7`03e19e30 notepad__!Window::init [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\Window.h @ 33] 00007ff7`040338a0 00007ff7`03e93f80 notepad__!RecentFilesHistorySubDlg::destroy [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\AboutDlg\AboutDlg.h @ 46] …

00007ff7`040338f8 00007ff7`03c7fd00 notepad__!Scintilla::Internal::SurfaceD2D::Initialised [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\scintilla\win32\PlatWin.cxx @ 1430] 00007ff7`04033900 00007ff7`03e0b5e0 notepad__!AboutDlg::run_dlgProc [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\WinControls\AboutDlg\AboutDlg.cpp @ 25] 00007ff7`04033908 00007ff7`040812e0 notepad__!DebugInfoDlg::`RTTI Complete Object Locator'


从上面的符号解析中,我们可以看到:AboutDlg继承了RecentFilesHistorySubDlg和Scintilla::Internal::SurfaceD2D等类。

对于Scintilla::Internal::SurfaceD2D这个类,查看下符号,我们看看其特殊之处:

0:000> x notepad__!Scintilla::Internal::SurfaceD2D* 00007ff7`0406c4c8 notepad__!Scintilla::Internal::SurfaceD2D::`vftable' = <function> *[2] 00007ff7`0406c898 notepad__!Scintilla::Internal::SurfaceD2D::`vftable' = <function> *[47] 00007ff7`03c83c18 notepad__!Scintilla::Internal::SurfaceD2D::Descent (class Scintilla::Internal::Font *) 00007ff7`03c72dd0 notepad__!Scintilla::Internal::SurfaceD2D::FlushCachedState (void) …… 00007ff7`03c81154 notepad__!Scintilla::Internal::SurfaceD2D::Stadium (class Scintilla::Internal::PRectangle *, class Scintilla::Internal::FillStroke *, Scintilla::Internal::Surface::Ends) 00007ff7`03c7fb04 notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D (struct ID2D1RenderTarget *, int, int, struct Scintilla::Internal::SurfaceMode, int) 00007ff7`03c83d50 notepad__!Scintilla::Internal::SurfaceD2D::AverageCharWidth (class Scintilla::Internal::Font *)

……

看到没有,这个有两个虚拟表指针。说明是多继承.通过查看代码确实也可以证实这一点

class SurfaceD2D : public Surface, public ISetRenderingParams {
	SurfaceMode mode;

	ID2D1RenderTarget *pRenderTarget = nullptr;
	ID2D1BitmapRenderTarget *pBitmapRenderTarget = nullptr;
	bool ownRenderTarget = false;
	int clipsActive = 0;
...
}



8.7 u命令:

反汇编。

以上面输出的

00007ff7`03c7fb04 notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D

为例进行说明。

0:000> u 00007ff7`03c7fb04
notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\scintilla\win32\PlatWin.cxx @ 1372]:
00007ff7`03c7fb04 48895c2418      mov     qword ptr [rsp+18h],rbx
00007ff7`03c7fb09 4889742420      mov     qword ptr [rsp+20h],rsi
00007ff7`03c7fb0e 57              push    rdi
00007ff7`03c7fb0f 4883ec40        sub     rsp,40h
00007ff7`03c7fb13 488bda          mov     rbx,rdx
00007ff7`03c7fb16 488bf9          mov     rdi,rcx
00007ff7`03c7fb19 488d0578cd3e00  lea     rax,[notepad__!Scintilla::Internal::SurfaceD2D::`vftable' (00007ff7`0406c898)]
00007ff7`03c7fb20 488901          mov     qword ptr [rcx],rax
0:000> u notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D
notepad__!Scintilla::Internal::SurfaceD2D::SurfaceD2D [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\scintilla\win32\PlatWin.cxx @ 1372]:
00007ff7`03c7fb04 48895c2418      mov     qword ptr [rsp+18h],rbx
00007ff7`03c7fb09 4889742420      mov     qword ptr [rsp+20h],rsi
00007ff7`03c7fb0e 57              push    rdi
00007ff7`03c7fb0f 4883ec40        sub     rsp,40h
00007ff7`03c7fb13 488bda          mov     rbx,rdx
00007ff7`03c7fb16 488bf9          mov     rdi,rcx
00007ff7`03c7fb19 488d0578cd3e00  lea     rax,[notepad__!Scintilla::Internal::SurfaceD2D::`vftable' (00007ff7`0406c898)]
00007ff7`03c7fb20 488901          mov     qword ptr [rcx],rax

通过上面的观察发现,可以通过地址来汇编,也可以通过符号进行反汇编。

通常情况下,当程序异常时会记录ExceptionAddress. 这个地址很有用。用它来汇编即可以找到出错代码的详细信息。


8.8 dt命令

查看变量的指令。从字面上面是d命令的变种。

比如静态变量

static const char * const apdlWordListDesc[] = {
    "processors",
    "commands",
    "slashommands",
    "starcommands",
    "arguments",
    "functions",
    0
};


通过符号找到静态变量的地址。根据这个地址打印出指针,根据指针即可以找到对应的字符串。

0:000> x notepad__!*apdlWordListDesc*
00007ff7`04068008 notepad__!apdlWordListDesc = char *[7]
0:000> dp 00007ff7`04068008
00007ff7`04068008  00007ff7`0407bbd8 00007ff7`0407bbc8
00007ff7`04068018  00007ff7`0407bbf8 00007ff7`0407bbe8
00007ff7`04068028  00007ff7`0407bc18 00007ff7`0407bc08
00007ff7`04068038  00000000`00000000 00000000`00000000
00007ff7`04068048  00000000`00000000 00000000`00000000
00007ff7`04068058  00000000`00000000 00000001`00000000
00007ff7`04068068  00000000`00000001 00000001`00000000
00007ff7`04068078  00000000`00000000 00000000`00000000
0:000> da 00007ff7`0407bbd8
00007ff7`0407bbd8  "processors"
0:000> da 00007ff7`0407bbc8
00007ff7`0407bbc8  "commands"



对于栈变量

由于栈在运行过程中,变量不断的压栈和出栈,这一部分信息不是静态的,无法追踪到历史信息,只能看到最新的栈变量信息.

建议使用工具栏的”调用栈”窗口,切换到栈。然后工具栏打开“局部变量”窗口,查看栈变量信息。如果下图所示:

image


对于堆变量

堆变量的查看,先要找到地址,可以从栈变量上下手,可以从静态变更下手,也可以从全局变量下手。只要能追踪到对应的指针地址就可以找到对应的值。


9. 示例:分析单例

对于大部程序中,都有一个全局变量,比如MFC中的theApp。或者一个单例,比如数据库实例或者配置实例。那么尝试从这个角度来追踪下,看看能不能查到更多的信息?


在main函数中,找到一个单例NppParameters,查看下符号,获取函数地址。

0:000> u notepad__!NppParameters::getInstance
Matched: 00007ff7`03e04050 notepad__!NppParameters::getInstance (void)
Matched: 00007ff7`03f5134c notepad__!NppParameters::getInstance =  (inline caller) notepad__!TabBarPlus::doOwnerDrawTab+cc
Matched: 00007ff7`03f512bb notepad__!NppParameters::getInstance =  (inline caller) notepad__!TabBarPlus::doOwnerDrawTab+3b
Matched: 00007ff7`03f5055b notepad__!NppParameters::getInstance =  (inline caller) notepad__!StatusBarSubclass+16b
Matched: 00007ff7`03f4f4b6 notepad__!NppParameters::getInstance =  (inline caller) notepad__!SplitterContainer::runProc+346
…
Ambiguous symbol error at 'notepad__!NppParameters::getInstance'


反汇编函数,拿到静态变量的地址。

0:000> u 00007ff7`03e04050 L20
notepad__!NppParameters::getInstance [D:\Documents\source\Notepad++ release 8.4.2\notepad-plus-plus-notepad-plus-plus-a02a254\PowerEditor\src\Parameters.h @ 1302]:
00007ff7`03e04050 4883ec28        sub     rsp,28h
00007ff7`03e04054 8b0de69f2e00    mov     ecx,dword ptr [notepad__!_tls_index (00007ff7`040ee040)]
00007ff7`03e0405a 65488b042558000000 mov   rax,qword ptr gs:[58h]
00007ff7`03e04063 ba04000000      mov     edx,4
00007ff7`03e04068 488b0cc8        mov     rcx,qword ptr [rax+rcx*8]
00007ff7`03e0406c 8b040a          mov     eax,dword ptr [rdx+rcx]
00007ff7`03e0406f 390593782d00    cmp     dword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)],eax
00007ff7`03e04075 7f2b            jg      notepad__!NppParameters::getInstance+0x52 (00007ff7`03e040a2)
00007ff7`03e04077 488b0582782d00  mov     rax,qword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::instance (00007ff7`040db900)]
00007ff7`03e0407e 4883c428        add     rsp,28h
00007ff7`03e04082 c3              ret
00007ff7`03e04083 48890576782d00  mov     qword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::instance (00007ff7`040db900)],rax
00007ff7`03e0408a 488d0d77782d00  lea     rcx,[notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)]
00007ff7`03e04091 e8323d1b00      call    notepad__!_Init_thread_footer (00007ff7`03fb7dc8)
00007ff7`03e04096 488b0563782d00  mov     rax,qword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::instance (00007ff7`040db900)]
00007ff7`03e0409d 4883c428        add     rsp,28h
00007ff7`03e040a1 c3              ret
00007ff7`03e040a2 488d0d5f782d00  lea     rcx,[notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)]
00007ff7`03e040a9 e87a3d1b00      call    notepad__!_Init_thread_header (00007ff7`03fb7e28)
00007ff7`03e040ae 833d53782d00ff  cmp     dword ptr [notepad__!`NppParameters::getInstancePointer'::`2'::$TSS0 (00007ff7`040db908)],0FFFFFFFFh
00007ff7`03e040b5 75c0            jne     notepad__!NppParameters::getInstance+0x27 (00007ff7`03e04077)
00007ff7`03e040b7 b9481b0000      mov     ecx,1B48h
00007ff7`03e040bc e8eb371b00      call    notepad__!operator new (00007ff7`03fb78ac)
00007ff7`03e040c1 4889442430      mov     qword ptr [rsp+30h],rax
00007ff7`03e040c6 4885c0          test    rax,rax
00007ff7`03e040c9 74b8            je      notepad__!NppParameters::getInstance+0x33 (00007ff7`03e04083)
00007ff7`03e040cb 488bc8          mov     rcx,rax
00007ff7`03e040ce e8bd6b0e00      call    notepad__!NppParameters::NppParameters (00007ff7`03eeac90)
00007ff7`03e040d3 90              nop
00007ff7`03e040d4 ebad            jmp     notepad__!NppParameters::getInstance+0x33 (00007ff7`03e04083)
00007ff7`03e040d6 cc              int     3
00007ff7`03e040d7 cc              int     3


根据上图的反汇编拿到了静态变量instance 的地址。由于instance 是一个指针,需要查看这个指针的内容。拿到对象NppParameters真正的地址。

class NppParameters final
{
private:
	static NppParameters* getInstancePointer() {
		static NppParameters* instance = new NppParameters;
		return instance;
	};

public:
	static NppParameters& getInstance() {
		return *getInstancePointer();
	};


根据真正的地址。按照 notepad__!NppParameters 进行解析。

dt notepad__!NppParameters 00000180`e0273830

0:000> dp 00007ff7`040db900 00007ff7`040db900 00000180`e0273830 00000000`80000001 00007ff7`040db910 00000001`00000000 00000000`00ffffff 00007ff7`040db920 00404050`00202030 00202030`00404050 …… 0:000> dt notepad__!NppParameters 00000180`e0273830 +0x000 _isTaskListRBUTTONUP_Active : 0 +0x004 L_END : 0n86 +0x008 _isFindReplacing : 0 +0x00c _dpiManager : DPIManager +0x018 _pXmlDoc : 0x00000180`e02e7830 TiXmlDocument +0x020 _pXmlUserDoc : 0x00000180`e22e3560 TiXmlDocument +0x028 _pXmlUserStylerDoc : 0x00000180`e22e3b90 TiXmlDocument +0x030 _pXmlUserLangDoc : (null) +0x038 _pXmlUserLangsDoc : std::vector<UdlXmlFileState,std::allocator<UdlXmlFileState> > +0x050 _pXmlToolIconsDoc : (null)


关这个命令的扩展

dt notepad__!NppParameters 00000180`e0273830

dt notepad__!NppParameters . 00000180`e0273830

增加一个点会展开一级显示,增加两个点会展开两级显示。

posted @ 2022-07-18 17:26  _terry  阅读(1062)  评论(0编辑  收藏  举报