/// single-threaded or multi-threaded apartment.
/// </summary>
#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
- private bool TrySetApartmentStateUnchecked(ApartmentState state)
+ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
{
ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state);
if (retState != state)
{
+ if (throwOnError)
+ {
+ string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState);
+ throw new InvalidOperationException(msg);
+ }
+
return false;
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern int SetApartmentStateNative(int state);
#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT
- private static bool TrySetApartmentStateUnchecked(ApartmentState state)
+ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
{
- return state == ApartmentState.Unknown;
+ if (state != ApartmentState.Unknown)
+ {
+ if (throwOnError)
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
+ }
+
+ return false;
+ }
+
+ return true;
}
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
try
{
- process.WaitForInputIdle(); // Give the file a chance to load
- Assert.Equal("notepad", process.ProcessName);
-
- // On some Windows versions, the file extension is not included in the title
- Assert.StartsWith(Path.GetFileNameWithoutExtension(tempFile), process.MainWindowTitle);
+ VerifyNotepadMainWindowTitle(process, tempFile);
}
finally
{
try
{
- process.WaitForInputIdle(); // Give the file a chance to load
- Assert.Equal("notepad", process.ProcessName);
-
- if (PlatformDetection.IsInAppContainer)
- {
- Assert.Throws<PlatformNotSupportedException>(() => process.MainWindowTitle);
- }
- else
- {
- // On some Windows versions, the file extension is not included in the title
- Assert.StartsWith(Path.GetFileNameWithoutExtension(tempFile), process.MainWindowTitle);
- }
+ VerifyNotepadMainWindowTitle(process, tempFile);
}
finally
{
try
{
- process.WaitForInputIdle(); // Give the file a chance to load
- Assert.Equal("notepad", process.ProcessName);
-
- // On some Windows versions, the file extension is not included in the title
- Assert.StartsWith(Path.GetFileNameWithoutExtension(tempFile), process.MainWindowTitle);
+ VerifyNotepadMainWindowTitle(process, tempFile);
}
finally
{
}
}
}
+
+ private void VerifyNotepadMainWindowTitle(Process process, string filename)
+ {
+ // On some Windows versions, the file extension is not included in the title
+ string expected = Path.GetFileNameWithoutExtension(filename);
+
+ process.WaitForInputIdle(); // Give the file a chance to load
+ Assert.Equal("notepad", process.ProcessName);
+
+ // Notepad calls CreateWindowEx with pWindowName of empty string, then calls SetWindowTextW
+ // with "Untitled - Notepad" then finally if you're opening a file, calls SetWindowTextW
+ // with something similar to "myfilename - Notepad". So there's a race between input idle
+ // and the expected MainWindowTitle because of how Notepad is implemented.
+ string title = process.MainWindowTitle;
+ int count = 0;
+ while (!title.StartsWith(expected) && count < 500)
+ {
+ Thread.Sleep(10);
+ process.Refresh();
+ title = process.MainWindowTitle;
+ count++;
+ }
+
+ Assert.StartsWith(expected, title);
+ }
}
}
protected Process CreateDefaultProcess()
{
+ if (_process != null)
+ throw new InvalidOperationException();
+
_process = CreateProcessLong();
_process.Start();
+ AddProcessForDispose(_process);
return _process;
}
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [OuterLoop("As written, takes 30 seconds")]
public async Task WaitAsyncForProcess()
{
Process p = CreateSleepProcess(WaitInMS);
<value>An attempt was made to transition a task to a final state when it had already completed.</value>
</data>
<data name="Thread_ApartmentState_ChangeFailed" xml:space="preserve">
- <value>Failed to set the specified COM apartment state.</value>
+ <value>Failed to set the specified COM apartment state. Current apartment state '{0}'.</value>
</data>
<data name="Thread_GetSetCompressedStack_NotSupported" xml:space="preserve">
<value>Use CompressedStack.(Capture/Run) instead.</value>
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StandardOleMarshalObject.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Thread.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Win32.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StandardOleMarshalObject.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Unix.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Threading\Thread.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
</ItemGroup>
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Threading
-{
- public sealed partial class Thread
- {
- private static Exception GetApartmentStateChangeFailedException() => new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
- }
-}
+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System.Threading
-{
- public sealed partial class Thread
- {
- private static Exception GetApartmentStateChangeFailedException() =>
- new InvalidOperationException(SR.Thread_ApartmentState_ChangeFailed);
- }
-}
[SupportedOSPlatform("windows")]
public void SetApartmentState(ApartmentState state)
{
- if (!TrySetApartmentState(state))
- {
- throw GetApartmentStateChangeFailedException();
- }
+ SetApartmentState(state, throwOnError:true);
}
public bool TrySetApartmentState(ApartmentState state)
{
+ return SetApartmentState(state, throwOnError:false);
+ }
+
+ private bool SetApartmentState(ApartmentState state, bool throwOnError)
+ {
switch (state)
{
case ApartmentState.STA:
throw new ArgumentOutOfRangeException(nameof(state), SR.ArgumentOutOfRange_Enum);
}
- return TrySetApartmentStateUnchecked(state);
+ return SetApartmentStateUnchecked(state, throwOnError);
}
[Obsolete("Thread.GetCompressedStack is no longer supported. Please use the System.Threading.CompressedStack class")]
RemoteExecutor.Invoke(() =>
{
Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState());
- Assert.Throws<InvalidOperationException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA));
+ AssertExtensions.ThrowsContains<InvalidOperationException>(() => Thread.CurrentThread.SetApartmentState(ApartmentState.STA), "MTA");
Thread.CurrentThread.SetApartmentState(ApartmentState.MTA);
}).Dispose();
}
return YieldInternal();
}
- private static bool TrySetApartmentStateUnchecked(ApartmentState state) => state == ApartmentState.Unknown;
+ private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
+ {
+ if (state != ApartmentState.Unknown)
+ {
+ if (throwOnError)
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
+ }
+
+ return false;
+ }
+
+ return true;
+ }
private ThreadState ValidateThreadState()
{