The previous article describes the basic concepts of handles and describes the process of creating file handles in C #. We already know that handles represent Windows internal objects, and file objects are one of them, but there are obviously more other types of objects in the system. This article will briefly describe the classification of Windows objects.
Handles can represent Windows objects in three categories, kernel objects (Kernel object), user objects (GDI object), and GDI objects, and the count of handles, user objects, and GDI objects in the Task Manager in the previous article corresponds to these types of objects. Why do we classify it this way? The reason is that these kinds of objects have different roles for the operating system, and they are managed and referenced in different ways. Kernel objects are primarily used for memory management, process execution, and interprocess communication, and user objects are used for system window management, while GDI objects are used to support graphical interfaces.
A small experiment to observe the change of the handle
Before enumerating the categories of Windows objects, let's look at an experiment on the number of handles, unlike the previous file object's handle, which belongs to the user object in this case. In the process of running the program, the creation and destruction of objects is dynamic, and the number of handles changes dynamically, even the simplest Windows Form program can reflect this intuitively. is a form program that has only text boxes and buttons, and the default input focus is on the text box after the program starts, you can press the TAB key to switch the focus between the text box and the button alternately. When we do this, we can see in Task Manager that the number of user objects is changing between 21 and 20. This number may be different in your environment, but at least it means that there is a user object being created and destroyed during focus switching, which is caret (caret).
Caret is one of the user objects, and this blinking cursor indicates the position of the input. We can create this symbol through the Windows API, customize its style, or set the blink time. When you create a caret, the Windows API does not return its handle because a window can only display one caret, access it through the handle of the window, or, more simply, see which thread is calling these APIs. However, the caret object and its handle are real, even if we do not need to get the handle.
Second, Windows Classification of Objects
As mentioned earlier, Windows objects are divided into kernel objects, user objects, and GDI objects, as well as examples of file objects and caret objects, in addition to many other types of objects. For a complete list of Windows objects, you can refer to MSDN's description of Object Categories (Windows), which lists the objects for each category, with detailed descriptions for each object, where you can find usage of these objects, and the corresponding Windows APIs, and more. This paper mainly discusses. NET objects and Windows objects, so simply list them here for quick reference.
Kernel objects: access token, change notification, communication device, console input, console screen buffer, desktop, event, event log, file, file map, heap, job, mail slot, module, mutex, pipeline, process, semaphore, socket, thread, timer, timer queue, timer queue timer, Update Resources and window stations.
User objects: accelerator key table, caret, cursor, Dynamic Data exchange session, Hook, Icon, menu, window, and window position.
GDI objects: bitmaps, brushes, device contexts, enhanced metafile, enhanced metafile device contexts, fonts, memory device contexts, metafile, metafile device contexts, color palettes, brushes, and regions.
As mentioned earlier, different categories of objects have different roles and characteristics. Kernel objects are primarily used for memory management, process execution, and interprocess communication. Multiple processes can share the same kernel object (such as files and events), but each process must create or open this object on its own to get its own handle, and specify different access rights, in which case a kernel object is referenced by a handle to multiple processes, and the user object is used for the system's window management, unlike the kernel object, A user object can have only one handle, but the handle is exposed to other processes, so other processes can get and use the handle to access the user object. As an example of a window (Windows) object, a process can get a handle to a Window object created by another process and send various messages to it, which is also a precondition for many automated test tools to be implemented, while GDI objects are used to support a graphical interface and only support a single object individual handle, but unlike user objects, The handle to the GDI object is private to the process.
third, with Windows object that corresponds to the. NET Object
. NET has many types that encapsulate the Windows objects listed above, and we use them with special care to reuse them and destroy them in a timely manner. The following table is an example of some correspondence (note that this is not a full list, nor a strict one by one correspondence), and subsequent articles will discuss some of the important types of usage.
. NET Object |
Handle to a Windows object referenced to |
Classification |
System.Threading.Tasks.Task |
Access tokens |
Kernel objects |
System.IO.FileSystemWatcher |
Change notification |
Kernel objects |
System.IO.FileStream |
File |
Kernel objects |
System.Threading.AutoResetEvent System.Threading.ManualResetEvent System.Xaml.XamlBackgroundReader |
Event |
Kernel objects |
System.Diagnostics.EventLog |
Event Log |
Kernel objects |
System.Threading.Thread |
Thread |
Kernel objects |
System.Threading.Mutex |
Mutex Amount |
Kernel objects |
System.Threading.Semaphore |
Signal Volume |
Kernel objects |
System.Windows.Forms.Cursor |
Cursor |
User objects |
System.Drawing.Icon |
Icon |
User objects |
System.Windows.Forms.Menu |
Menu |
User objects |
System.Windows.Forms.Control |
Window |
User objects |
System.Windows.Forms.Control System.Drawing.BufferedGraphicsManager System.Drawing.Bitmap |
Bitmap |
GDI objects |
System.Drawing.SolidBrush System.Drawing.TextureBrush |
painting brushes |
GDI objects |
System.Drawing.Font |
Font |
GDI objects |
Iv.. NET exceptions and phenomena related to handle leaks in
The previous article referred to the restriction of a handle, and when the number of handles to a process or system reaches the upper limit, the program runs with an exception. Common errors are System.ComponentModel.Win32Exception "Error creating window Handle", or "insufficient storage space to handle this command", etc., and memory tends to grow significantly when errors occur. If the system-level handle limit is reached and other programs run, the system may not be able to open any new menus and windows, and the window will appear incomplete. At this time to crawl dump and terminate the process of leaking handles, the system is often immediately back to normal.
V. Example of the first handle leak
The following example code contains the problem of a handle leak, in order to demonstrate convenience, the implementation of the code is the most simplistic, the rationality of the design will not be considered. The code simulates a scenario in which the program consists of a datareceiver constantly fetching real-time data from a data source, and Datareceiver initiates a dataanalyzer to analyze the data at a timed time. Imagine that the program has a dedicated sub-window to display this data, when the child window is temporarily closed, the real-time data acquisition and analysis process can also be temporarily terminated. During the long run of the program, the child window may be closed and opened multiple times by the user, so Datareceiver will be created several times, after the program starts the code simulation Datareceiver was created and dispose of 1000 times.
Using system;using system.threading;using system.threading.tasks;using system.windows.forms;using Timer = System.threading.timer;namespace leakexample{public partial class Form1:form {public Form1 () { InitializeComponent (); The datareceiver is created multiple times during the run of the Simulation Program Task.Factory.StartNew (() = {for (int i = 0; i <; i+ +) {using (IDisposable receiver = new Datareceiver ()) { Thread.Sleep (100); } } }); }} public class Datareceiver:idisposable {private Timer datasynctimer = null; Private Ianalyzer analyzer = null; private bool isdisposed = false; Public Datareceiver (): This (new Dataanalyzer ()) {} public datareceiver (Ianalyzer Dataanalyzer) { Datasynctimer = new Timer (GetData, NULL, 0, 500); Analyzer = Dataanalyzer; Analyzer. Start (); private void GetData (object state) {//Get data and put in cache} public void Dispose () {if (isdisposed) return; if (Datasynctimer! = null) {datasynctimer.dispose (); } isdisposed = true; }} public interface Ianalyzer {void Start (); void Stop (); } public class Dataanalyzer:ianalyzer {private Timer analyzetimer = null; public void Start () {Analyzetimer = new Timer (doanalyze, NULL, 0, 1000); } public void Stop () {if (Analyzetimer! = null) {Analyzetimer.dispose (); }} private void Doanalyze (object state) {//data is obtained from the cache and analyzed, taking 600 milliseconds thread.sleep ( 600); } }}
When you run this program, you can observe from the Task manager that the number of handles continues to grow and ultimately stabilize at a higher number. Although Datareceiver is created several times, the number of handles increases eventually far beyond the number of times it was created. Because of the simplicity of the code, you may well have seen the problem, but in a real project, it is difficult to see the root cause of the problem in a single glance because of the complexity of the software architecture and business logic code. The next article will start with this example, combining tools to analyze the cause of the problem and discuss how the timer works.
. NET object with Windows handle (ii): Examples of handle classifications and. NET handle leaks