Advanced .Net Debugging 5:基本調試任務(線程的操作、代碼審查、CLR內部的命令、診斷命令和崩潰轉儲文件)

来源:https://www.cnblogs.com/PatrickLiu/p/18060906
-Advertisement-
Play Games

在.NET中Newtonsoft.Json(Json.NET)是我們常用來進行Json序列化與反序列化的庫。 而在使用中常會遇到反序列化Json時,遇到不規則的Json數據解構而拋出異常。 Newtonsoft.Json 支持序列化和反序列化過程中的錯誤處理。 允許您捕獲錯誤並選擇是處理它並繼續序列 ...


一、介紹
    
這是我的《Advanced .Net Debugging》這個系列的第五篇文章。今天這篇文章的標題雖然叫做“基本調試任務”,但是這章的內容還是挺多的。上一篇我們瞭解了一些調.NET 框架中必要的概念,比如:記憶體轉儲、值類型轉儲、引用類型轉儲、數組轉儲和異常轉儲等,我們既能做到知其然,又能做到眼見為實,知其所以然,對我們分析.NET 程式有很大的幫助。今天這篇文章主要涉及的內容是線程的操作、代碼的審查和診斷命令等。SOSEX擴展的內容我就省略了,因為我這個系列的是基於 .NET 8 版本來寫的,SOSEX是基於 .NET Framework 版本的,如果大家想瞭解其內容,可以查看我的【高級調試】系列(我當前寫的是《Advanced .Net Debugging》系列,是不一樣的),當然,也可以看原書。【高級調試】系列主要是集中在 .NET Framework 版本的。如果我們想成為一名合格程式員,這些調試技巧都是必須要掌握的。
    如果在沒有說明的情況下,所有代碼的測試環境都是 Net 8.0,如果有變動,我會在項目章節里進行說明。好了,廢話不多說,開始我們今天的調試工作。

     調試環境我需要進行說明,以防大家不清楚,具體情況我已經羅列出來。
          操作系統:Windows Professional 10
          調試工具:Windbg Preview(Debugger Client:1.2306.1401.0,Debugger engine:10.0.25877.1004)和 NTSD(10.0.22621.2428 AMD64)
          下載地址:可以去Microsoft Store 去下載
          開發工具:Microsoft Visual Studio Community 2022 (64 位) - Current版本 17.8.3

          Net 版本:.Net 8.0
          CoreCLR源碼:源碼下載
二、調試源碼
    廢話不多說,本節是調試的源碼部分,沒有代碼,當然就談不上測試了,調試必須有載體。

    2.1、ExampleCore_3_1_9
 1 namespace ExampleCore_3_1_9
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             int a = 10;
 8             int b = 11;
 9             Console.WriteLine("X={0},Y={1}", a, b);
10             Test(12);
11             Console.ReadKey();
12         }
13 
14         private static void Test(int c)
15         {
16             Task.Run(Run1);
17             Task.Run(Run2);
18             Task.Run(Run3);
19         }
20 
21         private static void Run1()
22         {
23             Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run1 正在運行");
24             Console.ReadLine();
25             Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run1 結束運行");
26         }
27 
28         private static void Run2()
29         {
30             Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run2 正在運行");
31             Console.ReadLine();
32             Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run2 結束運行");
33         }
34 
35         private static void Run3()
36         {
37             Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run3 正在運行");
38             Console.ReadLine();
39             Console.WriteLine($"tid={Environment.CurrentManagedThreadId},Run3 結束運行");
40         }
41     }
42 }
View Code
    2.2、ExampleCore_3_1_10
 1 namespace ExampleCore_3_1_10
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var sum = Sum(10, 11);
 8             Console.WriteLine($"sum={sum}");
 9             Console.ReadLine();
10         }
11 
12         private static int Sum(int a, int b)
13         {
14             int i = a;
15             int j = b;
16             var sum = i + j;
17             return sum;
18         }
19     }
20 }
View Code
    2.3、ExampleCore_3_1_11
 1 namespace ExampleCore_3_1_11
 2 {
 3     internal class Program
 4     {
 5         private static List<byte[]> list = new List<byte[]>();
 6 
 7         static void Main(string[] args)
 8         {
 9             for (int i = 0; i < 100; i++)
10             {
11                 list.Add(new byte[100000]);
12             }
13             Console.WriteLine("數據添加完畢!");
14             Console.ReadLine();
15         }
16     }
17 }
View Code
    2.4、ExampleCore_3_1_12
 1 namespace ExampleCore_3_1_12
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("請輸入一個除數:");
 8             var num = Console.ReadLine();
 9             var result = 10 / Convert.ToInt32(num);
10 
11             Console.ReadLine();
12         }
13     }
14 }
View Code
三、基礎知識

    3.1、線程的操作

        在非托管代碼調試中,所有與線程相關的調試器命令都是以非托管 Windows 線程為基礎的,換個說法就是,用於調試非托管代碼的調試器命令使用的就是非托管 Windows 線程,調試器命令知道如何轉儲我們要轉儲的對象。但是,托管代碼就不一樣了,它的線程有自己的結構,調試器本身是無法對調用棧進行遍歷的。
        我們知道,CLR 能對托管代碼進行動態轉換,並且 JIT 編譯器可以將生成的機器代碼放在它認為合適的地方。非托管調試器是不知道 JIT 編譯器的任何知識,它也不知道生成的代碼放在何處,因此就不能正確的顯示棧回溯。

        3.1.1、ClrStack
            A、基礎知識
                SOS 調試器擴展提供了一個專門查看托管函數調用棧的命令,畢竟只有 JIT 編譯器更熟悉托管函數,也知道編譯後的機器碼放在什麼位置。
                這個命令就是【!clrstack】。
                !clrstack -a(all):這個命令表示將線程棧中的所有局部變數和參數全部輸出。
                !clrstack -p(parameter):這個命令表示將線程棧中的參數全部輸出。
                !clrstack -l(locals):這個命令表示將線程棧中的所有局部變數全部輸出。

                我們還可以查看所有托管線程棧,可以使用【~*e】命令。
                請註意:如果 ClrStack 命令在非托管線程的上下文中運行,將會顯示一個錯誤:
1 0:006> !clrstack
2 OS Thread Id: 0x335c (6)
3 Unable to walk the managed stack. The current thread is likely not a 
4 managed thread. You can run !clrthreads to get a list of managed threads in
5 the process
6 Failed to start stack walk: 80070057

            B、眼見為實
                調試源碼:ExampleCore_3_1_9
                調試任務:ClrStack 命令使用
                1)、NTSD 調試
                    編譯項目,打開【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,輸入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_3_1_9.exe】。
                    

                    打開【NTSD】調試器視窗。
                    

                    使用【g】命令運行調試器,知道調試器有如圖輸出,調試器會卡住暫停。
                    

                    按組合鍵【ctrl+c】進入中斷模式,在所有操作之前,我們先切換到托管線程,執行命令【~0s】。

1 0:003> ~0s
2 coreclr!CorSigUncompressElementType_EndPtr+0x18 [inlined in coreclr!MetaSig::CompareElementType+0x1d0]:
3 00007ffb`98b68e40 48ffc0          inc     rax

                    我們先使用【!clrstack】命令,後面不跟任何命令開關。

 1 0:000> !clrstack
 2 OS Thread Id: 0x3de4 (0)
 3         Child SP               IP Call Site
 4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8]
 5 000000F08ABCE5D0 00007FFC5382F269 System.Text.DecoderDBCS.GetChars(Byte[], Int32, Int32, Char[], Int32, Boolean)
 6 000000F08ABCE660 00007FFB92A28A51 System.IO.StreamReader.ReadBuffer()
 7 000000F08ABCE6B0 00007FFB92A290A4 System.IO.StreamReader.ReadLine()
 8 000000F08ABCE760 00007FFC5383005D System.IO.SyncTextReader.ReadLine()
 9 000000F08ABCE7B0 00007FFC53829319 System.Console.ReadLine()
10 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[])

                    它羅列出了調用棧。如果想查看每個棧幀的參數,我們可以使用【!clrstack -p】命令,很簡單,就不多說了。

1 0:000> !clrstack -p
2 OS Thread Id: 0x3de4 (0)
3         Child SP               IP Call Site
4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8]
5 。。。。。。(省略了)
6 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[])
7     PARAMETERS:(這裡就是參數所在地)
8         args (0x000000F08ABCE830) = 0x000002664e008ea0

                    如果想查看每個棧幀的局部變數,可以使用【!clrstack -l】命令。

1 0:000> !clrstack -l
2 OS Thread Id: 0x3de4 (0)
3         Child SP               IP Call Site
4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8]
5 。。。。。。(省略了)
6 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[])
7     LOCALS:(以下就是局部變數)
8         0x000000F08ABCE81C = 0x000000000000000a
9         0x000000F08ABCE818 = 0x000000000000000b

                    如果想查看每一棧幀的局部變數和參數,可以使用【!clrstack -a】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3de4 (0)
 3         Child SP               IP Call Site
 4 000000F08ABCDFF8 00007ffb98b68e40 [ExternalMethodFrame: 000000f08abcdff8]
 5 。。。。。。(省略了)
 6 000000F08ABCE7E0 00007FFB391219CF ExampleCore_3_1_9.Program.Main(System.String[])
 7     PARAMETERS:(這裡是參數區域)
 8         args (0x000000F08ABCE830) = 0x000002664e008ea0
 9     LOCALS:(這裡是局部變數區域)
10         0x000000F08ABCE81C = 0x000000000000000a
11         0x000000F08ABCE818 = 0x000000000000000b

                    很簡單,就不多數了。
                    還有一個命令,我們要熟悉一下,那就是~*e 命令。
                    ~ 命令輸出所有線程列表。

 1 0:000> ~
 2 .  0  Id: 3f64.3de4 Suspend: 1 Teb: 000000f0`8acb8000 Unfrozen
 3    1  Id: 3f64.ab4 Suspend: 1 Teb: 000000f0`8acd6000 Unfrozen ".NET Tiered Compilation Worker"
 4    2  Id: 3f64.b08 Suspend: 1 Teb: 000000f0`8acd8000 Unfrozen
 5 #  3  Id: 3f64.6d0 Suspend: 1 Teb: 000000f0`8acda000 Unfrozen
 6    4  Id: 3f64.220c Suspend: 1 Teb: 000000f0`8acc0000 Unfrozen ".NET EventPipe"
 7    5  Id: 3f64.1830 Suspend: 1 Teb: 000000f0`8acc2000 Unfrozen ".NET Debugger"
 8    6  Id: 3f64.3c34 Suspend: 1 Teb: 000000f0`8acc4000 Unfrozen ".NET Finalizer"
 9    8  Id: 3f64.10d0 Suspend: 1 Teb: 000000f0`8acca000 Unfrozen ".NET TP Worker"
10    9  Id: 3f64.3e9c Suspend: 1 Teb: 000000f0`8accc000 Unfrozen ".NET TP Gate"
11   10  Id: 3f64.3d90 Suspend: 1 Teb: 000000f0`8acce000 Unfrozen ".NET TP Worker"
12   11  Id: 3f64.1e48 Suspend: 1 Teb: 000000f0`8acd0000 Unfrozen ".NET TP Worker"

                    ~* 命令,顯示所有線程列表和入口函數。

 1 0:000> ~*
 2 .  0  Id: 3f64.3de4 Suspend: 1 Teb: 000000f0`8acb8000 Unfrozen
 3       Start: apphost!wmainCRTStartup (00007ff7`8a2b1360)
 4       Priority: 0  Priority class: 32  Affinity: f
 5    1  Id: 3f64.ab4 Suspend: 1 Teb: 000000f0`8acd6000 Unfrozen ".NET Tiered Compilation Worker"
 6       Start: coreclr!TieredCompilationManager::BackgroundWorkerBootstrapper0 (00007ffb`98c833f0)
 7       Priority: 0  Priority class: 32  Affinity: f
 8    2  Id: 3f64.b08 Suspend: 1 Teb: 000000f0`8acd8000 Unfrozen
 9       Start: KERNELBASE!CtrlRoutine (00007ffc`651d9c80)
10       Priority: 2  Priority class: 32  Affinity: f
11 #  3  Id: 3f64.6d0 Suspend: 1 Teb: 000000f0`8acda000 Unfrozen
12       Start: ntdll!DbgUiRemoteBreakin (00007ffc`6791ba10)
13       Priority: 0  Priority class: 32  Affinity: f
14    4  Id: 3f64.220c Suspend: 1 Teb: 000000f0`8acc0000 Unfrozen ".NET EventPipe"
15       Start: coreclr!server_thread (00007ffb`98c6b530)
16       Priority: 0  Priority class: 32  Affinity: f
17    5  Id: 3f64.1830 Suspend: 1 Teb: 000000f0`8acc2000 Unfrozen ".NET Debugger"
18       Start: coreclr!DebuggerRCThread::ThreadProcStatic (00007ffb`98c667f0)
19       Priority: 0  Priority class: 32  Affinity: f
20    6  Id: 3f64.3c34 Suspend: 1 Teb: 000000f0`8acc4000 Unfrozen ".NET Finalizer"
21       Start: coreclr!FinalizerThread::FinalizerThreadStart (00007ffb`98c4e370)
22       Priority: 2  Priority class: 32  Affinity: f
23    8  Id: 3f64.10d0 Suspend: 1 Teb: 000000f0`8acca000 Unfrozen ".NET TP Worker"
24       Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340)
25       Priority: 0  Priority class: 32  Affinity: f
26    9  Id: 3f64.3e9c Suspend: 1 Teb: 000000f0`8accc000 Unfrozen ".NET TP Gate"
27       Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340)
28       Priority: 0  Priority class: 32  Affinity: f
29   10  Id: 3f64.3d90 Suspend: 1 Teb: 000000f0`8acce000 Unfrozen ".NET TP Worker"
30       Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340)
31       Priority: 0  Priority class: 32  Affinity: f
32   11  Id: 3f64.1e48 Suspend: 1 Teb: 000000f0`8acd0000 Unfrozen ".NET TP Worker"
33       Start: coreclr!ThreadNative::KickOffThread (00007ffb`98c07340)
34       Priority: 0  Priority class: 32  Affinity: f

                2)、Windbg Preview 調試
                    編譯項目,打開【Windbg Preview】,依次點擊【文件】--->【Launch executable】,載入我們的項目文件:ExampleCore_3_1_9.exe,進入調試器。繼續使用【g】命令,運行調試器。我們的控制台程式輸出如下內容,如圖:
                    

                    點擊【break】按鈕,中斷調試器的執行,由於,我們手動中斷程式執行,需要將線程切換到托管線程上,執行命令【~0s】。

1 0:001> ~0s
2 ntdll!NtReadFile+0x14:
3 00007ffc`678eae54 c3              ret

                    我們繼續使用【!clrstack】命令,輸出托管線程的調用棧。

 1 0:000> !clrstack
 2 OS Thread Id: 0x3b40 (0)【底層操作系統的ID】
 3         Child SP               IP Call Site
 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 
 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 
 6 000000D3A5F7E530 00007ffc0bf676eb Interop+Kernel32.ReadFile(IntPtr, Byte*, Int32, Int32 ByRef, IntPtr) [/_/.../LibraryImports.g.cs @ 412]
 7 000000D3A5F7E620 00007ffc0bf6c9c0 System.ConsolePal+WindowsConsoleStream..../System/ConsolePal.Windows.cs @ 1150]
 8 000000D3A5F7E680 00007ffc0bf6c8bb System.ConsolePal+WindowsConsoleStream.Read(System.Span`1) [/_/src/libraries/....Windows.cs @ 1108]
 9 000000D3A5F7E6C0 00007ffc0bf6fb84 System.IO.ConsoleStream.Read(Byte[], Int32, Int32) [/_/src/libraries/.../ConsoleStream.cs @ 34]
10 000000D3A5F7E730 00007ffb8d1589c1 System.IO.StreamReader.ReadBuffer() [/_/src/libraries/System.Private.CoreLib/.../IO/StreamReader.cs @ 613]
11 000000D3A5F7E780 00007ffb8d1590a4 System.IO.StreamReader.ReadLine() [/_/src/libraries/System.Private.CoreLib/.../StreamReader.cs @ 802]
12 000000D3A5F7E830 00007ffc0bf7005d System.IO.SyncTextReader.ReadLine() [/_/src/libraries/System.Console/src/System/IO/SyncTextReader.cs @ 77]
13 000000D3A5F7E880 00007ffc0bf69319 System.Console.ReadLine() [/_/src/libraries/System.Console/src/System/Console.cs @ 752]
14 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_9\Program.cs @ 11]

                    我們可以使用【!clrstack -l】,查看托管線程調用棧,並顯示每個棧幀的局部變數。

 1 0:000> !clrstack -l
 2 OS Thread Id: 0x3b40 (0)
 3         Child SP               IP Call Site
 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 
 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 
 。。。。。。(省略了)49 
50 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_9\Program.cs @ 11]
51     LOCALS:
52         0x000000D3A5F7E8EC = 0x000000000000000a
53         0x000000D3A5F7E8E8 = 0x000000000000000b 這就是我們的局部變數

                    如果我們想查看托管調用棧的參數,可以使用【!clrstack -p】命令,p(parameter)參數。

 1 0:000> !clrstack -p
 2 OS Thread Id: 0x3b40 (0)
 3         Child SP               IP Call Site
 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 
 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 
 。。。。。。(省略了)47 
48 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_9\Program.cs @ 11]
49     PARAMETERS:
50         args (0x000000D3A5F7E900) = 0x000001efb4408ea0(這個就是 Main 方法的 args 參數)

                    0x000001efb4408ea0 這個地址就是 Program.Main 方法的 args 參數,args 是字元串數組,是引用類型,我們可以直接使用【!dumpobj】來確認。

1 0:000> !dumpobj 0x000001efb4408ea0
2 Name:        System.String[]
3 MethodTable: 00007ffb2e3cfab0
4 EEClass:     00007ffb2e27c440
5 Tracked Type: false
6 Size:        24(0x18) bytes
7 Array:       Rank 1, Number of elements 0, Type CLASS (Print Array)
8 Fields:
9 None

                    如果想同時查看局部變數和參數,可以使用【!clrstack -a】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x3b40 (0)
 3         Child SP               IP Call Site
 4 000000D3A5F7E560 00007ffc678eae54 [InlinedCallFrame: 000000d3a5f7e560] 
 5 000000D3A5F7E560 00007ffc0bf676eb [InlinedCallFrame: 000000d3a5f7e560] 
 6 。。。。。。(省略了)
 7 
 8 000000D3A5F7E8B0 00007ffb2e3419cf ExampleCore_3_1_9.Program.Main(System.String[]) [E:\Visual Studio\...\ExampleCore_3_1_9\Program.cs @ 11]
 9     PARAMETERS:
10         args (0x000000D3A5F7E900) = 0x000001efb4408ea0 (這是參數)
11     LOCALS:
12         0x000000D3A5F7E8EC = 0x000000000000000a (這是局部變數)
13         0x000000D3A5F7E8E8 = 0x000000000000000b (這是局部變數)

        3.1.2、Threads
            A、基礎知識
                如何你想枚舉出進程中所有的托管代碼線程,可以使用【!threads】命令,或者簡寫形式【!t】。當然,這個命令也有一些開關設置,-live 只羅列出那些處於活躍狀態的線程的信息,-special 只輸出進程中所有“特殊的”線程,比如:垃圾收集線程、調試器線程、線程池定時器線程等。
            B、眼見為實
                調試源碼:ExampleCore_3_1_9
                調試任務:Threads 命令的使用
                1)、NTSD 調試
                    編譯項目,打開【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,輸入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_9\bin\Debug\net8.0\ExampleCore_3_1_9.exe】,打開調試器視窗。
                    

                    

                    使用【g】命令,繼續運行調試器,等到調試器輸出以下內容:
                    

                    按【ctrl+c】組合鍵進入調試模式。

 1 0:013> !threads
 2 ThreadCount:      8
 3 UnstartedThread:  1
 4 BackgroundThread: 5
 5 PendingThread:    1
 6 DeadThread:       1
 7 Hosted Runtime:   no

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在當今數字化時代,構建高效、可靠的分散式系統是許多企業和開發團隊面臨的挑戰。微軟的 Orleans 框架為解決這些挑戰提供了一個強大而簡單的解決方案。本文將介紹 Orleans 的核心概念,並通過一個簡單的示例代碼來演示其用法。 什麼是 Orleans? Orleans 是由微軟開發的一個開源分散式 ...
  • 嗯,構建模塊,一行代碼的事情,愣是讓我寫成了一篇教程,太難了。在這個入門教程的第三部分中,我們學習瞭如何使用.NET Emit 構建模塊(Module)。通過創建和定義模塊,我們可以更好地組織和管理我們的代碼。在這個過程中,我們瞭解瞭如何使用 AssemblyBuilder 和 ModuleBuil... ...
  • 在 PostgreSQL 中,bytea_output 參數控制在查詢結果中 bytea 類型的顯示格式。預設情況下,bytea_output 的值為 hex,這意味著在查詢結果中,bytea 類型的數據以十六進位格式顯示。但是,如果你的應用程式期望以二進位格式獲取圖像數據,則將 bytea_out... ...
  • 為了優化我們公司網站的性能,我最近引入了瀏覽器預載入技術(Preload)。 這項技術可以顯著減少級聯情況,提高資源載入的並行度,從而加速網站的載入速度。 Preload的原理 Preload的原理是在瀏覽器解析HTML文檔時,提前載入頁面所需的關鍵資源,如樣式表、腳本文件和字體等。 通過預載入這些 ...
  • 前言 文本主要講 MinimalApis 中的使用自定義IResultModel和系統自帶IResult做響應返回值。 MinimalApis支持以下類型的返回值: string - 這包括 Task<string> 和 ValueTask<string> T(任何其他類型)- 這包括 Task<T ...
  • 一:背景 1. 講故事 前幾天有位朋友找到我,說他們的API服務程式跑著跑著CPU滿了降不下去,讓我幫忙看下怎麼回事,現在貌似民間只有我一個人專註dump分析,還是申明一下我dump分析是免費的,如果想學習.NET高級調試的分析技術,可以來我的訓練營看看,話不多說,dump分析走起! 二:WinDb ...
  • 前言 好久不用Arcgis,突然發現想用時,有點不會安裝了,所以這裡記錄一下安裝過程。 下載Arcgis 首先,下載一個arcgis版本,我這裡下的是10.1。 推薦【 gis思維(公眾號)】,【麻辣GIS(網站)】。 當然了,這都是很舊很舊的版本了,基本上沒有三維功能。 一定要下載帶註冊機的。 a ...
  • 在本章節中,我們討論瞭如何使用 C# Emit 來構建動態程式集,以獲得 AssemblyBuilder 這個程式集構建器,開啟構建程式集的第一步。同時我們也討論瞭如何使用 C# Emit 來構建動態程式集以及程式集的持久化。同時還分享了自己的乾貨,如何使用 CodeDom 和 Roslyn 來構建... ...
一周排行
    -Advertisement-
    Play Games
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...