Net 高級調試之九:SOSEX 擴展命令介紹

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

慎用遞歸 起因: 在學習Rust的時候,有一道語法練習題是計算斐波那契數列的第N項的值,這是一道非常簡單的題,但是引發了一個使用遞歸性能問題,考慮到用Rust的人不多,後面的代碼都是C#的,因為C#的語法更大眾一些,更好看懂 第一次解 public static ulong FibonacciNum ...


一、介紹
    今天是《Net 高級調試》的第九篇文章。這篇文章設計的內容挺多的,比如:擴展的斷點支持,如何查找元數據,棧回溯,對象檢查,死鎖檢測等等,內容挺多的。功能特別強大,使用特別方便,但是需要說明一點,這些功能不是 SOS 的功能,是 SOSEX 的擴展功能,但是,這一系列功能只是支持 Net Framework,在 Net Core 跨平臺版本是不支持的。雖然這些都是基礎,如果這些掌握不好,以後的高級調試的道路,也不好走。當然了,第一次看視頻或者看書,是很迷糊的,不知道如何操作,還是那句老話,一遍不行,那就再來一遍,還不行,那就再來一遍,俗話說的好,書讀千遍,其意自現。
     如果在沒有說明的情況下,所有代碼的測試環境都是 Net Framewok 4.8,但是,有些項目需要使用 Net Core 的項目,我會在項目章節里進行說明。好了,廢話不多說,開始我們今天的調試工作。

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

二、基礎知識

    1、SOSEX調試擴展
        不得不說在調試 Net Framework 程式的時候,這個擴展調試組件的使用率是僅次於官方的 SOS 插件的,這個插件的一個特點就是能看到大量的命令是以 m 開頭的,對應於 非托管命令的托管命令的表示。

        這一篇文章只是介紹常用的幾個命令,如果大家想瞭解更多,可以使用【!sosex.help】命令,查看 SOSEX 插件的所有命令。

    2、幾個相當實用的擴展命令。

        2.1、!mbp 下斷點命令。
            相信大家都用過 !bp 命令,這個命令是可以對非托管函數下斷點,如果我們想對托管函數下斷點,就可以使用 【!mbp】 這個命令,它是【bp】命令托管形式,可以給托管方法下斷點。

            【!mbp】命令使用的時候要註意一點,命令後跟的是文件名,包含尾碼名,如果類是獨立的類文件,就寫這個類文件的名稱就可以,如果是多個類包含在一個類文件里,就寫包含多個類文件的名稱就可以。

        2.2、觀察對象佈局。
            一般我們使用【!do】命令觀察一個對象,但是這樣只能觀察到一個平面圖,不能查看到立體的對象,如果想更全面的瞭解一個對象,我們可以使用【!mdt】命令,立體對象是指:引用類型包含引用類型,具有多層,【!do】命令只能查看當前層對象的結構。

        2.3、查找托管堆中指定的字元串。
            這個在 dump 調試的過程中做托管的記憶體搜索很有用,使用【!strings -m:xxx】命令。

        2.4、搜索元數據。
            我們在以前的調試 Notepad 的 SaveFile 方法的時候,使用【x】命令,這裡也有對應的托管版本的命令,就是【!mx】,這個命令對於我們查找函數特別擁有。
        2.5、觀察 Free 塊。
            這是一個比較高級的命令,在分析托管堆碎片的時侯比較有用,能夠提高分析效率。我們可以使用【!mfrag】命令。

        2.6、死鎖檢測。
            大家都知道【死鎖】相互等待對方釋放鎖資源造成的,一旦出現了死鎖的問題,我們可以用手工的方式分析出來的,但是這樣費時費力,SOSEX 提供了一個檢測命令,就是【!dlk(deadlock)】命令,用來檢測死鎖問題。

三、調試過程
    廢話不多說,這一節是具體的調試操作的過程,又可以說是眼見為實的過程,在開始之前,我還是要啰嗦兩句,這一節分為兩個部分,第一部分是測試的源碼部分,沒有代碼,當然就談不上測試了,調試必須有載體。第二部分就是根據具體的代碼來證實我們學到的知識,是具體的眼見為實。

    1、測試源碼
        1.1、Example_9_1_1 項目的源碼
            Program 的代碼:
 1 namespace Example_9_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             int a = 10;
 8             int b = 11;
 9             int c = 12;
10 
11             var person = new Person();
12 
13             person.Show();
14 
15             Console.ReadLine();
16         }
17     }
18 }
View Code
            Address的代碼:
1 namespace Example_9_1_1
2 {
3     public class Address
4     {
5         public string Name { get; set; }
6     }
7 }
View Code
            Person的代碼:
 1 namespace Example_9_1_1
 2 {
 3     public class Person
 4     {
 5         public string Name { get; set; }
 6 
 7         public Address Address { get; set; } = new Address() { Name = "河北省" };
 8 
 9         public void Show()
10         {
11             Console.WriteLine("Hello Person");
12         }
13     }
14 }
View Code
        1.2、Example_9_1_2 項目的源碼
 1 namespace Example_9_1_2
 2 {
 3     internal class Program
 4     {
 5         public static Person person=new Person();
 6         public static Student student = new Student();
 7 
 8         static void Main(string[] args)
 9         {
10             Task.Run(() =>
11             {
12                 lock (person)
13                 {
14                     Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已經進入1111 Person");
15                     Thread.Sleep(1000);
16                     lock (student)
17                     {
18                         Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已經進入1111 Student");
19                         Console.ReadLine();
20                     }
21                 }
22             });
23 
24             Task.Run(() =>
25             {
26                 lock(student)
27                 {
28                     Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已經進入2222 Student");
29                     Thread.Sleep(1000);
30                     lock (person)
31                     {
32                         Console.WriteLine($"tid={Environment.CurrentManagedThreadId},已經進入2222 Person");
33                         Console.ReadLine();
34                     }
35                 }
36             });
37 
38             Console.ReadLine();
39         }
40     }
41 
42     public class Student
43     {
44     }
45 
46     public class Person
47     {
48     }
49 }
View Code
    2、眼見為實
        項目的所有操作都是一樣的,所以就在這裡說明一下,但是每個測試例子,都需要重新啟動,並載入相應的應用程式,載入方法都是一樣的。流程如下:我們編譯項目,打開 Windbg,點擊【文件】----》【launch executable】附加程式,打開調試器的界面,程式已經處於中斷狀態。

        2.1、我們使用【!mbp】命令給托管函數下斷點(CLR沒載入就可以下斷點)。
            調試代碼:Example_9_1_1
            我們的任務:給 Program 類的 Main 方法的第十行:int b = 11;下斷點。
            我們進入 Windbg 界面,不需要運行,就可以直接下斷點,雖然這個時候 CLR 還沒有載入。【!mbp】命令後是程式文件名,必須包含文件擴展名,否則無效。
1 0:000> !mbp Program.cs 10
2 The CLR has not yet been initialized in the process.(CLR還沒有被初始化)
3 Breakpoint resolution will be attempted when the CLR is initialized.

            我們已經成功下了斷點,但是英文提示 CLR 還沒有被載入,我們使用【lm】命令查看一下載入模塊信息,顯示如下。

1 0:000> lm
2 start    end        module name
3 00a20000 00a28000   Example_9_1_1 C (pdb symbols)          C:\ProgramData\Dbg\sym\Example_9_1_1.pdb\...\Example_9_1_1.pdb
4 5bff0000 5c08f000   apphelp    (deferred)             
5 71520000 71572000   MSCOREE    (deferred)             
6 762f0000 76503000   KERNELBASE   (deferred)             
7 76ca0000 76d90000   KERNEL32   (deferred)             
8 76f10000 770b2000   ntdll      (pdb symbols)          C:\ProgramData\Dbg\sym\wntdll.pdb\DBC8C8F74C0E3696E951B77F0BB8569F1\wntdll.pdb

            的確如此,載入的模塊很少,根本沒又看到 CLR 的影子。
            然後,我們【g】一下,就會到我們下的斷點處暫停。效果如圖:
            

            接下來,我們在另外一個類中給一個方法直接下斷點,看看效果怎麼樣,如截圖:
            

             我們執行【!mbp】命令,後面跟類名,包含尾碼名和行號。

1 0:000> !mbp Person.cs 13
2 The CLR has not yet been initialized in the process.
3 Breakpoint resolution will be attempted when the CLR is initialized.

             然後,我們【g】一下,就會到我們下的斷點處暫停。效果如圖:
             

        2.2、我們如何立體的觀察一個對象。
            調試代碼:Example_9_1_1
            我們來看看 Person 對象的結構,它包含 string 類型的 Name 欄位,包含 Address 引用類型,Address 又包含 String 類型的 Name欄位,我們如何立體的查看它的結構呢?
            我們進入到 Windbg 調試界面,如果有斷點存在,我們可以使用【bc *】命令將所有的斷點全部清除,然後再使用【g】命令,運行程式,會在【Console.ReadLine();】這樣代碼處暫停,然後我們點擊【Break】按鈕,就是調試程式了。
            我們現在托管堆中查找一下 Person 對象,有了對象,才可以查看它的結構,使用【!dumpheap -type Person】命令查找。
1 0:006> !dumpheap -type Person
2  Address       MT     Size
3 029324c8 00ee4e28       16     
4 
5 Statistics:
6       MT    Count    TotalSize Class Name
7 00ee4e28        1           16 Example_9_1_1.Person
8 Total 1 objects

            上面列表中有了Person 對象地址和方法表的地址。

1 0:006> !mdt 029324c8
2 029324c8 (Example_9_1_1.Person)
3     <Name>k__BackingField:NULL (System.String)
4     <Address>k__BackingField:029324ec (Example_9_1_1.Address)
5 0:006> !mdt 029324c8 -r
6 029324c8 (Example_9_1_1.Person)
7     <Name>k__BackingField:NULL (System.String)
8     <Address>k__BackingField:029324ec (Example_9_1_1.Address)
9         <Name>k__BackingField:029324d8 (System.String) Length=3, String="河北省"

            如果我們想看【!mdt】如何使用,可以使用【!sosex.help !mdt】,這個輸出太多,摺疊了,可以自行查看。

  1 0:006> !sosex.help !mdt
  2 SOSEX - Copyright 2007-2014 by Steve Johnson - http://www.stevestechspot.com/
  3 To report bugs or offer feedback about SOSEX, please email [email protected]
  4 
  5 mdt
  6 Usage: !sosex.mdt [typename | paramname | localname | MT] [ADDR] [-r[:level]] [-e[:level]] [-start:index] [-count:n]
  7 
  8 Sample usages:
  9 "!sosex.mdt typeName"  (displays the names of the member fields of the specified type)
 10 "!sosex.mdt argName"   (displays the values of the fields of the specified parameter object. -r is valid)
 11 "!sosex.mdt localName" (displays the values of the fields of the specified local variable. -r is valid)
 12 "!sosex.mdt ADDR"      (displays the values of the fields of the object located at ADDR. -r is valid)
 13 "!sosex.mdt MT ADDR"   (displays the values of the fields of the value type specified by MT located at ADDR. -r is valid)
 14 
 15 Displays the fields of the specified object or type, optionally recursively.
 16 
 17 If -r is specified, fields will be displayed recursively down the object graph.  The -r switch is ONLY 
 18 applicable where an address is used, either by passing an address explicitly or when a param/local name 
 19 that resolves to an address is specified.  To limit the levels of the graph that are displayed, append the desired
 20 maximum level, preceded by a colon.  e.g. !mdt myVar -r:3
 21 
 22 The -e switch causes certain collection types to be expanded.  The currently expandable collection types are:
 23 Array, ArrayList, List, Hashtable and Dictionary.  You can also specify a maximum expansion level by appending 
 24 the desired maximum level, preceded by a colon.  e.g. !mdt myColl -e:3.  The minimum (and default) level is 2, 
 25 which means that the collection is expanded to show each element address and it's top level fields.
 26 
 27 If you pass -e for collection expansion, you can also pass -start:index to specify a start index and/or -count:n 
 28 to specify the number of elements to expand.
 29 
 30 Sample of collection expansion:
 31 0:000> !mdt -e ht1
 32 061ab360 (System.Collections.Hashtable)
 33 Count = 2
 34 [0] 061ab3a0
 35     key:061ab3c8 (BOXED System.Int32) BOXEDVAL=0x7b
 36     val:061ab3d4 (BOXED System.Int32) BOXEDVAL=0x4ce
 37 [1] 061ab3b8
 38     key:061ab3e0 (BOXED System.Int32) BOXEDVAL=0x1c8
 39     val:061ab3ec (BOXED System.Int32) BOXEDVAL=0x11d0
 40 
 41 0:000> !mdt -e ht2
 42 061ab3f8 (System.Collections.Hashtable)
 43 Count = 2
 44 [0] 061ab438
 45     key:061ab078 (System.String: "456key")
 46     val:061ab094 (System.String: "456value")
 47 [1] 061ab444
 48     key:061ab03c (System.String: "123key")
 49     val:061ab058 (System.String: "123value")
 50 
 51 The scope frame defaults to zero, but may be overridden via the !mframe command.  IMPORTANT:  The current
 52 scope frame corresponds to the frames listed by the !mk command.
 53 
 54 IMPORTANT NOTE: Sosex distinguishes between numeric and non-numeric strings in order to determine whether an
 55 address or a type/arg/local name is being passed in ambiguous circumstances.  If you want to pass a string value
 56 that could be interpreted as an expression by the debugger, enclose the name in single-quotes.  For example, for
 57 a local named d2, call: !mdt 'd2'.  If the name were not quoted in this circumstance, !mdt would attempt to display
 58 an object located at address 0xd2.
 59 
 60 Frame info for the sample output below:
 61 0:000> !mdv
 62 Frame 0x0: (ConsoleTestApp.ConsoleTestApp.Main(System.String[])):
 63 [A0]:args:0x2371804 (System.String[])
 64 [L0]:theGuid:{29b9c9c8-3751-42be-8c7a-8b92ff499588} VALTYPE (MT=6cd46c60, ADDR=002ff1bc) (System.Guid)
 65 [L1]:d2:0x63718e0 (System.AppDomain)
 66 [L2]:hMod:0x67280000 (System.Int32)
 67 [L3]:dummy:0x23721a4 (System.String) STRVAL="This is "THE" way to test!"
 68 [L4]:numThreads:0x2 (System.Int32)
 69 [L5]:theDate:2008/01/02 03:04:05.678 VALTYPE (MT=6cd49e98, ADDR=002ff1ac) (System.DateTime)
 70 [L6]:ts1:VALTYPE (MT=001e3198, ADDR=002ff1a4) (ConsoleTestApp.TestStruct)
 71 [L7]:ft:0x637544c (ConsoleTestApp.FTEST)
 72 [L8]:g1:<Retrieval mechanism not implemented. The variable type may be a generic type.>
 73 [L9]:g2:<Retrieval mechanism not implemented. The variable type may be a generic type.>
 74 [L10]:rnd:null (System.Random)
 75 [L11]:threads:null (System.Threading.Thread[])
 76 [L12]:i:0x0 (System.Int32)
 77 [L13]:ex:null (System.Exception)
 78 [L14]:CS$0$0000:VALTYPE (MT=001e3198, ADDR=002ff198) (ConsoleTestApp.TestStruct)
 79 [L15]:CS$4$0001:0x0 (System.Boolean)
 80 
 81 
 82 Sample output:
 83 0:000> !mdt theGuid
 84 002ff1bc (System.Guid) {29b9c9c8-3751-42be-8c7a-8b92ff499588} VALTYPE (MT=6cd46c60, ADDR=002ff1bc)
 85 
 86 0:000> !mdt ft
 87 0637544c (ConsoleTestApp.FTEST)
 88    _s1:06375460 (System.String: "String 1")
 89    _s2:06375484 (System.String: "String 2")
 90    _arr:063755c4 (System.String[,,], Elements: 8)
 91 
 92 0:000> !mdt 63718e0
 93 063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)
 94    _rp:063718b4 (System.Runtime.Remoting.Proxies.RemotingProxy)
 95    _stubData:023725dc (BOXED System.IntPtr) VALTYPE (MT=6cd6b114, ADDR=023725e0)
 96    _pMT:6cd6902c (System.IntPtr)
 97    _pInterfaceMT:00000000 (System.IntPtr)
 98    _stub:6d601e70 (System.IntPtr)
 99 
100 0:000> !mdt 63718e0 -r
101 063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)
102    _rp:063718b4 (System.Runtime.Remoting.Proxies.RemotingProxy)
103       _tp:063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)
104          <RECURSIVE>
105       _identity:06371698 (System.Runtime.Remoting.Identity)
106          _flags:0x4 (System.Int32)
107          _tpOrObject:063718e0 (System.Runtime.Remoting.Proxies.__TransparentProxy)
108             <RECURSIVE>
109          _ObjURI:02376858 (System.String: "/f578dbe2_cf0c_4e30_882b_14126f0b1654/kq_om1xc5idnrbhnqnr77cs0_1.rem")
110          _URL:NULL (System.String)
111          _objRef:023769e0 (System.Runtime.Remoting.ObjRef)
112             uri:02376858 (System.String: "/f578dbe2_cf0c_4e30_882b_14126f0b1654/kq_om1xc5idnrbhnqnr77cs0_1.rem")
113             typeInfo:02376cc4 (System.Runtime.Remoting.TypeInfo)
114                serverType:02377ea4 (System.String: "System.AppDomain, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
115                serverHierarchy:NULL (System.Object[])
116                interfacesImplemented:0237808c (System.String[], Elements: 2)
117             envoyInfo:NULL (System.Runtime.Remoting.IEnvoyInfo)
118             channelInfo:0237872c (System.Runtime.Remoting.ChannelInfo)
119                channelData:02378738 (System.Object[], Elements: 1)
120             objrefFlags:0x0 (System.Int32)
121             srvIdentity:023769fc (System.Runtime.InteropServices.GCHandle) VALTYPE (MT=6cd6bcb8, ADDR=023769fc)
122                m_handle:004e11ec (System.IntPtr)
123             domainID:0x2 (System.Int32)
124          _channelSink:06371890 (System.Runtime.Remoting.Channels.CrossAppDomainSink)
125             _xadData:023753c0 (System.Runtime.Remoting.Channels.CrossAppDomainData)
126                _ContextID:023753e0 (BOXED System.Int32) BOXEDVAL=0x69B9B0
127                _DomainID:0x2 (System.Int32)
128                _processGuid:023750bc (System.String: "81174e11_728a_4211_a674_f6f4d79419ba")
129          _envoyChain:063718a8 (System.Runtime.Remoting.Messaging.EnvoyTerminatorSink)
130          _dph:NULL (System.Runtime.Remoting.Contexts.DynamicPropertyHolder)
131          _lease:NULL (System.Runtime.Remoting.Lifetime.Lease)
132       _serverObject:NULL (System.MarshalByRefObject)
133       _flags:0x3 (System.Runtime.Remoting.Proxies.RealProxyFlags)
134       _srvIdentity:063718d0 (System.Runtime.InteropServices.GCHandle) VALTYPE (MT=6cd6bcb8, ADDR=063718d0)
135          m_handle:004e11ec (System.IntPtr)
136       _optFlags:0x7000000 (System.Int32)
137       _domainID:0x2 (System.Int32)
138       _ccm:NULL (System.Runtime.Remoting.Messaging.ConstructorCallMessage)
139       _ctorThread:0x0 (System.Int32)
140    _stubData:023725dc (BOXED System.IntPtr) VALTYPE (MT=6cd6b114, ADDR=023725e0)
141       m_value:ffffffff (System.UIntPtr)
142    _pMT:6cd6902c (System.IntPtr)
143    _pInterfaceMT:00000000 (System.IntPtr)
144    _stub:6d601e70 (System.IntPtr)
145 
146 0:000> !mdt args
147 02371804 (System.String[], Elements: 0)
148 
149 0:000> !mdt 001e3198 002ff1a4 -r
150 002ff1a4 (ConsoleTestApp.TestStruct) VALTYPE (MT=001e3198, ADDR=002ff1a4)
151    Member1:0x4D2 (System.UInt32)
152    Member2:0x162E (System.UInt32)
View Code
        2.3、使用【!strings】命令在托管堆中查找字元串。
            調試代碼:Example_9_1_1
            我們進入到 Windbg 調試界面,如果有斷點存在,我們可以使用【bc *】命令將所有的斷點全部清除,然後再使用【g】命令,運行程式,會在【Console.ReadLine();】這樣代碼處暫停,然後我們點擊【Break】按鈕,就是調試程式了。
            如果我們直接使用【!strings】命令,沒有任何參數,會把托管堆中的所有字元串全部列印出來。
 1 0:006> !strings
 2 Address    Gen    Length   Value
 3 ---------------------------------------
 4 02931228    0          0   
 5 02931254    0         94   E:\Visual Studio 2022\...\Example_9_1_1\bin\Debug\
 6 02931320    0        118   E:\Visual Studio 2022\...\Example_9_1_1\bin\Debug\Example_9_1_1.exe.Config
 7 0293148c    0          4   true
 8 029314a4    0         32   PARTIAL_TRUST_VISIBLE_ASSEMBLIES
 9 02931538    0        111   E:\Visual Studio 2022\..\Example_9_1_1\bin\Debug\Example_9_1_1.exe
10 ...(省略很多)
11 02933d8c    0          8   encoding
12 02933dac    0          6   stream
13 0293421c    0          5   bytes
14 02934234    0          5   chars
15 0293424c    0          9   charCount
16 0293426c    0          9   charIndex
17 0293428c    0          9   byteCount
18 029349fc    0          5   count
19 02934a14    

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

-Advertisement-
Play Games
更多相關文章
  • 如何用Java設計自動售貨機?是大多在高級Java開發人員面試中經常被問到的好問題之一。在典型的編碼面試中,你會得到一個問題描述來開發一個售貨機,在有限的時間內,通常2到3小時內,你需要在Java中編寫設計文檔、工作代碼和單元測試。這種Java面試的一個關鍵優勢是可以一次測試候選人的許多基本技能。為 ...
  • 公眾號「架構成長指南」,專註於生產實踐、雲原生、分散式系統、大數據技術分享。 在之前的幾個教程中,我們學了: 使用 RestTemplate 的 Spring Boot 微服務通信示例 使用 WebClient 的 Spring Boot 微服務通信示例 使用 Spring Cloud Open F ...
  • 學習視頻:【孫哥說Spring5:從設計模式到基本應用到應用級底層分析,一次深入淺出的Spring全探索。學不會Spring?只因你未遇見孫哥】 第七章、反轉控制與依賴註入 1.反轉(轉移)控制(IOC inverse of Control) 控制:對於成員變數賦值的控制權 反轉控制:把對於成員變數 ...
  • 在Java中創建多線程,往往都要通過Thread類來實現,今天學習下Java中創建多線程的三種方法[1]。 1.繼承Thread類 通過繼承 Thread類 實現多線程。 主要方法: 1.void run(), 線程開啟後,方法將被調用執行 2.void start(), 使此線程開始執行, Jav ...
  • Scipy的ODR正交距離回歸(ODR-Orthogonal Distance Regression)模塊,適用於回歸分析時,因變數和自變數之間存在非線性關係的情況。它提高了回歸分析的準確性和穩健性。對於需要解決非線性回歸問題的科研人員和工程師來說,它具有非常重要的意義。 ODR正交距離回歸模塊的作 ...
  • 引言 在WPF應用程式開發中,數據校驗是確保用戶輸入數據的正確性和完整性的重要一環。 之前在做一些參數配置功能時,最是頭疼各種參數校驗,查閱一些資料後,我總結了數據校驗方式有兩種: ValidationRule IDataErrorInfo 接下來分別介紹這兩種校驗方式。 ValidationRul ...
  • 最近. NET 8 的 WPF 推出了 WPF File Dialog改進,這樣無需再引用 Win32 命名空間就可以實現文件夾的選擇與存儲了,算是一個很方便的改進了。順手寫了一個小的 WPF 程式,在使用 Model-View-ViewModel(MVVM) 模式的時候,我不想使用 Prism 等 ...
  • 一:背景 1. 講故事 這些天計劃好好研究下tcp/ip,以及socket套接字,畢竟工控中設計到各種交互協議,如果只是模模糊糊的瞭解,對分析此類dump還是非常不利的,而研究協議最好的入手點就是用抓包工具 wireshark,廢話不多說,這篇通過 wireshark 提取一個小圖片作為入手。 二: ...
一周排行
    -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 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...