Net 高級調試之六:對象檢查之值類型、引用類型、數組和異常的轉儲

来源:https://www.cnblogs.com/PatrickLiu/archive/2023/11/08/17812156.html
-Advertisement-
Play Games

一:背景 1. 講故事 這段時間分析了幾個和網路故障有關的.NET程式之後,真的越來越體會到電腦基礎課的重要,比如 電腦網路 課,如果沒有對 tcpip協議 的深刻理解,解決這些問題真的很難,因為你只能在高層做黑盒測試,你無法看到 tcp 層面的握手和psh通訊。 這篇我們通過兩個小例子來理解一 ...


一、簡介
     今天是《Net 高級調試》的第六篇文章。記得我剛接觸 Net 框架的時候,還是挺有信心的,對所謂的值類型和引用類型也能說出自己的見解,畢竟,自己一直在努力。當然這些見解都是書本上的,並沒有做到眼見為實,所以總是有些東西說不清楚。今天,我們就好好的說說 C# 的類型,是從記憶體級別、從底層來說一下值類型、引用類型到底是什麼,它們在記憶體中的形態,還有也說說數組的記憶體形態,如何內部佈局的,以及我們如何查找由未捕捉的異常引起的程式崩潰。這些都是基礎的,如果這些掌握不好,以後的高級調試的道路,也不好走。自從我過了這一關,很多東西理解起來,比較透徹 了,但是,還必須努力。當然了,第一次看視頻或者看書,是很迷糊的,不知道如何操作,還是那句老話,一遍不行,那就再來一遍,還不行,那就再來一遍,俗話說的好,書讀千遍,其意自現。
     如果在沒有說明的情況下,所有代碼的測試環境都是 Net Framewok 4.8,但是,有時候為了查看源碼,可能需要使用 Net Core 的項目,我會在項目章節里進行說明。好了,廢話不多說,開始我們今天的調試工作。

       調試環境我需要進行說明,以防大家不清楚,具體情況我已經羅列出來。
          操作系統:Windows Professional 10
          調試工具:Windbg Preview(可以去Microsoft Store 去下載)
          開發工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源碼:源碼下載

二、基礎知識
    1、對象檢查
        1.1、簡介
            高級調試的目標就是分析應用程式故障所形成的原因,既然是故障,大多數情況下是對象的某種損壞,這就需要我們深入的瞭解各種對象的審查方法。

    2、各種檢查方法
        2.1、記憶體轉儲
              這個方式非常底層,從記憶體地址上觀察地址上的內容,常使用【dp】命令。出來【dp】命令,還有其他一些命令,比如:du,dw,db,da 等,如果想瞭解更多,可以查看 Windbg 的幫助文檔,命令是【.hh】。

        2.2、對值類型的轉儲
              對值類型的轉儲非常的簡單,一般通過【dp】命令觀察記憶體地址,從記憶體上提取內容,可以在結束處觀察 esp 指針,當然,如果知道棧地址,我們也可以使用【dp】命令查看內容,效果和查看 esp 是一樣的。

        2.3、對應用類型的轉儲
              如果我們想查看引用類型的內容,我們可以使用【!do】命令,查看應用類型的內容。

        2.4、對數組的轉儲
              C# 數組的記憶體佈局,大概有兩種形式,值類型和引用類型。
              1)值類型數組。
                  結構:同步塊索引,方法表,數組大小,數組元素1,數組元素2......

              2)引用類型數組。
                  結構:同步塊索引,方法表,數組大小,數組元素1,數組元素2......
                                      |      |
                               |------------------+      +--------------------|
                               |                       |
                            同步塊索引、方法表、內容        同步塊索引、方法表、內容

        2.5、對異常的轉儲
              有時候,程式存在未處理的異常導致的崩潰,在事後分析 dump 中要是能找到這個異常,對我們分析和解決問題會有很大的幫助。如果我們想查看異常的詳情,我們可以使用【!pe(print exception)】命令,列印異常的詳情,當然也可以使用【!do】命令查看更詳細的異常內容。

三、調試過程
    廢話不多說,這一節是具體的調試操作的過程,又可以說是眼見為實的過程,在開始之前,我還是要啰嗦兩句,這一節分為兩個部分,第一部分是測試的源碼部分,沒有代碼,當然就談不上測試了,調試必須有載體。第二部分就是根據具體的代碼來證實我們學到的知識,是具體的眼見為實。
    1、測試源碼
        1.1、Example_6_1_1
 1 namespace Example_6_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var person = new Person();
 8 
 9             Console.ReadLine();
10         }
11     }
12 
13     internal class Person
14     {
15         public int Age = 20;
16 
17         public string Name = "jack";
18     }
19 }
View Code
        1.2、Example_6_1_2
 1 namespace Example_6_1_2
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Debugger.Break();
 8 
 9             int a = 10;
10             int b = 11;
11             int c = 12;
12             int d = 13;
13         }
14     }
15 }
View Code
        1.3、Example_6_1_3
 1 namespace Example_6_1_3
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             int[] intArr = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
 8 
 9             string[] stringArr = { "1", "2", "3", "4", "5" };
10 
11             Debugger.Break();
12         }
13     }
14 }
View Code
        1.4、Example_6_1_4
 1 namespace Example_6_1_4
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine($"請輸入一個能夠整除的數字:");
 8             var num = Console.ReadLine();
 9 
10             var ret = 10 / Convert.ToInt32(num);
11         }
12     }
13 }
View Code
    2、眼見為實
        項目的所有操作都是一樣的,所以就在這裡說明一下,但是每個測試例子,都需要重新啟動,並載入相應的應用程式,載入方法都是一樣的。流程如下我們編譯項目,打開 Windbg,點擊【文件】----》【launch executable】附加程式,打開調試器的界面,程式已經處於中斷狀態。我們需要使用【g】命令,繼續運行程式,然後到達指定地點停止後,我們可以點擊【break】按鈕,就可以調試程式了。有時候可能需要切換到主線程,可以使用【~0s】命令。

        2.1、記憶體轉儲
            測試代碼:Example_6_1_1
            我們首先去托管堆中查找一下 Person 對象。【dp】命令的 p 是 pointer,指針的意思。
1 0:006> !dumpheap -type Person
2  Address       MT     Size
3 031424c8 013d4dec       16     
4 
5 Statistics:
6       MT    Count    TotalSize Class Name
7 013d4dec        1           16 Example_6_1_1.Person
8 Total 1 objects

            031424c8(Person 對象的指針地址) 013d4dec(Person 方法表的地址)

 1 0:006> !dumpobj /d 031424c8
 2 Name:        Example_6_1_1.Person
 3 MethodTable: 013d4dec
 4 EEClass:     013d12f0
 5 Size:        16(0x10) bytes
 6 File:        E:\Visual Studio 2022\Source\Projects\....\Example_6_1_1\bin\Debug\Example_6_1_1.exe
 7 Fields:
 8       MT    Field   Offset                 Type VT     Attr    Value Name
 9 706342a8  4000001        8         System.Int32  1 instance       20 Age
10 706324e4  4000002        4        System.String  0 instance 031424d8 Name

 

            上面兩段代碼,紅色標註的就是 Person 對象的方法表的地址,他們是一樣的。我們有了對象的地址,對象的地址其實就是類型句柄的地址,也就是知道通過快索引的地址,只要減去 0x4就可以,說明一下,每個引用類型都包含【同步塊索引】和【類型句柄】

 

1 0:006> dp 031424c8-0x4 l4
2 031424c4  00000000 013d4dec 031424d8 00000014

 

             00000000 就是同步塊索引,後面的 013d4dec 就是方法表的地址,最後兩項就是 Person 具體的值。00000014 就是十進位的20,也就是 Person 對象的 Age 欄位的值。031424d8 就是 Person 對象的 Name 的值。我們來驗證。

 1 0:006> !do 031424d8
 2 Name:        System.String
 3 MethodTable: 706324e4
 4 EEClass:     70737690
 5 Size:        22(0x16) bytes
 6 File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 7 String:      jack
 8 Fields:
 9       MT    Field   Offset                 Type VT     Attr    Value Name
10 706342a8  4000283        4         System.Int32  1 instance        4 m_stringLength
11 70632c9c  4000284        8          System.Char  1 instance       6a m_firstChar
12 706324e4  4000288       70        System.String  0   shared   static Empty
13     >> Domain:Value  01472260:NotInit  <<
14 
15 
16 0:006> ? 00000014
17 Evaluate expression: 20 = 00000014

              紅色標註的就是具體的值。我們可以繼續使用【dp】命令,查看 name 的詳情。

1 0:006> dp 031424d8 l4
2 031424d8  706324e4 00000004 0061006a 006b0063

              031424d8(字元串 Name 的地址) 706324e4(name 的方法表的地址) 00000004(字元串的長度) 0061006a 006b0063(這兩項就是具體的值),字元串的長度,我們可以通過 name 的地址加上 0x4,就可以得到字元串的長度。

1 0:006> dp 031424d8+0x4 l4
2 031424dc  00000004 0061006a 006b0063 00000000

 

              為什麼是加上 0x4,因為我們在使用【!do】命令,查看 name 詳情的時候,m_stringLength 的偏移量是4。      
              

              0061006a(看地址,我們是從低地址往高地址看),所以我們先看 006a,然後再查看 0061,006b0063這個地址也是一樣的。我們可以使用【du】命令查看它的內容,u就是 Unicode。006a 就是 j,0061就是a,0063就是 c,006b就是k。

1 0:006> du 031424d8+0x8
2 031424e0  "jack"

              為什麼加 0x8,因為偏移量是8。


        2.2、對值類型的轉儲。
            測試代碼:Example_6_1_2
            我們首先找到棧地址,可以使用【!clrstack】命令,有了棧地址,我們可以使用【bp】命令在該地址上設置斷點。
1 0:000> !clrstack 
2 OS Thread Id: 0x358c (0)
3 Child SP       IP Call Site
4 007af2e0 7566f262 [HelperMethodFrame: 007af2e0] System.Diagnostics.Debugger.BreakInternal()
5 007af35c 7146f195 System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 91]
6 007af384 029e0879 Example_6_1_2.Program.Main(System.String[]) [E:\Visual Studio 2022\Source\Projects\...\Example_6_1_2\Program.cs @ 9]
7 007af504 71daf036 [GCFrame: 007af504] 

            紅色標註的【029e0879】就是【Program.Main】方法的棧地址,然後我們設置斷點,【g】繼續運行。

1 0:000> bp 029e0879
2 0:000> g
3 Breakpoint 0 hit
4 eax=00000000 ebx=007af434 ecx=72227ced edx=00b239b0 esi=00000000 edi=007af3b0
5 eip=029e0879 esp=007af384 ebp=007af398 iopl=0         nv up ei pl zr na pe nc
6 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
7 Example_6_1_2!COM+_Entry_Point <PERF> (Example_6_1_2+0x23d0879):
8 029e0879 90              nop

            在斷點處暫停,如圖:
            

            然後,使用【g】命令,繼續運行。

1 0:000> g
2 Breakpoint 1 hit
3 eax=00000000 ebx=007af434 ecx=72227ced edx=00b239b0 esi=00000000 edi=007af3b0
4 eip=029e0896 esp=007af384 ebp=007af398 iopl=0         nv up ei pl zr na pe nc
5 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
6 Example_6_1_2!COM+_Entry_Point <PERF> (Example_6_1_2+0x23d0896):
7 029e0896 90              nop

            然後,我們使用【dp esp】命令查看詳細,當然我們可以可以使用【dp 007af384】,dp 命令後跟的是棧地址,都是可以的。

1 0:000> dp esp
2 007af384  0000000d 0000000c 0000000b 0000000a
3 007af394  02a024bc 007af3a4 71daf036 00b239b0
4 007af3a4  007af3f8 71db22da 007af434 007af3e8
5 007af3b4  71ea23d0 007af504 71db2284 b7ed3afc
6 007af3c4  007af544 007af4c8 007af47c 71dbec59
7 007af3d4  007af434 00000000 b7ed3afc 007af3b0
8 007af3e4  007af4c8 007af574 71ea09b0 c64cea1c
9 007af3f4  00000001 007af460 71db859b 00000000

            紅色標註的就是變數賦的值。彙編代碼如下:

            

            當然,我們也可以通過【!clrstack -l】命令也可以找到所有局部變數。
 1 0:000> !clrstack -l
 2 OS Thread Id: 0x358c (0)
 3 Child SP       IP Call Site
 4 007af384 029e0896 Example_6_1_2.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_2\Program.cs @ 15]
 5     LOCALS:
 6         0x007af390 = 0x0000000a(a 變數的值,等號前面是棧地址)
 7         0x007af38c = 0x0000000b(a 變數的值,等號前面是棧地址,等不 a 的棧地址減去 0x4)
 8         0x007af388 = 0x0000000c(a 變數的值,等號前面是棧地址,等不 b 的棧地址減去 0x4
 9         0x007af384 = 0x0000000d(a 變數的值,等號前面是棧地址,等不 c 的棧地址減去 0x4
10 
11 007af504 71daf036 [GCFrame: 007af504] 

            棧地址是由高到低分配的,0x007af384 是變數d 的地址,0x007af384+0x4=0x007af388,這個地址就是c 變數的地址,0x007af388+0x4=0x007af38c,0x007af38c這個地址就是 b 變數的地址,0x007af38c+0x4就是 a 變數的地址,也就是 0x007af390


        2.3、對應用類型的轉儲。
            測試代碼:Example_6_1_1
 1 0:000> !clrstack -l
 2 OS Thread Id: 0x38fc (0)
 3 Child SP       IP Call Site
 4 00aff25c 777110fc [InlinedCallFrame: 00aff25c] 
 5 00aff258 70d99b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
 6 
 7 ...
 8 
 9 00aff358 0117088e Example_6_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_1\Program.cs @ 11]
10     LOCALS:
11         0x00aff360 = 0x02bb24c8
12 
13 00aff4dc 71daf036 [GCFrame: 00aff4dc] 

            我們查看線程棧了,找到了局部變數。也找到了地址,我們通過【!do】或者【!dumpobj】查看Person 變數。

 1 0:000> !DumpObj /d 02bb24c8
 2 Name:        Example_6_1_1.Person
 3 MethodTable: 00d14dec
 4 EEClass:     00d112f0
 5 Size:        16(0x10) bytes
 6 File:        E:\Visual Studio 2022\Source\Projects\...\Example_6_1_1\bin\Debug\Example_6_1_1.exe
 7 Fields:
 8       MT    Field   Offset                 Type VT     Attr    Value Name
 9 708f42a8  4000001        8         System.Int32  1 instance       20 Age
10 708f24e4  4000002        4        System.String  0 instance 02bb24d8 Name(紅色又是一個地址)

            紅色的可以在【!do】,我們就可以看到詳情。

 1 0:000> !DumpObj /d 02bb24d8
 2 Name:        System.String
 3 MethodTable: 708f24e4
 4 EEClass:     709f7690
 5 Size:        22(0x16) bytes
 6 File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 7 String:      jack(字元串 name 變數的值)
 8 Fields:
 9       MT    Field   Offset                 Type VT     Attr    Value Name
10 708f42a8  4000283        4         System.Int32  1 instance        4 m_stringLength
11 708f2c9c  4000284        8          System.Char  1 instance       6a m_firstChar
12 708f24e4  4000288       70        System.String  0   shared   static Empty
13     >> Domain:Value  00d6d780:NotInit  <<
            說明:我們有了對象的地址,也可以使用【dp】命令,只不過不太好看。
1 0:000> dp 02bb24c8 l4
2 02bb24c8  00d14dec 02bb24d8 00000014 80000000

            00d14dec 這個地址就是 Person 對象的方法表的地址,02bb24d8 就是name 變數的地址。00000014 就是 age 變數的值。


        2.4、對數組的轉儲。
            測試代碼:Example_6_1_3
           
1)值類型數組
              
我們定義的數組:int[] intArr = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
              
我們先列印出線程棧。
 1 0:000> !clrstack -l
 2 OS Thread Id: 0x2e54 (0)
 3 Child SP       IP Call Site
 4 00afeeec 7566f262 [HelperMethodFrame: 00afeeec] System.Diagnostics.Debugger.BreakInternal()
 5 00afef68 7146f195 System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 91]
 6 
 7 00afef90 00ce0920 Example_6_1_3.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_6_1_3\Program.cs @ 13]
 8     LOCALS:
 9         0x00afef9c = 0x02b82518(int 數組,值類型數組)
10         0x00afef98 = 0x02b82574(string 數組,引用類型的數組)
11 
12 00aff118 71daf036 [GCFrame: 00aff118] 

                我們可以點擊棧地址後面的地址,就可以查看詳情,相當於【!do】命令。

 1 0:000> !DumpObj /d 02b82518
 2 Name:        System.Int32[]
 3 MethodTable: 708f426c
 4 EEClass:     709f805c
 5 Size:        56(0x38) bytes
 6 Array:       Rank 1, Number of elements 11, Type Int32 (Print Array)
 7 Fields:
 8 None
 9 0:000> !DumpObj /d 02b82574
10 Name:        System.String[]
11 MethodTable: 708f2d74
12 EEClass:     709f7820
13 Size:        32(0x20) bytes
14 Array:       Rank 1, Number of elements 5, Type CLASS (Print Array)
15 Fields:
16 None

                使用【dp】命令,查看結果。                

1 0:000> dp 0x02b82518-0x4 L20(為什麼要減去 0x4,因為當前對象的指針指向方法表,每個域是占用4個位元組,L20 的l 是不區分大小寫的)
2 02b82514  00000000 708f426c 0000000b 0000000a
3 02b82524  0000000b 0000000c 0000000d 0000000e
4 02b82534  0000000f 00000010 00000011 00000012
5 02b82544  00000013 00000014 00000000 708f4354
6 02b82554  00000000 00000000 00000000 00000000
7 02b82564  00000000 00000000 00c44dd8 00000000
8 02b82574  708f2d74 00000005 02b824c8 02b824d8
9 02b82584  02b824e8 02b824f8 02b82508 00000000

                00000000 表示的同步塊所有,為0就是什麼數據都沒有,708f426c 就是方法表的地址。0000000b 表示的數組長度,十進位是11,表示有11個元素。此項後面的就是數組元素了。
                  如果想可視化的查看,可以使用命令【!da -details】,內容太多,摺疊了。

  1 0:000> !da -details 0x02b82518
  2 Name:        System.Int32[]
  3 MethodTable: 708f426c
  4 EEClass:     709f805c
  5 Size:        56(0x38) bytes
  6 Array:       Rank 1, Number of elements 11, Type Int32
  7 Element Methodtable: 708f42a8
  8 [0] 02b82520
  9     Name:        System.Int32
 10     MethodTable: 708f42a8
 11     EEClass:     709f8014
 12     Size:        12(0xc) bytes
 13     File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 14     Fields:
 15               MT    Field   Offset                 Type VT     Attr    Value Name
 16         708f42a8  40005a2        0             System.Int32      1     instance           10     m_value
 17 [1] 02b82524
 18     Name:        System.Int32
 19     MethodTable: 708f42a8
 20     EEClass:     709f8014
 21     Size:        12(0xc) bytes
 22     File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
 23     Fields:
 24               MT    Field   Offset                 Type VT     Attr    Value Name
 25         708f42a8  40005a2        0             System.Int32      1     instance           11     m_value
 26 [2] 02b82528
 27     Name:        System.Int32
 28 	   

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

-Advertisement-
Play Games
更多相關文章
  • 字元串操作 1.字元串的翻轉 # 方式一 s = 'hello world' print(s[::-1) # 方式二 from functools import reduce print(reduce(lambda x,y:y+x, s)) 2.判斷字元串是否是迴文 利用字元串翻轉操作可以查看字元串 ...
  • 前言 由於相容性問題,使得我們若想用較新版本的 PyTorch,通過 GPU 方式訓練模型,也得更換較新版本得 CUDA 工具包。然而 CUDA 的版本又與電腦顯卡的驅動程式版本關聯,如果是低版本的顯卡驅動程式安裝 CUDA11 及以上肯定會失敗。 比如 GTX750Ti 或 GTX1050Ti,出 ...
  • 寫在前面 此異常非彼異常,標題所說的異常是業務上的異常。 最近做了一個需求,消防的設備巡檢,如果巡檢發現異常,通過手機端提交,後臺的實時監控頁面實時獲取到該設備的信息及位置,然後安排員工去處理。 因為需要服務端主動向客戶端發送消息,所以很容易的就想到了用WebSocket來實現這一功能。 WebSo ...
  • Volatile 保證可見性 private volatile static Integer num = 0; 使用了volatile關鍵字,即可保證它本身可被其他線程的工作記憶體感知,即變化時也會被同步變化。 不保證原子性 原子性:不可分割 線程A在執行任務時是不可被打擾的,也不能被分割,要麼同時成 ...
  • 原文:juejin.cn/post/7283798251403821056 本文筆者計劃從全局角度來對Mybatis的整體架構及進行一次回顧和總結,希望能幫助你更加透徹的理解Mybatis。 1、前言 MyBatis是一款ORM(Object-Relational Mapping)框架,其主要用於將 ...
  • 問題 運行Springboot測試類時,查詢資料庫裡面數據顯示如下白網頁 程式報如下錯誤 解決方案 Spring Boot應用未能啟動的原因是它沒有找到合適的資料庫配置具體來說,它需要一個數據源(DataSource),但未能在你的配置中找出,也沒有找到任何嵌入式資料庫(H2, HSQL 或 Der ...
  • 哈嘍大家好,我是鹹魚 不知道有沒有小伙伴跟我一樣,剛開始學習 Python 的時候都聽說過 Python 是一種解釋型語言,因為它在運行的時候會逐行解釋並執行,而 C++ 這種是編譯型語言 不過我今天看到了一篇文章,作者提出 Python 其實也有編譯的過程,解釋器會先編譯再執行 不但如此,作者還認 ...
  • 目錄 Welcome to YARP - 1.認識YARP並搭建反向代理服務 Welcome to YARP - 2.配置功能 2.1 - 配置文件(Configuration Files) 2.2 - 配置提供者(Configuration Providers) 2.3 - 配置過濾器(Confi ...
一周排行
    -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 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...