Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<!-- Not a bug, these APIs are in the allowed list but under kernel32 and we need it under normaliz.dll -->
normaliz.dll!IsNormalizedString
normaliz.dll!NormalizeString

<!-- On Nano these are stubs that return failure -->
user32.dll!GetProcessWindowStation
user32.dll!GetUserObjectInformationW
Original file line number Diff line number Diff line change
Expand Up @@ -225,5 +225,7 @@ internal partial class User32

public const int UOI_FLAGS = 1;

public const int HWND_BROADCAST = 0xffff;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ internal partial class Interop
internal partial class User32
{
[DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
public static extern bool GetUserObjectInformationW(IntPtr hObj, int nIndex, ref USEROBJECTFLAGS pvBuffer, int nLength, ref int lpnLengthNeeded);
public static extern unsafe bool GetUserObjectInformationW(IntPtr hObj, int nIndex, void* pvBuffer, uint nLength, ref uint lpnLengthNeeded);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,32 +77,24 @@ private static unsafe bool UserInteractive
{
get
{
if (Environment.OSVersion.Platform == System.PlatformID.Win32NT)
IntPtr hwinsta = Interop.User32.GetProcessWindowStation();
if (hwinsta != IntPtr.Zero && s_processWinStation != hwinsta)
{
IntPtr hwinsta = IntPtr.Zero;

hwinsta = Interop.User32.GetProcessWindowStation();
if (hwinsta != IntPtr.Zero && s_processWinStation != hwinsta)
{
s_isUserInteractive = true;
s_isUserInteractive = true;

int lengthNeeded = 0;
Interop.User32.USEROBJECTFLAGS flags = default;
uint dummy = 0;
Interop.User32.USEROBJECTFLAGS flags = default;

if (Interop.User32.GetUserObjectInformationW(hwinsta, Interop.User32.UOI_FLAGS, ref flags, sizeof(Interop.User32.USEROBJECTFLAGS), ref lengthNeeded))
if (Interop.User32.GetUserObjectInformationW(hwinsta, Interop.User32.UOI_FLAGS, &flags, (uint)sizeof(Interop.User32.USEROBJECTFLAGS), ref dummy))
{
if ((flags.dwFlags & Interop.User32.WSF_VISIBLE) == 0)
{
if ((flags.dwFlags & Interop.User32.WSF_VISIBLE) == 0)
{
s_isUserInteractive = false;
}
s_isUserInteractive = false;
}
s_processWinStation = hwinsta;
}
s_processWinStation = hwinsta;
}
else
{
s_isUserInteractive = true;
}

return s_isUserInteractive;
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -1325,11 +1325,22 @@
<Compile Include="$(CommonPath)Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs">
<Link>Common\Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs</Link>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.Constants.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.Constants.cs">
<Link>Common\Interop\Windows\User32\Interop.Constants.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.LoadString.cs">
<Link>Common\Interop\Windows\User32\Interop.LoadString.cs</Link>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\User32\Interop.SendMessageTimeout.cs" />
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetProcessWindowStation.cs">
<Link>Common\Interop\Windows\User32\Interop.GetProcessWindowStation.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.GetUserObjectInformation.cs">
<Link>Common\Interop\Windows\User32\Interop.GetUserObjectInformation.cs</Link>
</Compile>
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs">
<Link>Common\Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs</Link>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Internal\Win32\RegistryKey.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public static partial class Environment
{
private static Func<string, object>? s_directoryCreateDirectory;

public static bool UserInteractive => true;

private static string CurrentDirectoryCore
{
get => Interop.Sys.GetCwd();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,28 @@ public static string SystemDirectory
}
}

public static unsafe bool UserInteractive
{
get
{
// Per documentation of GetProcessWindowStation, this handle should not be closed
IntPtr handle = Interop.User32.GetProcessWindowStation();
if (handle != IntPtr.Zero)
{
Interop.User32.USEROBJECTFLAGS flags = default;
uint dummy = 0;
if (Interop.User32.GetUserObjectInformationW(handle, Interop.User32.UOI_FLAGS, &flags, (uint)sizeof(Interop.User32.USEROBJECTFLAGS), ref dummy))
{
return ((flags.dwFlags & Interop.User32.WSF_VISIBLE) == 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you forgot to invert this logic. Also, should we add back the static property like we had in desktop?
https://referencesource.microsoft.com/#mscorlib/system/environment.cs,1435

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ack - thank you @ericstj !

Re the static, @jkotas pointed out that there may be a way to change a process to/from interactive - although I didn't do it successfully myself. See mention here #770 (comment) I saw no reason to cache the value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll make the simple fix for now. FWIW I'm now able to reproduce the test failure, so I can confirm fail/then-fix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would explain #1724 also. I was not albe to run the service controller tests locally because of #1262. I tested locally by hand instead, and apparently did so before I made this mistake.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments crossed. Great, I was about to do it, but apparently you've already got those tests working so you can verify it as well. thanks

}
}

// If we can't determine, return true optimistically
// This will include cases like Windows Nano which do not expose WindowStations
return true;
}
}

public static unsafe long WorkingSet
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ public static OperatingSystem OSVersion
}
}

public static bool UserInteractive => true;

public static Version Version
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,23 @@ public void SystemPageSize_Valid()
}

[Fact]
public void UserInteractive_True()
[PlatformSpecific(TestPlatforms.AnyUnix)]
public void UserInteractive_Unix_True()
{
Assert.True(Environment.UserInteractive);
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public void UserInteractive_Windows_DoesNotThrow()
{
var dummy = Environment.UserInteractive; // Does not throw
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindowsNanoServer))]
public void UserInteractive_WindowsNano()
{
// Defaults to true on Nano, because it doesn't expose WindowStations
Assert.True(Environment.UserInteractive);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,16 @@ public void TestOnExecuteCustomCommand()
ServiceController controller = ConnectToServer();

controller.ExecuteCommand(128);
Assert.Equal(128, _testService.GetByte());
// Response from test service:
// 128 => Environment.UserInteractive == false
// 129 => Environment.UserInteractive == true
//
// On Windows Nano and other SKU that do not expose Window Stations, Environment.UserInteractive
// will always return true, even within a service process.
// Otherwise, we expect it to be false.
// (This is the only place we verify Environment.UserInteractive can return false)
byte expected = PlatformDetection.HasWindowsShell ? (byte)128 : (byte)129;
Assert.Equal(expected, _testService.GetByte());

controller.Stop();
Assert.Equal((int)PipeMessageByteCode.Stop, _testService.GetByte());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ protected override void OnContinue()
protected override void OnCustomCommand(int command)
{
base.OnCustomCommand(command);

if (Environment.UserInteractive) // see ServiceBaseTests.TestOnExecuteCustomCommand()
command++;

WriteStreamAsync(PipeMessageByteCode.OnCustomCommand, command).Wait();
}

Expand Down