Advanced .Net Debugging 4:基本調試任務(對象檢查:記憶體、值類型、引用類型、數組和異常的轉儲)

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

引言 本片文章分享一下之前遇到的WPF應用在觸摸屏下使用時的兩個問題。 場景 具體場景就是一個配置界面, ScrollViewer 中包含一個StackPanel 然後縱向堆疊,已滾動的方式查看,然後包含多個 TextBlock 、 TextBox 以及DataGrid ,期間遇到了兩個問題: WP ...


一、介紹
    這是我的《Advanced .Net Debugging》這個系列的第四篇文章。今天這篇文章的標題雖然叫做“基本調試任務”,但是這章的內容還是挺多的。由於內容太多,故原書的第三章內容我分兩篇文章來寫。上一篇我們瞭解了一些調試技巧,比如:單步調試、下斷點、過程調試等,這篇文章主要涉及的內容是對象的轉儲,記憶體的轉儲,值類型的轉儲,引用類型的轉儲、數組的轉儲、異常的轉儲等。第一次說到“轉儲”,可能大家不知道什麼意思,其實就是把我們想要的內容輸出出來或者說是列印出來,方便我們分析問題。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_6
  1 using System.Diagnostics;
  2 
  3 namespace ExampleCore_3_1_6
  4 {
  5     public class ObjTypes
  6     {
  7         public struct Coordinate
  8         {
  9             public int xCord;
 10             public int yCord;
 11             public int zCord;
 12 
 13             public Coordinate(int x, int y, int z)
 14             {
 15                 xCord = x;
 16                 yCord = y;
 17                 zCord = z;
 18             }
 19         }
 20 
 21         private Coordinate coordinate;
 22 
 23         int[] intArray = [1, 2, 3, 4, 5];
 24 
 25         string[] strArray = ["Welcome", "to", "Advanced", ".NET", "Debugging"];
 26 
 27         static void Main(string[] args)
 28         {
 29             Coordinate point = new Coordinate(100, 100, 100);
 30             Console.WriteLine("Press any key to continue(AddCoordinate)");
 31             Console.ReadKey();
 32             ObjTypes ob = new ObjTypes();
 33             ob.AddCoordinate(point);
 34 
 35             Console.WriteLine("Press any key to continue(Arrays)");
 36             Console.ReadKey();
 37             ob.PrintArrays();
 38 
 39             Console.WriteLine("Press any key to continue(Generics)");
 40             Console.ReadKey();
 41             Comparer<int> c = new Comparer<int>();
 42             Console.WriteLine("Greater:{0}", c.GreaterThan(5, 10));
 43 
 44             Console.WriteLine("Preaa any key to continue(Exception)");
 45             Console.ReadKey();
 46             ob.ThrowException(null);
 47         }
 48        
 49         public void AddCoordinate(Coordinate coord)
 50         {
 51             coordinate.xCord += coord.xCord;
 52             coordinate.yCord += coord.yCord;
 53             coordinate.zCord += coord.zCord;
 54 
 55             Console.WriteLine("x:{0},y:{1},z:{2}", coordinate.xCord, coordinate.yCord, coordinate.zCord);
 56         }
 57 
 58         public void PrintArrays()
 59         {
 60             foreach (int i in intArray)
 61             {
 62                 Console.WriteLine("Int:{0}", i);
 63             }
 64             foreach (string s in strArray)
 65             {
 66                 Console.WriteLine("Str:{0}", s);
 67             }
 68         }
 69 
 70         public void ThrowException(ObjTypes? obj)
 71         {
 72             if (obj == null)
 73             {
 74                 throw new ArgumentException("Obj cannot be null");
 75             }
 76         }        
 77     }
 78     public class Comparer<T> where T : IComparable
 79     {
 80         public T GreaterThan(T d, T d2)
 81         {
 82             int ret = d.CompareTo(d2);
 83             if (ret > 0)
 84             {
 85                 return d;
 86             }
 87             else
 88             {
 89                 return d2;
 90             }
 91         }
 92 
 93         public T LessThan(T d, T d2)
 94         {
 95             int ret = d.CompareTo(d2);
 96             if (ret < 0)
 97             {
 98                 return d;
 99             }
100             else
101             {
102                 return d2;
103             }
104         }
105     }
106 }
View Code
    2.2、ExampleCore_3_1_7
 1 namespace ExampleCore_3_1_7
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var person = new Person();
 8             Console.ReadLine();
 9         }
10     }
11     internal class Person
12     {
13         public int Age = 20;
14 
15         public string Name = "jack";
16     }
17 }
View Code
    2.3、ExampleCore_3_1_8
 1 namespace ExampleCore_3_1_8
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("Welcome to .NET Advanced Debugging!");
 8 
 9             Person person = new Person() { Name = "PatrickLiu", Age = 32, HomeAddress = new Address() { Country = "China", Province = "冀州", City = "直隸總督府", Region = "廣平大街23號", PostalCode = "213339" } };
10 
11             Console.WriteLine($"名稱:{person.Name},地址:{person.HomeAddress}");
12 
13             Console.Read();
14         }
15     }
16 
17     public class Person
18     {
19         public int Age { get; set; }
20         public string? Name { get; set; }
21         public Address? HomeAddress { get; set; }
22     }
23 
24     public class Address
25     {
26         public string? Country { get; set; }
27         public string? Province { get; set; }
28         public string? City { get; set; }
29         public string? Region { get; set; }
30         public string? PostalCode { get; set; }
31         public override string ToString()
32         {
33             return $"{Country}-{Province}-{City}-{Region}-{PostalCode}";
34         }
35     }
36 }
View Code
三、基礎知識
    本節的內容也很多,本來打算這篇文章分為:3.1、3.2、3.3、3.4、3.5、3.6 共 6 節就將原書的第三章剩下的內容全部寫完,但是內容太多,就只保留一節了。便於學習和閱讀。下一篇,怎麼排版再定吧。
    3.1、對象檢查
        本節,我們將介紹一些命令用來分析程式的狀態,以確定程式的故障。我們先來介紹非托管調試器中一些常用的命令,然後在介紹在 SOS 調試擴展中針對托管代碼調試的命令。
        3.1.1、記憶體轉儲
            A、基礎知識
                在調試器中有很多命令都可以轉儲記憶體的內容,這個方式非常底層,從記憶體地址上觀察地址上的內容。最常使用的命令是【d(顯示記憶體)】,比如:【dp】命令。根據轉儲的數據類型不同,命令【d】也有很多不同的變化,比如:du,dw,db,da 等,如果想瞭解更多,可以查看 Windbg 的幫助文檔,命令是【.hh】。
                其他一些變化形式:
                。du 命令把被轉儲的記憶體視作為 Unicode 字元。
                。da 命令把被轉儲的記憶體視作為 ASCII字元。
                。dw 命令把被轉儲的記憶體視作為字(word)。
                。db 命令把被轉儲的記憶體視作為位元組值和 ASCII 字元。
                。dq 命令把被轉儲的記憶體視作為四字值(quad word)。

            B、眼見為實:
                1)、NTSD 調試
                    調試源碼:ExampleCore_3_1_7
                    我們編譯項目,打開【Visual Studio 2022 Developer Command Prompt v17.9.2】,輸入命令:NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.EXE。
                    

                    打開【NTSD】調試器視窗。
                    

                    繼續使用【g】命令,運行調試器,等調試器卡住後,按【ctrl+c】組合鍵鍵入調試器的中斷模式。
                    切換到主線程,執行命令【~0s】。

1 0:009> ~0s
2 coreclr!GetThreadNULLOk+0x1e [inlined in coreclr!CrstBase::Enter+0x32]:
3 00007ff9`bd119fa2 488b34c8        mov     rsi,qword ptr [rax+rcx*8] ds:0000026c`600cb0b0=0000026c600ba2d0

                    使用【!clrstack -a】查看托管代碼的線程調用棧。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x954 (0)
 3         Child SP               IP Call Site
 4 000000785C77DCE8 00007ff9bd119fa2 [ExternalMethodFrame: 000000785c77dce8]
 5 ......(省略了)
 6 000000785C77E4D0 00007FF95D621987 ExampleCore_3_1_7.Program.Main(System.String[])
 7     PARAMETERS:
 8         args (0x000000785C77E520) = 0x0000026c64808ea0
 9     LOCALS:
10         0x000000785C77E508 = 0x0000026c64809640

                    0x0000026c64809640 紅色標註的就是 Person 類型的局部變數 person 的地址。我們可以使用【!dumpobj /d 0x0000026c64809640】查看 person 的詳情。

 1 0:000> !dumpobj /d 0x0000026c64809640
 2 Name:        ExampleCore_3_1_7.Person
 3 MethodTable: 00007ff95d6d93e0(方法表地址)
 4 EEClass:     00007ff95d6e1f18
 5 Tracked Type: false
 6 Size:        32(0x20) bytes
 7 File:        E:\Visual Studio 2022\...\ExampleCore_3_1_7\bin\Debug\net8.0\ExampleCore_3_1_7.dll
 8 Fields:
 9               MT    Field   Offset                 Type VT     Attr            Value Name
10 00007ff95d591188  4000001       10         System.Int32  1 instance               20 Age
11 00007ff95d60ec08  4000002        8        System.String  0 instance 000002acf67a04a0 Name
12 0:000>

                    同樣,我們可以使用【dp】命令也能看到 person 的詳情信息,只是不是很直觀。

1 0:000> dp 0x0000026c64809640
2 0000026c`64809640  00007ff9`5d6d93e0 000002ac`f67a04a0
3 0000026c`64809650  00000000`00000014 00000000`00000000
4 0000026c`64809660  00007ff9`5d555fa8 00000000`00000000
5 0000026c`64809670  00000000`00000000 00007ff9`5d60ec08
6 0000026c`64809680  0065006b`0000000c 006c0065`006e0072
7 0000026c`64809690  0064002e`00320033 00000000`006c006c
8 0000026c`648096a0  00000000`00000000 00007ff9`5d700d68
9 0000026c`648096b0  00000000`00000000 00000000`00000000

                    上面使用【!dumpobj】和【dp】命令我們找到的方法表地址都是一樣的。000002ac`f67a04a0 這個項就是 Name 域的地址,因為 Name 是引用類型,所以這裡是一個地址。我們可以繼續使用【!dumpobj /d 000002ac`f67a04a0】來驗證。

 1 0:000> !dumpobj /d 000002ac`f67a04a0
 2 Name:        System.String
 3 MethodTable: 00007ff95d60ec08
 4 EEClass:     00007ff95d5ea500
 5 Tracked Type: false
 6 Size:        30(0x1e) bytes
 7 File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.2\System.Private.CoreLib.dll
 8 String:      jack(我們賦的值)
 9 Fields:
10               MT    Field   Offset                 Type VT     Attr            Value Name
11 00007ff95d591188  400033b        8         System.Int32  1 instance                4 _stringLength
12 00007ff95d59b538  400033c        c          System.Char  1 instance               6a _firstChar
13 00007ff95d60ec08  400033a       c8        System.String  0   static 000002acf67a0008 Empty

                      我們可以使用【dumpobj】命令,當然也可以使用【dp】命令,都可以看到想看到的信息,不過是【dp】可讀性差很多。
                      如果我們查看命令如何使用,可以使用【.hh】命令。

1 0:000> .hh

                      效果如圖:
                      

                      就能打開調試的幫助文件。

                      

                      我們可以打開【索引】選項,查看我們想要查看的命令。
                      


                2)、Windbg Preview 調試
                    調試源碼:ExampleCore_3_1_7
                    我們編譯項目,打開 Windbg,點擊【文件】----》【Launch executable】附加程式 ExampleCore_3_1_7.exe,打開調試器的界面,程式已經處於中斷狀態。由於顯示的內容太多,我們可以使用【.cls】命令清空調試器的界面。我們使用【g】命令繼續運行程式,調試器會在【Console.ReadLine()】這行代碼處卡住,我們點擊【break】按鈕,就可以調試程式了。
                    查看是否在主線程,如果不是,切換到主線程,執行命令【~0s】。
1 0:006> ~0s
2 ntdll!NtReadFile+0x14:
3 00007ffa`9576ae54 c3              ret

                    我們查看一下當前托管的線程棧,執行命令【!clrstack -a】。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x8fc (0)
 3         Child SP               IP Call Site
 4 000000D30A57E230 00007ffa9576ae54 [InlinedCallFrame: 000000d30a57e230] 
 5 000000D30A57E230 00007ff9e4f076eb [InlinedCallFrame: 000000d30a57e230] 
 6 ......(省略了)
 7 
 8 000000D30A57E580 00007ff95e471987 ExampleCore_3_1_7.Program.Main(System.String[]) [E:\Visual Studio\..\ExampleCore_3_1_7\Program.cs @ 8]
 9     PARAMETERS:
10         args (0x000000D30A57E5D0) = 0x0000021dc3808ea0
11     LOCALS:
12         0x000000D30A57E5B8 = 0x0000021dc3809640

                    0x0000021dc3809640 紅色標註的就是 Person 類型的局部變數 person 的地址。我們直接使用【dp】命令轉儲出 person 的值。

1 0:000> dp 0x0000021dc3809640
2 0000021d`c3809640  00007ff9`5e5293e0 0000025e`558d04a0
3 0000021d`c3809650  00000000`00000014 00000000`00000000
4 0000021d`c3809660  00007ff9`5e3a5fa8 00000000`00000000
5 0000021d`c3809670  00000000`00000000 00007ff9`5e45ec08
6 0000021d`c3809680  0065006b`0000000c 006c0065`006e0072
7 0000021d`c3809690  0064002e`00320033 00000000`006c006c
8 0000021d`c38096a0  00000000`00000000 00007ff9`5e550d68
9 0000021d`c38096b0  00000000`00000000 00000000`00000000

                    最左邊的一列給出了每行記憶體的起始地址,後面是記憶體的內容。00007ff9`5e5293e0 紅色標註的就是方法表。我們可以驗證。執行命令【!dumpheap -type Person】。

1 0:000> !dumpheap -type Person
2          Address               MT           Size
3     021dc3809640     7ff95e5293e0             32 
4 
5 Statistics:
6           MT Count TotalSize Class Name
7 7ff95e5293e0     1        32 ExampleCore_3_1_7.Person
8 Total 1 objects, 32 bytes

                    7ff95e5293e0 和 00007ff9`5e5293e0 兩個值是一樣的。
                    
                    雖然直接輸出記憶體的內容很有用,但是閱讀起來就很麻煩。當我們調試托管代碼的時候,使用 SOS 擴展命令會提供更直接的信息。
                    如果我們想查看有關調試器的各種命令。我們可以使用【.hh】幫助文件。

1 windbg> .hh

                    同樣能打開調試器的幫助視窗。
                    

 

        3.1.2、值類型的轉儲
            A、基礎知識
                我們知道.NET 的類型分為值類型和引用類型,那我們如何判斷一個指針指向的是否是值類型呢,最佳的方式就是使用【dumpobj】命令,但它只對引用類型有效。【dumpobj】命令的參數是一個指向引用類型的指針,如果指針指向的是值類型,【dumpobj】命令就會輸出:<Note:this object has an  invalid CLASS field>Invalid object。
            B、眼見為實
                1)、NTSD 調試
                    調試源碼:ExampleCore_3_1_6
                    1.1)、查看獨立的值類型
                        編譯項目,打開【Visual Studio 2022 Developer Command Prompt v17.9.2】命令行工具,輸入命令【NTSD E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\ExampleCore_3_1_6\bin\Debug\net8.0\ExampleCore_3_1_6.exe】。
                        

                        打開【NTSD】調試器視窗。
                        

                        輸出內容太多,使用【.cls】命令,清理一下屏幕。然後使用【g】命令,運行調試器。調試器輸出:Press any key to continue(AddCoordinate),如圖:
                        

                        輸入【ctrl+c】組合鍵進入中斷模式。我們現在查看一下托管線程棧,可以使用【!clrstack -a】命令。

1 0:002> !clrstack -a
2 OS Thread Id: 0x1764 (2)
3 Unable to walk the managed stack. The current thread is likely not a
4 managed thread. You can run !threads to get a list of managed threads in
5 the process
6 Failed to start stack walk: 80070057

                        如圖:
                        

                        該命令執行錯誤,提示不是一個有效的托管線程,由於我們是手動中斷程式的執行,調試器的線程上下文是在調試器線程上,它是一個非托管線程,因此,在執行該命令之前,需要切換到托管線程的上下文中。執行命令【~0s】。

1 0:002> ~0s
2 ntdll!NtDeviceIoControlFile+0x14:
3 00007ffb`2fdaae74 c3              ret
4 0:000>

                        我們再次執行【!clrstack -a】命令。

 1 0:000> !clrstack -a
 2 OS Thread Id: 0x2c1c (0)
 3         Child SP               IP Call Site
 4 000000CD41F7E5E8 00007ffb2fdaae74 [InlinedCallFrame: 000000cd41f7e5e8]
 5 000000CD41F7E5E8 00007ffb1b68787a [InlinedCallFrame: 000000cd41f7e5e8]
 6 。。。。。。(省略了)
 7 000000CD41F7E770 00007FF9F55919B6 ExampleCore_3_1_6.ObjTypes.Main(System.String[])
 8     PARAMETERS:
 9         args (0x000000CD41F7E840) = 0x000002a492808ea0
10     LOCALS:
11         0x000000CD41F7E820 = 0x0000006400000064
12         0x000000CD41F7E818 = 0x0000000000000000
13         0x000000CD41F7E810 = 0x0000000000000000

                        這時,【clrstack】命令輸出了托管線程的棧回溯,包括每個棧幀的局部變數和參數。我們主要關註【ExampleCore_3_1_6.ObjTypes.Main】棧幀和地址【0x000000CD41F7E820】上的局部變數。【0x000000CD41F7E820】這個地址我們不知道它指向的是一個值類型還是引用類型。我們可以使用【dumpobj】命令做一個測試,因為該命令只對引用類型實例起作用。

1 0:000> !dumpobj 	   

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

-Advertisement-
Play Games
更多相關文章
  • 在Word中,表格是一個強大的工具,它可以幫助你更好地組織、呈現和分析信息。本文將介紹如何使用Python在Word中創建表格並填入數據、圖片,以及設置表格樣式等。 Python Word庫: 要使用Python在Word中創建或操作表格,需要先將Spire.Doc for Python這個第三方庫 ...
  • 在 Java 的java.util.concurrent包中,除了提供底層鎖、併發同步等工具類以外,還提供了一組原子操作類,大多以Atomic開頭,他們位於java.util.concurrent.atomic包下。 ...
  • 本文將從leader處理器入手,詳細分析node的增刪改查流程及監聽器原理。 回顧數據讀寫流程 leader ZookeeperServer.processPacket封裝Request並提交給業務處理器 LeaderRequestProcessor做本地事務升級 PrepRequestProces ...
  • 先看效果圖; 再說一下思路: 打開設計器,屬性裡面找到"顏色",設置為漸變色,將漸變色設置為9段,分別是,紅橙黃綠青藍紫白黑(Red,Orange,Yellow,Lime,Cyan,Blue,Magenta,White,Black); 然後移動滑塊兒,比如在紅色和橙色和黃色之間移動的時候,會發現顏色 ...
  • 概述:在WPF中,Command是一種優秀的機制,通過它,我們能夠將用戶界面操作與業務邏輯分離,提高代碼的可維護性和可重用性。通過自定義ICommand介面的實現(如RelayCommand),我們能夠輕鬆創建併在XAML中綁定命令,實現清晰的MVVM架構。這種模式使得應用程式的開發更加靈活,同時提 ...
  • .NET Framework 和 .NET Core 2.0 及更低版本中由 HttpClient 使用的預設消息處理程式為HttpClientHandler。 從 .NET Core 2.1 開始,類SocketsHttpHandler提供了更高級別的 HTTP 網路類(例如HttpClient ...
  • 1、KingBaseES 人大金倉資料庫 是多資料庫、多模式。2、public 為預設模式,因此,預設情況下,我們可以在 public 模式下新建表即可。總的來說,使用 CYQ.Data 框架操作 KingBaseES 資料庫可以簡化開發流程,提高開發效率,同時也增強了系統的穩定性和安全性 ...
  • 一、多播委托的應用--觀察者模式 遇到一個開發的問題? 面試者:以面向對象的思想實現一下的場景: 貓:Miao一聲,緊接著引發了一系列的行為~ Miao:引發了一系列的動作; 從代碼層面來說:代碼這樣寫好嗎? 貓職責不單一(貓就是貓,他的行為只有Miao一聲) 依賴太重,依賴了很多的普通類; 被依賴 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 微服務架構已經成為搭建高效、可擴展系統的關鍵技術之一,然而,現有許多微服務框架往往過於複雜,使得我們普通開發者難以快速上手並體驗到微服務帶了的便利。為瞭解決這一問題,於是作者精心打造了一款最接地氣的 .NET 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...