• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dapplo / Dapplo.Windows / 22231723592

20 Feb 2026 04:16PM UTC coverage: 33.416% (-0.07%) from 33.49%
22231723592

push

github

Lakritzator
Small fixes for tests and namespaces

611 of 1948 branches covered (31.37%)

Branch coverage included in aggregate %.

2 of 8 new or added lines in 2 files covered. (25.0%)

231 existing lines in 16 files now uncovered.

1668 of 4872 relevant lines covered (34.24%)

30.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Dapplo.Windows.Messages/WindowsSessionListener.cs
1
// Copyright (c) Dapplo and contributors. All rights reserved.
2
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3

4
#if !NETSTANDARD2_0
5
using System;
6
using System.Runtime.InteropServices;
7
using Dapplo.Windows.Messages.Enumerations;
8
using Dapplo.Windows.Messages.Native;
9

10
namespace Dapplo.Windows.Messages;
11

12
/// <summary>
13
///     A listener for Windows session change events.
14
///     Currently handles lock/unlock and logon/logoff events.
15
///     Other session change events (console connect/disconnect, remote connect/disconnect, etc.) are not exposed but can be added in the future.
16
/// </summary>
17
public class WindowsSessionListener : IDisposable
18
{
19
    private IDisposable _subscription;
20
    private volatile bool _isPaused;
21
    private volatile bool _isDisposed;
UNCOV
22
    private readonly object _lock = new object();
×
23

24
    /// <summary>
25
    ///     Flags for WtsRegisterSessionNotification
26
    /// </summary>
27
    private const int NOTIFY_FOR_THIS_SESSION = 0;
28

29
    /// <summary>
30
    /// See <a href="https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsregistersessionnotification">WTSRegisterSessionNotification function</a>
31
    /// Registers the specified window to receive session change notifications.
32
    /// </summary>
33
    /// <param name="hWnd">Handle to the window to receive session change notifications</param>
34
    /// <param name="dwFlags">Specifies which session notifications to receive</param>
35
    /// <returns>Returns true if successful</returns>
36
    [DllImport("wtsapi32.dll", SetLastError = true)]
37
    private static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags);
38

39
    /// <summary>
40
    /// See <a href="https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsunregistersessionnotification">WTSUnRegisterSessionNotification function</a>
41
    /// Unregisters the specified window so that it receives no further session change notifications.
42
    /// </summary>
43
    /// <param name="hWnd">Handle to the window to stop receiving session change notifications</param>
44
    /// <returns>Returns true if successful</returns>
45
    [DllImport("wtsapi32.dll", SetLastError = true)]
46
    private static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);
47

48
    /// <summary>
49
    ///     Event fired when a session lock or unlock occurs
50
    /// </summary>
51
    public event EventHandler<SessionChangeEventArgs> SessionLockChange;
52

53
    /// <summary>
54
    ///     Event fired when a session logon or logoff occurs
55
    /// </summary>
56
    public event EventHandler<SessionChangeEventArgs> SessionLogonChange;
57

58
    /// <summary>
59
    ///     Starts listening for session change events
60
    /// </summary>
61
    public void Start()
62
    {
UNCOV
63
        if (_isDisposed)
×
64
        {
UNCOV
65
            throw new ObjectDisposedException(nameof(WindowsSessionListener));
×
66
        }
67

UNCOV
68
        lock (_lock)
×
69
        {
UNCOV
70
            if (_subscription != null)
×
71
            {
UNCOV
72
                return; // Already started
×
73
            }
74

UNCOV
75
            if (_isDisposed)
×
76
            {
UNCOV
77
                throw new ObjectDisposedException(nameof(WindowsSessionListener));
×
78
            }
79

UNCOV
80
            _isPaused = false;
×
81

82
            _subscription = SharedMessageWindow.Listen(
×
83
                onSetup: hwnd =>
×
UNCOV
84
                {
×
85
                    if (!WTSRegisterSessionNotification((IntPtr)hwnd, NOTIFY_FOR_THIS_SESSION))
×
UNCOV
86
                    {
×
UNCOV
87
                        throw new InvalidOperationException("Failed to register for session notifications");
×
88
                    }
×
89
                },
×
90
                onTeardown: hwnd => WTSUnRegisterSessionNotification((IntPtr)hwnd)
×
UNCOV
91
            )
×
UNCOV
92
            .Subscribe(m =>
×
UNCOV
93
            {
×
UNCOV
94
                if (_isPaused || m.Msg != (uint)WindowsMessages.WM_WTSSESSION_CHANGE)
×
UNCOV
95
                {
×
UNCOV
96
                    return;
×
97
                }
×
98

×
UNCOV
99
                var eventType = (WtsSessionChangeEvents)(int)m.WParam;
×
UNCOV
100
                var sessionId = (int)m.LParam;
×
UNCOV
101

×
UNCOV
102
                var args = new SessionChangeEventArgs(eventType, sessionId);
×
UNCOV
103

×
UNCOV
104
                switch (eventType)
×
105
                {
×
106
                    case WtsSessionChangeEvents.WTS_SESSION_LOCK:
×
UNCOV
107
                    case WtsSessionChangeEvents.WTS_SESSION_UNLOCK:
×
UNCOV
108
                        SessionLockChange?.Invoke(this, args);
×
UNCOV
109
                        break;
×
UNCOV
110

×
UNCOV
111
                    case WtsSessionChangeEvents.WTS_SESSION_LOGON:
×
UNCOV
112
                    case WtsSessionChangeEvents.WTS_SESSION_LOGOFF:
×
113
                        SessionLogonChange?.Invoke(this, args);
×
UNCOV
114
                        break;
×
115
                }
×
UNCOV
116
            });
×
UNCOV
117
        }
×
118
    }
×
119

120
    /// <summary>
121
    ///     Pauses listening for session change events
122
    /// </summary>
123
    public void Pause()
124
    {
125
        _isPaused = true;
×
126
    }
×
127

128
    /// <summary>
129
    ///     Resumes listening for session change events after being paused
130
    /// </summary>
131
    public void Resume()
132
    {
133
        _isPaused = false;
×
UNCOV
134
    }
×
135

136
    /// <summary>
137
    ///     Stops listening for session change events
138
    /// </summary>
139
    public void Stop()
140
    {
141
        lock (_lock)
×
142
        {
UNCOV
143
            if (_subscription != null)
×
144
            {
UNCOV
145
                _subscription.Dispose();
×
UNCOV
146
                _subscription = null;
×
147
            }
148
            _isPaused = false;
×
UNCOV
149
        }
×
UNCOV
150
    }
×
151

152
    /// <summary>
153
    ///     Disposes the listener and stops listening for events
154
    /// </summary>
155
    public void Dispose()
156
    {
UNCOV
157
        if (_isDisposed)
×
158
        {
UNCOV
159
            return;
×
160
        }
161

UNCOV
162
        _isDisposed = true;
×
UNCOV
163
        Stop();
×
164
        GC.SuppressFinalize(this);
×
UNCOV
165
    }
×
166
}
167
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc