Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 4c89f6c

Browse files
JeremyKuhnetarekgh
authored andcommitted
Add ShellEx support for Windows & fix issues (#20216) (#20273)
* Add ShellEx support for Windows & fix issues (#20216) * Add ShellEx support for Windows & fix issues Adds ShellExecute support for Windows (outside of UAP). Unlike desktop UseShellExecute is not the default. In the process of ding this I ran across two issues: 1- The owned process handle would be disposed after hitting members on Process. Getting the ProcessName, for example, would cause a closed handle exception on the next call that used the handle. 2- The MainWindowTitle import wasn't set to the right charset. In addition the allocated buffer size wasn't correct. Fixed and added debug checks for failed api calls. * Address feedback - Add usings in tests - Handle missing API * Move to new using pattern - Remove newly dead string - Other feedback * More feedback changes * Fix some Http tests for UAP Fixed some of the HttpRequestMessage tests for UAP. HttpRequestMessage has a different default value for the Version field. Added PlatformDetection.IsUap to support this. Implementation of this property was based on investigations for #19907. This property is true on both 'uap' and 'uap-aot' tests. Fixes #19907 * disable for nano (#20289) * Fix UseShellExec tests for Nano (#20293) * Fix UseShellExec for Nano * comment * Fix title test * No notepad on nano
1 parent 25c3843 commit 4c89f6c

20 files changed

+638
-305
lines changed

src/Common/src/Interop/Windows/Interop.Errors.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal partial class Errors
2626
internal const int ERROR_FILE_EXISTS = 0x50;
2727
internal const int ERROR_INVALID_PARAMETER = 0x57;
2828
internal const int ERROR_BROKEN_PIPE = 0x6D;
29+
internal const int ERROR_CALL_NOT_IMPLEMENTED = 0x78;
2930
internal const int ERROR_INSUFFICIENT_BUFFER = 0x7A;
3031
internal const int ERROR_INVALID_NAME = 0x7B;
3132
internal const int ERROR_NEGATIVE_SEEK = 0x83;
@@ -53,6 +54,9 @@ internal partial class Errors
5354
internal const int ERROR_NO_TOKEN = 0x3f0;
5455
internal const int ERROR_DLL_INIT_FAILED = 0x45A;
5556
internal const int ERROR_COUNTER_TIMEOUT = 0x461;
57+
internal const int ERROR_NO_ASSOCIATION = 0x483;
58+
internal const int ERROR_DDE_FAIL = 0x484;
59+
internal const int ERROR_DLL_NOT_FOUND = 0x485;
5660
internal const int ERROR_NOT_FOUND = 0x490;
5761
internal const int ERROR_NON_ACCOUNT_SID = 0x4E9;
5862
internal const int ERROR_NOT_ALL_ASSIGNED = 0x514;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Runtime.InteropServices;
7+
8+
internal partial class Interop
9+
{
10+
internal partial class Shell32
11+
{
12+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
13+
internal unsafe struct SHELLEXECUTEINFO
14+
{
15+
public uint cbSize;
16+
public uint fMask;
17+
public IntPtr hwnd;
18+
public char* lpVerb;
19+
public char* lpFile;
20+
public char* lpParameters;
21+
public char* lpDirectory;
22+
public int nShow;
23+
public IntPtr hInstApp;
24+
public IntPtr lpIDList;
25+
public IntPtr lpClass;
26+
public IntPtr hkeyClass;
27+
public uint dwHotKey;
28+
// This is a union of hIcon and hMonitor
29+
public IntPtr hIconMonitor;
30+
public IntPtr hProcess;
31+
}
32+
33+
internal const int SW_HIDE = 0;
34+
internal const int SW_SHOWNORMAL = 1;
35+
internal const int SW_SHOWMINIMIZED = 2;
36+
internal const int SW_SHOWMAXIMIZED = 3;
37+
38+
internal const int SE_ERR_FNF = 2;
39+
internal const int SE_ERR_PNF = 3;
40+
internal const int SE_ERR_ACCESSDENIED = 5;
41+
internal const int SE_ERR_OOM = 8;
42+
internal const int SE_ERR_DLLNOTFOUND = 32;
43+
internal const int SE_ERR_SHARE = 26;
44+
internal const int SE_ERR_ASSOCINCOMPLETE = 27;
45+
internal const int SE_ERR_DDETIMEOUT = 28;
46+
internal const int SE_ERR_DDEFAIL = 29;
47+
internal const int SE_ERR_DDEBUSY = 30;
48+
internal const int SE_ERR_NOASSOC = 31;
49+
50+
internal const uint SEE_MASK_FLAG_DDEWAIT = 0x00000100;
51+
internal const uint SEE_MASK_NOCLOSEPROCESS = 0x00000040;
52+
internal const uint SEE_MASK_FLAG_NO_UI = 0x00000400;
53+
54+
[DllImport(Libraries.Shell32, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
55+
internal unsafe static extern bool ShellExecuteExW(
56+
SHELLEXECUTEINFO* pExecInfo);
57+
}
58+
}

src/Common/src/Interop/Windows/user32/Interop.GetWindowTextLength.cs renamed to src/Common/src/Interop/Windows/user32/Interop.GetWindowTextLengthW.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal partial class Interop
99
{
1010
internal partial class User32
1111
{
12-
[DllImport(Libraries.User32, EntryPoint = "GetWindowTextLengthW")]
13-
public static extern int GetWindowTextLength(IntPtr hWnd);
12+
[DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)]
13+
public static extern int GetWindowTextLengthW(IntPtr hWnd);
1414
}
1515
}

src/Common/src/Interop/Windows/user32/Interop.GetWindowText.cs renamed to src/Common/src/Interop/Windows/user32/Interop.GetWindowTextW.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ internal partial class Interop
1010
{
1111
internal partial class User32
1212
{
13-
[DllImport(Libraries.User32, EntryPoint = "GetWindowTextW")]
14-
public static extern int GetWindowText(IntPtr hWnd, [Out]StringBuilder lpString, int nMaxCount);
13+
[DllImport(Libraries.User32, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
14+
public static extern int GetWindowTextW(IntPtr hWnd, [Out]StringBuilder lpString, int nMaxCount);
1515
}
1616
}

src/Common/tests/System/PlatformDetection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static partial class PlatformDetection
1818
// do it in a way that failures don't cascade.
1919
//
2020

21+
public static bool IsUap => IsWinRT || IsNetNative;
2122
public static bool IsFullFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase);
2223
public static bool IsNetNative => RuntimeInformation.FrameworkDescription.StartsWith(".NET Native", StringComparison.OrdinalIgnoreCase);
2324

src/System.Diagnostics.Process/System.Diagnostics.Process.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Microsoft Visual Studio Solution File, Format Version 12.00
2-
# Visual Studio 14
3-
VisualStudioVersion = 14.0.25420.1
2+
# Visual Studio 15
3+
VisualStudioVersion = 15.0.26430.4
44
MinimumVisualStudioVersion = 10.0.40219.1
55
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Diagnostics.Process.Tests", "tests\System.Diagnostics.Process.Tests.csproj", "{E1114510-844C-4BB2-BBAD-8595BD16E24B}"
66
ProjectSection(ProjectDependencies) = postProject
@@ -20,8 +20,14 @@ EndProject
2020
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Diagnostics.Process", "ref\System.Diagnostics.Process.csproj", "{98B33275-39D8-4997-867D-04C69C69885E}"
2121
EndProject
2222
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}"
23+
ProjectSection(SolutionItems) = preProject
24+
tests\Configurations.props = tests\Configurations.props
25+
EndProjectSection
2326
EndProject
2427
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}"
28+
ProjectSection(SolutionItems) = preProject
29+
src\PinvokeAnalyzerExceptionList.analyzerdata = src\PinvokeAnalyzerExceptionList.analyzerdata
30+
EndProjectSection
2531
EndProject
2632
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{2E666815-2EDB-464B-9DF6-380BF4789AD4}"
2733
EndProject

src/System.Diagnostics.Process/src/PinvokeAnalyzerExceptionList.analyzerdata

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ user32.dll!IsWindowVisible
3232
user32.dll!PostMessageW
3333
user32.dll!SendMessageTimeoutW
3434
user32.dll!WaitForInputIdle
35+
shell32.dll!ShellExecuteExW

src/System.Diagnostics.Process/src/Resources/Strings.resx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,6 @@
154154
<data name="PendingAsyncOperation" xml:space="preserve">
155155
<value>An async read operation has already been started on the stream.</value>
156156
</data>
157-
<data name="UseShellExecute" xml:space="preserve">
158-
<value>UseShellExecute must always be set to false.</value>
159-
</data>
160157
<data name="InvalidParameter" xml:space="preserve">
161158
<value>Invalid value '{1}' for parameter '{0}'.</value>
162159
</data>
@@ -238,4 +235,13 @@
238235
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
239236
<value>Non-negative number required.</value>
240237
</data>
241-
</root>
238+
<data name="CantStartAsUser" xml:space="preserve">
239+
<value>The Process object must have the UseShellExecute property set to false in order to start a process as a user.</value>
240+
</data>
241+
<data name="CantUseEnvVars" xml:space="preserve">
242+
<value>The Process object must have the UseShellExecute property set to false in order to use environment variables.</value>
243+
</data>
244+
<data name="UseShellExecuteNotSupported" xml:space="preserve">
245+
<value>UseShellExecute is not supported on this platform.</value>
246+
</data>
247+
</root>

0 commit comments

Comments
 (0)