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

dapplo / Dapplo.Windows / 22325004088

23 Feb 2026 09:11PM UTC coverage: 32.847% (-0.2%) from 33.028%
22325004088

push

github

Lakritzator
Merge branch 'master' of https://github.com/dapplo/Dapplo.Windows

619 of 2002 branches covered (30.92%)

Branch coverage included in aggregate %.

1702 of 5064 relevant lines covered (33.61%)

29.45 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

9
namespace Dapplo.Windows.Messages;
10

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

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

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

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

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

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

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

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

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

79
            _isPaused = false;
×
80

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

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

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

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

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

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

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

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

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

161
        _isDisposed = true;
×
162
        Stop();
×
163
        GC.SuppressFinalize(this);
×
164
    }
×
165
}
166
#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