forked from microsoft/winget-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWinGetServerInstance.cs
More file actions
196 lines (171 loc) · 7.51 KB
/
Copy pathWinGetServerInstance.cs
File metadata and controls
196 lines (171 loc) · 7.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// -----------------------------------------------------------------------------
// <copyright file="WinGetServerInstance.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
// -----------------------------------------------------------------------------
namespace WinGetTestCommon
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
/// <summary>
/// Represents an instance of a Windows Package Manager (WinGet) server.
/// </summary>
public class WinGetServerInstance
{
/// <summary>
/// The name of the executable for the COM server.
/// </summary>
public const string ServerExecutableName = "WindowsPackageManagerServer";
/// <summary>
/// The package family name for the development package.
/// </summary>
public const string DevelopmentPackageFamilyName = "WinGetDevCLI_8wekyb3d8bbwe";
/// <summary>
/// The window name for the COM server message window.
/// </summary>
public const string TargetWindowName = "WingetMessageOnlyWindow";
/// <summary>
/// Gets the process for the server.
/// </summary>
public required Process Process { get; init; }
/// <summary>
/// Gets a value indicating whether the current server has an associated window.
/// </summary>
public bool HasWindow
{
get
{
return EnumerateWindowHandles(TargetWindowName).Count > 0;
}
}
/// <summary>
/// Sends a specified message to a window.
/// </summary>
/// <param name="message">The message to be sent to the window.</param>
/// <returns>True to indicate that the message was sent and processed within the timeout; false otherwise.</returns>
public bool SendMessage(WindowMessage message)
{
const int TRUE = 0x1;
const int ENDSESSION_CLOSEAPP = 0x1;
const uint SMTO_ABORTIFHUNG = 0x0002;
const uint TIMEOUT_MS = 5000;
var windowHandles = EnumerateWindowHandles(TargetWindowName);
if (windowHandles.Count > 1)
{
throw new InvalidOperationException($"Target process has more than one window named `{TargetWindowName}`");
}
foreach (var hWnd in windowHandles)
{
IntPtr result;
bool success;
switch (message)
{
case WindowMessage.Close:
success = SendMessageTimeout(hWnd, (uint)message, IntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, TIMEOUT_MS, out result) != IntPtr.Zero;
break;
case WindowMessage.QueryEndSession:
success = SendMessageTimeout(hWnd, (uint)message, IntPtr.Zero, (IntPtr)ENDSESSION_CLOSEAPP, SMTO_ABORTIFHUNG, TIMEOUT_MS, out result) != IntPtr.Zero;
break;
case WindowMessage.EndSession:
success = SendMessageTimeout(hWnd, (uint)message, (IntPtr)TRUE, (IntPtr)ENDSESSION_CLOSEAPP, SMTO_ABORTIFHUNG, TIMEOUT_MS, out result) != IntPtr.Zero;
break;
default:
throw new NotImplementedException("Unexpected window message");
}
return success;
}
return false;
}
/// <summary>
/// Retrieves an array of all available WinGet server instances.
/// </summary>
/// <returns>
/// An array of <see cref="WinGetServerInstance"/> objects representing the available server instances.
/// The array will be empty if no instances are available.
/// </returns>
public static List<WinGetServerInstance> GetInstances()
{
Process[] processes = Process.GetProcessesByName(ServerExecutableName);
List<WinGetServerInstance> result = new List<WinGetServerInstance>();
foreach (Process process in processes)
{
try
{
string? familyName = GetProcessPackageFamilyName(process);
if (familyName == DevelopmentPackageFamilyName)
{
result.Add(new WinGetServerInstance { Process = process });
}
}
catch
{
// Ignore processes that we can't access or that aren't packaged
}
}
return result;
}
private static string? GetProcessPackageFamilyName(Process process)
{
const int ERROR_INSUFFICIENT_BUFFER = 122;
int length = 0;
int result = GetPackageFamilyName(process.Handle, ref length, null);
if (result == ERROR_INSUFFICIENT_BUFFER)
{
var sb = new System.Text.StringBuilder(length);
result = GetPackageFamilyName(process.Handle, ref length, sb);
if (result == 0)
{
return sb.ToString();
}
}
return null;
}
private List<IntPtr> EnumerateWindowHandles(string windowName)
{
List<IntPtr> windowHandles = new List<IntPtr>();
int processId = Process.Id;
bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam)
{
GetWindowThreadProcessId(hWnd, out int windowProcessId);
if (windowProcessId == processId)
{
// Get the window title
var sb = new System.Text.StringBuilder(256);
int length = GetWindowText(hWnd, sb, sb.Capacity);
if (length > 0 && sb.ToString() == windowName)
{
windowHandles.Add(hWnd);
}
}
return true;
}
EnumWindows(EnumWindowsProc, IntPtr.Zero);
return windowHandles;
}
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProcDelegate lpEnumFunc, IntPtr lParam);
private delegate bool EnumWindowsProcDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessageTimeout(
IntPtr hWnd,
uint Msg,
IntPtr wParam,
IntPtr lParam,
uint fuFlags,
uint uTimeout,
out IntPtr lpdwResult
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetPackageFamilyName(
IntPtr hProcess,
ref int packageFamilyNameLength,
System.Text.StringBuilder? packageFamilyName
);
}
}